Repository navigation
feat(studio): studio.image writes a generated design image into the agent's workspace (ADR 0006 slice 2) - #513
Merged
Merged
Conversation
…gent's workspace ADR 0006, slice 2 of the accepted route. The new host tool `studio.image` takes a prompt (or a job id to poll), runs `model.image` under the calling agent's own identity on the UI thread with the other host calls, hands back a job id when the provider outlasts the call's deadline, and writes the decoded image through the scoped workspace, as `studio.inspect` writes its PNG. Two running jobs per agent and conversation, eight on the device, a ten-minute collection window, no output path argument. The model service's grant callback admits the system agent while developer mode covers it, with `device` as its scope; apps are admitted as before. Release builds are unchanged: the studio is compiled out of them. Docs: walkthrough tool row and a paragraph on cost and bounds (EN + zh), ADR 0006 state table and route item 2, phone README clause (EN + zh); an independent language judge's replacements are applied. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…led and expired jobs, system agent only Review of #513: - every job carries the developer-mode tag and app of the call that started it: once the tag no longer stands, a queued call is not sent, a running one is cancelled at App Hub, an answer is dropped and nothing is written; collecting re-checks the tag, the grant and, for an app peer, its account - a cancelled first call abandons its job: the queued call is skipped or cancelled, the slot freed; the agent never learned the id - expired waits are answered with a timeout and cancelled at App Hub; a queued call the UI thread picks up with under 130 s left is refused - the UI thread passes the reply text on; the job's thread parses it - caps follow the model service: two running per app, four held (finished images count), two per conversation; a finished image is collected once and a concurrent poll sees "being written"; a job is registered after its request is built; the watcher thread is spawned fallibly - the tool is offered to the system agent only; the model service's grant and scope are named functions, the device scope gated on the studio build - tests: the real executor against a fake service under a family of its own (start, poll, collect once, another conversation refused, a cancel abandons, developer mode ending drops a running job and a queued call), pump admission and expiry, bounds, decoding, declarations and admission - docs follow (EN + zh); the language judge's replacements are applied Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…mage tool is not offered to app agents The model grant and scope functions moved the `app-hub`/`native_mobile` cfg off `register_host_services`, so the default desktop build failed; all three now carry it. The peer-offering test expects every studio app tool but `studio.image` for an app's agent. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… cancel through the UI thread Second review of #513 (7e3b69c): - the UI thread owns only the queue, the waits and a cancel list: a queued call is admitted by the developer-mode generation (lock-free) and the time left; cancels and revocations go through the list the pump drains, which skips a queued call or cancels a sent one at App Hub, so the isolate-closed listeners run on the UI thread and a cancel cannot miss a call taken but not yet dispatched - each job's watcher thread notices developer mode moving, re-checks the tag, fails the job and has it cancelled; it then holds the finished image for its window and drops it on expiry or revocation; no sweep on the UI thread - the watcher starts before the call is queued, so a request nobody waits for is never sent; `step` and `finish` check the tag outside the job lock - the relay's grant and the developer test spool exclude `studio.image` for an app's agent and for fixtures (`studio::granted_to`) - tests: the real executor in a child process with the real developer-mode controller (an app's agent refused for the system-only reason, turning developer mode off ends a job in flight), seam tests for the generation-moved paths, the relay's refusal, a `cargo test --release` guard; `SYSTEM` imported by the tests only - docs: a conversation holds two jobs; a finished image is forgotten on revocation (EN + zh, judged) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…all; a revoked job is dropped at once Final check of #513 (6785f4d), three nits: - the developer-mode generation is read once before the watcher starts and carried by the queued call, so a move in between is refused by the UI thread's admission and the call is never sent - a job whose developer mode ended is removed by its watcher instead of being held with a thread waking every second until a poll or the TTL; `finish` reports whether it recorded anything, and `hold` runs only then - the two child-process tests return early in a build without the studio Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Contributor
Author
|
Local CI passed on 77a5719 ( GitHub would run for this PR: desktop.yml, phone.yml, apps.yml. Merged on this local pass (macOS runner queue); GitHub CI runs on the merge commit on main.
Notes:
Native GitHub acceptance passed on the same head: embedded-browser.yml. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ADR 0006, slice 2 of the accepted route:
studio.image, an agent tool that generates one image through the host'smodel.imageservice and writes it into the system agent's workspace. Developer builds only, like the otherstudio.*tools, and offered to the system agent only in this slice.crates/shell/src/host_tools/studio_image.rs. The tool takesprompt(optionallysize,seed,class) to start a generation, orjobalone to poll one. Themodel.imagecall is queued for the UI thread and dispatched fromhost_tools::pump()the way a component's host calls are, under the system agent's own app id, withfrom_sheet/may_promptfalse. The executor's thread only waits, for the call's share of the kernel deadline (timeout_msminus 1.5 s), then returns{job, state: "running"}; a later call with thatjobcollects the image. The UI thread owns only that queue, the waits and a cancel list, and never takes the job table or the developer-mode lock; each job has a watcher thread that parses the answer, notices developer mode moving, and ends the job when it expires. The decoded image is written into the caller's workspace through the samescoped::Workspacepath asstudio.inspect(.studio-<uuid>.png, or.jpgif the service says so) and the reply carriespath,width,height,format.studio::granted_to).additionalProperties: falseand no output path argument.apps.rs). Themodelservice's grant callback (model_grant) also admits the system agent while developer mode covers it (studio::system_caller_admitted), and the scope callback (model_scope) answersdevicefor the system agent in a build that has the studio. Apps are admitted as before, as themselves. Release builds are unchanged:SUPPORTEDis false there.studio.rs,mod.rs).studio.imagejoinsAPP_TOOLSbut is declared to, and executed for, the system agent only (SYSTEM_ONLY); the executor branches to the new module with the call's developer tag;Workspace::output_imageacceptspng/jpg.Not in this PR: the first live call (needs a configured image provider and the owner's go-ahead for the paid call), reference images through
files.pick_photo, and octos's ownimage_generationtool, which still has no backend.Review
The second-review findings on aed37ef (one major: revocation stopped neither the write nor a generation; minors: orphaned cancel, lazy expiry, no
cancel_heapon expiry, wait measured from queue time, reply parsed on the UI thread, cap above the service's bound, budgets adding up across agents, docs; nits) are addressed in 55671d9, as listed above. The second review on 7e3b69c (two minors: the fix had put the job lock and the developer-mode lock on the UI thread;cancel_heapran off the UI thread and could miss a call taken but not yet dispatched; nits: watcher started after queueing, the relay still granting the tool to an app's agent, the test spool, an unused import, a release-build test, the docs' bounds) is addressed in 6785f4d as described above. The final check on 6785f4d found no critical, major or minor issues; its three nits (one developer-mode generation read shared by a job's watcher and its queued call, a revoked job dropped at once instead of held, the release-build guard on the child-process tests) are folded in as b1ceffd. Not taken from the nits:512x512andseedstay offered (the tool's description says some providers refuse them, and the service's refusal reaches the agent); no remaining-budget field is passed through because the image reply carries none.Tests
cd phone && cargo test --features mobile-apps,dev-mode -p octosense-shell, the CI variant--features mobile-apps,toolbox-peers,craft-engines -p octosense-shell host_tools, and the CI set--locked --features mobile-apps -p octosense-shell.cargo test --locked -p octosense-shell(default features, which compile the module out, as they dostudio_bundles),cargo test --locked -p octosense-shell --features app-hub studio,cargo test --locked -p octosense-app-peers.OCTOSENSE_DEV_MODE=all): the real executor refuses an app's agent for the system-only reason before any service call and serves the system agent, and turning developer mode off ends a job in flight. The relay test checks an app's agent naming the tool is refused by the relay.On 77a5719 (all three review rounds, merged with main 8b98596): phone
mobile-apps,dev-mode1287 passed; CI variant 138 passed; CI set--locked --features mobile-apps1287 passed; desktop default 1054 passed; desktop--features app-hub studio47 passed; app-peers 22 passed. Two-run local gate on 77a5719 (tools/ci-local.sh --only all): run A on the Linux build host 53 PASS, 8 SKIPPED (the rom.yml steps that need a JDK, the Android SDK and node, which that host lacks), 0 FAIL; run B on macOS with those tools on PATH 59 PASS, 0 FAIL; bothtarget/ci-local/last.jsonrecords pass and bind to this head. No rom files are touched.🤖 Generated with Claude Code