Repository navigation
feat(bundle): a bundle.toml names the plugin packages a bundle requires - #377
earakely-scale wants to merge 6 commits into
Conversation
A bundle's optional bundle.toml lists the pip requirements it needs (requires = ["agentenv-browser>=1.2"]). Resolving a bundle refuses one this environment doesn't meet, before anything runs, naming the one agent-env plugin add command that installs them; in a terminal, run and bundle register offer to run it and then run the command again. A run or registration of a writable folder run by its path that has no bundle.toml creates one from the plugin distributions whose types the bundle uses, at least the version installed, never over a file that appeared meanwhile. A dry run says what it would write. An existing bundle.toml is never edited: the run says what to add, so requires = [] opts out. A type only configuration registers gets a hint, never a requirement. bundle init --from-task writes the same file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t left empty On a full disk the in-place fallback could leave an empty bundle.toml, which reads as requires = [] and silently opts the bundle out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, and nothing is written without links A plugin installed at a local version (1.2.0+acme.1) is required at >=1.2.0, a bound pip takes. A requirement naming extras is met only when what those extras need is installed too. A filesystem without hard links gets the hint rather than a bundle.toml written in place, which another run could read half-written. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
E2E + chaos verification (1a0086e)One Ubuntu 24.04 VM in agent-environments-admin (m5.2xlarge, Docker 28, uv 0.10), torn down after. The wheel came from this branch. Two fake plugins were installed offline from a local wheel folder: With the plugin
Without it
The prompt (pty)
Bad Chaos
Found and fixed along the way:
🤖 Generated with Claude Code |
…from, and say to upgrade agent-env itself A type only configuration registers is hinted with the installed package its class's module comes from, when one ships it, so whoever runs the bundle knows what to install as well as which configuration it needs. A bundle.toml requiring agent-env itself (agentenv-framework or its protocol) says to upgrade agent-env with the installer that installed it: plugin add refuses to change agent-env, so it's left out of the command and the prompt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Follow-up verification (3b5ade9, e832512, then 70f92eb)The full VM round ran on a fresh agent-environments-admin VM for each commit (torn down after). On e832512: 84 cases, all as designed. On 70f92eb, the behaviour-neutral refactor, every case's exit code, prompt result and message matched e832512's. Every earlier case gave the same result, and chaos was clean again (60 SIGKILLs: 0 partial, 0 staging files; 12 racing runs: one writer, all passed). New cases:
🤖 Generated with Claude Code |
…one running an installed bundle what to declare A bundle.toml string can carry control or format characters, which the install prompt and the plugin add command would print as they are, so a shared bundle could make the prompt show one package while installing another. Such a requirement is refused where the file is read. An installed bundle's runner can't edit it, so runs by name, the A2A validator's writes and an installed bundle served by OpenEnv say nothing of what to declare; its requirements are still checked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… installed-package check beside the plugin one The importer's needs are the same Use records a run detects, with their Provider, and its bundle.toml comes from detected(), so how a requirement is written lives in one place. A Declaring setting (write, would write, hint, quiet), worked out once by declaring_for, replaces the own, dry_run and installed flags and register's write_requires. unmet() and the extras check move to plugins/_requirements.py, beside incompatibility(), which now shares their version comparison. The tests stand in for installed packages through one fake_plugins helper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Summary
A bundle shared with someone now says which plugin packages it needs, and a machine without them is told how to install them before anything runs.
bundle.toml: an optional file at the bundle root,requires = ["agentenv-browser>=1.2", ...], pip (PEP 508) requirements, markers allowed. URLs are refused, and so is a requirement holding a character that can't be printed, which could make the install prompt show a different package from the one it installs.run,run --dry-run,bundle register) checks each requirement against what's installed, including what any extras it names need, and refuses with every unmet one and the one command that installs them all:agentenv-framework(or its protocol) says to upgrade agent-env with the installer that installed it;plugin adddoesn't change agent-env, so it's left out of the command and never prompted for.agent-env runandbundle registerthen askInstall agentenv-acme>=1.2 with agent-env plugin add? [y/N]. Yes installs throughplugin addand runs the same command again in a new process, so the new plugins load. No, or no terminal, refuses as above. A dry run never asks, and the rerun never asks twice.bundle.toml, writes one requiring>=the installed version of each plugin distribution whose step, env or artifact type the bundle uses. The bound drops a local version label (1.2.0+acme.1→>=1.2.0). It is created whole or not at all (staged and hard-linked into place), never over a file that appeared meanwhile; on a filesystem without hard links, or a full one, nothing is written and the run gives the hint. A dry run sayswould write bundle.toml: ...instead.bundle.tomlstays as written. A run says what it doesn't require and the line to declare, sorequires = []is how a bundle opts out. Read-only folders and the OpenEnv service path get the same hint and write nothing. An installed bundle (run by name) says nothing of what to declare, since whoever runs it can't edit it; its requirements are still checked.config.tomlgets one hint line, never a requirement. When an installed package ships its class, the line names it ("…which this machine's configuration registers, from agentenvhub-sdk 0.21.2; whoever runs it needs that package and its configuration").bundle init --from-taskwrites the samebundle.tomlfrom the needs it already detects; that detection moved tobundle/requires.pyso both share it.Review guide
bundle/requires.py: the refusal (unmet_problems), detection (Providers,uses,detected, shared with the importer), anddeclare/_create, driven by oneDeclaringsetting (declaring_for).plugins/_requirements.py:unmet, the check of requirements against what's installed, extras included, besideincompatibility(), sharing its version comparison.bundle/parse.py: readsbundle.toml(it was a reserved name).bundle/resolve.py: raisesUnmetRequirements(aBundleError) alongside every other problem.bundle/run.py,bundle/register.py: say or write it.Registration.requirescarries the lines, sobundle registerprints them once, not in both passes.cli/_install_prompt.py: the prompt and the re-exec.Test plan
make linttst/unit/bundle/requires_test.py(parse, detection, unmet messages, create/dry-run/hint/opt-out/read-only, racing creators, no-link filesystems, run/register/CLI, the prompt's yes/no/no-TTY/rerun/failed-install paths); updated importer, parse and run tests🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge; no new actionable issues were found.Summary
Adds optional
bundle.tomlrequirements, checks installed packages before running, and offers installation in a terminal.Diagram
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Read bundle] --> B[Check required packages] B -->|Met| C[Plan and check bundle] B -->|Unmet| D[Refuse with install instructions] D -->|Terminal user agrees| E[Install and restart command] E --> A C --> F[Write declaration or show hint] F --> G[Register or run]Reviews (6) · Last reviewed commit: "refactor(bundle): one detection model, o..." · Reviewed by Greptile