@openparachute/agent 0.1.1 → 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.
Files changed (598) hide show
  1. package/.parachute/module.json +124 -8
  2. package/LICENSE +2 -16
  3. package/README.md +118 -166
  4. package/package.json +32 -43
  5. package/scripts/spawn-agent.ts +371 -0
  6. package/src/_parked/interactive-spawn.test.ts +324 -0
  7. package/src/_parked/interactive-spawn.ts +701 -0
  8. package/src/agent-defs.test.ts +1504 -0
  9. package/src/agent-defs.ts +1702 -0
  10. package/src/agent-mcp-config.test.ts +115 -0
  11. package/src/agent-mcp-config.ts +115 -0
  12. package/src/agents.test.ts +360 -0
  13. package/src/agents.ts +379 -0
  14. package/src/auth.test.ts +46 -0
  15. package/src/auth.ts +140 -0
  16. package/src/backends/attached-queue.test.ts +376 -0
  17. package/src/backends/attached-queue.ts +372 -0
  18. package/src/backends/programmatic.test.ts +1715 -0
  19. package/src/backends/programmatic.ts +927 -0
  20. package/src/backends/registry.test.ts +1494 -0
  21. package/src/backends/registry.ts +1202 -0
  22. package/src/backends/stream-json.test.ts +570 -0
  23. package/src/backends/stream-json.ts +392 -0
  24. package/src/backends/types.ts +223 -0
  25. package/src/bridge.ts +417 -0
  26. package/src/channel-backend-wiring.test.ts +237 -0
  27. package/src/credentials.test.ts +274 -0
  28. package/src/credentials.ts +380 -0
  29. package/src/cron.test.ts +342 -0
  30. package/src/cron.ts +380 -0
  31. package/src/daemon-agent-def-api.test.ts +166 -0
  32. package/src/daemon-agent-defs-api.test.ts +953 -0
  33. package/src/daemon-agent-env-api.test.ts +338 -0
  34. package/src/daemon-attached-queue-store.test.ts +65 -0
  35. package/src/daemon-config-api.test.ts +962 -0
  36. package/src/daemon-jobs-api.test.ts +271 -0
  37. package/src/daemon-vault-chat.test.ts +250 -0
  38. package/src/daemon.test.ts +746 -0
  39. package/src/daemon.ts +3314 -0
  40. package/src/def-vaults.test.ts +136 -0
  41. package/src/def-vaults.ts +165 -0
  42. package/src/delivery-state.test.ts +110 -0
  43. package/src/delivery-state.ts +154 -0
  44. package/src/effective-env.test.ts +114 -0
  45. package/src/effective-env.ts +184 -0
  46. package/src/env-compat.ts +39 -0
  47. package/src/grants.test.ts +638 -0
  48. package/src/grants.ts +675 -0
  49. package/src/hub-jwt.test.ts +161 -0
  50. package/src/hub-jwt.ts +182 -0
  51. package/src/jobs.test.ts +245 -0
  52. package/src/jobs.ts +266 -0
  53. package/src/mcp-http.test.ts +265 -0
  54. package/src/mcp-http.ts +771 -0
  55. package/src/mint-token.test.ts +152 -0
  56. package/src/mint-token.ts +139 -0
  57. package/src/module-manifest.test.ts +158 -0
  58. package/src/oauth-discovery.ts +134 -0
  59. package/src/programmatic-wiring.test.ts +838 -0
  60. package/src/registry.test.ts +227 -0
  61. package/src/registry.ts +228 -0
  62. package/src/resolve-port.test.ts +64 -0
  63. package/src/routing.test.ts +184 -0
  64. package/src/routing.ts +76 -0
  65. package/src/runner.test.ts +506 -0
  66. package/src/runner.ts +255 -0
  67. package/src/sandbox/config.test.ts +150 -0
  68. package/src/sandbox/config.ts +102 -0
  69. package/src/sandbox/egress.test.ts +113 -0
  70. package/src/sandbox/egress.ts +123 -0
  71. package/src/sandbox/index.ts +180 -0
  72. package/src/sandbox/live-seatbelt.test.ts +277 -0
  73. package/src/sandbox/mounts.test.ts +154 -0
  74. package/src/sandbox/mounts.ts +133 -0
  75. package/src/sandbox/sandbox.test.ts +168 -0
  76. package/src/sandbox/types.ts +382 -0
  77. package/src/services-manifest.test.ts +106 -0
  78. package/src/services-manifest.ts +95 -0
  79. package/src/spa-serve.test.ts +116 -0
  80. package/src/spa-serve.ts +116 -0
  81. package/src/spawn-agent-cli.test.ts +172 -0
  82. package/src/spawn-agent.test.ts +1218 -0
  83. package/src/spawn-agent.ts +569 -0
  84. package/src/spawn-deps.test.ts +54 -0
  85. package/src/spawn-deps.ts +166 -0
  86. package/src/telegram/api.ts +153 -0
  87. package/src/terminal-assets.test.ts +50 -0
  88. package/src/terminal-assets.ts +79 -0
  89. package/src/terminal-ui.ts +305 -0
  90. package/src/terminal.test.ts +530 -0
  91. package/src/terminal.ts +458 -0
  92. package/src/transport.ts +270 -0
  93. package/src/transports/http-ui.test.ts +455 -0
  94. package/src/transports/http-ui.ts +201 -0
  95. package/src/transports/telegram.test.ts +174 -0
  96. package/src/transports/telegram.ts +426 -0
  97. package/src/transports/vault.test.ts +2011 -0
  98. package/src/transports/vault.ts +1790 -0
  99. package/src/ui-kit.test.ts +178 -0
  100. package/src/ui-kit.ts +402 -0
  101. package/tsconfig.json +8 -14
  102. package/web/ui/tsconfig.json +2 -1
  103. package/.claude/scheduled_tasks.lock +0 -1
  104. package/.claude/settings.json +0 -5
  105. package/.claude/skills/add-atomic-chat-tool/SKILL.md +0 -243
  106. package/.claude/skills/add-atomic-chat-tool/atomic-chat-mcp-stdio.ts +0 -229
  107. package/.claude/skills/add-codex/SKILL.md +0 -161
  108. package/.claude/skills/add-dashboard/SKILL.md +0 -138
  109. package/.claude/skills/add-dashboard/resources/dashboard-pusher.ts +0 -495
  110. package/.claude/skills/add-emacs/SKILL.md +0 -296
  111. package/.claude/skills/add-gcal-tool/SKILL.md +0 -210
  112. package/.claude/skills/add-gchat/REMOVE.md +0 -6
  113. package/.claude/skills/add-gchat/SKILL.md +0 -92
  114. package/.claude/skills/add-gchat/VERIFY.md +0 -3
  115. package/.claude/skills/add-github/REMOVE.md +0 -6
  116. package/.claude/skills/add-github/SKILL.md +0 -148
  117. package/.claude/skills/add-github/VERIFY.md +0 -3
  118. package/.claude/skills/add-gmail-tool/SKILL.md +0 -229
  119. package/.claude/skills/add-imessage/REMOVE.md +0 -6
  120. package/.claude/skills/add-imessage/SKILL.md +0 -113
  121. package/.claude/skills/add-imessage/VERIFY.md +0 -3
  122. package/.claude/skills/add-karpathy-llm-wiki/SKILL.md +0 -110
  123. package/.claude/skills/add-karpathy-llm-wiki/llm-wiki.md +0 -75
  124. package/.claude/skills/add-linear/REMOVE.md +0 -6
  125. package/.claude/skills/add-linear/SKILL.md +0 -168
  126. package/.claude/skills/add-linear/VERIFY.md +0 -3
  127. package/.claude/skills/add-macos-statusbar/SKILL.md +0 -133
  128. package/.claude/skills/add-macos-statusbar/add/src/statusbar.swift +0 -147
  129. package/.claude/skills/add-matrix/REMOVE.md +0 -6
  130. package/.claude/skills/add-matrix/SKILL.md +0 -148
  131. package/.claude/skills/add-matrix/VERIFY.md +0 -3
  132. package/.claude/skills/add-ollama-provider/SKILL.md +0 -179
  133. package/.claude/skills/add-ollama-tool/SKILL.md +0 -193
  134. package/.claude/skills/add-opencode/SKILL.md +0 -229
  135. package/.claude/skills/add-parallel/SKILL.md +0 -290
  136. package/.claude/skills/add-resend/REMOVE.md +0 -6
  137. package/.claude/skills/add-resend/SKILL.md +0 -93
  138. package/.claude/skills/add-resend/VERIFY.md +0 -3
  139. package/.claude/skills/add-signal/REMOVE.md +0 -13
  140. package/.claude/skills/add-signal/SKILL.md +0 -318
  141. package/.claude/skills/add-signal/VERIFY.md +0 -5
  142. package/.claude/skills/add-slack/REMOVE.md +0 -6
  143. package/.claude/skills/add-slack/SKILL.md +0 -112
  144. package/.claude/skills/add-slack/VERIFY.md +0 -3
  145. package/.claude/skills/add-teams/REMOVE.md +0 -6
  146. package/.claude/skills/add-teams/SKILL.md +0 -207
  147. package/.claude/skills/add-teams/VERIFY.md +0 -3
  148. package/.claude/skills/add-vercel/SKILL.md +0 -147
  149. package/.claude/skills/add-vercel/container-skills/vercel-cli/SKILL.md +0 -103
  150. package/.claude/skills/add-webex/REMOVE.md +0 -6
  151. package/.claude/skills/add-webex/SKILL.md +0 -88
  152. package/.claude/skills/add-webex/VERIFY.md +0 -3
  153. package/.claude/skills/add-wechat/REMOVE.md +0 -49
  154. package/.claude/skills/add-wechat/SKILL.md +0 -170
  155. package/.claude/skills/add-wechat/scripts/wire-dm.ts +0 -172
  156. package/.claude/skills/add-whatsapp/SKILL.md +0 -264
  157. package/.claude/skills/add-whatsapp-cloud/REMOVE.md +0 -6
  158. package/.claude/skills/add-whatsapp-cloud/SKILL.md +0 -95
  159. package/.claude/skills/add-whatsapp-cloud/VERIFY.md +0 -3
  160. package/.claude/skills/claw/SKILL.md +0 -131
  161. package/.claude/skills/claw/scripts/claw +0 -374
  162. package/.claude/skills/convert-to-apple-container/SKILL.md +0 -212
  163. package/.claude/skills/customize/SKILL.md +0 -110
  164. package/.claude/skills/debug/SKILL.md +0 -349
  165. package/.claude/skills/get-qodo-rules/SKILL.md +0 -122
  166. package/.claude/skills/get-qodo-rules/references/output-format.md +0 -41
  167. package/.claude/skills/get-qodo-rules/references/pagination.md +0 -33
  168. package/.claude/skills/get-qodo-rules/references/repository-scope.md +0 -26
  169. package/.claude/skills/init-first-agent/SKILL.md +0 -120
  170. package/.claude/skills/init-onecli/SKILL.md +0 -270
  171. package/.claude/skills/manage-channels/SKILL.md +0 -87
  172. package/.claude/skills/manage-mounts/SKILL.md +0 -47
  173. package/.claude/skills/migrate-from-openclaw/MIGRATE_CRONS.md +0 -100
  174. package/.claude/skills/migrate-from-openclaw/SKILL.md +0 -447
  175. package/.claude/skills/migrate-from-openclaw/scripts/discover-openclaw.ts +0 -734
  176. package/.claude/skills/migrate-from-openclaw/scripts/extract-channel-credentials.ts +0 -476
  177. package/.claude/skills/migrate-nanoclaw/SKILL.md +0 -484
  178. package/.claude/skills/migrate-nanoclaw/diagnostics.md +0 -51
  179. package/.claude/skills/qodo-pr-resolver/SKILL.md +0 -326
  180. package/.claude/skills/qodo-pr-resolver/resources/providers.md +0 -329
  181. package/.claude/skills/update-nanoclaw/SKILL.md +0 -243
  182. package/.claude/skills/update-nanoclaw/diagnostics.md +0 -48
  183. package/.claude/skills/update-skills/SKILL.md +0 -130
  184. package/.claude/skills/use-native-credential-proxy/SKILL.md +0 -167
  185. package/.claude/skills/x-integration/SKILL.md +0 -417
  186. package/.claude/skills/x-integration/agent.ts +0 -243
  187. package/.claude/skills/x-integration/host.ts +0 -155
  188. package/.claude/skills/x-integration/lib/browser.ts +0 -148
  189. package/.claude/skills/x-integration/lib/config.ts +0 -62
  190. package/.claude/skills/x-integration/scripts/like.ts +0 -56
  191. package/.claude/skills/x-integration/scripts/post.ts +0 -66
  192. package/.claude/skills/x-integration/scripts/quote.ts +0 -80
  193. package/.claude/skills/x-integration/scripts/reply.ts +0 -74
  194. package/.claude/skills/x-integration/scripts/retweet.ts +0 -62
  195. package/.claude/skills/x-integration/scripts/setup.ts +0 -87
  196. package/.github/CODEOWNERS +0 -10
  197. package/.github/PULL_REQUEST_TEMPLATE.md +0 -18
  198. package/.github/workflows/bump-version.yml +0 -35
  199. package/.github/workflows/ci.yml +0 -39
  200. package/.github/workflows/label-pr.yml +0 -40
  201. package/.github/workflows/update-tokens.yml +0 -43
  202. package/.husky/pre-commit +0 -1
  203. package/.mcp.json +0 -3
  204. package/.nvmrc +0 -1
  205. package/.prettierrc +0 -4
  206. package/CHANGELOG.md +0 -221
  207. package/CLAUDE.md +0 -307
  208. package/CODE_OF_CONDUCT.md +0 -128
  209. package/CONTRIBUTING.md +0 -159
  210. package/CONTRIBUTORS.md +0 -26
  211. package/LICENSE-NANOCLAW-MIT +0 -21
  212. package/README_ja.md +0 -194
  213. package/README_zh.md +0 -194
  214. package/assets/nanoclaw-favicon.png +0 -0
  215. package/assets/nanoclaw-icon.png +0 -0
  216. package/assets/nanoclaw-logo-dark.png +0 -0
  217. package/assets/nanoclaw-logo.png +0 -0
  218. package/assets/nanoclaw-profile.jpeg +0 -0
  219. package/assets/nanoclaw-sales.png +0 -0
  220. package/assets/social-preview.jpg +0 -0
  221. package/config-examples/mount-allowlist.json +0 -25
  222. package/container/.dockerignore +0 -2
  223. package/container/CLAUDE.md +0 -21
  224. package/container/Dockerfile +0 -121
  225. package/container/agent-runner/bun.lock +0 -243
  226. package/container/agent-runner/package.json +0 -22
  227. package/container/agent-runner/scripts/sdk-signal-probe.ts +0 -169
  228. package/container/agent-runner/src/config.ts +0 -55
  229. package/container/agent-runner/src/db/connection.ts +0 -267
  230. package/container/agent-runner/src/db/index.ts +0 -20
  231. package/container/agent-runner/src/db/messages-in.ts +0 -138
  232. package/container/agent-runner/src/db/messages-out.ts +0 -143
  233. package/container/agent-runner/src/db/session-routing.ts +0 -30
  234. package/container/agent-runner/src/db/session-state.test.ts +0 -100
  235. package/container/agent-runner/src/db/session-state.ts +0 -79
  236. package/container/agent-runner/src/destinations.ts +0 -135
  237. package/container/agent-runner/src/formatter.test.ts +0 -167
  238. package/container/agent-runner/src/formatter.ts +0 -260
  239. package/container/agent-runner/src/index.ts +0 -110
  240. package/container/agent-runner/src/integration.test.ts +0 -121
  241. package/container/agent-runner/src/mcp-tools/agents.instructions.md +0 -26
  242. package/container/agent-runner/src/mcp-tools/agents.ts +0 -66
  243. package/container/agent-runner/src/mcp-tools/core.instructions.md +0 -27
  244. package/container/agent-runner/src/mcp-tools/core.ts +0 -262
  245. package/container/agent-runner/src/mcp-tools/index.ts +0 -22
  246. package/container/agent-runner/src/mcp-tools/interactive.instructions.md +0 -22
  247. package/container/agent-runner/src/mcp-tools/interactive.ts +0 -169
  248. package/container/agent-runner/src/mcp-tools/scheduling.instructions.md +0 -40
  249. package/container/agent-runner/src/mcp-tools/scheduling.ts +0 -299
  250. package/container/agent-runner/src/mcp-tools/self-mod.instructions.md +0 -25
  251. package/container/agent-runner/src/mcp-tools/self-mod.ts +0 -120
  252. package/container/agent-runner/src/mcp-tools/server.ts +0 -54
  253. package/container/agent-runner/src/mcp-tools/types.ts +0 -6
  254. package/container/agent-runner/src/poll-loop.test.ts +0 -248
  255. package/container/agent-runner/src/poll-loop.ts +0 -437
  256. package/container/agent-runner/src/providers/claude.ts +0 -379
  257. package/container/agent-runner/src/providers/factory.test.ts +0 -19
  258. package/container/agent-runner/src/providers/factory.ts +0 -13
  259. package/container/agent-runner/src/providers/index.ts +0 -6
  260. package/container/agent-runner/src/providers/mock.ts +0 -77
  261. package/container/agent-runner/src/providers/provider-registry.ts +0 -33
  262. package/container/agent-runner/src/providers/types.ts +0 -82
  263. package/container/agent-runner/src/scheduling/task-script.ts +0 -121
  264. package/container/agent-runner/src/timezone.test.ts +0 -93
  265. package/container/agent-runner/src/timezone.ts +0 -107
  266. package/container/agent-runner/tsconfig.json +0 -14
  267. package/container/build.sh +0 -48
  268. package/container/entrypoint.sh +0 -16
  269. package/container/skills/agent-browser/SKILL.md +0 -159
  270. package/container/skills/frontend-engineer/SKILL.md +0 -157
  271. package/container/skills/self-customize/SKILL.md +0 -87
  272. package/container/skills/slack-formatting/SKILL.md +0 -94
  273. package/container/skills/vercel-cli/SKILL.md +0 -111
  274. package/container/skills/welcome/SKILL.md +0 -85
  275. package/docs/APPLE-CONTAINER-NETWORKING.md +0 -90
  276. package/docs/BRANCH-FORK-MAINTENANCE.md +0 -81
  277. package/docs/README.md +0 -25
  278. package/docs/SDK_DEEP_DIVE.md +0 -643
  279. package/docs/SECURITY.md +0 -162
  280. package/docs/agent-runner-details.md +0 -749
  281. package/docs/api-details.md +0 -365
  282. package/docs/architecture-diagram.html +0 -422
  283. package/docs/architecture-diagram.md +0 -215
  284. package/docs/architecture.md +0 -751
  285. package/docs/audit/2026-04-30-channel-endpoint-audit.md +0 -36
  286. package/docs/build-and-runtime.md +0 -80
  287. package/docs/cross-mount-stress/README.md +0 -112
  288. package/docs/cross-mount-stress/container-writer-retry.mjs +0 -55
  289. package/docs/cross-mount-stress/container-writer-slow.mjs +0 -42
  290. package/docs/cross-mount-stress/container-writer.mjs +0 -47
  291. package/docs/cross-mount-stress/host-writer-retry.mjs +0 -55
  292. package/docs/cross-mount-stress/host-writer-slow.mjs +0 -43
  293. package/docs/cross-mount-stress/host-writer.mjs +0 -47
  294. package/docs/db-central.md +0 -316
  295. package/docs/db-session.md +0 -183
  296. package/docs/db.md +0 -119
  297. package/docs/design/2026-04-29-vault-management-ui.md +0 -231
  298. package/docs/design/2026-04-30-channel-wiring-rework.md +0 -234
  299. package/docs/design/2026-05-01-channel-wiring-approvals-deep-dive.md +0 -272
  300. package/docs/design/2026-05-02-channel-policy-and-approval-routing.md +0 -250
  301. package/docs/docker-sandboxes.md +0 -359
  302. package/docs/isolation-model.md +0 -88
  303. package/docs/ollama.md +0 -79
  304. package/docs/parachute-integration.md +0 -109
  305. package/docs/post-night-rebirth-reflections.md +0 -151
  306. package/eslint.config.js +0 -32
  307. package/pnpm-workspace.yaml +0 -8
  308. package/repo-tokens/README.md +0 -113
  309. package/repo-tokens/action.yml +0 -186
  310. package/repo-tokens/badge.svg +0 -23
  311. package/repo-tokens/examples/green.svg +0 -14
  312. package/repo-tokens/examples/red.svg +0 -14
  313. package/repo-tokens/examples/yellow-green.svg +0 -14
  314. package/repo-tokens/examples/yellow.svg +0 -14
  315. package/scripts/chat.ts +0 -101
  316. package/scripts/cleanup-sessions.sh +0 -150
  317. package/scripts/init-cli-agent.ts +0 -171
  318. package/scripts/init-first-agent.ts +0 -377
  319. package/scripts/parachute.ts +0 -158
  320. package/scripts/run-migrations.ts +0 -105
  321. package/scripts/sanity-live-poll.ts +0 -95
  322. package/scripts/seed-discord.ts +0 -79
  323. package/scripts/test-v2-agent.ts +0 -106
  324. package/scripts/test-v2-channel-e2e.ts +0 -265
  325. package/scripts/test-v2-host.ts +0 -184
  326. package/src/channels/adapter.ts +0 -214
  327. package/src/channels/ask-question.ts +0 -46
  328. package/src/channels/channel-registry.test.ts +0 -421
  329. package/src/channels/channel-registry.ts +0 -313
  330. package/src/channels/chat-sdk-bridge.test.ts +0 -84
  331. package/src/channels/chat-sdk-bridge.ts +0 -652
  332. package/src/channels/cli.ts +0 -276
  333. package/src/channels/discord.ts +0 -90
  334. package/src/channels/index.ts +0 -17
  335. package/src/channels/telegram-markdown-sanitize.test.ts +0 -78
  336. package/src/channels/telegram-markdown-sanitize.ts +0 -55
  337. package/src/channels/telegram-pairing.test.ts +0 -254
  338. package/src/channels/telegram-pairing.ts +0 -339
  339. package/src/channels/telegram.ts +0 -279
  340. package/src/channels/trust-hint.test.ts +0 -48
  341. package/src/channels/trust-hint.ts +0 -75
  342. package/src/claude-md-compose.migrate.test.ts +0 -64
  343. package/src/claude-md-compose.ts +0 -205
  344. package/src/command-gate.ts +0 -63
  345. package/src/config.test.ts +0 -93
  346. package/src/config.ts +0 -108
  347. package/src/container-config.ts +0 -167
  348. package/src/container-runner.test.ts +0 -32
  349. package/src/container-runner.ts +0 -576
  350. package/src/container-runtime.test.ts +0 -169
  351. package/src/container-runtime.ts +0 -92
  352. package/src/db/_bun-sqlite-shim.ts +0 -88
  353. package/src/db/agent-activity.test.ts +0 -155
  354. package/src/db/agent-activity.ts +0 -121
  355. package/src/db/agent-groups.ts +0 -77
  356. package/src/db/connection.migrate.test.ts +0 -143
  357. package/src/db/connection.ts +0 -224
  358. package/src/db/db-v2.test.ts +0 -440
  359. package/src/db/dropped-messages.ts +0 -44
  360. package/src/db/index.ts +0 -40
  361. package/src/db/messaging-groups.ts +0 -252
  362. package/src/db/migrations/001-initial.ts +0 -112
  363. package/src/db/migrations/002-chat-sdk-state.ts +0 -36
  364. package/src/db/migrations/008-dropped-messages.ts +0 -27
  365. package/src/db/migrations/009-drop-pending-credentials.ts +0 -13
  366. package/src/db/migrations/010-engage-modes.ts +0 -103
  367. package/src/db/migrations/011-pending-sender-approvals.ts +0 -40
  368. package/src/db/migrations/012-channel-registration.ts +0 -48
  369. package/src/db/migrations/013-approval-render-metadata.ts +0 -27
  370. package/src/db/migrations/014-secrets.ts +0 -44
  371. package/src/db/migrations/015-secrets-drop-host-pattern.ts +0 -18
  372. package/src/db/migrations/016-secret-assignments.ts +0 -30
  373. package/src/db/migrations/017-agent-activity.ts +0 -40
  374. package/src/db/migrations/018-oauth-app-configs.ts +0 -34
  375. package/src/db/migrations/019-oauth-app-connections.ts +0 -48
  376. package/src/db/migrations/020-agent-app-connections.ts +0 -28
  377. package/src/db/migrations/021-pending-oauth-states.ts +0 -35
  378. package/src/db/migrations/022-app-connections-provider.ts +0 -25
  379. package/src/db/migrations/023-agent-group-secret-mode.test.ts +0 -124
  380. package/src/db/migrations/023-agent-group-secret-mode.ts +0 -65
  381. package/src/db/migrations/024-collapse-approvals.test.ts +0 -249
  382. package/src/db/migrations/024-collapse-approvals.ts +0 -182
  383. package/src/db/migrations/025-secret-mode-check.test.ts +0 -155
  384. package/src/db/migrations/025-secret-mode-check.ts +0 -49
  385. package/src/db/migrations/026-user-dms-bot-id.test.ts +0 -116
  386. package/src/db/migrations/026-user-dms-bot-id.ts +0 -54
  387. package/src/db/migrations/027-provider-credentials.ts +0 -41
  388. package/src/db/migrations/_test-helpers.ts +0 -41
  389. package/src/db/migrations/index.ts +0 -127
  390. package/src/db/migrations/module-agent-to-agent-destinations.ts +0 -84
  391. package/src/db/migrations/module-approvals-pending-approvals.ts +0 -42
  392. package/src/db/migrations/module-approvals-title-options.ts +0 -40
  393. package/src/db/schema.ts +0 -258
  394. package/src/db/session-db.test.ts +0 -93
  395. package/src/db/session-db.ts +0 -325
  396. package/src/db/sessions.ts +0 -241
  397. package/src/delivery.test.ts +0 -148
  398. package/src/delivery.ts +0 -445
  399. package/src/env.ts +0 -74
  400. package/src/group-folder.test.ts +0 -35
  401. package/src/group-folder.ts +0 -44
  402. package/src/group-init.ts +0 -92
  403. package/src/host-core.test.ts +0 -456
  404. package/src/host-sweep.test.ts +0 -146
  405. package/src/host-sweep.ts +0 -287
  406. package/src/index.ts +0 -227
  407. package/src/install-slug.ts +0 -33
  408. package/src/log.test.ts +0 -81
  409. package/src/log.ts +0 -117
  410. package/src/mcp/http.ts +0 -72
  411. package/src/mcp/server.ts +0 -92
  412. package/src/mcp/stdio.ts +0 -51
  413. package/src/mcp/tools/activity.ts +0 -88
  414. package/src/mcp/tools/agent-groups.ts +0 -183
  415. package/src/mcp/tools/approvals.ts +0 -122
  416. package/src/mcp/tools/channels.ts +0 -199
  417. package/src/mcp/tools/index.ts +0 -27
  418. package/src/mcp/tools/oauth.ts +0 -48
  419. package/src/mcp/tools/secrets.ts +0 -169
  420. package/src/mcp/tools/sessions.ts +0 -135
  421. package/src/mcp/types.ts +0 -51
  422. package/src/modules/agent-to-agent/agent-route.test.ts +0 -46
  423. package/src/modules/agent-to-agent/agent-route.ts +0 -223
  424. package/src/modules/agent-to-agent/create-agent.ts +0 -127
  425. package/src/modules/agent-to-agent/db/agent-destinations.ts +0 -135
  426. package/src/modules/agent-to-agent/index.ts +0 -22
  427. package/src/modules/agent-to-agent/write-destinations.ts +0 -59
  428. package/src/modules/approvals/agent.md +0 -45
  429. package/src/modules/approvals/index.ts +0 -21
  430. package/src/modules/approvals/picks.test.ts +0 -291
  431. package/src/modules/approvals/primitive.ts +0 -279
  432. package/src/modules/approvals/project.md +0 -27
  433. package/src/modules/approvals/response-handler.ts +0 -87
  434. package/src/modules/index.ts +0 -24
  435. package/src/modules/interactive/agent.md +0 -21
  436. package/src/modules/interactive/index.ts +0 -69
  437. package/src/modules/interactive/project.md +0 -12
  438. package/src/modules/mount-security/index.ts +0 -448
  439. package/src/modules/mount-security/migrate.test.ts +0 -91
  440. package/src/modules/permissions/access.ts +0 -28
  441. package/src/modules/permissions/channel-approval.test.ts +0 -389
  442. package/src/modules/permissions/channel-approval.ts +0 -188
  443. package/src/modules/permissions/db/agent-group-members.ts +0 -44
  444. package/src/modules/permissions/db/pending-channel-approvals.test.ts +0 -86
  445. package/src/modules/permissions/db/pending-channel-approvals.ts +0 -66
  446. package/src/modules/permissions/db/pending-sender-approvals.ts +0 -60
  447. package/src/modules/permissions/db/user-dms.ts +0 -58
  448. package/src/modules/permissions/db/user-roles.ts +0 -85
  449. package/src/modules/permissions/db/users.ts +0 -38
  450. package/src/modules/permissions/index.ts +0 -421
  451. package/src/modules/permissions/permissions.test.ts +0 -358
  452. package/src/modules/permissions/sender-approval.test.ts +0 -470
  453. package/src/modules/permissions/sender-approval.ts +0 -165
  454. package/src/modules/permissions/user-dm.ts +0 -200
  455. package/src/modules/provider-credentials/db.ts +0 -121
  456. package/src/modules/provider-credentials/index.ts +0 -12
  457. package/src/modules/provider-credentials/spawn.test.ts +0 -206
  458. package/src/modules/provider-credentials/spawn.ts +0 -114
  459. package/src/modules/scheduling/actions.ts +0 -113
  460. package/src/modules/scheduling/db.test.ts +0 -282
  461. package/src/modules/scheduling/db.ts +0 -148
  462. package/src/modules/scheduling/index.ts +0 -34
  463. package/src/modules/scheduling/recurrence.test.ts +0 -98
  464. package/src/modules/scheduling/recurrence.ts +0 -54
  465. package/src/modules/self-mod/agent.md +0 -30
  466. package/src/modules/self-mod/apply.ts +0 -85
  467. package/src/modules/self-mod/index.ts +0 -30
  468. package/src/modules/self-mod/project.md +0 -39
  469. package/src/modules/self-mod/request.ts +0 -91
  470. package/src/modules/typing/index.ts +0 -165
  471. package/src/oauth/agent-app-connections.ts +0 -103
  472. package/src/oauth/app-configs.test.ts +0 -64
  473. package/src/oauth/app-configs.ts +0 -114
  474. package/src/oauth/app-connections.test.ts +0 -109
  475. package/src/oauth/app-connections.ts +0 -178
  476. package/src/oauth/crypto.ts +0 -56
  477. package/src/oauth/flow.ts +0 -104
  478. package/src/oauth/providers/google.test.ts +0 -38
  479. package/src/oauth/providers/google.ts +0 -46
  480. package/src/oauth/providers/index.ts +0 -48
  481. package/src/oauth/state-store.test.ts +0 -54
  482. package/src/oauth/state-store.ts +0 -93
  483. package/src/parachute/README.md +0 -27
  484. package/src/parachute/create-agent.test.ts +0 -83
  485. package/src/parachute/create-agent.ts +0 -122
  486. package/src/parachute/group-status.test.ts +0 -165
  487. package/src/parachute/group-status.ts +0 -136
  488. package/src/parachute/types.ts +0 -41
  489. package/src/parachute/vault-mcp.test.ts +0 -251
  490. package/src/parachute/vault-mcp.ts +0 -232
  491. package/src/platform-id.test.ts +0 -104
  492. package/src/platform-id.ts +0 -109
  493. package/src/providers/index.ts +0 -6
  494. package/src/providers/provider-container-registry.ts +0 -58
  495. package/src/response-registry.ts +0 -45
  496. package/src/router.ts +0 -530
  497. package/src/secrets/crypto.test.ts +0 -45
  498. package/src/secrets/crypto.ts +0 -55
  499. package/src/secrets/index.ts +0 -355
  500. package/src/secrets/master-key.ts +0 -70
  501. package/src/secrets/secrets.test.ts +0 -354
  502. package/src/session-manager.migrate.test.ts +0 -59
  503. package/src/session-manager.ts +0 -433
  504. package/src/startup-bootstrap.test.ts +0 -226
  505. package/src/startup-bootstrap.ts +0 -207
  506. package/src/state-sqlite.ts +0 -182
  507. package/src/timezone.test.ts +0 -64
  508. package/src/timezone.ts +0 -37
  509. package/src/types.ts +0 -230
  510. package/src/web/auth.test.ts +0 -335
  511. package/src/web/auth.ts +0 -214
  512. package/src/web/discord-validate.test.ts +0 -77
  513. package/src/web/discord-validate.ts +0 -88
  514. package/src/web/hub-discovery.test.ts +0 -98
  515. package/src/web/hub-discovery.ts +0 -69
  516. package/src/web/routes/activity.ts +0 -106
  517. package/src/web/routes/agent-provider.test.ts +0 -282
  518. package/src/web/routes/agent-provider.ts +0 -309
  519. package/src/web/routes/approvals.ts +0 -185
  520. package/src/web/routes/apps.ts +0 -434
  521. package/src/web/routes/channels-mg-detail.test.ts +0 -324
  522. package/src/web/routes/channels-mga-detail.test.ts +0 -425
  523. package/src/web/routes/channels.ts +0 -489
  524. package/src/web/routes/oauth-providers.ts +0 -42
  525. package/src/web/routes/secrets.test.ts +0 -175
  526. package/src/web/routes/secrets.ts +0 -282
  527. package/src/web/routes/sessions.ts +0 -123
  528. package/src/web/routes/settings.test.ts +0 -106
  529. package/src/web/routes/settings.ts +0 -247
  530. package/src/web/routes/setup-status.ts +0 -205
  531. package/src/web/routes/vaults.test.ts +0 -389
  532. package/src/web/routes/vaults.ts +0 -225
  533. package/src/web/server-version.test.ts +0 -16
  534. package/src/web/server.ts +0 -1003
  535. package/src/web/services-manifest.test.ts +0 -120
  536. package/src/web/services-manifest.ts +0 -61
  537. package/src/web/static-serve.test.ts +0 -255
  538. package/src/web/static-serve.ts +0 -104
  539. package/src/web/telegram-validate.test.ts +0 -116
  540. package/src/web/telegram-validate.ts +0 -107
  541. package/src/web/vault-proxy.test.ts +0 -214
  542. package/src/web/vault-proxy.ts +0 -120
  543. package/src/web/wire-channel.ts +0 -181
  544. package/src/webhook-server.ts +0 -134
  545. package/vitest.config.ts +0 -18
  546. package/web/README.md +0 -63
  547. package/web/ui/index.html +0 -13
  548. package/web/ui/package.json +0 -35
  549. package/web/ui/pnpm-lock.yaml +0 -2164
  550. package/web/ui/scripts/verify-base.mjs +0 -31
  551. package/web/ui/src/App.tsx +0 -88
  552. package/web/ui/src/components/ActivityFeed.tsx +0 -444
  553. package/web/ui/src/components/AgentGroupPicker.tsx +0 -263
  554. package/web/ui/src/components/AgentProviderCards.tsx +0 -220
  555. package/web/ui/src/components/CredentialForm.tsx +0 -214
  556. package/web/ui/src/components/ScopeGrants.tsx +0 -74
  557. package/web/ui/src/components/StatusDot.tsx +0 -43
  558. package/web/ui/src/components/VaultPicker.tsx +0 -127
  559. package/web/ui/src/components/setup/AdapterInstallStep.tsx +0 -178
  560. package/web/ui/src/components/setup/AgentGroupStep.tsx +0 -43
  561. package/web/ui/src/components/setup/ChannelPickStep.tsx +0 -74
  562. package/web/ui/src/components/setup/DoneStep.tsx +0 -49
  563. package/web/ui/src/components/setup/PrereqStep.tsx +0 -129
  564. package/web/ui/src/components/setup/TestConnectionStep.tsx +0 -108
  565. package/web/ui/src/components/setup/TestMessageStep.tsx +0 -104
  566. package/web/ui/src/components/setup/WireChannelStep.tsx +0 -166
  567. package/web/ui/src/components/setup/types.ts +0 -105
  568. package/web/ui/src/lib/api.test.ts +0 -410
  569. package/web/ui/src/lib/api.ts +0 -1210
  570. package/web/ui/src/lib/auth.test.ts +0 -139
  571. package/web/ui/src/lib/auth.ts +0 -348
  572. package/web/ui/src/lib/channel-adapters.ts +0 -136
  573. package/web/ui/src/main.tsx +0 -19
  574. package/web/ui/src/routes/ApprovalsList.tsx +0 -294
  575. package/web/ui/src/routes/Apps.tsx +0 -613
  576. package/web/ui/src/routes/ChannelWireDetail.test.tsx +0 -233
  577. package/web/ui/src/routes/ChannelWireDetail.tsx +0 -403
  578. package/web/ui/src/routes/ChannelsList.tsx +0 -158
  579. package/web/ui/src/routes/GroupDetail.tsx +0 -755
  580. package/web/ui/src/routes/GroupList.tsx +0 -187
  581. package/web/ui/src/routes/MessagingGroupDetail.test.tsx +0 -233
  582. package/web/ui/src/routes/MessagingGroupDetail.tsx +0 -306
  583. package/web/ui/src/routes/NewGroupWizard.tsx +0 -390
  584. package/web/ui/src/routes/OAuthCallback.tsx +0 -56
  585. package/web/ui/src/routes/SecretsList.tsx +0 -921
  586. package/web/ui/src/routes/SessionsList.tsx +0 -220
  587. package/web/ui/src/routes/SettingsAgentProvider.tsx +0 -109
  588. package/web/ui/src/routes/SettingsApprovals.tsx +0 -234
  589. package/web/ui/src/routes/SetupWizard.tsx +0 -219
  590. package/web/ui/src/routes/VaultDetail.test.tsx +0 -361
  591. package/web/ui/src/routes/VaultDetail.tsx +0 -960
  592. package/web/ui/src/routes/VaultsList.tsx +0 -295
  593. package/web/ui/src/routes/WireChannelPage.tsx +0 -413
  594. package/web/ui/src/styles.css +0 -608
  595. package/web/ui/src/test/setup.ts +0 -23
  596. package/web/ui/src/vite-env.d.ts +0 -10
  597. package/web/ui/vite.config.ts +0 -34
  598. package/web/ui/vitest.config.ts +0 -25
@@ -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/`.