Phase 2 / PR #3 of the module refactor. Moves the approval and interactive- question flows out of core and into src/modules/, wired through the response dispatcher and delivery action registries. New modules: - src/modules/interactive/ — registers a response handler that claims pending_questions rows, writes question_response to the session DB, wakes the container. createPendingQuestion call stays inline in delivery.ts (guarded by hasTable) per plan. - src/modules/approvals/ — registers 3 delivery actions (install_packages, request_rebuild, add_mcp_server), a response handler for pending_approvals (including OneCLI action fall-through), an adapter-ready hook that boots the OneCLI manual-approval handler, and a shutdown hook that stops it. OneCLI implementation (src/onecli-approvals.ts) moves into the module. Core lifecycle hooks added (narrow, not registries): - onDeliveryAdapterReady(cb) in delivery.ts — fires when setDeliveryAdapter runs (or immediately if already set). Used by approvals for OneCLI boot. - onShutdown(cb) in index.ts — fires on SIGTERM/SIGINT. Used by approvals for OneCLI teardown. - getDeliveryAdapter() getter in delivery.ts — for live-flow adapter access in registered delivery actions. Core shrinks: delivery.ts 911 → 665 lines, index.ts 405 → 224 lines. dispatchResponse now logs "Unclaimed response" instead of falling through to an inline handler — the inline fallback moved into the two modules. Migration files renamed to the module-<name>-<short>.ts convention: - 003-pending-approvals.ts → module-approvals-pending-approvals.ts - 007-pending-approvals-title-options.ts → module-approvals-title-options.ts Migration.name fields unchanged so existing DBs treat them as already-applied. Degradation verified: emptying the modules barrel builds clean and 137/137 tests pass. Actions would log "Unknown system action"; button clicks would log "Unclaimed response". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2.4 KiB
Self-modification tools (require admin approval)
Three fire-and-forget tools change your container image or config. Each sends an approval card to an admin's DM; you get notified via system chat on approve/reject.
install_packages
Add apt and/or npm packages to your container image. On approval, the config is updated AND the image is rebuilt in the same step — you'll get a follow-up prompt ~5s after rebuild telling you to verify the packages are available.
install_packages({
apt: ["ripgrep", "jq"], // names only, no version specs or flags
npm: ["@anthropic-ai/sdk"], // global install
reason: "need rg for fast code search"
})
- Max 20 packages per request.
- Names must match strict regex (blocks shell injection via
vim; curl evil.com). - After approval: rebuild runs automatically. You do NOT need to call
request_rebuildseparately.
add_mcp_server
Wire an EXISTING third-party MCP server into your runtime config. You must already know the exact command and args.
add_mcp_server({
name: "github",
command: "npx",
args: ["@modelcontextprotocol/server-github"],
env: { GITHUB_TOKEN: "..." }
})
- Does NOT install packages. Use
install_packagesfirst if the command isn't already available. - On approval, container is killed so the next message wakes it with the new server wired up.
request_rebuild
Rebuild your container image. Only useful if you've already landed install_packages approvals whose rebuild step failed, or if you're recovering from a bad config edit.
request_rebuild({ reason: "previous install_packages rebuild failed" })
How approval works
You won't see the admin's response in your current turn. After approval, the container is killed and next time a message arrives your container starts fresh on the new image. If a follow-up system prompt fires (as with install_packages), you'll see it and should act on it — verify the change, report to the user.
If denied, you'll get a chat message telling you the request was rejected. Do not retry automatically; explain to the user what was denied.
Credential approvals (OneCLI)
When you call an external API that requires credentials, OneCLI may prompt an admin for approval before releasing the token. This happens transparently: the HTTP call blocks until admin approves or denies. No action needed from you — just make the call. If it errors out with a credential failure, tell the user and stop.