Repository navigation
feat(bundle): use an installed bundle's envs, agents and artifacts from any bundle, the default agent included - #244
earakely-scale wants to merge 13 commits into
Conversation
Verification on fresh Linux VMs (Ubuntu 24.04, Docker 29), at d5c143f, e3108ee and 61437c2The test packages were real wheels, installed with
"Old" is
Integration: sail_vm, live, at e3108ee (the image change)"Old" is #244 before an image became its build context. Every run deploys on real Sail VMs.
The single-run gaps are VM-start noise. Over 8 alternating rounds, the installed agent's deploy step averaged 28.0 s old and 30.6 s new. arm64, at 61437c2
Providers
🤖 Generated with Claude Code |
d5c143f to
6252839
Compare
…sages Plan.origins maps an entry from another bundle to that bundle; Plan.bundle_of and Plan.path read it, and the planner, ledger, materialize, preflight and run use them where they read an entry's files or name its path. Nothing is pulled from another bundle yet, so origins is empty and behaviour is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…agent default_agent_refs names the agent fields a step dict leaves unset that deploy the configured default. Every core step's unset agent field does, but the rubrics judge's: it declares default_when, true only when the judge deploys its own agent. The importer reads the declarations instead of naming the two step types. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…to the run The planner resolves a store-bound env, agent or artifact id under an installed bundle's id root by parsing and resolving that bundle, once per run, and adding the entity and what it references in its bundle to the plan as the bundle's own entities are added. The reference then points at it, so every check, preflight and write treats it as local. A step falling back to the configured default agent does the same when this bundle or an installed one defines that agent; a store default is still preflight's to check. An installed id takes no version pin, and one its bundle doesn't define, or a bundle that can't be read, is refused where it's named rather than read from the store. A pulled entry is named after its bundle, as <dist>/<bundle>:<path>. run, installed.checked and the importer's check pass installed_bundles. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ndle uses it A ledger row is keyed by the bundle its write comes from, not the bundle being run, so every bundle using an installed bundle's agent, and a run of that bundle itself, reuse one write of it instead of rewriting it as "last written by" another bundle. materializing takes the ids to lock, and a run locks the entities it pulls in with its own, so two runs writing one shared agent take turns and the second reuses the first's write. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e run writes one Outside a bundle run, nothing writes an @Local agent: deploy_agent and the rubrics judge's own deploy now say so when the store doesn't hold one, naming the fields to point elsewhere, instead of only that it isn't found. A store id is reported as before. Adds an integration test running an installed package's agent from two bundles on local docker, by id and as the configured default. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…nset agent field means Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…le first, and import installed entities unpinned A pulled entity naming an entity of the bundle being run now points at it, rather than reading it as a store id the bundle doesn't write. An installed bundle's ids are mapped as resolve maps a bundle's own, so a pulled entity naming its bundle's image folder pulls that image. A pin on a default agent this bundle or an installed one defines is refused, as a pin on any reference to one is. The importer leaves a reference to an installed bundle's entity unpinned, reported as floating, and drops a pin the stored task held, so the import passes its own check. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…builds for the deploys here An image a bundle builds is written as its build context whichever way a run deploys it, so its version depends only on its sources, and a pulled agent shared by bundles on different sandboxes is no longer rewritten by each, nor swapped under a running task. Each deploy builds it where it runs: - A run builds it with this machine's Docker as it writes it, when a deploy here runs it or none the run models does, so a failed build writes nothing. A reused one Docker no longer holds is built again. - This machine's builds are native and labeled with their sources, built once per sources and locked, and a context's tag is its sources. The local provider builds a context it's given lazily too, and skips pulling one it built. A smol VM loads a saved copy of this machine's build rather than build one cold. - Preflight no longer chooses a form per run, so one run can deploy an image both here and on VMs. The ledger digest drops the form, and SCHEME 2 rewrites every version once. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ls back to the default The rubrics judge's unset judge_a2a_agent_id follows the one rule too. A judge that deploys no agent of its own then has a bundle run plan the default it won't deploy, which costs a shared write and at most one build per machine; a store default is still preflight's to check, and preflight still skips such a judge. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
f72f5bf to
64a6fcf
Compare
…one copy per image name for VMs here An image only a step preflight doesn't model uses, such as install_agent's on a VM elsewhere, is built where it runs rather than on this machine, so a run deploying nowhere here needs no Docker for it, as before. A saved copy of this machine's build is kept per image name as well as per sources, since a VM loads it under the name it was saved with. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
… does The integration tests stand the local sandbox in for a remote VM by giving it a platform, and check the build a VM makes for it; only a local sandbox recording none, as it does for real, builds natively with this machine's Docker. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ds it for its own A bundle wrote its build contexts recorded for linux/amd64, which an arm64 VM, such as a SmolVM on Apple silicon, refused before building. A context recording no platform is built for the platform of the VM that runs it, as the local sandbox and this machine's build already did. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Re Greptile's outside-diff P1, Arm64 SmolVM deploys fail ( A bundle image now records no platform ( Verified:
Also in this push, ebafb28: a local sandbox given a platform builds a context as a VM does. This fixes integration-local-slow. |
Summary
A bundle can now use an env, agent or artifact that an installed bundle ships, either by naming its id or by leaving an agent field unset when the configured default agent is one. It works on the first
agent-env run, with noagent-env run <installed bundle>beforehand.Until now, a user bundle naming
@local/<dist>/<bundle>/<entity>failed its plan with "not in the store". Even after the installed bundle had been run, each user bundle rewrote the shared entity, because ledger rows and locks belonged to the bundle being run.To the planner, an entity from another bundle is just an entity.
@local/<dist>/<bundle>/root), then the store.[agents] default_a2a_agent_id. When this bundle or an installed one defines that agent, the planner adds the reference and pulls it in. A default that lives in the store is still checked by preflight, as today.EntityRef.agentfield falls back to the configured default (entity.refs.default_agent_refs), for every step, the rubrics judge's included. The importer reads it instead of naming step types. A judge that deploys no agent of its own then has a run plan a default it won't deploy. That costs a shared write and at most one build per machine. Preflight still skips such a judge, and still checks a store default itself.Plan.originsmaps a pulled entry to its bundle (Plan.bundle_of,Plan.path). It's used for:AuthoringContext);<dist>/<bundle>:<path>, e.g.agentenv-framework/agents:agents/claude-code. A pulled entry's own problems carry the same prefix.materializing()takes the ids to lock, and a run locks the entities it pulls in, so two runs writing one shared agent take turns.check_bundle(plugin check, the importer's check) does the same lookup without reading a store.bundle init --from-taskleaves a reference to an installed bundle's entity unpinned and reports it as floating, dropping a pin the stored task held, so the import passes its own check.deploy_agentand the judge's own deploy explain an@localagent the store doesn't hold: anagent-env runof a bundle writes one. They name the fields that point elsewhere. A store id is reported as before.Not changed: preflight still models each deploy step itself, as it does for envs and sandboxes. The bundle's own entities, store references and dry runs behave as before.
A built image is its build context, built where each deploy runs. Sharing one write of a pulled agent across bundles exposed a problem. How an image was written used to depend on the run:
So bundles on different sandboxes rewrote the shared image back and forth, and a task could pick up another bundle's form mid-run.
Now an image's version depends only on its sources:
put_context), and a context's tag is its sources.install_agent's, is built where it runs. A failed build still writes nothing, and "building with docker" prints as before. A reused image that Docker no longer holds is built again.prepare_image) and skips pulling an image it built. A smol VM loads a saved copy of this machine's build, rather than building one cold.providers/sandbox_providers/host_images.py).Tests
Unit:
installed_entities_test.pycovers:deploy_agentand an agent judge; a direct LLM judge and a task-deployed judge don't pull);Plus the unset-agent rule per step in
test_entity_refs.py, an import of an installed entity inimporter_test.py, and the outside-run error inunwritten_local_agent_test.py. Unit tier: 8118 passed.Integration:
run_bundle_installed_agent_test.pyinstalls a package registering a bundle with an echo agent folder. One bundle names the agent by id and builds and runs it on local docker. A second bundle deploys it as the configured default and reuses the build.Time to an image running, old vs new, on fresh x86 and arm64 VMs: each round starts from a clean build cache, and wall time is per
agent-env run. New is equal or faster everywhere:The agent is a Claude-Code-sized image (Node, a global npm install, pip). Builds on arm64 are native.
On a fresh Linux VM: real wheels and the real CLI, covering reuse, concurrency, edits, refusals, a back-reference, an import and a modal_vm dry run, plus live sail_vm deploys and arm64 runs. Results are in the verification comment.
🤖 Generated with Claude Code


Confidence Score: 5/5
No new issue in the latest changes blocks merging.Summary
Bundles can use installed bundles' environments, agents, and artifacts without running those bundles first.
Diagram
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Bundle reference or default agent] --> B[Look in running bundle] B --> C[Look in installed bundles] C --> D[Add entity and its dependencies] D --> E[Lock shared IDs] E --> F[Reuse or write through ledger] F --> G[Store image build context] G --> H[Build on deployment target] H --> I[Run tasks]Reviews (6) · Last reviewed commit: "fix(bundle): a bundle image records no p..." · Reviewed by Greptile