@openparachute/agent 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.parachute/module.json +124 -8
- package/LICENSE +2 -16
- package/README.md +118 -166
- package/package.json +32 -43
- package/scripts/spawn-agent.ts +371 -0
- package/src/_parked/interactive-spawn.test.ts +324 -0
- package/src/_parked/interactive-spawn.ts +701 -0
- package/src/agent-defs.test.ts +1504 -0
- package/src/agent-defs.ts +1702 -0
- package/src/agent-mcp-config.test.ts +115 -0
- package/src/agent-mcp-config.ts +115 -0
- package/src/agents.test.ts +360 -0
- package/src/agents.ts +379 -0
- package/src/auth.test.ts +46 -0
- package/src/auth.ts +140 -0
- package/src/backends/attached-queue.test.ts +376 -0
- package/src/backends/attached-queue.ts +372 -0
- package/src/backends/programmatic.test.ts +1715 -0
- package/src/backends/programmatic.ts +927 -0
- package/src/backends/registry.test.ts +1494 -0
- package/src/backends/registry.ts +1202 -0
- package/src/backends/stream-json.test.ts +570 -0
- package/src/backends/stream-json.ts +392 -0
- package/src/backends/types.ts +223 -0
- package/src/bridge.ts +417 -0
- package/src/channel-backend-wiring.test.ts +237 -0
- package/src/credentials.test.ts +274 -0
- package/src/credentials.ts +380 -0
- package/src/cron.test.ts +342 -0
- package/src/cron.ts +380 -0
- package/src/daemon-agent-def-api.test.ts +166 -0
- package/src/daemon-agent-defs-api.test.ts +953 -0
- package/src/daemon-agent-env-api.test.ts +338 -0
- package/src/daemon-attached-queue-store.test.ts +65 -0
- package/src/daemon-config-api.test.ts +962 -0
- package/src/daemon-jobs-api.test.ts +271 -0
- package/src/daemon-vault-chat.test.ts +250 -0
- package/src/daemon.test.ts +746 -0
- package/src/daemon.ts +3314 -0
- package/src/def-vaults.test.ts +136 -0
- package/src/def-vaults.ts +165 -0
- package/src/delivery-state.test.ts +110 -0
- package/src/delivery-state.ts +154 -0
- package/src/effective-env.test.ts +114 -0
- package/src/effective-env.ts +184 -0
- package/src/env-compat.ts +39 -0
- package/src/grants.test.ts +638 -0
- package/src/grants.ts +675 -0
- package/src/hub-jwt.test.ts +161 -0
- package/src/hub-jwt.ts +182 -0
- package/src/jobs.test.ts +245 -0
- package/src/jobs.ts +266 -0
- package/src/mcp-http.test.ts +265 -0
- package/src/mcp-http.ts +771 -0
- package/src/mint-token.test.ts +152 -0
- package/src/mint-token.ts +139 -0
- package/src/module-manifest.test.ts +158 -0
- package/src/oauth-discovery.ts +134 -0
- package/src/programmatic-wiring.test.ts +838 -0
- package/src/registry.test.ts +227 -0
- package/src/registry.ts +228 -0
- package/src/resolve-port.test.ts +64 -0
- package/src/routing.test.ts +184 -0
- package/src/routing.ts +76 -0
- package/src/runner.test.ts +506 -0
- package/src/runner.ts +255 -0
- package/src/sandbox/config.test.ts +150 -0
- package/src/sandbox/config.ts +102 -0
- package/src/sandbox/egress.test.ts +113 -0
- package/src/sandbox/egress.ts +123 -0
- package/src/sandbox/index.ts +180 -0
- package/src/sandbox/live-seatbelt.test.ts +277 -0
- package/src/sandbox/mounts.test.ts +154 -0
- package/src/sandbox/mounts.ts +133 -0
- package/src/sandbox/sandbox.test.ts +168 -0
- package/src/sandbox/types.ts +382 -0
- package/src/services-manifest.test.ts +106 -0
- package/src/services-manifest.ts +95 -0
- package/src/spa-serve.test.ts +116 -0
- package/src/spa-serve.ts +116 -0
- package/src/spawn-agent-cli.test.ts +172 -0
- package/src/spawn-agent.test.ts +1218 -0
- package/src/spawn-agent.ts +569 -0
- package/src/spawn-deps.test.ts +54 -0
- package/src/spawn-deps.ts +166 -0
- package/src/telegram/api.ts +153 -0
- package/src/terminal-assets.test.ts +50 -0
- package/src/terminal-assets.ts +79 -0
- package/src/terminal-ui.ts +305 -0
- package/src/terminal.test.ts +530 -0
- package/src/terminal.ts +458 -0
- package/src/transport.ts +270 -0
- package/src/transports/http-ui.test.ts +455 -0
- package/src/transports/http-ui.ts +201 -0
- package/src/transports/telegram.test.ts +174 -0
- package/src/transports/telegram.ts +426 -0
- package/src/transports/vault.test.ts +2011 -0
- package/src/transports/vault.ts +1790 -0
- package/src/ui-kit.test.ts +178 -0
- package/src/ui-kit.ts +402 -0
- package/tsconfig.json +8 -14
- package/web/ui/tsconfig.json +2 -1
- package/.claude/scheduled_tasks.lock +0 -1
- package/.claude/settings.json +0 -5
- package/.claude/skills/add-atomic-chat-tool/SKILL.md +0 -243
- package/.claude/skills/add-atomic-chat-tool/atomic-chat-mcp-stdio.ts +0 -229
- package/.claude/skills/add-codex/SKILL.md +0 -161
- package/.claude/skills/add-dashboard/SKILL.md +0 -138
- package/.claude/skills/add-dashboard/resources/dashboard-pusher.ts +0 -495
- package/.claude/skills/add-emacs/SKILL.md +0 -296
- package/.claude/skills/add-gcal-tool/SKILL.md +0 -210
- package/.claude/skills/add-gchat/REMOVE.md +0 -6
- package/.claude/skills/add-gchat/SKILL.md +0 -92
- package/.claude/skills/add-gchat/VERIFY.md +0 -3
- package/.claude/skills/add-github/REMOVE.md +0 -6
- package/.claude/skills/add-github/SKILL.md +0 -148
- package/.claude/skills/add-github/VERIFY.md +0 -3
- package/.claude/skills/add-gmail-tool/SKILL.md +0 -229
- package/.claude/skills/add-imessage/REMOVE.md +0 -6
- package/.claude/skills/add-imessage/SKILL.md +0 -113
- package/.claude/skills/add-imessage/VERIFY.md +0 -3
- package/.claude/skills/add-karpathy-llm-wiki/SKILL.md +0 -110
- package/.claude/skills/add-karpathy-llm-wiki/llm-wiki.md +0 -75
- package/.claude/skills/add-linear/REMOVE.md +0 -6
- package/.claude/skills/add-linear/SKILL.md +0 -168
- package/.claude/skills/add-linear/VERIFY.md +0 -3
- package/.claude/skills/add-macos-statusbar/SKILL.md +0 -133
- package/.claude/skills/add-macos-statusbar/add/src/statusbar.swift +0 -147
- package/.claude/skills/add-matrix/REMOVE.md +0 -6
- package/.claude/skills/add-matrix/SKILL.md +0 -148
- package/.claude/skills/add-matrix/VERIFY.md +0 -3
- package/.claude/skills/add-ollama-provider/SKILL.md +0 -179
- package/.claude/skills/add-ollama-tool/SKILL.md +0 -193
- package/.claude/skills/add-opencode/SKILL.md +0 -229
- package/.claude/skills/add-parallel/SKILL.md +0 -290
- package/.claude/skills/add-resend/REMOVE.md +0 -6
- package/.claude/skills/add-resend/SKILL.md +0 -93
- package/.claude/skills/add-resend/VERIFY.md +0 -3
- package/.claude/skills/add-signal/REMOVE.md +0 -13
- package/.claude/skills/add-signal/SKILL.md +0 -318
- package/.claude/skills/add-signal/VERIFY.md +0 -5
- package/.claude/skills/add-slack/REMOVE.md +0 -6
- package/.claude/skills/add-slack/SKILL.md +0 -112
- package/.claude/skills/add-slack/VERIFY.md +0 -3
- package/.claude/skills/add-teams/REMOVE.md +0 -6
- package/.claude/skills/add-teams/SKILL.md +0 -207
- package/.claude/skills/add-teams/VERIFY.md +0 -3
- package/.claude/skills/add-vercel/SKILL.md +0 -147
- package/.claude/skills/add-vercel/container-skills/vercel-cli/SKILL.md +0 -103
- package/.claude/skills/add-webex/REMOVE.md +0 -6
- package/.claude/skills/add-webex/SKILL.md +0 -88
- package/.claude/skills/add-webex/VERIFY.md +0 -3
- package/.claude/skills/add-wechat/REMOVE.md +0 -49
- package/.claude/skills/add-wechat/SKILL.md +0 -170
- package/.claude/skills/add-wechat/scripts/wire-dm.ts +0 -172
- package/.claude/skills/add-whatsapp/SKILL.md +0 -264
- package/.claude/skills/add-whatsapp-cloud/REMOVE.md +0 -6
- package/.claude/skills/add-whatsapp-cloud/SKILL.md +0 -95
- package/.claude/skills/add-whatsapp-cloud/VERIFY.md +0 -3
- package/.claude/skills/claw/SKILL.md +0 -131
- package/.claude/skills/claw/scripts/claw +0 -374
- package/.claude/skills/convert-to-apple-container/SKILL.md +0 -212
- package/.claude/skills/customize/SKILL.md +0 -110
- package/.claude/skills/debug/SKILL.md +0 -349
- package/.claude/skills/get-qodo-rules/SKILL.md +0 -122
- package/.claude/skills/get-qodo-rules/references/output-format.md +0 -41
- package/.claude/skills/get-qodo-rules/references/pagination.md +0 -33
- package/.claude/skills/get-qodo-rules/references/repository-scope.md +0 -26
- package/.claude/skills/init-first-agent/SKILL.md +0 -120
- package/.claude/skills/init-onecli/SKILL.md +0 -270
- package/.claude/skills/manage-channels/SKILL.md +0 -87
- package/.claude/skills/manage-mounts/SKILL.md +0 -47
- package/.claude/skills/migrate-from-openclaw/MIGRATE_CRONS.md +0 -100
- package/.claude/skills/migrate-from-openclaw/SKILL.md +0 -447
- package/.claude/skills/migrate-from-openclaw/scripts/discover-openclaw.ts +0 -734
- package/.claude/skills/migrate-from-openclaw/scripts/extract-channel-credentials.ts +0 -476
- package/.claude/skills/migrate-nanoclaw/SKILL.md +0 -484
- package/.claude/skills/migrate-nanoclaw/diagnostics.md +0 -51
- package/.claude/skills/qodo-pr-resolver/SKILL.md +0 -326
- package/.claude/skills/qodo-pr-resolver/resources/providers.md +0 -329
- package/.claude/skills/update-nanoclaw/SKILL.md +0 -243
- package/.claude/skills/update-nanoclaw/diagnostics.md +0 -48
- package/.claude/skills/update-skills/SKILL.md +0 -130
- package/.claude/skills/use-native-credential-proxy/SKILL.md +0 -167
- package/.claude/skills/x-integration/SKILL.md +0 -417
- package/.claude/skills/x-integration/agent.ts +0 -243
- package/.claude/skills/x-integration/host.ts +0 -155
- package/.claude/skills/x-integration/lib/browser.ts +0 -148
- package/.claude/skills/x-integration/lib/config.ts +0 -62
- package/.claude/skills/x-integration/scripts/like.ts +0 -56
- package/.claude/skills/x-integration/scripts/post.ts +0 -66
- package/.claude/skills/x-integration/scripts/quote.ts +0 -80
- package/.claude/skills/x-integration/scripts/reply.ts +0 -74
- package/.claude/skills/x-integration/scripts/retweet.ts +0 -62
- package/.claude/skills/x-integration/scripts/setup.ts +0 -87
- package/.github/CODEOWNERS +0 -10
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -18
- package/.github/workflows/bump-version.yml +0 -35
- package/.github/workflows/ci.yml +0 -39
- package/.github/workflows/label-pr.yml +0 -40
- package/.github/workflows/update-tokens.yml +0 -43
- package/.husky/pre-commit +0 -1
- package/.mcp.json +0 -3
- package/.nvmrc +0 -1
- package/.prettierrc +0 -4
- package/CHANGELOG.md +0 -263
- package/CLAUDE.md +0 -307
- package/CODE_OF_CONDUCT.md +0 -128
- package/CONTRIBUTING.md +0 -159
- package/CONTRIBUTORS.md +0 -26
- package/LICENSE-NANOCLAW-MIT +0 -21
- package/README_ja.md +0 -194
- package/README_zh.md +0 -194
- package/assets/nanoclaw-favicon.png +0 -0
- package/assets/nanoclaw-icon.png +0 -0
- package/assets/nanoclaw-logo-dark.png +0 -0
- package/assets/nanoclaw-logo.png +0 -0
- package/assets/nanoclaw-profile.jpeg +0 -0
- package/assets/nanoclaw-sales.png +0 -0
- package/assets/social-preview.jpg +0 -0
- package/config-examples/mount-allowlist.json +0 -25
- package/container/.dockerignore +0 -2
- package/container/CLAUDE.md +0 -21
- package/container/Dockerfile +0 -121
- package/container/agent-runner/bun.lock +0 -243
- package/container/agent-runner/package.json +0 -22
- package/container/agent-runner/scripts/sdk-signal-probe.ts +0 -169
- package/container/agent-runner/src/config.ts +0 -55
- package/container/agent-runner/src/db/connection.ts +0 -267
- package/container/agent-runner/src/db/index.ts +0 -20
- package/container/agent-runner/src/db/messages-in.ts +0 -138
- package/container/agent-runner/src/db/messages-out.ts +0 -143
- package/container/agent-runner/src/db/session-routing.ts +0 -30
- package/container/agent-runner/src/db/session-state.test.ts +0 -100
- package/container/agent-runner/src/db/session-state.ts +0 -79
- package/container/agent-runner/src/destinations.ts +0 -135
- package/container/agent-runner/src/formatter.test.ts +0 -167
- package/container/agent-runner/src/formatter.ts +0 -260
- package/container/agent-runner/src/index.ts +0 -110
- package/container/agent-runner/src/integration.test.ts +0 -121
- package/container/agent-runner/src/mcp-tools/agents.instructions.md +0 -26
- package/container/agent-runner/src/mcp-tools/agents.ts +0 -66
- package/container/agent-runner/src/mcp-tools/core.instructions.md +0 -27
- package/container/agent-runner/src/mcp-tools/core.ts +0 -262
- package/container/agent-runner/src/mcp-tools/index.ts +0 -22
- package/container/agent-runner/src/mcp-tools/interactive.instructions.md +0 -22
- package/container/agent-runner/src/mcp-tools/interactive.ts +0 -169
- package/container/agent-runner/src/mcp-tools/scheduling.instructions.md +0 -40
- package/container/agent-runner/src/mcp-tools/scheduling.ts +0 -299
- package/container/agent-runner/src/mcp-tools/self-mod.instructions.md +0 -25
- package/container/agent-runner/src/mcp-tools/self-mod.ts +0 -120
- package/container/agent-runner/src/mcp-tools/server.ts +0 -54
- package/container/agent-runner/src/mcp-tools/types.ts +0 -6
- package/container/agent-runner/src/poll-loop.test.ts +0 -248
- package/container/agent-runner/src/poll-loop.ts +0 -437
- package/container/agent-runner/src/providers/claude.ts +0 -379
- package/container/agent-runner/src/providers/factory.test.ts +0 -19
- package/container/agent-runner/src/providers/factory.ts +0 -13
- package/container/agent-runner/src/providers/index.ts +0 -6
- package/container/agent-runner/src/providers/mock.ts +0 -77
- package/container/agent-runner/src/providers/provider-registry.ts +0 -33
- package/container/agent-runner/src/providers/types.ts +0 -82
- package/container/agent-runner/src/scheduling/task-script.ts +0 -121
- package/container/agent-runner/src/timezone.test.ts +0 -93
- package/container/agent-runner/src/timezone.ts +0 -107
- package/container/agent-runner/tsconfig.json +0 -14
- package/container/build.sh +0 -48
- package/container/entrypoint.sh +0 -16
- package/container/skills/agent-browser/SKILL.md +0 -159
- package/container/skills/frontend-engineer/SKILL.md +0 -157
- package/container/skills/self-customize/SKILL.md +0 -87
- package/container/skills/slack-formatting/SKILL.md +0 -94
- package/container/skills/vercel-cli/SKILL.md +0 -111
- package/container/skills/welcome/SKILL.md +0 -85
- package/docs/APPLE-CONTAINER-NETWORKING.md +0 -90
- package/docs/BRANCH-FORK-MAINTENANCE.md +0 -81
- package/docs/README.md +0 -25
- package/docs/SDK_DEEP_DIVE.md +0 -643
- package/docs/SECURITY.md +0 -162
- package/docs/agent-runner-details.md +0 -749
- package/docs/api-details.md +0 -365
- package/docs/architecture-diagram.html +0 -422
- package/docs/architecture-diagram.md +0 -215
- package/docs/architecture.md +0 -751
- package/docs/audit/2026-04-30-channel-endpoint-audit.md +0 -36
- package/docs/build-and-runtime.md +0 -80
- package/docs/cross-mount-stress/README.md +0 -112
- package/docs/cross-mount-stress/container-writer-retry.mjs +0 -55
- package/docs/cross-mount-stress/container-writer-slow.mjs +0 -42
- package/docs/cross-mount-stress/container-writer.mjs +0 -47
- package/docs/cross-mount-stress/host-writer-retry.mjs +0 -55
- package/docs/cross-mount-stress/host-writer-slow.mjs +0 -43
- package/docs/cross-mount-stress/host-writer.mjs +0 -47
- package/docs/db-central.md +0 -316
- package/docs/db-session.md +0 -183
- package/docs/db.md +0 -119
- package/docs/design/2026-04-29-vault-management-ui.md +0 -231
- package/docs/design/2026-04-30-channel-wiring-rework.md +0 -234
- package/docs/design/2026-05-01-channel-wiring-approvals-deep-dive.md +0 -272
- package/docs/design/2026-05-02-channel-policy-and-approval-routing.md +0 -250
- package/docs/docker-sandboxes.md +0 -359
- package/docs/isolation-model.md +0 -88
- package/docs/ollama.md +0 -79
- package/docs/parachute-integration.md +0 -109
- package/docs/post-night-rebirth-reflections.md +0 -151
- package/eslint.config.js +0 -32
- package/pnpm-workspace.yaml +0 -8
- package/repo-tokens/README.md +0 -113
- package/repo-tokens/action.yml +0 -186
- package/repo-tokens/badge.svg +0 -23
- package/repo-tokens/examples/green.svg +0 -14
- package/repo-tokens/examples/red.svg +0 -14
- package/repo-tokens/examples/yellow-green.svg +0 -14
- package/repo-tokens/examples/yellow.svg +0 -14
- package/scripts/chat.ts +0 -101
- package/scripts/cleanup-sessions.sh +0 -150
- package/scripts/init-cli-agent.ts +0 -172
- package/scripts/init-first-agent.ts +0 -378
- package/scripts/parachute.ts +0 -158
- package/scripts/run-migrations.ts +0 -105
- package/scripts/sanity-live-poll.ts +0 -95
- package/scripts/seed-discord.ts +0 -80
- package/scripts/test-v2-agent.ts +0 -106
- package/scripts/test-v2-channel-e2e.ts +0 -265
- package/scripts/test-v2-host.ts +0 -184
- package/src/channels/adapter.ts +0 -214
- package/src/channels/api-translator.test.ts +0 -306
- package/src/channels/api-translator.ts +0 -214
- package/src/channels/ask-question.ts +0 -46
- package/src/channels/channel-registry.test.ts +0 -421
- package/src/channels/channel-registry.ts +0 -313
- package/src/channels/chat-sdk-bridge.test.ts +0 -84
- package/src/channels/chat-sdk-bridge.ts +0 -652
- package/src/channels/cli.ts +0 -276
- package/src/channels/discord.ts +0 -90
- package/src/channels/index.ts +0 -17
- package/src/channels/telegram-markdown-sanitize.test.ts +0 -78
- package/src/channels/telegram-markdown-sanitize.ts +0 -55
- package/src/channels/telegram-pairing.test.ts +0 -254
- package/src/channels/telegram-pairing.ts +0 -339
- package/src/channels/telegram.ts +0 -279
- package/src/channels/trust-hint.test.ts +0 -48
- package/src/channels/trust-hint.ts +0 -75
- package/src/claude-md-compose.migrate.test.ts +0 -64
- package/src/claude-md-compose.ts +0 -205
- package/src/command-gate.ts +0 -63
- package/src/config.test.ts +0 -93
- package/src/config.ts +0 -128
- package/src/container-config.ts +0 -167
- package/src/container-runner.test.ts +0 -32
- package/src/container-runner.ts +0 -576
- package/src/container-runtime.test.ts +0 -269
- package/src/container-runtime.ts +0 -167
- package/src/db/_bun-sqlite-shim.ts +0 -88
- package/src/db/agent-activity.test.ts +0 -155
- package/src/db/agent-activity.ts +0 -121
- package/src/db/agent-groups.ts +0 -77
- package/src/db/connection.migrate.test.ts +0 -176
- package/src/db/connection.ts +0 -259
- package/src/db/db-v2.test.ts +0 -440
- package/src/db/dropped-messages.ts +0 -44
- package/src/db/index.ts +0 -40
- package/src/db/messaging-groups.ts +0 -252
- package/src/db/migrations/001-initial.ts +0 -112
- package/src/db/migrations/002-chat-sdk-state.ts +0 -36
- package/src/db/migrations/008-dropped-messages.ts +0 -27
- package/src/db/migrations/009-drop-pending-credentials.ts +0 -13
- package/src/db/migrations/010-engage-modes.ts +0 -103
- package/src/db/migrations/011-pending-sender-approvals.ts +0 -40
- package/src/db/migrations/012-channel-registration.ts +0 -48
- package/src/db/migrations/013-approval-render-metadata.ts +0 -27
- package/src/db/migrations/014-secrets.ts +0 -44
- package/src/db/migrations/015-secrets-drop-host-pattern.ts +0 -18
- package/src/db/migrations/016-secret-assignments.ts +0 -30
- package/src/db/migrations/017-agent-activity.ts +0 -40
- package/src/db/migrations/018-oauth-app-configs.ts +0 -34
- package/src/db/migrations/019-oauth-app-connections.ts +0 -48
- package/src/db/migrations/020-agent-app-connections.ts +0 -28
- package/src/db/migrations/021-pending-oauth-states.ts +0 -35
- package/src/db/migrations/022-app-connections-provider.ts +0 -25
- package/src/db/migrations/023-agent-group-secret-mode.test.ts +0 -124
- package/src/db/migrations/023-agent-group-secret-mode.ts +0 -65
- package/src/db/migrations/024-collapse-approvals.test.ts +0 -249
- package/src/db/migrations/024-collapse-approvals.ts +0 -182
- package/src/db/migrations/025-secret-mode-check.test.ts +0 -155
- package/src/db/migrations/025-secret-mode-check.ts +0 -49
- package/src/db/migrations/026-user-dms-bot-id.test.ts +0 -116
- package/src/db/migrations/026-user-dms-bot-id.ts +0 -54
- package/src/db/migrations/027-provider-credentials.ts +0 -41
- package/src/db/migrations/_test-helpers.ts +0 -41
- package/src/db/migrations/index.ts +0 -127
- package/src/db/migrations/module-agent-to-agent-destinations.ts +0 -84
- package/src/db/migrations/module-approvals-pending-approvals.ts +0 -42
- package/src/db/migrations/module-approvals-title-options.ts +0 -40
- package/src/db/schema.ts +0 -258
- package/src/db/session-db.test.ts +0 -93
- package/src/db/session-db.ts +0 -325
- package/src/db/sessions.ts +0 -241
- package/src/delivery.test.ts +0 -148
- package/src/delivery.ts +0 -445
- package/src/env.ts +0 -74
- package/src/group-folder.test.ts +0 -35
- package/src/group-folder.ts +0 -44
- package/src/group-init.ts +0 -92
- package/src/host-core.test.ts +0 -456
- package/src/host-sweep.test.ts +0 -146
- package/src/host-sweep.ts +0 -287
- package/src/index.ts +0 -232
- package/src/install-slug.ts +0 -33
- package/src/log.test.ts +0 -81
- package/src/log.ts +0 -117
- package/src/mcp/http.ts +0 -72
- package/src/mcp/server.ts +0 -92
- package/src/mcp/stdio.ts +0 -51
- package/src/mcp/tools/activity.ts +0 -88
- package/src/mcp/tools/agent-groups.ts +0 -183
- package/src/mcp/tools/approvals.ts +0 -122
- package/src/mcp/tools/channels.test.ts +0 -126
- package/src/mcp/tools/channels.ts +0 -134
- package/src/mcp/tools/index.ts +0 -27
- package/src/mcp/tools/oauth.ts +0 -48
- package/src/mcp/tools/secrets.ts +0 -169
- package/src/mcp/tools/sessions.ts +0 -135
- package/src/mcp/types.ts +0 -51
- package/src/modules/agent-to-agent/agent-route.test.ts +0 -46
- package/src/modules/agent-to-agent/agent-route.ts +0 -223
- package/src/modules/agent-to-agent/create-agent.ts +0 -127
- package/src/modules/agent-to-agent/db/agent-destinations.ts +0 -135
- package/src/modules/agent-to-agent/index.ts +0 -22
- package/src/modules/agent-to-agent/write-destinations.ts +0 -59
- package/src/modules/approvals/agent.md +0 -45
- package/src/modules/approvals/index.ts +0 -21
- package/src/modules/approvals/picks.test.ts +0 -291
- package/src/modules/approvals/primitive.ts +0 -279
- package/src/modules/approvals/project.md +0 -27
- package/src/modules/approvals/response-handler.ts +0 -87
- package/src/modules/index.ts +0 -24
- package/src/modules/interactive/agent.md +0 -21
- package/src/modules/interactive/index.ts +0 -69
- package/src/modules/interactive/project.md +0 -12
- package/src/modules/mount-security/expand-path.test.ts +0 -82
- package/src/modules/mount-security/index.ts +0 -459
- package/src/modules/mount-security/migrate.test.ts +0 -91
- package/src/modules/permissions/access.ts +0 -28
- package/src/modules/permissions/channel-approval.test.ts +0 -389
- package/src/modules/permissions/channel-approval.ts +0 -188
- package/src/modules/permissions/db/agent-group-members.ts +0 -44
- package/src/modules/permissions/db/pending-channel-approvals.test.ts +0 -86
- package/src/modules/permissions/db/pending-channel-approvals.ts +0 -66
- package/src/modules/permissions/db/pending-sender-approvals.ts +0 -60
- package/src/modules/permissions/db/user-dms.ts +0 -58
- package/src/modules/permissions/db/user-roles.ts +0 -85
- package/src/modules/permissions/db/users.ts +0 -38
- package/src/modules/permissions/index.ts +0 -421
- package/src/modules/permissions/permissions.test.ts +0 -358
- package/src/modules/permissions/sender-approval.test.ts +0 -641
- package/src/modules/permissions/sender-approval.ts +0 -165
- package/src/modules/permissions/user-dm.ts +0 -200
- package/src/modules/provider-credentials/db.ts +0 -121
- package/src/modules/provider-credentials/index.ts +0 -12
- package/src/modules/provider-credentials/spawn.test.ts +0 -206
- package/src/modules/provider-credentials/spawn.ts +0 -114
- package/src/modules/scheduling/actions.ts +0 -113
- package/src/modules/scheduling/db.test.ts +0 -282
- package/src/modules/scheduling/db.ts +0 -148
- package/src/modules/scheduling/index.ts +0 -34
- package/src/modules/scheduling/recurrence.test.ts +0 -98
- package/src/modules/scheduling/recurrence.ts +0 -54
- package/src/modules/self-mod/agent.md +0 -30
- package/src/modules/self-mod/apply.ts +0 -85
- package/src/modules/self-mod/index.ts +0 -30
- package/src/modules/self-mod/project.md +0 -39
- package/src/modules/self-mod/request.ts +0 -91
- package/src/modules/typing/index.ts +0 -165
- package/src/oauth/agent-app-connections.ts +0 -103
- package/src/oauth/app-configs.test.ts +0 -64
- package/src/oauth/app-configs.ts +0 -114
- package/src/oauth/app-connections.test.ts +0 -109
- package/src/oauth/app-connections.ts +0 -178
- package/src/oauth/crypto.ts +0 -56
- package/src/oauth/flow.ts +0 -104
- package/src/oauth/providers/google.test.ts +0 -38
- package/src/oauth/providers/google.ts +0 -46
- package/src/oauth/providers/index.ts +0 -48
- package/src/oauth/state-store.test.ts +0 -54
- package/src/oauth/state-store.ts +0 -93
- package/src/parachute/README.md +0 -27
- package/src/parachute/create-agent.test.ts +0 -83
- package/src/parachute/create-agent.ts +0 -122
- package/src/parachute/group-status.test.ts +0 -165
- package/src/parachute/group-status.ts +0 -136
- package/src/parachute/types.ts +0 -41
- package/src/parachute/vault-mcp.test.ts +0 -251
- package/src/parachute/vault-mcp.ts +0 -232
- package/src/platform-id.test.ts +0 -104
- package/src/platform-id.ts +0 -109
- package/src/providers/index.ts +0 -6
- package/src/providers/provider-container-registry.ts +0 -58
- package/src/response-registry.ts +0 -45
- package/src/router.ts +0 -530
- package/src/secrets/crypto.test.ts +0 -45
- package/src/secrets/crypto.ts +0 -55
- package/src/secrets/index.ts +0 -461
- package/src/secrets/master-key.ts +0 -70
- package/src/secrets/secrets.test.ts +0 -651
- package/src/session-manager.attachments.test.ts +0 -171
- package/src/session-manager.dup-skip.test.ts +0 -173
- package/src/session-manager.migrate.test.ts +0 -59
- package/src/session-manager.ts +0 -451
- package/src/startup-bootstrap.test.ts +0 -226
- package/src/startup-bootstrap.ts +0 -207
- package/src/state-sqlite.ts +0 -182
- package/src/timezone.test.ts +0 -64
- package/src/timezone.ts +0 -37
- package/src/types.ts +0 -233
- package/src/web/auth.test.ts +0 -335
- package/src/web/auth.ts +0 -214
- package/src/web/discord-validate.test.ts +0 -77
- package/src/web/discord-validate.ts +0 -88
- package/src/web/hub-discovery.test.ts +0 -98
- package/src/web/hub-discovery.ts +0 -69
- package/src/web/routes/activity.ts +0 -106
- package/src/web/routes/agent-provider.test.ts +0 -282
- package/src/web/routes/agent-provider.ts +0 -309
- package/src/web/routes/approvals.ts +0 -185
- package/src/web/routes/apps.ts +0 -434
- package/src/web/routes/channels-mg-detail.test.ts +0 -324
- package/src/web/routes/channels-mga-detail.test.ts +0 -472
- package/src/web/routes/channels.ts +0 -311
- package/src/web/routes/oauth-providers.ts +0 -42
- package/src/web/routes/secrets.test.ts +0 -220
- package/src/web/routes/secrets.ts +0 -317
- package/src/web/routes/sessions.ts +0 -123
- package/src/web/routes/settings.test.ts +0 -106
- package/src/web/routes/settings.ts +0 -247
- package/src/web/routes/setup-status.ts +0 -205
- package/src/web/routes/vaults.test.ts +0 -389
- package/src/web/routes/vaults.ts +0 -225
- package/src/web/server-version.test.ts +0 -16
- package/src/web/server.ts +0 -1024
- package/src/web/services-manifest.test.ts +0 -148
- package/src/web/services-manifest.ts +0 -66
- package/src/web/static-serve.test.ts +0 -255
- package/src/web/static-serve.ts +0 -104
- package/src/web/telegram-validate.test.ts +0 -116
- package/src/web/telegram-validate.ts +0 -107
- package/src/web/vault-proxy.test.ts +0 -214
- package/src/web/vault-proxy.ts +0 -120
- package/src/web/wire-channel.ts +0 -181
- package/src/webhook-server.ts +0 -134
- package/vitest.config.ts +0 -18
- package/web/README.md +0 -63
- package/web/ui/index.html +0 -13
- package/web/ui/package.json +0 -35
- package/web/ui/pnpm-lock.yaml +0 -2164
- package/web/ui/scripts/verify-base.mjs +0 -31
- package/web/ui/src/App.tsx +0 -88
- package/web/ui/src/components/ActivityFeed.tsx +0 -444
- package/web/ui/src/components/AgentGroupPicker.tsx +0 -263
- package/web/ui/src/components/AgentProviderCards.tsx +0 -220
- package/web/ui/src/components/CredentialForm.tsx +0 -214
- package/web/ui/src/components/ScopeGrants.tsx +0 -74
- package/web/ui/src/components/StatusDot.tsx +0 -43
- package/web/ui/src/components/VaultPicker.tsx +0 -127
- package/web/ui/src/components/setup/AdapterInstallStep.tsx +0 -178
- package/web/ui/src/components/setup/AgentGroupStep.tsx +0 -43
- package/web/ui/src/components/setup/ChannelPickStep.tsx +0 -74
- package/web/ui/src/components/setup/DoneStep.tsx +0 -49
- package/web/ui/src/components/setup/PrereqStep.tsx +0 -129
- package/web/ui/src/components/setup/TestConnectionStep.tsx +0 -108
- package/web/ui/src/components/setup/TestMessageStep.tsx +0 -104
- package/web/ui/src/components/setup/WireChannelStep.tsx +0 -166
- package/web/ui/src/components/setup/types.ts +0 -105
- package/web/ui/src/lib/api.test.ts +0 -410
- package/web/ui/src/lib/api.ts +0 -1248
- package/web/ui/src/lib/auth.test.ts +0 -352
- package/web/ui/src/lib/auth.ts +0 -405
- package/web/ui/src/lib/channel-adapters.ts +0 -136
- package/web/ui/src/main.tsx +0 -19
- package/web/ui/src/routes/ApprovalsList.tsx +0 -294
- package/web/ui/src/routes/Apps.tsx +0 -613
- package/web/ui/src/routes/ChannelWireDetail.test.tsx +0 -233
- package/web/ui/src/routes/ChannelWireDetail.tsx +0 -403
- package/web/ui/src/routes/ChannelsList.tsx +0 -158
- package/web/ui/src/routes/GroupDetail.test.tsx +0 -206
- package/web/ui/src/routes/GroupDetail.tsx +0 -880
- package/web/ui/src/routes/GroupList.tsx +0 -187
- package/web/ui/src/routes/MessagingGroupDetail.test.tsx +0 -233
- package/web/ui/src/routes/MessagingGroupDetail.tsx +0 -306
- package/web/ui/src/routes/NewGroupWizard.tsx +0 -390
- package/web/ui/src/routes/OAuthCallback.tsx +0 -56
- package/web/ui/src/routes/SecretsList.tsx +0 -942
- package/web/ui/src/routes/SessionsList.tsx +0 -220
- package/web/ui/src/routes/SettingsAgentProvider.tsx +0 -109
- package/web/ui/src/routes/SettingsApprovals.tsx +0 -234
- package/web/ui/src/routes/SetupWizard.tsx +0 -219
- package/web/ui/src/routes/VaultDetail.test.tsx +0 -363
- package/web/ui/src/routes/VaultDetail.tsx +0 -960
- package/web/ui/src/routes/VaultsList.tsx +0 -295
- package/web/ui/src/routes/WireChannelPage.tsx +0 -413
- package/web/ui/src/styles.css +0 -608
- package/web/ui/src/test/setup.ts +0 -23
- package/web/ui/src/vite-env.d.ts +0 -10
- package/web/ui/vite.config.ts +0 -34
- package/web/ui/vitest.config.ts +0 -25
package/docs/architecture.md
DELETED
|
@@ -1,751 +0,0 @@
|
|
|
1
|
-
# parachute-agent Architecture
|
|
2
|
-
|
|
3
|
-
> parachute-agent is a single-process Bun service that runs an AI agent companion
|
|
4
|
-
> across many channels — Telegram, Discord, CLI, web. State lives in
|
|
5
|
-
> SQLite — one central DB on the host plus a small per-session DB mounted
|
|
6
|
-
> into each container. Per-session Docker containers sandbox agent
|
|
7
|
-
> execution; secrets are managed natively at rest in AES-256-GCM.
|
|
8
|
-
> parachute-agent is a first-class Parachute module: it discovers the hub,
|
|
9
|
-
> accepts hub-issued JWTs, and attaches Parachute vaults so agents can
|
|
10
|
-
> read and write the user's knowledge graph.
|
|
11
|
-
|
|
12
|
-
This document is the canonical architecture reference for parachute-agent
|
|
13
|
-
(formerly paraclaw, renamed in 0.1.0). It defines the primitives every
|
|
14
|
-
other doc references, the runtime shape, the schema, the API surface, the
|
|
15
|
-
trust model, and the integration contract with the rest of the Parachute
|
|
16
|
-
ecosystem. It is aimed at contributors who want to understand or extend
|
|
17
|
-
parachute-agent — not at users picking it up for the first time. For that,
|
|
18
|
-
start at the project README.
|
|
19
|
-
|
|
20
|
-
parachute-agent draws engine-room patterns from NanoClaw (per-session
|
|
21
|
-
containers, channel adapters, the message-as-IO discipline), from OneCLI
|
|
22
|
-
(per-agent secret modes, approval gating), and from tinyclaw/borg
|
|
23
|
-
(single-process collapse, in-process secrets, security zones). None of those
|
|
24
|
-
are dependencies of parachute-agent v1; parachute-agent owns its surface end
|
|
25
|
-
to end.
|
|
26
|
-
|
|
27
|
-
## Shape at a glance
|
|
28
|
-
|
|
29
|
-
```
|
|
30
|
-
┌──────────────────────────────────────────────┐
|
|
31
|
-
│ parachute-agent (single Bun process, 1944) │
|
|
32
|
-
│ │
|
|
33
|
-
inbound ─────▶ channel adapters ──▶ router ──▶ session manager
|
|
34
|
-
(telegram, │ (telegram, │ │ │
|
|
35
|
-
discord, │ discord, │ ▼ │
|
|
36
|
-
cli, web) │ cli) │ sessions row │
|
|
37
|
-
│ ▲ │ │ │
|
|
38
|
-
│ │ │ ▼ │
|
|
39
|
-
│ outbound │ container-runner
|
|
40
|
-
│ delivery ◀──┐ │ │ │
|
|
41
|
-
│ │ │ ▼ │
|
|
42
|
-
│ host sweep │ │ docker container
|
|
43
|
-
│ (60s) │ │ │ │
|
|
44
|
-
│ │ │ mounts inbound.db (RO) │
|
|
45
|
-
│ │ │ mounts outbound.db (RW)│
|
|
46
|
-
│ ▲──────┴────┴─────────────▼ │
|
|
47
|
-
│ central agent.db per-session DBs│
|
|
48
|
-
│ (host-only writer) (one writer │
|
|
49
|
-
│ per file) │
|
|
50
|
-
│ agent_groups · sessions inbound.db: │
|
|
51
|
-
│ messaging_groups · channels messages_in │
|
|
52
|
-
│ secrets · approvals outbound.db: │
|
|
53
|
-
│ users · user_roles messages_out│
|
|
54
|
-
│ session_state│
|
|
55
|
-
└──────────────────────────────────────────────┘
|
|
56
|
-
▲ ▲
|
|
57
|
-
│ JWT │ MCP
|
|
58
|
-
│ │
|
|
59
|
-
Parachute hub Parachute vault(s)
|
|
60
|
-
(OAuth issuer) (knowledge graph)
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
Everything is a message. The host writes inbound rows into the session's
|
|
64
|
-
`messages_in`; the agent-runner inside the container reads them, calls
|
|
65
|
-
Claude, and writes `messages_out`. The host sweeps `messages_out` and
|
|
66
|
-
delivers via the same adapter that received the inbound. There is no
|
|
67
|
-
IPC, no stdin piping, no file watcher — only rows in SQLite and a
|
|
68
|
-
heartbeat file.
|
|
69
|
-
|
|
70
|
-
## Primitives
|
|
71
|
-
|
|
72
|
-
The schema is the contract. Most primitives below correspond to a table
|
|
73
|
-
in the **central** `~/.parachute/agent/agent.db`. The message-bus
|
|
74
|
-
tables — `messages_in`, `messages_out`, `session_state` — live in
|
|
75
|
-
**per-session** files under `data/sessions/<agent_group_id>/<session_id>/`, split into
|
|
76
|
-
`inbound.db` and `outbound.db` so each file has exactly one writer
|
|
77
|
-
across the bind mount. The §Schema section explains why; the TypeScript
|
|
78
|
-
types are in `src/db/`.
|
|
79
|
-
|
|
80
|
-
### Agent group
|
|
81
|
-
|
|
82
|
-
A workspace plus an agent identity — the unit a user creates ("my Notes
|
|
83
|
-
assistant", "my Code helper"). Every other primitive hangs off an agent
|
|
84
|
-
group.
|
|
85
|
-
|
|
86
|
-
| Field | Notes |
|
|
87
|
-
|---|---|
|
|
88
|
-
| `id` | stable string, ULID-shaped |
|
|
89
|
-
| `name` | human-facing display name |
|
|
90
|
-
| `folder` | filesystem slug under `groups/<folder>/` (CLAUDE.md, skills, mounts) |
|
|
91
|
-
| `instructions` | agent CLAUDE.md content (composed at container spawn) |
|
|
92
|
-
| `agent_provider` | `claude` (default), `opencode` |
|
|
93
|
-
| `container_config_json` | mounts, env, image base, install_packages, mcp servers |
|
|
94
|
-
| `created_at`, `updated_at` | ISO-8601 |
|
|
95
|
-
|
|
96
|
-
An agent group owns zero or more **vault attachments**, zero or more
|
|
97
|
-
**channel wires**, zero or more **sessions**, and any **secrets** scoped
|
|
98
|
-
to it. Deleting an agent group cascades through all of them.
|
|
99
|
-
|
|
100
|
-
### Session
|
|
101
|
-
|
|
102
|
-
A live conversation state for an agent group. Every session has a
|
|
103
|
-
per-session Docker container. Sessions are how parachute-agent scales context
|
|
104
|
-
horizontally — each session has its own Claude conversation, its own
|
|
105
|
-
`messages_in` / `messages_out` slices, its own container resource budget.
|
|
106
|
-
|
|
107
|
-
| Field | Notes |
|
|
108
|
-
|---|---|
|
|
109
|
-
| `id` | ULID |
|
|
110
|
-
| `agent_group_id` | foreign key to `agent_groups.id` |
|
|
111
|
-
| `messaging_group_id` | nullable; the platform thread this session serves |
|
|
112
|
-
| `mode` | `agent-shared` · `shared` · `per-thread` |
|
|
113
|
-
| `status` | `active` · `idle` · `closed` |
|
|
114
|
-
| `created_at`, `last_active_at` | ISO-8601 |
|
|
115
|
-
| `container_state_json` | last known runtime state, image tag, last heartbeat |
|
|
116
|
-
|
|
117
|
-
The three session modes capture the channel-isolation choices a user makes
|
|
118
|
-
when wiring a platform to an agent group. `agent-shared` means one session
|
|
119
|
-
per agent group across all channels (every message lands in the same
|
|
120
|
-
context). `shared` means one session per messaging group (a Slack channel
|
|
121
|
-
or a Discord guild). `per-thread` means one session per platform thread
|
|
122
|
-
(every Slack thread or GitHub PR comment chain is its own conversation).
|
|
123
|
-
The mode is stored on the session row and respected by the router.
|
|
124
|
-
|
|
125
|
-
### Channel wire
|
|
126
|
-
|
|
127
|
-
A binding from a messaging-platform thread to an agent group. The router
|
|
128
|
-
turns inbound platform events into `messages_in` rows by looking up the
|
|
129
|
-
matching wire.
|
|
130
|
-
|
|
131
|
-
| Field | Notes |
|
|
132
|
-
|---|---|
|
|
133
|
-
| `id` | ULID |
|
|
134
|
-
| `channel_type` | `telegram` · `discord` · `cli` (others land as plugins later) |
|
|
135
|
-
| `messaging_group_id` | host-internal id for the platform thread |
|
|
136
|
-
| `agent_group_id` | foreign key |
|
|
137
|
-
| `engage_mode` | `mention` · `pattern` · `all` |
|
|
138
|
-
| `engage_pattern` | regex (when `engage_mode = pattern`) |
|
|
139
|
-
| `sender_scope` | `allowlist` · `all` |
|
|
140
|
-
| `ignored_message_policy` | `drop` · `silent` |
|
|
141
|
-
| `priority` | tie-breaker when multiple wires match |
|
|
142
|
-
|
|
143
|
-
Wires are created via the API — by the setup wizard during install, by
|
|
144
|
-
the web UI later, or programmatically. There is no on-disk config file
|
|
145
|
-
that has to stay in sync; the database is the source of truth.
|
|
146
|
-
|
|
147
|
-
### Vault attachment
|
|
148
|
-
|
|
149
|
-
An OAuth-bound credential that lets an agent group's container reach a
|
|
150
|
-
Parachute vault over MCP. Vault attachments are how agents read and write
|
|
151
|
-
the user's knowledge graph from inside a sandboxed session.
|
|
152
|
-
|
|
153
|
-
Storage is filesystem-scoped, not database-scoped. Two files per group:
|
|
154
|
-
|
|
155
|
-
- `groups/<folder>/container.json` — the MCP entry the container reads.
|
|
156
|
-
The bearer lives in the `Authorization: Bearer <pvt_…>` header.
|
|
157
|
-
- `groups/<folder>/parachute.json` — host-side metadata: `vaultBaseUrl`,
|
|
158
|
-
`vault_name`, `scope` (`vault:read|write|admin`), `tokenLabel`,
|
|
159
|
-
`attachedAt`. No bearer here — that's only in `container.json`.
|
|
160
|
-
|
|
161
|
-
The bearer is minted via parachute-hub's OAuth flow (hub-as-issuer; see
|
|
162
|
-
`design/2026-04-20-hub-as-portal-oauth-and-service-catalog.md` in
|
|
163
|
-
`parachute.computer`). The token sits plaintext on disk in
|
|
164
|
-
`container.json` and inside the container at `/workspace/agent/container.json`
|
|
165
|
-
(read-only mount). Access control is filesystem permissions on the group
|
|
166
|
-
folder — the bearer is **not** AES-encrypted. Outbound third-party API
|
|
167
|
-
keys go through the encrypted `secrets` layer; vault tokens deliberately
|
|
168
|
-
do not, because the MCP entry must be world-readable to the in-container
|
|
169
|
-
Claude Agent SDK at spawn time. See `docs/parachute-integration.md` for
|
|
170
|
-
the full flow.
|
|
171
|
-
|
|
172
|
-
### Secret
|
|
173
|
-
|
|
174
|
-
An encrypted credential value managed by parachute-agent. Secrets replace
|
|
175
|
-
OneCLI-as-a-dependency for the v1 line: parachute-agent owns the layer. The
|
|
176
|
-
threat model and the on-disk format are described in `docs/SECURITY.md`;
|
|
177
|
-
the schema is summarized here.
|
|
178
|
-
|
|
179
|
-
| Field | Notes |
|
|
180
|
-
|---|---|
|
|
181
|
-
| `id` | ULID |
|
|
182
|
-
| `name` | unique within `agent_group_id` scope (or globally if null) |
|
|
183
|
-
| `value_encrypted` | AES-256-GCM, key from `~/.parachute/agent/master.key` |
|
|
184
|
-
| `kind` | `channel-token` · `api-key` · `generic` |
|
|
185
|
-
| `agent_group_id` | nullable; null = global |
|
|
186
|
-
| `assigned_mode` | `all` · `selective` (per-agent injection scope) |
|
|
187
|
-
| `created_at`, `updated_at` | ISO-8601 |
|
|
188
|
-
|
|
189
|
-
The master key lives at `~/.parachute/agent/master.key` (32 bytes, mode
|
|
190
|
-
0600, generated on first start). Secrets are decrypted in-process at
|
|
191
|
-
session spawn and injected into the container as environment variables —
|
|
192
|
-
never as chat context, never in URL params.
|
|
193
|
-
|
|
194
|
-
### Approval
|
|
195
|
-
|
|
196
|
-
A pending action that requires human consent before parachute-agent will apply
|
|
197
|
-
it. Today: `install_packages`, `add_mcp_server`, `access-new-credential`.
|
|
198
|
-
The primitive is general — any host-side handler can request an approval.
|
|
199
|
-
|
|
200
|
-
| Field | Notes |
|
|
201
|
-
|---|---|
|
|
202
|
-
| `id` | ULID |
|
|
203
|
-
| `agent_group_id` | foreign key |
|
|
204
|
-
| `kind` | string identifier matched to a registered handler |
|
|
205
|
-
| `action_payload_json` | the action to apply on approve |
|
|
206
|
-
| `status` | `pending` · `approved` · `rejected` · `expired` |
|
|
207
|
-
| `requested_at`, `decided_at` | ISO-8601 |
|
|
208
|
-
| `requested_by_session` | foreign key to `sessions.id` |
|
|
209
|
-
| `delivery_state_json` | how the approval was surfaced (UI, channel DM) |
|
|
210
|
-
|
|
211
|
-
Approvals surface in the web UI's notifications panel and via channel DM
|
|
212
|
-
to the agent group's owner — `pickApprover` resolves the recipient from
|
|
213
|
-
`user_roles` (scoped admin → global admin → owner). Crucially, parachute-agent
|
|
214
|
-
makes the policy and the delivery one decision: there is no separate
|
|
215
|
-
gateway process holding requests while the host doesn't know about them.
|
|
216
|
-
If approvals are configured, they are routed; if they are not configured,
|
|
217
|
-
nothing hangs.
|
|
218
|
-
|
|
219
|
-
## Trust zones
|
|
220
|
-
|
|
221
|
-
parachute-agent inherits the **infra / core / perimeter** vocabulary from borg.
|
|
222
|
-
Three named tiers, with explicit cross-zone rules:
|
|
223
|
-
|
|
224
|
-
- **infra** — parachute-agent itself, channel adapters, the host process, the
|
|
225
|
-
router, the sweep loop. Deterministic plumbing. Non-messageable: an agent
|
|
226
|
-
cannot send a chat message *to* infra; it can only request actions
|
|
227
|
-
through documented MCP tools. Code in `src/` outside `src/modules/`
|
|
228
|
-
generally lives here.
|
|
229
|
-
- **core** — agent groups and their sessions doing user work with
|
|
230
|
-
credentials. The container is the boundary. Per-session Docker
|
|
231
|
-
isolation is the primary enforcement: each container mounts only its
|
|
232
|
-
own `inbound.db` (read-only) and `outbound.db` (read-write), so
|
|
233
|
-
cleartext message rows are visible only to that session's container —
|
|
234
|
-
one core actor cannot read another's queue. The central `agent.db`
|
|
235
|
-
is *never* mounted into a container; it is host-only state.
|
|
236
|
-
- **perimeter** — exposed surfaces. The web UI's `/api/*` endpoints, the
|
|
237
|
-
channel inbound webhooks, anything an external actor can reach. Every
|
|
238
|
-
perimeter entry point authenticates (hub-issued JWT for `/api/*`,
|
|
239
|
-
signature-verified webhooks for channels) before crossing into infra.
|
|
240
|
-
|
|
241
|
-
Cross-zone actions require approval; same-zone actions are direct. In v1
|
|
242
|
-
this is vocabulary plus three load-bearing checks: hub-issued JWT on
|
|
243
|
-
every perimeter route, the per-session container/DB mount on every core
|
|
244
|
-
action, and approval-on-credentialed-action wherever core asks infra to
|
|
245
|
-
do something irreversible (install a package, attach a vault, mount a
|
|
246
|
-
new path). A richer enforcement matrix is deferred to v2; this is the
|
|
247
|
-
v1 contract.
|
|
248
|
-
|
|
249
|
-
## Runtime model
|
|
250
|
-
|
|
251
|
-
### Single-process Bun host
|
|
252
|
-
|
|
253
|
-
parachute-agent is **one Bun process**. No host-Node-plus-container-Bun split,
|
|
254
|
-
no separate web server. The process owns:
|
|
255
|
-
|
|
256
|
-
1. The HTTP listener on port 1944 (configurable via parachute hub
|
|
257
|
-
port-authority — `paths: ["/claw"]` in `.parachute/module.json`).
|
|
258
|
-
2. The web UI bundle (Vite + React + TS), served from `web/ui/dist/` under
|
|
259
|
-
the mount prefix.
|
|
260
|
-
3. The router (`src/router.ts`), which converts platform events into
|
|
261
|
-
`messages_in` rows and wakes the appropriate session.
|
|
262
|
-
4. The delivery loop (`src/delivery.ts`), which reads `messages_out` and
|
|
263
|
-
delivers through channel adapters.
|
|
264
|
-
5. The host sweep (`src/host-sweep.ts`), a 60-second tick that handles
|
|
265
|
-
stale processing, due-message wakeups, and recurrence.
|
|
266
|
-
6. The container-runner (`src/container-runner.ts`), which spawns and
|
|
267
|
-
reaps per-session Docker containers.
|
|
268
|
-
7. The secret store (in-process AES-256-GCM unwrap).
|
|
269
|
-
|
|
270
|
-
The Bun-everywhere choice is deliberate. NanoClaw ran the host on Node
|
|
271
|
-
and the agent-runner on Bun, which works but leaves two lockfiles and two
|
|
272
|
-
test-runner conventions. parachute-agent collapses to Bun: `bun:sqlite` on both
|
|
273
|
-
sides, one `bun.lock`, one `bun test`. `.parachute/module.json` therefore
|
|
274
|
-
sets `startCmd: ["bun", "src/index.ts"]` — the legacy
|
|
275
|
-
`pnpm exec tsx web/server/src/server.ts` path is dropped once the web
|
|
276
|
-
server folds into the same process.
|
|
277
|
-
|
|
278
|
-
### Per-session Docker containers
|
|
279
|
-
|
|
280
|
-
Each session gets a container. The image (`parachute-agent-image-<slug>:latest`) is
|
|
281
|
-
built once by `./container/build.sh`; the runtime mounts the agent
|
|
282
|
-
group's folder, the session's `inbound.db` (read-only) and
|
|
283
|
-
`outbound.db` (read-write) at known paths under `/workspace/` — and
|
|
284
|
-
nothing else from the host's data tree — plus a writable workspace.
|
|
285
|
-
Secrets land as env vars at container start, decrypted in-process by
|
|
286
|
-
the container-runner from the central DB.
|
|
287
|
-
|
|
288
|
-
The container is the trust boundary. It runs as an unprivileged user
|
|
289
|
-
inside the container, has only the mounts the agent group config grants,
|
|
290
|
-
and reaches the network only for hosts the host's outbound proxy allows
|
|
291
|
-
(default: vault attachments + provider APIs). `docker` is the runtime by
|
|
292
|
-
default; `apple-container` is supported on macOS and selected when
|
|
293
|
-
configured. See `src/container-runtime.ts` for the runtime selection.
|
|
294
|
-
|
|
295
|
-
Containers are spawned on demand: when the host writes the first
|
|
296
|
-
`messages_in` row for a session and no container is running, the runner
|
|
297
|
-
calls `docker run` and the agent-runner inside immediately starts
|
|
298
|
-
polling. After a configurable idle timeout the container is reaped; the
|
|
299
|
-
next message respawns it. Sessions are durable across container
|
|
300
|
-
lifecycles — containers are not.
|
|
301
|
-
|
|
302
|
-
### How the host and container talk
|
|
303
|
-
|
|
304
|
-
Through SQLite — but **not** through the central DB. Each session has
|
|
305
|
-
two per-session files under `data/sessions/<agent_group_id>/<session_id>/`:
|
|
306
|
-
|
|
307
|
-
- `inbound.db` — **host writes, container reads.** Mounted into the
|
|
308
|
-
container read-only at `/workspace/.inbound.db`. Holds `messages_in`.
|
|
309
|
-
- `outbound.db` — **container writes, host reads.** Mounted into the
|
|
310
|
-
container read-write at `/workspace/.outbound.db`. Holds
|
|
311
|
-
`messages_out` and `session_state`.
|
|
312
|
-
|
|
313
|
-
The split is *load-bearing*: it gives every file exactly one writer
|
|
314
|
-
across the bind mount. The central `agent.db` never enters a
|
|
315
|
-
container; the host writes new inbound rows into the session's
|
|
316
|
-
`inbound.db` before it spawns or wakes the container, and the host's
|
|
317
|
-
sweep and active-poll loops open and close `outbound.db` to read
|
|
318
|
-
delivered messages.
|
|
319
|
-
|
|
320
|
-
Three cross-mount invariants are load-bearing on every per-session file
|
|
321
|
-
(`src/session-manager.ts:5–11`):
|
|
322
|
-
|
|
323
|
-
1. `journal_mode=DELETE` — WAL's mmapped `-shm` file does not refresh
|
|
324
|
-
from host writes to guest reads across a Docker bind mount. The
|
|
325
|
-
container would silently miss every new message. DELETE-mode rollback
|
|
326
|
-
journals work; WAL does not.
|
|
327
|
-
2. The host opens, writes, and **closes** the file on every operation.
|
|
328
|
-
A long-lived connection freezes the other side's page cache at first
|
|
329
|
-
read, so closing is what invalidates the cross-mount view.
|
|
330
|
-
3. **Exactly one writer per file.** DELETE-mode journal-unlink is not
|
|
331
|
-
atomic across the mount, so concurrent writers corrupt the DB.
|
|
332
|
-
`inbound.db` is host-write-only; `outbound.db` is
|
|
333
|
-
container-write-only. There is no path where both sides hold a write
|
|
334
|
-
handle to the same file.
|
|
335
|
-
|
|
336
|
-
Session liveness is signalled by a heartbeat file at
|
|
337
|
-
`/workspace/.heartbeat` — touched by the agent-runner on every loop
|
|
338
|
-
tick. The host reads its mtime to detect stale containers without
|
|
339
|
-
touching either DB. There is no `last_heartbeat` column.
|
|
340
|
-
|
|
341
|
-
This is why the v1 schema is **split**, not unified. Both tinyclaw and
|
|
342
|
-
borg pointed at "drop the two-DB session model" as a NanoClaw
|
|
343
|
-
simplification target — but neither validated dropping the split across
|
|
344
|
-
a Docker bind mount. tinyclaw is single-process (no mount in the IPC
|
|
345
|
-
path); borg uses a JSONL file-queue (no SQLite at all). Their single-DB
|
|
346
|
-
collapse applies to single-process state, not to container-mounted
|
|
347
|
-
message queues with two writers. parachute-agent keeps NanoClaw's two-file
|
|
348
|
-
per-session shape verbatim and adds the central `agent.db`
|
|
349
|
-
extraction on top — central state is host-only, message queues stay
|
|
350
|
-
split for one-writer-per-file.
|
|
351
|
-
|
|
352
|
-
## Schema
|
|
353
|
-
|
|
354
|
-
State splits across two SQLite surfaces, by *who can write to what*:
|
|
355
|
-
|
|
356
|
-
- **Central** `~/.parachute/agent/agent.db` — host-only writer. Never
|
|
357
|
-
mounted into a container. Holds every primitive that isn't a live
|
|
358
|
-
message queue.
|
|
359
|
-
- **Per-session** under `data/sessions/<agent_group_id>/<session_id>/` — two files:
|
|
360
|
-
`inbound.db` (host writes, container reads) and `outbound.db`
|
|
361
|
-
(container writes, host reads). Both run in `journal_mode=DELETE` and
|
|
362
|
-
obey the one-writer-per-file rule. See "How the host and container
|
|
363
|
-
talk" above for the cross-mount reasoning.
|
|
364
|
-
|
|
365
|
-
Migrations for all three surfaces live under `src/db/migrations/` and
|
|
366
|
-
run on host start. The central migrations apply once; the per-session
|
|
367
|
-
schemas are created on session spawn.
|
|
368
|
-
|
|
369
|
-
### Central `agent.db`
|
|
370
|
-
|
|
371
|
-
```sql
|
|
372
|
-
-- the unit a user creates
|
|
373
|
-
CREATE TABLE agent_groups (
|
|
374
|
-
id TEXT PRIMARY KEY,
|
|
375
|
-
name TEXT NOT NULL,
|
|
376
|
-
folder TEXT NOT NULL UNIQUE,
|
|
377
|
-
instructions TEXT,
|
|
378
|
-
agent_provider TEXT NOT NULL DEFAULT 'claude',
|
|
379
|
-
container_config_json TEXT NOT NULL,
|
|
380
|
-
created_at TEXT NOT NULL,
|
|
381
|
-
updated_at TEXT NOT NULL
|
|
382
|
-
);
|
|
383
|
-
|
|
384
|
-
-- a live conversation; one container per row.
|
|
385
|
-
-- Lifecycle metadata only — the live message queue lives in the
|
|
386
|
-
-- per-session inbound.db / outbound.db, never here.
|
|
387
|
-
CREATE TABLE sessions (
|
|
388
|
-
id TEXT PRIMARY KEY,
|
|
389
|
-
agent_group_id TEXT NOT NULL REFERENCES agent_groups(id) ON DELETE CASCADE,
|
|
390
|
-
messaging_group_id TEXT REFERENCES messaging_groups(id) ON DELETE SET NULL,
|
|
391
|
-
mode TEXT NOT NULL,
|
|
392
|
-
status TEXT NOT NULL DEFAULT 'active',
|
|
393
|
-
container_state_json TEXT,
|
|
394
|
-
created_at TEXT NOT NULL,
|
|
395
|
-
last_active_at TEXT NOT NULL
|
|
396
|
-
);
|
|
397
|
-
|
|
398
|
-
-- platform-thread identity (one row per Slack channel, Telegram chat, etc.)
|
|
399
|
-
CREATE TABLE messaging_groups (
|
|
400
|
-
id TEXT PRIMARY KEY,
|
|
401
|
-
channel_type TEXT NOT NULL,
|
|
402
|
-
platform_id TEXT NOT NULL,
|
|
403
|
-
thread_id TEXT,
|
|
404
|
-
display_name TEXT,
|
|
405
|
-
created_at TEXT NOT NULL,
|
|
406
|
-
UNIQUE (channel_type, platform_id, thread_id)
|
|
407
|
-
);
|
|
408
|
-
|
|
409
|
-
-- the routing rules from a messaging group to an agent group
|
|
410
|
-
CREATE TABLE channel_wires (
|
|
411
|
-
id TEXT PRIMARY KEY,
|
|
412
|
-
channel_type TEXT NOT NULL,
|
|
413
|
-
messaging_group_id TEXT NOT NULL REFERENCES messaging_groups(id) ON DELETE CASCADE,
|
|
414
|
-
agent_group_id TEXT NOT NULL REFERENCES agent_groups(id) ON DELETE CASCADE,
|
|
415
|
-
engage_mode TEXT NOT NULL DEFAULT 'all',
|
|
416
|
-
engage_pattern TEXT,
|
|
417
|
-
sender_scope TEXT NOT NULL DEFAULT 'all',
|
|
418
|
-
ignored_message_policy TEXT NOT NULL DEFAULT 'drop',
|
|
419
|
-
priority INTEGER NOT NULL DEFAULT 0,
|
|
420
|
-
created_at TEXT NOT NULL
|
|
421
|
-
);
|
|
422
|
-
|
|
423
|
-
-- vault attachments are NOT in the central DB — they live on disk under
|
|
424
|
-
-- groups/<folder>/parachute.json (metadata) and groups/<folder>/container.json
|
|
425
|
-
-- (the actual MCP entry with bearer token in the Authorization header).
|
|
426
|
-
-- The bearer is plaintext on disk; access control is filesystem permissions
|
|
427
|
-
-- on the group folder, not encryption at rest. See docs/parachute-integration.md.
|
|
428
|
-
|
|
429
|
-
-- secrets at rest (AES-256-GCM)
|
|
430
|
-
CREATE TABLE secrets (
|
|
431
|
-
id TEXT PRIMARY KEY,
|
|
432
|
-
name TEXT NOT NULL,
|
|
433
|
-
value_encrypted BLOB NOT NULL,
|
|
434
|
-
kind TEXT NOT NULL,
|
|
435
|
-
agent_group_id TEXT REFERENCES agent_groups(id) ON DELETE CASCADE,
|
|
436
|
-
assigned_mode TEXT NOT NULL DEFAULT 'all',
|
|
437
|
-
created_at TEXT NOT NULL,
|
|
438
|
-
updated_at TEXT NOT NULL,
|
|
439
|
-
UNIQUE (agent_group_id, name)
|
|
440
|
-
);
|
|
441
|
-
|
|
442
|
-
-- pending approvals (host-mediated)
|
|
443
|
-
CREATE TABLE approvals (
|
|
444
|
-
id TEXT PRIMARY KEY,
|
|
445
|
-
agent_group_id TEXT NOT NULL REFERENCES agent_groups(id) ON DELETE CASCADE,
|
|
446
|
-
kind TEXT NOT NULL,
|
|
447
|
-
action_payload_json TEXT NOT NULL,
|
|
448
|
-
status TEXT NOT NULL DEFAULT 'pending',
|
|
449
|
-
requested_at TEXT NOT NULL,
|
|
450
|
-
decided_at TEXT,
|
|
451
|
-
requested_by_session TEXT REFERENCES sessions(id) ON DELETE SET NULL,
|
|
452
|
-
delivery_state_json TEXT
|
|
453
|
-
);
|
|
454
|
-
|
|
455
|
-
-- minimal user identity for ownership and approvals
|
|
456
|
-
CREATE TABLE users (
|
|
457
|
-
id TEXT PRIMARY KEY, -- "<channel>:<handle>" or hub user id
|
|
458
|
-
kind TEXT NOT NULL,
|
|
459
|
-
display_name TEXT,
|
|
460
|
-
created_at TEXT NOT NULL
|
|
461
|
-
);
|
|
462
|
-
|
|
463
|
-
CREATE TABLE user_roles (
|
|
464
|
-
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
465
|
-
role TEXT NOT NULL, -- 'owner' | 'admin'
|
|
466
|
-
agent_group_id TEXT REFERENCES agent_groups(id) ON DELETE CASCADE,
|
|
467
|
-
PRIMARY KEY (user_id, role, agent_group_id)
|
|
468
|
-
);
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
The user/user_roles pair is intentionally minimal compared to NanoClaw's
|
|
472
|
-
four-table model. tinyclaw and borg both pointed out that the
|
|
473
|
-
`users → messaging_groups → agent_groups → sessions` chain was
|
|
474
|
-
gold-plating — parachute-agent keeps users only because approvals need a
|
|
475
|
-
recipient, and roles only because owner-vs-admin is load-bearing for
|
|
476
|
-
those approvals. Membership-as-access-gate (NanoClaw's
|
|
477
|
-
`agent_group_members`) is dropped; perimeter auth is JWT-on-every-route.
|
|
478
|
-
|
|
479
|
-
### Per-session `inbound.db` / `outbound.db`
|
|
480
|
-
|
|
481
|
-
Two files per session under `data/sessions/<agent_group_id>/<session_id>/`, both
|
|
482
|
-
opened with `journal_mode=DELETE`. No foreign keys to the central DB
|
|
483
|
-
(neither file can reference a row it cannot see) and no cross-file
|
|
484
|
-
references either — `outbound.db.in_reply_to` is an opaque message id
|
|
485
|
-
the agent-runner copies forward, not an enforced reference.
|
|
486
|
-
|
|
487
|
-
`inbound.db` — host-write, container-read:
|
|
488
|
-
|
|
489
|
-
```sql
|
|
490
|
-
CREATE TABLE messages_in (
|
|
491
|
-
id TEXT PRIMARY KEY,
|
|
492
|
-
kind TEXT NOT NULL, -- 'chat' | 'chat-sdk' | 'task' | 'webhook' | 'system'
|
|
493
|
-
timestamp TEXT NOT NULL,
|
|
494
|
-
status TEXT NOT NULL DEFAULT 'pending',
|
|
495
|
-
status_changed TEXT,
|
|
496
|
-
process_after TEXT, -- ISO-8601; NULL = process immediately
|
|
497
|
-
recurrence TEXT, -- cron expression; NULL = one-shot
|
|
498
|
-
tries INTEGER NOT NULL DEFAULT 0,
|
|
499
|
-
platform_id TEXT,
|
|
500
|
-
channel_type TEXT,
|
|
501
|
-
thread_id TEXT,
|
|
502
|
-
content TEXT NOT NULL -- JSON blob, format depends on kind
|
|
503
|
-
);
|
|
504
|
-
```
|
|
505
|
-
|
|
506
|
-
`outbound.db` — container-write, host-read:
|
|
507
|
-
|
|
508
|
-
```sql
|
|
509
|
-
CREATE TABLE messages_out (
|
|
510
|
-
id TEXT PRIMARY KEY,
|
|
511
|
-
in_reply_to TEXT, -- opaque copy of messages_in.id
|
|
512
|
-
timestamp TEXT NOT NULL,
|
|
513
|
-
delivered INTEGER NOT NULL DEFAULT 0,
|
|
514
|
-
deliver_after TEXT,
|
|
515
|
-
recurrence TEXT,
|
|
516
|
-
kind TEXT NOT NULL,
|
|
517
|
-
platform_id TEXT,
|
|
518
|
-
channel_type TEXT,
|
|
519
|
-
thread_id TEXT,
|
|
520
|
-
content TEXT NOT NULL
|
|
521
|
-
);
|
|
522
|
-
|
|
523
|
-
CREATE TABLE session_state (
|
|
524
|
-
key TEXT PRIMARY KEY,
|
|
525
|
-
value TEXT NOT NULL
|
|
526
|
-
);
|
|
527
|
-
```
|
|
528
|
-
|
|
529
|
-
`session_state` lives on the **outbound** side because only the
|
|
530
|
-
container writes it (last-seen Claude session id, last formatter mode,
|
|
531
|
-
in-progress streaming context). It's a scratchpad, not a long-term
|
|
532
|
-
store; durable per-agent state belongs in the central DB and crosses
|
|
533
|
-
the boundary as a `system` message.
|
|
534
|
-
|
|
535
|
-
Five message kinds: `chat`, `chat-sdk`, `task`, `webhook`, `system`.
|
|
536
|
-
The agent sees `chat` and `chat-sdk` as user-facing turns; `task` is a
|
|
537
|
-
scheduled firing; `webhook` is an arbitrary HTTP-triggered event;
|
|
538
|
-
`system` is the host's response to a system action the agent requested.
|
|
539
|
-
`system`-out is how the agent asks the host to do things — register a
|
|
540
|
-
group, reset a session, install a package, attach a vault — and
|
|
541
|
-
`system`-in is how the host answers.
|
|
542
|
-
|
|
543
|
-
Scheduling is *not* a separate subsystem. `process_after` (inbound) and
|
|
544
|
-
`deliver_after` (outbound) plus `recurrence` give one-shot and
|
|
545
|
-
cron-style firing on the same tables. The 60-second host sweep visits
|
|
546
|
-
every session pair in turn — it's the one place that crosses the
|
|
547
|
-
per-session file boundary in bulk — and handles every condition in one
|
|
548
|
-
query family.
|
|
549
|
-
|
|
550
|
-
## API surface
|
|
551
|
-
|
|
552
|
-
Every `/api/*` endpoint requires a hub-issued JWT — operator JWT for
|
|
553
|
-
admin tooling, user OAuth bearer for the browser. parachute-agent validates
|
|
554
|
-
against the hub's JWKS. Two endpoints are unauthenticated by design:
|
|
555
|
-
`/api/health` (operational probe) and `/api/discovery` (returns hub
|
|
556
|
-
origin so the SPA can bootstrap OAuth without baking the origin into the
|
|
557
|
-
bundle).
|
|
558
|
-
|
|
559
|
-
```
|
|
560
|
-
# agent groups
|
|
561
|
-
GET /api/agent-groups
|
|
562
|
-
POST /api/agent-groups
|
|
563
|
-
GET /api/agent-groups/:folder
|
|
564
|
-
POST /api/agent-groups/:folder/sessions
|
|
565
|
-
POST /api/agent-groups/:folder/attach-vault
|
|
566
|
-
POST /api/agent-groups/:folder/wire-channel
|
|
567
|
-
|
|
568
|
-
# sessions
|
|
569
|
-
GET /api/sessions/:id
|
|
570
|
-
POST /api/sessions/:id/close
|
|
571
|
-
|
|
572
|
-
# channels
|
|
573
|
-
GET /api/channels
|
|
574
|
-
POST /api/channels/install
|
|
575
|
-
POST /api/channels/:type/test
|
|
576
|
-
|
|
577
|
-
# secrets (names + metadata only — never values over the wire)
|
|
578
|
-
GET /api/secrets
|
|
579
|
-
POST /api/secrets
|
|
580
|
-
DELETE /api/secrets/:id
|
|
581
|
-
|
|
582
|
-
# approvals
|
|
583
|
-
GET /api/approvals
|
|
584
|
-
POST /api/approvals/:id/decide
|
|
585
|
-
|
|
586
|
-
# parachute integration
|
|
587
|
-
GET /api/vaults # proxies hub well-known
|
|
588
|
-
GET /api/discovery # hub origin (unauth)
|
|
589
|
-
GET /api/setup/status # readiness probe
|
|
590
|
-
|
|
591
|
-
# realtime
|
|
592
|
-
GET /stream # SSE: groups, sessions, approvals
|
|
593
|
-
GET /api/health # unauth liveness probe
|
|
594
|
-
```
|
|
595
|
-
|
|
596
|
-
Scopes (declared in `.parachute/module.json` under `scopes.defines`):
|
|
597
|
-
`claw:read`, `claw:write`, `claw:admin`. Operator tokens carry all three.
|
|
598
|
-
Read-only browser sessions request `claw:read`; the setup wizard escalates
|
|
599
|
-
to `claw:admin` for the install actions.
|
|
600
|
-
|
|
601
|
-
The shapes above are illustrative — the canonical request/response shapes
|
|
602
|
-
live in the route definitions under `web/server/src/`. The point of the
|
|
603
|
-
list is to make the *surface* legible.
|
|
604
|
-
|
|
605
|
-
## Parachute integration
|
|
606
|
-
|
|
607
|
-
parachute-agent ships as a first-class Parachute module. The contract:
|
|
608
|
-
|
|
609
|
-
### `.parachute/module.json`
|
|
610
|
-
|
|
611
|
-
The lifecycle and routing manifest the hub reads on install:
|
|
612
|
-
|
|
613
|
-
```json
|
|
614
|
-
{
|
|
615
|
-
"name": "agent",
|
|
616
|
-
"manifestName": "parachute-agent",
|
|
617
|
-
"displayName": "Parachute Agent",
|
|
618
|
-
"tagline": "Manage your Parachute agent groups + vault attachments.",
|
|
619
|
-
"kind": "frontend",
|
|
620
|
-
"port": 1944,
|
|
621
|
-
"paths": ["/agent"],
|
|
622
|
-
"health": "/api/health",
|
|
623
|
-
"startCmd": ["bun", "src/index.ts"],
|
|
624
|
-
"scopes": {
|
|
625
|
-
"defines": ["agent:read", "agent:write", "agent:admin"]
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
```
|
|
629
|
-
|
|
630
|
-
Mount: parachute-agent lives under `/agent/` behind the hub on the user's tailnet.
|
|
631
|
-
The UI bundle and every server route honour the mount via
|
|
632
|
-
`import.meta.env.BASE_URL` and `PARACHUTE_AGENT_WEB_MOUNT` (see CLAUDE.md's
|
|
633
|
-
"Web UI (mount-aware)" section). The mount is not optional and not
|
|
634
|
-
hardcoded.
|
|
635
|
-
|
|
636
|
-
### `/.well-known/parachute.json`
|
|
637
|
-
|
|
638
|
-
parachute-agent publishes its capability card at `/.well-known/parachute.json`,
|
|
639
|
-
following the well-known shape from
|
|
640
|
-
`parachute-patterns/patterns/well-known-discovery-rfc.md`. Content:
|
|
641
|
-
display name, scopes defined, well-known URLs for OAuth resource
|
|
642
|
-
metadata, services catalog (so peer Parachute modules can discover
|
|
643
|
-
parachute-agent without hardcoding).
|
|
644
|
-
|
|
645
|
-
### Hub-as-issuer OAuth
|
|
646
|
-
|
|
647
|
-
parachute-agent is a resource server, not an issuer. Tokens are minted by
|
|
648
|
-
parachute-hub at `:1939`. The flow:
|
|
649
|
-
|
|
650
|
-
1. Browser hits `/agent/`.
|
|
651
|
-
2. SPA reads `/api/discovery` → hub origin.
|
|
652
|
-
3. SPA redirects to `<hub>/oauth/authorize?...` with PKCE.
|
|
653
|
-
4. User consents on the hub (with hub's owner-password + optional TOTP).
|
|
654
|
-
5. Hub redirects back to `/agent/oauth/callback` with a code.
|
|
655
|
-
6. SPA exchanges at `<hub>/oauth/token` → bearer with `agent:*` scopes (legacy `claw:*` grants normalize through 0.1.x).
|
|
656
|
-
7. SPA stores the bearer; every `/api/*` call carries it.
|
|
657
|
-
|
|
658
|
-
Vault attachments use the same hub-as-issuer flow but for `vault:*`
|
|
659
|
-
scopes — and the resulting bearer lands in the agent group's
|
|
660
|
-
`groups/<folder>/container.json` (plaintext, filesystem-scoped). The full
|
|
661
|
-
design is in
|
|
662
|
-
`parachute.computer/design/2026-04-20-hub-as-portal-oauth-and-service-catalog.md`.
|
|
663
|
-
|
|
664
|
-
### Lifecycle hooks
|
|
665
|
-
|
|
666
|
-
`parachute install`, `parachute start`, `parachute restart`,
|
|
667
|
-
`parachute stop` — the hub drives lifecycle via the manifest. parachute-agent's
|
|
668
|
-
install command runs migrations, generates the master key if absent, and
|
|
669
|
-
registers in the hub's services catalog. Start runs `bun src/index.ts`.
|
|
670
|
-
|
|
671
|
-
## What's inherited vs what was rewritten
|
|
672
|
-
|
|
673
|
-
| Domain | Inherited from | What changed |
|
|
674
|
-
|---|---|---|
|
|
675
|
-
| Per-session containers | NanoClaw | Image renamed `parachute-agent-image-<slug>`; runtime stays Docker/Apple-container |
|
|
676
|
-
| Channel adapter shape | NanoClaw | Telegram + Discord + CLI live in `src/channels/` permanently; rest become plugins |
|
|
677
|
-
| Chat SDK bridge | NanoClaw | Kept as-is in `src/channels/chat-sdk-bridge.ts` |
|
|
678
|
-
| Message-as-IO discipline | NanoClaw | Same `messages_in` / `messages_out` columns; one file per session instead of two |
|
|
679
|
-
| Cross-mount DELETE-mode invariant | NanoClaw | Preserved — load-bearing for the host↔container DB seam |
|
|
680
|
-
| Approval primitive | OneCLI | Rewritten in-process; `pickApprover` resolves from `user_roles`; no gateway daemon |
|
|
681
|
-
| Per-agent secret scoping | OneCLI heritage | `assigned_mode` concept preserved; storage replaced with native AES-256-GCM |
|
|
682
|
-
| Trust zones (infra/core/perimeter) | borg | Vocabulary adopted; v1 enforces at JWT + container/DB-mount + approval boundaries |
|
|
683
|
-
| Single-process collapse | tinyclaw, borg | Bun-everywhere *host*; per-session DB still mounted — collapse is single-process state, not the IPC seam |
|
|
684
|
-
| File-queue option | borg | Considered, rejected — SQLite-with-DELETE-mode is already proven across the mount |
|
|
685
|
-
| Skills system | NanoClaw | Retired in favour of UI; channel install moves into `/api/channels/install` |
|
|
686
|
-
| Setup wizard's credential-capture | NanoClaw | Replaced by `/api/secrets` native page |
|
|
687
|
-
| Entity model (4-table) | NanoClaw | Flattened — only `users`, `user_roles`, no `agent_group_members` |
|
|
688
|
-
| Two-file session split (`inbound.db` + `outbound.db`) | NanoClaw | Preserved verbatim — load-bearing for one-writer-per-file across the bind mount |
|
|
689
|
-
| Heartbeat-via-file | NanoClaw | Kept; `/workspace/.heartbeat` mtime |
|
|
690
|
-
|
|
691
|
-
## Self-tests
|
|
692
|
-
|
|
693
|
-
The morning smoke (run by team-lead before declaring the rebuild done):
|
|
694
|
-
|
|
695
|
-
1. `parachute install ~/ParachuteComputer/paraclaw` → installs from current branch (the GitHub repo slug `paraclaw` is preserved through the rename to keep existing clones + remote URLs working).
|
|
696
|
-
2. `parachute start agent` → parachute-agent boots; logs show Bun, central + per-session SQLite ready, Telegram adapter loaded.
|
|
697
|
-
3. `curl /agent/api/setup/status` → `ready=true`.
|
|
698
|
-
4. Browser load `/agent/` → control panel renders.
|
|
699
|
-
5. Create agent group via UI.
|
|
700
|
-
6. Wire Telegram channel from UI.
|
|
701
|
-
7. DM the bot — reply within ~10s.
|
|
702
|
-
8. `curl /agent/api/secrets` → secrets list (names only, never values).
|
|
703
|
-
9. `parachute restart claw` → clean restart, no error.
|
|
704
|
-
10. Tests pass: `bun test` 100%, typecheck clean, biome clean.
|
|
705
|
-
|
|
706
|
-
## Decisions resolved in v1
|
|
707
|
-
|
|
708
|
-
A few seams the seed left implicit, captured here so other tentacles
|
|
709
|
-
share one contract:
|
|
710
|
-
|
|
711
|
-
- **Trust zones in v1 = three checks, not a matrix.** Hub-issued JWT on
|
|
712
|
-
every perimeter route; per-session container/DB-mount on every core
|
|
713
|
-
action; approval-on-credentialed-action on infra↔core crossings
|
|
714
|
-
(install_packages, add_mcp_server, attach-vault, mount). The richer
|
|
715
|
-
enforcement matrix (read/write/delete per primitive per zone) is v2.
|
|
716
|
-
- **`startCmd` is `["bun", "src/index.ts"]`.** The legacy
|
|
717
|
-
`pnpm exec tsx web/server/src/server.ts` path is dropped; the web
|
|
718
|
-
server is part of the host process post-migration.
|
|
719
|
-
- **`messaging_groups.thread_id` allows NULL.** Adapters without a
|
|
720
|
-
stable thread (CLI, single-DM Telegram) write NULL. SQLite treats
|
|
721
|
-
NULLs as distinct in unique indexes, which is the desired behaviour
|
|
722
|
-
here — different DM sessions for the same `(channel_type, platform_id)`
|
|
723
|
-
pair stay distinct rows. The routing layer matches on the
|
|
724
|
-
three-tuple and falls back to the NULL row when no thread is given.
|
|
725
|
-
- **Vault token refresh is deferred to v2.** Hub-issued tokens have an
|
|
726
|
-
expiry; `parachute.json` does not yet carry refresh tokens or expiry
|
|
727
|
-
metadata. v1 assumes long-lived bearers. Adding refresh is an additive
|
|
728
|
-
shape change to the JSON schema, no migration required.
|
|
729
|
-
|
|
730
|
-
## Reference: key files
|
|
731
|
-
|
|
732
|
-
| File | Purpose |
|
|
733
|
-
|---|---|
|
|
734
|
-
| `src/index.ts` | Entry point — DB init, migrations, channel adapters, delivery polls, sweep, shutdown |
|
|
735
|
-
| `src/router.ts` | Inbound routing: messaging group → agent group → session → `messages_in` → wake |
|
|
736
|
-
| `src/delivery.ts` | Polls `messages_out`, delivers via adapter, handles system actions |
|
|
737
|
-
| `src/host-sweep.ts` | 60s sweep: stale detection, due-message wake, recurrence |
|
|
738
|
-
| `src/session-manager.ts` | Resolves sessions; opens/closes `inbound.db` + `outbound.db`; documents the cross-mount invariants |
|
|
739
|
-
| `src/container-runner.ts` | Spawns per-session containers with `inbound.db` (RO) + `outbound.db` (RW) mounts and secret env injection |
|
|
740
|
-
| `src/container-runtime.ts` | Runtime selection (Docker vs Apple containers), orphan cleanup |
|
|
741
|
-
| `src/channels/` | Channel adapter infra (registry, Chat SDK bridge) and the inlined Telegram/Discord/CLI adapters |
|
|
742
|
-
| `src/parachute/` | Hub discovery, vault attach helpers, `module.json`/`well-known` plumbing |
|
|
743
|
-
| `src/db/migrations/` | Schema migrations |
|
|
744
|
-
| `web/ui/` | Vite + React + TS control panel |
|
|
745
|
-
| `web/server/src/server.ts` | (Legacy in-tree.) HTTP surface — folds into `src/index.ts` after Bun migration |
|
|
746
|
-
| `container/agent-runner/` | The in-container poll loop, formatter, MCP tools |
|
|
747
|
-
| `container/build.sh` | Image build (`parachute-agent-image-<slug>:latest`) |
|
|
748
|
-
|
|
749
|
-
For the deep dives — agent-runner internals, MCP tool surface, build and
|
|
750
|
-
runtime split, isolation model — see the per-doc files alongside this one
|
|
751
|
-
in `docs/`.
|