feat(agent-bundle): add Amp native plugin factory - #729
Conversation
🦋 Changeset detectedLatest commit: d1f6dcf The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2df178f533
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
ScriptedAlchemy
left a comment
There was a problem hiding this comment.
Current-head review — 894c7321f09bc2df66f052ac2034044b90f2e042
Hold for the lifecycle/location corrections below and a rebased integration gate. The no-account decision on #711 remains accepted: authenticated Amp model execution is unverified, not a new merge prerequisite. These findings concern framework-owned behavior that can be tested without an account.
Reviewed the complete changed-path inventory and traced the adapter/factory, event lowering, registry/manifest integration, installer/uninstaller, focused tests, and documented proof. Repository/native-host tests were not run by this reviewer. A standalone Node calculation reproduced the path expressions below; that was not an installation or filesystem-mutation test.
P2 — The generated callback factory never disposes the child processes it starts
File: src/adapters/amp.ts, factorySource / generated runHook.
runHook starts Bun.spawn, collects stdout/stderr with unbounded Response(...).text(), and waits on Promise.all([child.exited, stdout, stderr]). There is no tracked child ownership, disposal registration, or finally-path termination. Reload/unload can therefore abandon a still-running plain hook or fail to settle pending work; a hung child/stream has no cleanup path in this factory. This is a source-derived lifecycle gap, not a reproduced orphan-process incident.
Amp's official PluginAPI onDispose specifically provides bounded cleanup on unload/reload/graceful host shutdown for resources including children. Wire the generated process owner to that lifecycle, stop admitting work during disposal, and settle/terminate owned children within the host's documented budget. Cover spawn/stdin/read failure paths and bound protocol capture rather than collecting arbitrary output forever. Reuse existing process/stream handling where viable instead of introducing a parallel lifecycle framework.
Do not invent an event ctx.signal or a session.end callback: those are not established by the pinned event contract. Do not claim crash/SIGKILL cleanup that onDispose cannot provide.
Acceptance gap: amp-adapter.test.ts replaces Bun.spawn with a fake that ignores executable arguments; the compiled wrapper test launches the wrapper directly through Node. Neither proves the actual generated factory → real child → compiled wrapper path or disposal. Add an account-free generated-factory test with a deliberately blocked real child; invoke the registered dispose callback and assert the invocation settles and the owned process is gone. Verify the actual interpreter/argv used by that path and normal/error outputs as well. This does not require an authenticated Amp turn.
P2 — Project installation has an explicit root; typed uninstall cannot target the same root
Files: src/install/install.ts, InstallBundleOptions.projectRoot / ampInstallLocation; src/install/uninstall.ts, UninstallBundleOptions / uninstallAmp.
Install supports { scope: 'project', projectRoot: '/projects/A' } from a caller elsewhere. UninstallBundleOptions has no projectRoot, yet uninstallAmp delegates to the same location function, which then falls back to process.cwd(). The supported typed round trip cannot address A without changing global cwd; it may report not installed or address another project containing the same plugin.
Share the explicit location option across install, uninstall and uninstall plan. Extend amp-install.test.ts: call both operations from outside A, give A explicitly, and retain a separate sentinel/installation in caller project B. Existing project coverage only verifies installation. Do not solve this with process-global chdir or ambient-root guessing.
P2 — Empty or relative XDG_CONFIG_HOME makes user installation cwd-dependent
File: the same ampInstallLocation implementation.
join(environment['XDG_CONFIG_HOME'] ?? join(home, '.config'), 'amp') only defaults null/undefined. Reproducing that expression yields:
XDG_CONFIG_HOME unset -> /tmp/amp-review/home/.config/amp/plugins/probe
XDG_CONFIG_HOME="" -> amp/plugins/probe
XDG_CONFIG_HOME="relative" -> relative/amp/plugins/probe
The XDG specification requires an empty configuration variable to use the home default and treats relative values as invalid. Apply the existing/shared absolute-XDG fallback policy before deriving both install and uninstall locations. Add unset, empty, relative and valid absolute cases; assert no cwd-relative plugin directory is created or selected. Do not normalize a relative XDG value against cwd and call it a user-config root.
Previous review findings visibly fixed
The reserved/strict-mode factory identifiers, remote MCP URL/header path-token validation, and mixed-case Amp installation-name mismatch now have implementation changes and focused regression tests. I am not reposting those old inline findings as current defects. Answer/resolve their existing threads with the fixing evidence rather than duplicating them.
The selected-skill MCP constraint, refusal to invent relocation semantics, content-only runtime absence, explicit registration, and preservation of foreign files/settings are appropriate scoped decisions. Final API-required allow is not being confused here with the old Claude implicit-permission defect.
Integration and evidence gate
GitHub currently reports this branch conflicting with main (596d3371e9a9efbd2e84561826f5f390fefb2c3d). It changes the closed manifest to v4, public types, emitted roots and lifecycle consumers; the PR body currently records 81 focused tests and disposable type/fake-host proof, not the complete integrated gate.
After resolving conflicts, record the repository's allowed local build/typecheck/lint/unit gate, the full integration coverage implicated by the output/manifest changes, affected packed tests, and the English/Chinese docsite gate on the exact resulting head. Preserve existing Claude/Codex/Cursor/portable, installed-artifact, Workbench and npm-root behavior. The exact-head PR-workflow query returned no runs; that is not a failed CI result and does not replace or invalidate a properly recorded local gate. No approval, source modification or merge is performed by this review.
e775a19 to
d1f6dcf
Compare
Summary
amptarget, emitting one.amp/plugins/<name>/index.jsasync PluginAPI factory with explicit Skill and inline event registrationdocuments.entryand theampbuilt-in identityCloses #711
Verification
pnpm build && pnpm typecheck && pnpm lint && pnpm test:unit && pnpm test:route-unit && pnpm test:projection && pnpm test:integration:run— passed on currentorigin/main(62e5c3a)pnpm test:packed— passedpnpm docs:site:build— passed, including locale parity, diagnostics coverage, build, and link checks@ampcode/plugin@0.0.0-20260907001852-gf348fedtypecheck and typed fake PluginAPI import — passedamp --helpandamp skill add --help— passed without authenticationProof matrix
amp plugins listamp skills list --jsonDeslop
Deslop: GPT-5.6 Sol, 5 edits — removed content-only runtime helpers, reused strict-record/path-token and uninstall-pruning helpers, separated recursive adapter roots from assets, and consolidated Amp MCP validation.
Self-review
Reviewer: Claude Fable 5.1 Thinking High (
change-risk-reviewer), run twice after fixes and once after rebasing over #730/#732.BUN_BE_BUN=1, with launch assertions.My_Plugin.projectRoot: fixed with a round-trip regression.registerLifecycleCommands, added Amp only to install/uninstall, and kept development installs narrowed toDevInstallHost.Final review: no merge blockers. Residual live activation and callback execution remain deliberately
unverifiedbecause no Amp account is available; they are not represented as unsupported.