@openparachute/agent 0.1.2 → 0.2.2

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 (608) hide show
  1. package/.parachute/module.json +124 -8
  2. package/LICENSE +2 -16
  3. package/README.md +118 -166
  4. package/package.json +35 -42
  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/dist/assets/index-C-iWdFFV.css +1 -0
  103. package/web/ui/dist/assets/index-VFETBk0a.js +60 -0
  104. package/web/ui/dist/index.html +15 -0
  105. package/web/ui/tsconfig.json +2 -1
  106. package/.claude/scheduled_tasks.lock +0 -1
  107. package/.claude/settings.json +0 -5
  108. package/.claude/skills/add-atomic-chat-tool/SKILL.md +0 -243
  109. package/.claude/skills/add-atomic-chat-tool/atomic-chat-mcp-stdio.ts +0 -229
  110. package/.claude/skills/add-codex/SKILL.md +0 -161
  111. package/.claude/skills/add-dashboard/SKILL.md +0 -138
  112. package/.claude/skills/add-dashboard/resources/dashboard-pusher.ts +0 -495
  113. package/.claude/skills/add-emacs/SKILL.md +0 -296
  114. package/.claude/skills/add-gcal-tool/SKILL.md +0 -210
  115. package/.claude/skills/add-gchat/REMOVE.md +0 -6
  116. package/.claude/skills/add-gchat/SKILL.md +0 -92
  117. package/.claude/skills/add-gchat/VERIFY.md +0 -3
  118. package/.claude/skills/add-github/REMOVE.md +0 -6
  119. package/.claude/skills/add-github/SKILL.md +0 -148
  120. package/.claude/skills/add-github/VERIFY.md +0 -3
  121. package/.claude/skills/add-gmail-tool/SKILL.md +0 -229
  122. package/.claude/skills/add-imessage/REMOVE.md +0 -6
  123. package/.claude/skills/add-imessage/SKILL.md +0 -113
  124. package/.claude/skills/add-imessage/VERIFY.md +0 -3
  125. package/.claude/skills/add-karpathy-llm-wiki/SKILL.md +0 -110
  126. package/.claude/skills/add-karpathy-llm-wiki/llm-wiki.md +0 -75
  127. package/.claude/skills/add-linear/REMOVE.md +0 -6
  128. package/.claude/skills/add-linear/SKILL.md +0 -168
  129. package/.claude/skills/add-linear/VERIFY.md +0 -3
  130. package/.claude/skills/add-macos-statusbar/SKILL.md +0 -133
  131. package/.claude/skills/add-macos-statusbar/add/src/statusbar.swift +0 -147
  132. package/.claude/skills/add-matrix/REMOVE.md +0 -6
  133. package/.claude/skills/add-matrix/SKILL.md +0 -148
  134. package/.claude/skills/add-matrix/VERIFY.md +0 -3
  135. package/.claude/skills/add-ollama-provider/SKILL.md +0 -179
  136. package/.claude/skills/add-ollama-tool/SKILL.md +0 -193
  137. package/.claude/skills/add-opencode/SKILL.md +0 -229
  138. package/.claude/skills/add-parallel/SKILL.md +0 -290
  139. package/.claude/skills/add-resend/REMOVE.md +0 -6
  140. package/.claude/skills/add-resend/SKILL.md +0 -93
  141. package/.claude/skills/add-resend/VERIFY.md +0 -3
  142. package/.claude/skills/add-signal/REMOVE.md +0 -13
  143. package/.claude/skills/add-signal/SKILL.md +0 -318
  144. package/.claude/skills/add-signal/VERIFY.md +0 -5
  145. package/.claude/skills/add-slack/REMOVE.md +0 -6
  146. package/.claude/skills/add-slack/SKILL.md +0 -112
  147. package/.claude/skills/add-slack/VERIFY.md +0 -3
  148. package/.claude/skills/add-teams/REMOVE.md +0 -6
  149. package/.claude/skills/add-teams/SKILL.md +0 -207
  150. package/.claude/skills/add-teams/VERIFY.md +0 -3
  151. package/.claude/skills/add-vercel/SKILL.md +0 -147
  152. package/.claude/skills/add-vercel/container-skills/vercel-cli/SKILL.md +0 -103
  153. package/.claude/skills/add-webex/REMOVE.md +0 -6
  154. package/.claude/skills/add-webex/SKILL.md +0 -88
  155. package/.claude/skills/add-webex/VERIFY.md +0 -3
  156. package/.claude/skills/add-wechat/REMOVE.md +0 -49
  157. package/.claude/skills/add-wechat/SKILL.md +0 -170
  158. package/.claude/skills/add-wechat/scripts/wire-dm.ts +0 -172
  159. package/.claude/skills/add-whatsapp/SKILL.md +0 -264
  160. package/.claude/skills/add-whatsapp-cloud/REMOVE.md +0 -6
  161. package/.claude/skills/add-whatsapp-cloud/SKILL.md +0 -95
  162. package/.claude/skills/add-whatsapp-cloud/VERIFY.md +0 -3
  163. package/.claude/skills/claw/SKILL.md +0 -131
  164. package/.claude/skills/claw/scripts/claw +0 -374
  165. package/.claude/skills/convert-to-apple-container/SKILL.md +0 -212
  166. package/.claude/skills/customize/SKILL.md +0 -110
  167. package/.claude/skills/debug/SKILL.md +0 -349
  168. package/.claude/skills/get-qodo-rules/SKILL.md +0 -122
  169. package/.claude/skills/get-qodo-rules/references/output-format.md +0 -41
  170. package/.claude/skills/get-qodo-rules/references/pagination.md +0 -33
  171. package/.claude/skills/get-qodo-rules/references/repository-scope.md +0 -26
  172. package/.claude/skills/init-first-agent/SKILL.md +0 -120
  173. package/.claude/skills/init-onecli/SKILL.md +0 -270
  174. package/.claude/skills/manage-channels/SKILL.md +0 -87
  175. package/.claude/skills/manage-mounts/SKILL.md +0 -47
  176. package/.claude/skills/migrate-from-openclaw/MIGRATE_CRONS.md +0 -100
  177. package/.claude/skills/migrate-from-openclaw/SKILL.md +0 -447
  178. package/.claude/skills/migrate-from-openclaw/scripts/discover-openclaw.ts +0 -734
  179. package/.claude/skills/migrate-from-openclaw/scripts/extract-channel-credentials.ts +0 -476
  180. package/.claude/skills/migrate-nanoclaw/SKILL.md +0 -484
  181. package/.claude/skills/migrate-nanoclaw/diagnostics.md +0 -51
  182. package/.claude/skills/qodo-pr-resolver/SKILL.md +0 -326
  183. package/.claude/skills/qodo-pr-resolver/resources/providers.md +0 -329
  184. package/.claude/skills/update-nanoclaw/SKILL.md +0 -243
  185. package/.claude/skills/update-nanoclaw/diagnostics.md +0 -48
  186. package/.claude/skills/update-skills/SKILL.md +0 -130
  187. package/.claude/skills/use-native-credential-proxy/SKILL.md +0 -167
  188. package/.claude/skills/x-integration/SKILL.md +0 -417
  189. package/.claude/skills/x-integration/agent.ts +0 -243
  190. package/.claude/skills/x-integration/host.ts +0 -155
  191. package/.claude/skills/x-integration/lib/browser.ts +0 -148
  192. package/.claude/skills/x-integration/lib/config.ts +0 -62
  193. package/.claude/skills/x-integration/scripts/like.ts +0 -56
  194. package/.claude/skills/x-integration/scripts/post.ts +0 -66
  195. package/.claude/skills/x-integration/scripts/quote.ts +0 -80
  196. package/.claude/skills/x-integration/scripts/reply.ts +0 -74
  197. package/.claude/skills/x-integration/scripts/retweet.ts +0 -62
  198. package/.claude/skills/x-integration/scripts/setup.ts +0 -87
  199. package/.github/CODEOWNERS +0 -10
  200. package/.github/PULL_REQUEST_TEMPLATE.md +0 -18
  201. package/.github/workflows/bump-version.yml +0 -35
  202. package/.github/workflows/ci.yml +0 -39
  203. package/.github/workflows/label-pr.yml +0 -40
  204. package/.github/workflows/update-tokens.yml +0 -43
  205. package/.husky/pre-commit +0 -1
  206. package/.mcp.json +0 -3
  207. package/.nvmrc +0 -1
  208. package/.prettierrc +0 -4
  209. package/CHANGELOG.md +0 -263
  210. package/CLAUDE.md +0 -307
  211. package/CODE_OF_CONDUCT.md +0 -128
  212. package/CONTRIBUTING.md +0 -159
  213. package/CONTRIBUTORS.md +0 -26
  214. package/LICENSE-NANOCLAW-MIT +0 -21
  215. package/README_ja.md +0 -194
  216. package/README_zh.md +0 -194
  217. package/assets/nanoclaw-favicon.png +0 -0
  218. package/assets/nanoclaw-icon.png +0 -0
  219. package/assets/nanoclaw-logo-dark.png +0 -0
  220. package/assets/nanoclaw-logo.png +0 -0
  221. package/assets/nanoclaw-profile.jpeg +0 -0
  222. package/assets/nanoclaw-sales.png +0 -0
  223. package/assets/social-preview.jpg +0 -0
  224. package/config-examples/mount-allowlist.json +0 -25
  225. package/container/.dockerignore +0 -2
  226. package/container/CLAUDE.md +0 -21
  227. package/container/Dockerfile +0 -121
  228. package/container/agent-runner/bun.lock +0 -243
  229. package/container/agent-runner/package.json +0 -22
  230. package/container/agent-runner/scripts/sdk-signal-probe.ts +0 -169
  231. package/container/agent-runner/src/config.ts +0 -55
  232. package/container/agent-runner/src/db/connection.ts +0 -267
  233. package/container/agent-runner/src/db/index.ts +0 -20
  234. package/container/agent-runner/src/db/messages-in.ts +0 -138
  235. package/container/agent-runner/src/db/messages-out.ts +0 -143
  236. package/container/agent-runner/src/db/session-routing.ts +0 -30
  237. package/container/agent-runner/src/db/session-state.test.ts +0 -100
  238. package/container/agent-runner/src/db/session-state.ts +0 -79
  239. package/container/agent-runner/src/destinations.ts +0 -135
  240. package/container/agent-runner/src/formatter.test.ts +0 -167
  241. package/container/agent-runner/src/formatter.ts +0 -260
  242. package/container/agent-runner/src/index.ts +0 -110
  243. package/container/agent-runner/src/integration.test.ts +0 -121
  244. package/container/agent-runner/src/mcp-tools/agents.instructions.md +0 -26
  245. package/container/agent-runner/src/mcp-tools/agents.ts +0 -66
  246. package/container/agent-runner/src/mcp-tools/core.instructions.md +0 -27
  247. package/container/agent-runner/src/mcp-tools/core.ts +0 -262
  248. package/container/agent-runner/src/mcp-tools/index.ts +0 -22
  249. package/container/agent-runner/src/mcp-tools/interactive.instructions.md +0 -22
  250. package/container/agent-runner/src/mcp-tools/interactive.ts +0 -169
  251. package/container/agent-runner/src/mcp-tools/scheduling.instructions.md +0 -40
  252. package/container/agent-runner/src/mcp-tools/scheduling.ts +0 -299
  253. package/container/agent-runner/src/mcp-tools/self-mod.instructions.md +0 -25
  254. package/container/agent-runner/src/mcp-tools/self-mod.ts +0 -120
  255. package/container/agent-runner/src/mcp-tools/server.ts +0 -54
  256. package/container/agent-runner/src/mcp-tools/types.ts +0 -6
  257. package/container/agent-runner/src/poll-loop.test.ts +0 -248
  258. package/container/agent-runner/src/poll-loop.ts +0 -437
  259. package/container/agent-runner/src/providers/claude.ts +0 -379
  260. package/container/agent-runner/src/providers/factory.test.ts +0 -19
  261. package/container/agent-runner/src/providers/factory.ts +0 -13
  262. package/container/agent-runner/src/providers/index.ts +0 -6
  263. package/container/agent-runner/src/providers/mock.ts +0 -77
  264. package/container/agent-runner/src/providers/provider-registry.ts +0 -33
  265. package/container/agent-runner/src/providers/types.ts +0 -82
  266. package/container/agent-runner/src/scheduling/task-script.ts +0 -121
  267. package/container/agent-runner/src/timezone.test.ts +0 -93
  268. package/container/agent-runner/src/timezone.ts +0 -107
  269. package/container/agent-runner/tsconfig.json +0 -14
  270. package/container/build.sh +0 -48
  271. package/container/entrypoint.sh +0 -16
  272. package/container/skills/agent-browser/SKILL.md +0 -159
  273. package/container/skills/frontend-engineer/SKILL.md +0 -157
  274. package/container/skills/self-customize/SKILL.md +0 -87
  275. package/container/skills/slack-formatting/SKILL.md +0 -94
  276. package/container/skills/vercel-cli/SKILL.md +0 -111
  277. package/container/skills/welcome/SKILL.md +0 -85
  278. package/docs/APPLE-CONTAINER-NETWORKING.md +0 -90
  279. package/docs/BRANCH-FORK-MAINTENANCE.md +0 -81
  280. package/docs/README.md +0 -25
  281. package/docs/SDK_DEEP_DIVE.md +0 -643
  282. package/docs/SECURITY.md +0 -162
  283. package/docs/agent-runner-details.md +0 -749
  284. package/docs/api-details.md +0 -365
  285. package/docs/architecture-diagram.html +0 -422
  286. package/docs/architecture-diagram.md +0 -215
  287. package/docs/architecture.md +0 -751
  288. package/docs/audit/2026-04-30-channel-endpoint-audit.md +0 -36
  289. package/docs/build-and-runtime.md +0 -80
  290. package/docs/cross-mount-stress/README.md +0 -112
  291. package/docs/cross-mount-stress/container-writer-retry.mjs +0 -55
  292. package/docs/cross-mount-stress/container-writer-slow.mjs +0 -42
  293. package/docs/cross-mount-stress/container-writer.mjs +0 -47
  294. package/docs/cross-mount-stress/host-writer-retry.mjs +0 -55
  295. package/docs/cross-mount-stress/host-writer-slow.mjs +0 -43
  296. package/docs/cross-mount-stress/host-writer.mjs +0 -47
  297. package/docs/db-central.md +0 -316
  298. package/docs/db-session.md +0 -183
  299. package/docs/db.md +0 -119
  300. package/docs/design/2026-04-29-vault-management-ui.md +0 -231
  301. package/docs/design/2026-04-30-channel-wiring-rework.md +0 -234
  302. package/docs/design/2026-05-01-channel-wiring-approvals-deep-dive.md +0 -272
  303. package/docs/design/2026-05-02-channel-policy-and-approval-routing.md +0 -250
  304. package/docs/docker-sandboxes.md +0 -359
  305. package/docs/isolation-model.md +0 -88
  306. package/docs/ollama.md +0 -79
  307. package/docs/parachute-integration.md +0 -109
  308. package/docs/post-night-rebirth-reflections.md +0 -151
  309. package/eslint.config.js +0 -32
  310. package/pnpm-workspace.yaml +0 -8
  311. package/repo-tokens/README.md +0 -113
  312. package/repo-tokens/action.yml +0 -186
  313. package/repo-tokens/badge.svg +0 -23
  314. package/repo-tokens/examples/green.svg +0 -14
  315. package/repo-tokens/examples/red.svg +0 -14
  316. package/repo-tokens/examples/yellow-green.svg +0 -14
  317. package/repo-tokens/examples/yellow.svg +0 -14
  318. package/scripts/chat.ts +0 -101
  319. package/scripts/cleanup-sessions.sh +0 -150
  320. package/scripts/init-cli-agent.ts +0 -172
  321. package/scripts/init-first-agent.ts +0 -378
  322. package/scripts/parachute.ts +0 -158
  323. package/scripts/run-migrations.ts +0 -105
  324. package/scripts/sanity-live-poll.ts +0 -95
  325. package/scripts/seed-discord.ts +0 -80
  326. package/scripts/test-v2-agent.ts +0 -106
  327. package/scripts/test-v2-channel-e2e.ts +0 -265
  328. package/scripts/test-v2-host.ts +0 -184
  329. package/src/channels/adapter.ts +0 -214
  330. package/src/channels/api-translator.test.ts +0 -306
  331. package/src/channels/api-translator.ts +0 -214
  332. package/src/channels/ask-question.ts +0 -46
  333. package/src/channels/channel-registry.test.ts +0 -421
  334. package/src/channels/channel-registry.ts +0 -313
  335. package/src/channels/chat-sdk-bridge.test.ts +0 -84
  336. package/src/channels/chat-sdk-bridge.ts +0 -652
  337. package/src/channels/cli.ts +0 -276
  338. package/src/channels/discord.ts +0 -90
  339. package/src/channels/index.ts +0 -17
  340. package/src/channels/telegram-markdown-sanitize.test.ts +0 -78
  341. package/src/channels/telegram-markdown-sanitize.ts +0 -55
  342. package/src/channels/telegram-pairing.test.ts +0 -254
  343. package/src/channels/telegram-pairing.ts +0 -339
  344. package/src/channels/telegram.ts +0 -279
  345. package/src/channels/trust-hint.test.ts +0 -48
  346. package/src/channels/trust-hint.ts +0 -75
  347. package/src/claude-md-compose.migrate.test.ts +0 -64
  348. package/src/claude-md-compose.ts +0 -205
  349. package/src/command-gate.ts +0 -63
  350. package/src/config.test.ts +0 -93
  351. package/src/config.ts +0 -128
  352. package/src/container-config.ts +0 -167
  353. package/src/container-runner.test.ts +0 -32
  354. package/src/container-runner.ts +0 -576
  355. package/src/container-runtime.test.ts +0 -269
  356. package/src/container-runtime.ts +0 -167
  357. package/src/db/_bun-sqlite-shim.ts +0 -88
  358. package/src/db/agent-activity.test.ts +0 -155
  359. package/src/db/agent-activity.ts +0 -121
  360. package/src/db/agent-groups.ts +0 -77
  361. package/src/db/connection.migrate.test.ts +0 -176
  362. package/src/db/connection.ts +0 -259
  363. package/src/db/db-v2.test.ts +0 -440
  364. package/src/db/dropped-messages.ts +0 -44
  365. package/src/db/index.ts +0 -40
  366. package/src/db/messaging-groups.ts +0 -252
  367. package/src/db/migrations/001-initial.ts +0 -112
  368. package/src/db/migrations/002-chat-sdk-state.ts +0 -36
  369. package/src/db/migrations/008-dropped-messages.ts +0 -27
  370. package/src/db/migrations/009-drop-pending-credentials.ts +0 -13
  371. package/src/db/migrations/010-engage-modes.ts +0 -103
  372. package/src/db/migrations/011-pending-sender-approvals.ts +0 -40
  373. package/src/db/migrations/012-channel-registration.ts +0 -48
  374. package/src/db/migrations/013-approval-render-metadata.ts +0 -27
  375. package/src/db/migrations/014-secrets.ts +0 -44
  376. package/src/db/migrations/015-secrets-drop-host-pattern.ts +0 -18
  377. package/src/db/migrations/016-secret-assignments.ts +0 -30
  378. package/src/db/migrations/017-agent-activity.ts +0 -40
  379. package/src/db/migrations/018-oauth-app-configs.ts +0 -34
  380. package/src/db/migrations/019-oauth-app-connections.ts +0 -48
  381. package/src/db/migrations/020-agent-app-connections.ts +0 -28
  382. package/src/db/migrations/021-pending-oauth-states.ts +0 -35
  383. package/src/db/migrations/022-app-connections-provider.ts +0 -25
  384. package/src/db/migrations/023-agent-group-secret-mode.test.ts +0 -124
  385. package/src/db/migrations/023-agent-group-secret-mode.ts +0 -65
  386. package/src/db/migrations/024-collapse-approvals.test.ts +0 -249
  387. package/src/db/migrations/024-collapse-approvals.ts +0 -182
  388. package/src/db/migrations/025-secret-mode-check.test.ts +0 -155
  389. package/src/db/migrations/025-secret-mode-check.ts +0 -49
  390. package/src/db/migrations/026-user-dms-bot-id.test.ts +0 -116
  391. package/src/db/migrations/026-user-dms-bot-id.ts +0 -54
  392. package/src/db/migrations/027-provider-credentials.ts +0 -41
  393. package/src/db/migrations/_test-helpers.ts +0 -41
  394. package/src/db/migrations/index.ts +0 -127
  395. package/src/db/migrations/module-agent-to-agent-destinations.ts +0 -84
  396. package/src/db/migrations/module-approvals-pending-approvals.ts +0 -42
  397. package/src/db/migrations/module-approvals-title-options.ts +0 -40
  398. package/src/db/schema.ts +0 -258
  399. package/src/db/session-db.test.ts +0 -93
  400. package/src/db/session-db.ts +0 -325
  401. package/src/db/sessions.ts +0 -241
  402. package/src/delivery.test.ts +0 -148
  403. package/src/delivery.ts +0 -445
  404. package/src/env.ts +0 -74
  405. package/src/group-folder.test.ts +0 -35
  406. package/src/group-folder.ts +0 -44
  407. package/src/group-init.ts +0 -92
  408. package/src/host-core.test.ts +0 -456
  409. package/src/host-sweep.test.ts +0 -146
  410. package/src/host-sweep.ts +0 -287
  411. package/src/index.ts +0 -232
  412. package/src/install-slug.ts +0 -33
  413. package/src/log.test.ts +0 -81
  414. package/src/log.ts +0 -117
  415. package/src/mcp/http.ts +0 -72
  416. package/src/mcp/server.ts +0 -92
  417. package/src/mcp/stdio.ts +0 -51
  418. package/src/mcp/tools/activity.ts +0 -88
  419. package/src/mcp/tools/agent-groups.ts +0 -183
  420. package/src/mcp/tools/approvals.ts +0 -122
  421. package/src/mcp/tools/channels.test.ts +0 -126
  422. package/src/mcp/tools/channels.ts +0 -134
  423. package/src/mcp/tools/index.ts +0 -27
  424. package/src/mcp/tools/oauth.ts +0 -48
  425. package/src/mcp/tools/secrets.ts +0 -169
  426. package/src/mcp/tools/sessions.ts +0 -135
  427. package/src/mcp/types.ts +0 -51
  428. package/src/modules/agent-to-agent/agent-route.test.ts +0 -46
  429. package/src/modules/agent-to-agent/agent-route.ts +0 -223
  430. package/src/modules/agent-to-agent/create-agent.ts +0 -127
  431. package/src/modules/agent-to-agent/db/agent-destinations.ts +0 -135
  432. package/src/modules/agent-to-agent/index.ts +0 -22
  433. package/src/modules/agent-to-agent/write-destinations.ts +0 -59
  434. package/src/modules/approvals/agent.md +0 -45
  435. package/src/modules/approvals/index.ts +0 -21
  436. package/src/modules/approvals/picks.test.ts +0 -291
  437. package/src/modules/approvals/primitive.ts +0 -279
  438. package/src/modules/approvals/project.md +0 -27
  439. package/src/modules/approvals/response-handler.ts +0 -87
  440. package/src/modules/index.ts +0 -24
  441. package/src/modules/interactive/agent.md +0 -21
  442. package/src/modules/interactive/index.ts +0 -69
  443. package/src/modules/interactive/project.md +0 -12
  444. package/src/modules/mount-security/expand-path.test.ts +0 -82
  445. package/src/modules/mount-security/index.ts +0 -459
  446. package/src/modules/mount-security/migrate.test.ts +0 -91
  447. package/src/modules/permissions/access.ts +0 -28
  448. package/src/modules/permissions/channel-approval.test.ts +0 -389
  449. package/src/modules/permissions/channel-approval.ts +0 -188
  450. package/src/modules/permissions/db/agent-group-members.ts +0 -44
  451. package/src/modules/permissions/db/pending-channel-approvals.test.ts +0 -86
  452. package/src/modules/permissions/db/pending-channel-approvals.ts +0 -66
  453. package/src/modules/permissions/db/pending-sender-approvals.ts +0 -60
  454. package/src/modules/permissions/db/user-dms.ts +0 -58
  455. package/src/modules/permissions/db/user-roles.ts +0 -85
  456. package/src/modules/permissions/db/users.ts +0 -38
  457. package/src/modules/permissions/index.ts +0 -421
  458. package/src/modules/permissions/permissions.test.ts +0 -358
  459. package/src/modules/permissions/sender-approval.test.ts +0 -641
  460. package/src/modules/permissions/sender-approval.ts +0 -165
  461. package/src/modules/permissions/user-dm.ts +0 -200
  462. package/src/modules/provider-credentials/db.ts +0 -121
  463. package/src/modules/provider-credentials/index.ts +0 -12
  464. package/src/modules/provider-credentials/spawn.test.ts +0 -206
  465. package/src/modules/provider-credentials/spawn.ts +0 -114
  466. package/src/modules/scheduling/actions.ts +0 -113
  467. package/src/modules/scheduling/db.test.ts +0 -282
  468. package/src/modules/scheduling/db.ts +0 -148
  469. package/src/modules/scheduling/index.ts +0 -34
  470. package/src/modules/scheduling/recurrence.test.ts +0 -98
  471. package/src/modules/scheduling/recurrence.ts +0 -54
  472. package/src/modules/self-mod/agent.md +0 -30
  473. package/src/modules/self-mod/apply.ts +0 -85
  474. package/src/modules/self-mod/index.ts +0 -30
  475. package/src/modules/self-mod/project.md +0 -39
  476. package/src/modules/self-mod/request.ts +0 -91
  477. package/src/modules/typing/index.ts +0 -165
  478. package/src/oauth/agent-app-connections.ts +0 -103
  479. package/src/oauth/app-configs.test.ts +0 -64
  480. package/src/oauth/app-configs.ts +0 -114
  481. package/src/oauth/app-connections.test.ts +0 -109
  482. package/src/oauth/app-connections.ts +0 -178
  483. package/src/oauth/crypto.ts +0 -56
  484. package/src/oauth/flow.ts +0 -104
  485. package/src/oauth/providers/google.test.ts +0 -38
  486. package/src/oauth/providers/google.ts +0 -46
  487. package/src/oauth/providers/index.ts +0 -48
  488. package/src/oauth/state-store.test.ts +0 -54
  489. package/src/oauth/state-store.ts +0 -93
  490. package/src/parachute/README.md +0 -27
  491. package/src/parachute/create-agent.test.ts +0 -83
  492. package/src/parachute/create-agent.ts +0 -122
  493. package/src/parachute/group-status.test.ts +0 -165
  494. package/src/parachute/group-status.ts +0 -136
  495. package/src/parachute/types.ts +0 -41
  496. package/src/parachute/vault-mcp.test.ts +0 -251
  497. package/src/parachute/vault-mcp.ts +0 -232
  498. package/src/platform-id.test.ts +0 -104
  499. package/src/platform-id.ts +0 -109
  500. package/src/providers/index.ts +0 -6
  501. package/src/providers/provider-container-registry.ts +0 -58
  502. package/src/response-registry.ts +0 -45
  503. package/src/router.ts +0 -530
  504. package/src/secrets/crypto.test.ts +0 -45
  505. package/src/secrets/crypto.ts +0 -55
  506. package/src/secrets/index.ts +0 -461
  507. package/src/secrets/master-key.ts +0 -70
  508. package/src/secrets/secrets.test.ts +0 -651
  509. package/src/session-manager.attachments.test.ts +0 -171
  510. package/src/session-manager.dup-skip.test.ts +0 -173
  511. package/src/session-manager.migrate.test.ts +0 -59
  512. package/src/session-manager.ts +0 -451
  513. package/src/startup-bootstrap.test.ts +0 -226
  514. package/src/startup-bootstrap.ts +0 -207
  515. package/src/state-sqlite.ts +0 -182
  516. package/src/timezone.test.ts +0 -64
  517. package/src/timezone.ts +0 -37
  518. package/src/types.ts +0 -233
  519. package/src/web/auth.test.ts +0 -335
  520. package/src/web/auth.ts +0 -214
  521. package/src/web/discord-validate.test.ts +0 -77
  522. package/src/web/discord-validate.ts +0 -88
  523. package/src/web/hub-discovery.test.ts +0 -98
  524. package/src/web/hub-discovery.ts +0 -69
  525. package/src/web/routes/activity.ts +0 -106
  526. package/src/web/routes/agent-provider.test.ts +0 -282
  527. package/src/web/routes/agent-provider.ts +0 -309
  528. package/src/web/routes/approvals.ts +0 -185
  529. package/src/web/routes/apps.ts +0 -434
  530. package/src/web/routes/channels-mg-detail.test.ts +0 -324
  531. package/src/web/routes/channels-mga-detail.test.ts +0 -472
  532. package/src/web/routes/channels.ts +0 -311
  533. package/src/web/routes/oauth-providers.ts +0 -42
  534. package/src/web/routes/secrets.test.ts +0 -220
  535. package/src/web/routes/secrets.ts +0 -317
  536. package/src/web/routes/sessions.ts +0 -123
  537. package/src/web/routes/settings.test.ts +0 -106
  538. package/src/web/routes/settings.ts +0 -247
  539. package/src/web/routes/setup-status.ts +0 -205
  540. package/src/web/routes/vaults.test.ts +0 -389
  541. package/src/web/routes/vaults.ts +0 -225
  542. package/src/web/server-version.test.ts +0 -16
  543. package/src/web/server.ts +0 -1024
  544. package/src/web/services-manifest.test.ts +0 -148
  545. package/src/web/services-manifest.ts +0 -66
  546. package/src/web/static-serve.test.ts +0 -255
  547. package/src/web/static-serve.ts +0 -104
  548. package/src/web/telegram-validate.test.ts +0 -116
  549. package/src/web/telegram-validate.ts +0 -107
  550. package/src/web/vault-proxy.test.ts +0 -214
  551. package/src/web/vault-proxy.ts +0 -120
  552. package/src/web/wire-channel.ts +0 -181
  553. package/src/webhook-server.ts +0 -134
  554. package/vitest.config.ts +0 -18
  555. package/web/README.md +0 -63
  556. package/web/ui/index.html +0 -13
  557. package/web/ui/package.json +0 -35
  558. package/web/ui/pnpm-lock.yaml +0 -2164
  559. package/web/ui/scripts/verify-base.mjs +0 -31
  560. package/web/ui/src/App.tsx +0 -88
  561. package/web/ui/src/components/ActivityFeed.tsx +0 -444
  562. package/web/ui/src/components/AgentGroupPicker.tsx +0 -263
  563. package/web/ui/src/components/AgentProviderCards.tsx +0 -220
  564. package/web/ui/src/components/CredentialForm.tsx +0 -214
  565. package/web/ui/src/components/ScopeGrants.tsx +0 -74
  566. package/web/ui/src/components/StatusDot.tsx +0 -43
  567. package/web/ui/src/components/VaultPicker.tsx +0 -127
  568. package/web/ui/src/components/setup/AdapterInstallStep.tsx +0 -178
  569. package/web/ui/src/components/setup/AgentGroupStep.tsx +0 -43
  570. package/web/ui/src/components/setup/ChannelPickStep.tsx +0 -74
  571. package/web/ui/src/components/setup/DoneStep.tsx +0 -49
  572. package/web/ui/src/components/setup/PrereqStep.tsx +0 -129
  573. package/web/ui/src/components/setup/TestConnectionStep.tsx +0 -108
  574. package/web/ui/src/components/setup/TestMessageStep.tsx +0 -104
  575. package/web/ui/src/components/setup/WireChannelStep.tsx +0 -166
  576. package/web/ui/src/components/setup/types.ts +0 -105
  577. package/web/ui/src/lib/api.test.ts +0 -410
  578. package/web/ui/src/lib/api.ts +0 -1248
  579. package/web/ui/src/lib/auth.test.ts +0 -352
  580. package/web/ui/src/lib/auth.ts +0 -405
  581. package/web/ui/src/lib/channel-adapters.ts +0 -136
  582. package/web/ui/src/main.tsx +0 -19
  583. package/web/ui/src/routes/ApprovalsList.tsx +0 -294
  584. package/web/ui/src/routes/Apps.tsx +0 -613
  585. package/web/ui/src/routes/ChannelWireDetail.test.tsx +0 -233
  586. package/web/ui/src/routes/ChannelWireDetail.tsx +0 -403
  587. package/web/ui/src/routes/ChannelsList.tsx +0 -158
  588. package/web/ui/src/routes/GroupDetail.test.tsx +0 -206
  589. package/web/ui/src/routes/GroupDetail.tsx +0 -880
  590. package/web/ui/src/routes/GroupList.tsx +0 -187
  591. package/web/ui/src/routes/MessagingGroupDetail.test.tsx +0 -233
  592. package/web/ui/src/routes/MessagingGroupDetail.tsx +0 -306
  593. package/web/ui/src/routes/NewGroupWizard.tsx +0 -390
  594. package/web/ui/src/routes/OAuthCallback.tsx +0 -56
  595. package/web/ui/src/routes/SecretsList.tsx +0 -942
  596. package/web/ui/src/routes/SessionsList.tsx +0 -220
  597. package/web/ui/src/routes/SettingsAgentProvider.tsx +0 -109
  598. package/web/ui/src/routes/SettingsApprovals.tsx +0 -234
  599. package/web/ui/src/routes/SetupWizard.tsx +0 -219
  600. package/web/ui/src/routes/VaultDetail.test.tsx +0 -363
  601. package/web/ui/src/routes/VaultDetail.tsx +0 -960
  602. package/web/ui/src/routes/VaultsList.tsx +0 -295
  603. package/web/ui/src/routes/WireChannelPage.tsx +0 -413
  604. package/web/ui/src/styles.css +0 -608
  605. package/web/ui/src/test/setup.ts +0 -23
  606. package/web/ui/src/vite-env.d.ts +0 -10
  607. package/web/ui/vite.config.ts +0 -34
  608. package/web/ui/vitest.config.ts +0 -25
@@ -0,0 +1,382 @@
1
+ /**
2
+ * Agent-spec + Sandbox contract types.
3
+ *
4
+ * The **agent spec** (design §4.1) is the single declaration of everything an
5
+ * arm may reach: its MCP surface (channels + vault), its network egress, and its
6
+ * filesystem view. Scope and isolation are both read off this one object, so
7
+ * there is exactly one place that says "what is this arm allowed to touch."
8
+ *
9
+ * design/2026-06-14-sandboxed-agent-sessions.md §4.1, §4.4, §4.5
10
+ *
11
+ * The Sandbox **contract** (design §3.1) is held constant; the **mechanism**
12
+ * varies by platform (Seatbelt on macOS, bubblewrap on Linux) behind it. v1
13
+ * ships one backend (Anthropic's sandbox-runtime); the escalation rung (§3.4 —
14
+ * gVisor / full VM) is a second backend added later without touching callers.
15
+ */
16
+
17
+ /** Read/write mode for a declared mount. */
18
+ export type MountMode = "ro" | "rw";
19
+
20
+ /**
21
+ * A filesystem bind beyond the implicit workspace + runtime/config. Each entry
22
+ * binds a host path to a mount path at `ro` or `rw` (design §4.5).
23
+ *
24
+ * On macOS the sandbox profile is glob-capable, so `hostPath` may contain glob
25
+ * patterns; on Linux it must be a literal path (the runtime does not glob there).
26
+ * For v1 we bind the host path directly (`mountPath` is recorded for the future
27
+ * bubblewrap bind-remap and for the spec→mandate seam, §4.6 — it does not change
28
+ * the v1 Seatbelt path, which has no path-remapping layer).
29
+ */
30
+ export interface AgentMount {
31
+ /** Path on the host to expose into the session. */
32
+ hostPath: string;
33
+ /** Path the session sees it at. Recorded for the future bind-remap; see note above. */
34
+ mountPath: string;
35
+ /** Read-only or read-write. */
36
+ mode: MountMode;
37
+ /**
38
+ * Opt-in cross-session share by name (design §4.5). A deliberate hole in
39
+ * session-to-session isolation — honored here, but the trust caveat (prefer
40
+ * shared-`ro` from the producer, never shared-`rw` across a trust boundary) is
41
+ * doc-level for v1. Plumbed through so a future use is a considered decision.
42
+ */
43
+ shared?: string;
44
+ }
45
+
46
+ /** The vault binding for an arm: which vault, what access, optionally tag-scoped. */
47
+ export interface AgentVaultSpec {
48
+ /** Vault instance name (e.g. "default"). */
49
+ name: string;
50
+ /** Access verb minted into the vault token. */
51
+ access: "read" | "write" | "admin";
52
+ /**
53
+ * Optional tag scope — narrows the minted vault token to these tags via the
54
+ * `permissions.scoped_tags` claim (e.g. `["#agent/message"]`). Omitted = the
55
+ * verb's full scope across the vault.
56
+ */
57
+ tags?: string[];
58
+ }
59
+
60
+ /** An additional MCP server to wire in, by URL (design §4.1 `otherMcps`). */
61
+ export interface OtherMcpSpec {
62
+ /** Entry key in the generated `mcpServers` object. */
63
+ name: string;
64
+ /** Streamable-HTTP MCP URL. */
65
+ url: string;
66
+ /**
67
+ * Scope to mint a token for, if this MCP is hub-gated. Omitted = no token
68
+ * (an unauthenticated / externally-authenticated MCP).
69
+ */
70
+ scope?: string;
71
+ /** Audience to mint the token under. Defaults to inferred from scope by the hub. */
72
+ audience?: string;
73
+ }
74
+
75
+ /**
76
+ * An agent spec — the complete least-privilege envelope for one launched arm
77
+ * (design §4.1). `egress` and `mounts` are additive to a non-removable base; the
78
+ * spec only ever *adds*.
79
+ */
80
+ /**
81
+ * A channel binding for an arm. A bare string is shorthand for `{ name, access:
82
+ * "write" }` (back-compat — the common read+write single-threaded session); the object
83
+ * form scopes a channel read-only so an arm that only *watches* a channel mints
84
+ * `agent:read` and never `agent:write` (the "scope an arm to channel X
85
+ * read-only" use case).
86
+ */
87
+ export interface AgentChannelSpec {
88
+ /** Channel name (the `/mcp/<channel>` segment). */
89
+ name: string;
90
+ /**
91
+ * Channel access. `"write"` (default) mints `agent:read agent:write`;
92
+ * `"read"` mints `agent:read` only — the arm can be woken + read the channel
93
+ * but cannot reply.
94
+ */
95
+ access?: "read" | "write";
96
+ }
97
+
98
+ /** A channel entry: a bare name (= write access) or the scoped object form. */
99
+ export type AgentChannel = string | AgentChannelSpec;
100
+
101
+ /**
102
+ * Which backend drives the agent (design 2026-06-16-pluggable-agent-backend.md +
103
+ * 2026-06-18-channel-backend.md). There are exactly TWO — the `interactive` (tmux)
104
+ * backend was RETIRED 2026-06-19 (design 2026-06-19-retire-interactive-backend.md);
105
+ * `channel` is what it was reaching for, done right:
106
+ *
107
+ * - `"programmatic"` (the DEFAULT): NO resident process. An inbound message becomes
108
+ * one on-demand `claude -p --resume <sid>` turn ({@link AgentBackend}); the reply
109
+ * is posted back as an outbound `#agent/message/outbound` note. No idle session →
110
+ * nothing to go deaf, no reconnect, no replay, no consent gate. The reliable
111
+ * primary path; best for clean per-message "do a task, report back" turns.
112
+ * - `"attached"` (design 2026-06-18-channel-backend.md; the backend value was named
113
+ * `"channel"` before this rename — see the BACK-COMPAT note below): the turn is
114
+ * delivered over a channel to a Claude Code session the OPERATOR runs themselves
115
+ * (their machine, their env/creds, unsandboxed) and has connected (is "attached") to
116
+ * the channel's MCP endpoint. The daemon runs NO `claude -p`; the inbound
117
+ * `#agent/message/inbound` notes accumulate as a durable queue (the vault IS the
118
+ * queue). The connected session PULLs the next message (an MCP tool), works, and
119
+ * REPLYs (an MCP tool) — the daemon writes the outbound note + marks the inbound
120
+ * handled. Routed to the {@link AttachedQueueRegistry}, entirely bypassing the
121
+ * programmatic serial worker (the daemon routing fork). Claim state lives on the
122
+ * inbound note's `status` (`pending | in-flight | handled`), so it's restart-safe.
123
+ *
124
+ * BACK-COMPAT (backend VALUE rename `"channel"` → `"attached"`): an already-persisted
125
+ * def (or `spec.json`) whose `backend` is the legacy `"channel"` is DUAL-READ —
126
+ * normalized to `"attached"` on read (`parseAgentDef` in agent-defs.ts; the daemon
127
+ * routing fork also accepts a legacy un-normalized value defensively). Only the new
128
+ * `"attached"` value is ever WRITTEN. (Note: the ROUTING KEY `channel` — the agent's
129
+ * address, the `/mcp/<channel>` URL segment, `metadata.channel` on notes — is a
130
+ * SEPARATE concept and is deliberately unchanged.)
131
+ *
132
+ * BACK-COMPAT (retired `interactive`): a persisted `spec.json` that carries the retired
133
+ * `backend:"interactive"` value (or omits `backend` entirely — pre-field specs were
134
+ * interactive) is no longer re-registered on boot (the boot re-register reads
135
+ * `spec.backend === "programmatic"` exactly; anything else is skipped — see daemon.ts),
136
+ * so a stale interactive spec on disk is inert, never migrated and never launched.
137
+ */
138
+ export type AgentBackendKind = "programmatic" | "attached";
139
+
140
+ /**
141
+ * How a channel's {@link AgentSpec.systemPrompt} composes with Claude Code's own
142
+ * default system prompt (design 2026-06-16-channel-system-prompt.md):
143
+ *
144
+ * - `"append"` (DEFAULT): KEEP Claude Code's capable default system prompt
145
+ * (~2.4k tokens of tool/agentic instruction) and ADD the channel's role on top
146
+ * — `claude -p --append-system-prompt(-file) <X>`. The right default: the
147
+ * channel gets strong specificity without losing CC's base competence.
148
+ * - `"replace"`: REPLACE the default entirely with the channel's prompt —
149
+ * `claude -p --system-prompt(-file) <X>`. A fully-custom persona for an
150
+ * operator who wants total control of the system layer (and leanness on the
151
+ * subscription — fewer base tokens per turn).
152
+ *
153
+ * Either mode is orthogonal to CLAUDE.md, which is a SEPARATE context layer
154
+ * unaffected by both flags (only `--bare` would drop it — and `--bare` is
155
+ * deliberately NOT implemented; see the design note: it is API-key-only by design
156
+ * and would force metered billing off the subscription).
157
+ */
158
+ export type SystemPromptMode = "append" | "replace";
159
+
160
+ /**
161
+ * The agent's EXECUTION-LIFECYCLE mode — how a turn relates to the agent's
162
+ * conversation thread (the architecture synthesis, Phase 3 prerequisite). The UNIFIED
163
+ * model is `definition -> thread -> message`: EVERYTHING is a thread, and BOTH modes
164
+ * materialize a `#agent/thread` note (the structural unification — a "run" was always a
165
+ * thread with one turn). An agent is either SINGLE-THREADED or MULTI-THREADED; the
166
+ * distinction is defined entirely by `claude -p` session-id semantics + the thread's
167
+ * identity:
168
+ *
169
+ * - `"single-threaded"` (DEFAULT; = today's behavior): ONE persistent session id
170
+ * per channel. Each turn `--resume`s the stored id and persists the returned id
171
+ * after — the channel transcript IS the thread. It materializes exactly ONE
172
+ * `#agent/thread` note per channel, named after the definition, UPSERTED in place
173
+ * each turn; the note body holds a rolling SUMMARY of the conversation (turn_count +
174
+ * cumulative usage roll up). A scheduled runner job for a single-threaded def is a
175
+ * synthetic inbound that RESUMES that one thread (continuing the chat). This is
176
+ * exactly what every agent does today (plus the now-materialized thread note).
177
+ *
178
+ * - `"multi-threaded"`: turns are THREAD-KEYED. TODAY — because no inbound carries
179
+ * a thread id yet — every fire mints a FRESH thread: do NOT read the prior session
180
+ * id (no `--resume`) and do NOT persist the returned id to the channel store, so
181
+ * each fire is a clean, independent invocation with no conversation continuity. It
182
+ * materializes ONE `#agent/thread` note per FIRE (the per-fire record: input + reply
183
+ * + status + timing). This is what an operator reaches for when a scheduled job
184
+ * should be a clean task run, NOT a silent continuation of the chat thread.
185
+ *
186
+ * ("one-shot" was the prior name for this mode — it was only ever the DEGENERATE
187
+ * FIRST-TURN of a multi-threaded agent, so the term retires. Continuation-by-
188
+ * thread-id — resuming a SPECIFIC prior thread — is a DEFERRED increment: it needs
189
+ * thread-id routing on the inbound, a thread-keyed session store, per-thread drain
190
+ * serialization, and recording the minted session/thread id into the thread note so
191
+ * a thread becomes resumable. When it lands, the SAME mode simply gains continuation
192
+ * with NO operator-facing change and NO migration; the fresh-per-fire shape that ships
193
+ * now is its degenerate case.)
194
+ */
195
+ export type AgentMode = "single-threaded" | "multi-threaded";
196
+
197
+ export interface AgentSpec {
198
+ /** Human-readable arm name; used as the tmux session + workspace slug. */
199
+ name: string;
200
+ /**
201
+ * Channels to attach (one MCP entry each). Each entry is a bare name (read+write,
202
+ * back-compat) or `{ name, access: "read" }` to scope a channel read-only.
203
+ */
204
+ channels: AgentChannel[];
205
+ /**
206
+ * WORKING DIRECTORY — a real host path the agent operates from (design
207
+ * 2026-06-16-agent-filesystem-and-sharing.md, the working-directory axis). When
208
+ * set, this absolute path becomes the agent's CWD and an rw working-root in the
209
+ * sandbox (it's bound rw + readable, exactly like an `rw` mount that is also the
210
+ * cwd). It is SHAREABLE — two agents (or a runner job, or a plain script) can
211
+ * point at the same dir (shared-rw concurrency is a known, deferred caveat; the
212
+ * agents step on each other like humans in a repo without git discipline).
213
+ *
214
+ * CRITICAL — the working dir is DECOUPLED from the agent's PRIVATE RUNTIME HOME.
215
+ * The seeded `CLAUDE_CONFIG_DIR` (`seedAgentHome`), `tmp`, `spec.json`,
216
+ * `system-prompt.txt`, and ESPECIALLY `.mcp.json` (which inlines the scoped
217
+ * vault/channel tokens — secrets) STAY in the per-agent private `sessions/<name>/`
218
+ * dir, 0600, NEVER written into this shared `workspace`. The governing principle
219
+ * (the design note's "line"): capability/credential/state is per-agent private;
220
+ * the working dir is shareable. `--mcp-config` / `--system-prompt-file` point at
221
+ * the private dir by ABSOLUTE path, so they're unaffected by the cwd change.
222
+ *
223
+ * Unset → today's behavior EXACTLY: the cwd is the private `sessions/<name>` dir
224
+ * (which is also the synthetic workspace).
225
+ */
226
+ workspace?: string;
227
+ /** Optional vault binding. */
228
+ vault?: AgentVaultSpec;
229
+ /** Additional MCP servers, by URL. */
230
+ otherMcps?: OtherMcpSpec[];
231
+ /**
232
+ * Filesystem READ scope — ONE of Anthropic's two containment boundaries
233
+ * (https://www.anthropic.com/engineering/claude-code-sandboxing). Orthogonal to
234
+ * {@link network} — the agent's reach into the local disk and its reach onto the
235
+ * network are independent controls, deliberately NOT bundled.
236
+ *
237
+ * - `"workspace"` (DEFAULT): SCOPED reads. The home tree (`/Users` on macOS,
238
+ * `/home` on Linux) is DENIED, then re-allowed ONLY for the per-session
239
+ * workspace + the claude runtime + declared mounts. The agent literally
240
+ * cannot read the operator's secrets — `~/.parachute/operator.token` (the
241
+ * hub bearer), SSH keys, other projects — even though the network is open.
242
+ * This is the security-correct default: scoped disk, exfiltration surface
243
+ * removed at the source.
244
+ *
245
+ * - `"full"`: BROAD reads (the runtime default — the whole filesystem is
246
+ * readable). An explicit, deliberate escape hatch for the rare agent that
247
+ * genuinely needs to read across the operator's disk AND is trusted not to
248
+ * leak it. Combined with `network: "open"` this is the maximum-reach posture
249
+ * — only choose it knowingly.
250
+ *
251
+ * WRITES are confined to the per-session workspace + rw mounts in BOTH cases
252
+ * (the agent can never corrupt the operator's files or escape its workspace),
253
+ * exactly per Anthropic's "read/write the cwd, block outside" model.
254
+ */
255
+ filesystem?: "workspace" | "full";
256
+ /**
257
+ * Network egress — the SECOND containment boundary, orthogonal to
258
+ * {@link filesystem}:
259
+ *
260
+ * - `"open"` (DEFAULT): full internet, no restriction. The right default for
261
+ * an owner-operated agent on a trusted box (claude needs the network to be
262
+ * useful) — SAFE because the `"workspace"` filesystem default already keeps
263
+ * local secrets unreadable, so "open network" can't exfiltrate what the
264
+ * agent can't see.
265
+ *
266
+ * - `"restricted"`: egress confined to a non-removable base
267
+ * (`{ Anthropic API, hub/vault }`) UNIONed with {@link egress}. For an agent
268
+ * fed FOREIGN/untrusted input, where you want to bound where it can reach
269
+ * even for data it legitimately holds.
270
+ */
271
+ network?: "open" | "restricted";
272
+ /**
273
+ * Network egress hosts ADDITIVE to the non-removable base — only meaningful
274
+ * under `network: "restricted"` (when the network is `"open"` this is ignored).
275
+ * A restricted code-building agent opens exactly the package/source hosts it
276
+ * needs here.
277
+ */
278
+ egress?: string[];
279
+ /**
280
+ * Filesystem mounts — ADDITIVE to the default private per-session workspace
281
+ * (rw) + the implicit runtime/claude-config (ro). Under `filesystem:
282
+ * "workspace"` (the default) these are the ONLY host paths re-allowed for
283
+ * reads beyond the workspace — mount the project you want the agent to work on.
284
+ */
285
+ mounts?: AgentMount[];
286
+ /**
287
+ * Which stored Claude credential to inject (design §6). Default = "operator".
288
+ * This stream injects the credential as a passed-in param/placeholder; Stream 3
289
+ * builds the real per-channel secret store.
290
+ */
291
+ credentialRef?: string;
292
+ /**
293
+ * Which backend drives the agent (design 2026-06-16-pluggable-agent-backend.md +
294
+ * 2026-06-18-channel-backend.md) — `"programmatic"` (the default; on-demand
295
+ * `claude -p` turns, no resident process) or `"attached"` (handled by a Claude Code
296
+ * session the operator connects — "attaches" — to the channel's MCP endpoint; the
297
+ * value was named `"channel"` before the rename, dual-read on load). The `interactive`
298
+ * (tmux) backend was retired 2026-06-19. See {@link AgentBackendKind}. Persisted in
299
+ * spec.json so a daemon restart re-registers a programmatic agent on boot.
300
+ */
301
+ backend?: AgentBackendKind;
302
+ /**
303
+ * Which model the PROGRAMMATIC backend runs the turn on — passed verbatim to
304
+ * `claude -p --model <value>`. Accepts a Claude Code alias (`opus` / `sonnet` /
305
+ * `haiku`) or a full model id (e.g. `claude-opus-4-8`). Unset → no `--model`
306
+ * flag, so the turn inherits Claude Code's own default (Sonnet today). Only the
307
+ * programmatic backend reads this (a `channel`-backend turn runs in the
308
+ * operator's own session, whose model the operator controls). Set from the def's
309
+ * `metadata.model`; persisted in spec.json. NOT shell-interpolated — it's a
310
+ * discrete argv element, so an arbitrary string can't inject.
311
+ */
312
+ model?: string;
313
+ /**
314
+ * Per-channel system prompt — the operator gives the channel a specific role,
315
+ * backend-visible, so the agent gets strong specificity decoupled from the
316
+ * workspace + CLAUDE.md (design 2026-06-16-channel-system-prompt.md). Set when
317
+ * creating/configuring the channel; written to a per-session file and passed on
318
+ * EVERY `claude -p` turn (the flags are per-invocation, not persistent — a
319
+ * `--resume` turn re-passes it too). Unset → today's behavior (CC's default
320
+ * prompt, untouched). Persisted in spec.json.
321
+ */
322
+ systemPrompt?: string;
323
+ /**
324
+ * How {@link systemPrompt} composes with Claude Code's default system prompt —
325
+ * `"append"` (DEFAULT, keep CC's base + add the role) or `"replace"` (full
326
+ * custom persona). Only meaningful when `systemPrompt` is set. See
327
+ * {@link SystemPromptMode}.
328
+ */
329
+ systemPromptMode?: SystemPromptMode;
330
+ /**
331
+ * The execution-lifecycle mode (the Phase-3 prerequisite). `"single-threaded"`
332
+ * (DEFAULT, = today): one persistent session per channel, `--resume`d + persisted
333
+ * each turn, the channel transcript is the thread. `"multi-threaded"`: thread-keyed —
334
+ * today (no inbound thread id yet) every fire mints a fresh thread (no `--resume`, the
335
+ * returned session id is NOT persisted to the channel store). BOTH modes now materialize
336
+ * an `#agent/thread` note (the unified model `definition -> thread -> message`): a
337
+ * single-threaded agent upserts ONE thread note per channel (named after the def, rolling
338
+ * summary); a multi-threaded agent writes one thread note per fire. Read at the
339
+ * session-handling chokepoint (the programmatic backend's `deliver` resume block) +
340
+ * governs the thread note's identity (one-per-channel upsert vs one-per-fire). Persisted
341
+ * in spec.json (set from the def's `metadata.mode`). See {@link AgentMode}.
342
+ */
343
+ mode?: AgentMode;
344
+ /**
345
+ * The `#agent/definition` note id this agent was instantiated from — the
346
+ * provenance carried into the `#agent/thread` note a turn materializes (so a thread
347
+ * record links back to its def; BOTH modes). A plain id STRING for now (interim — typed
348
+ * link fields are a future vault feature). Set by {@link parseAgentDef} from the note
349
+ * id; unset for a spec not sourced from a def note (then the thread note carries no
350
+ * definition link).
351
+ */
352
+ definition?: string;
353
+ }
354
+
355
+ /**
356
+ * The default workspace + runtime binds the contract always grants, independent
357
+ * of any spec. The caller supplies the concrete paths (resolved against the
358
+ * session's state dir + the runtime/config location) — keeping this module
359
+ * free of filesystem-layout assumptions and test-sandboxable.
360
+ */
361
+ export interface BaseBinds {
362
+ /** Private per-session workspace (rw). */
363
+ workspace: string;
364
+ /**
365
+ * Read-only runtime/claude-config paths the session needs to run `claude`
366
+ * (e.g. the claude config dir). Always bound `ro`.
367
+ */
368
+ runtimeReadOnly: string[];
369
+ }
370
+
371
+ /** Platform the Sandbox config is being built for. */
372
+ export type SandboxPlatform = "darwin" | "linux";
373
+
374
+ /**
375
+ * Normalize a channel entry (bare name or object) to `{ name, access }`. A bare
376
+ * string defaults to `write` (back-compat); the object form defaults to `write`
377
+ * when `access` is omitted.
378
+ */
379
+ export function normalizeChannel(ch: AgentChannel): { name: string; access: "read" | "write" } {
380
+ if (typeof ch === "string") return { name: ch, access: "write" };
381
+ return { name: ch.name, access: ch.access ?? "write" };
382
+ }
@@ -0,0 +1,106 @@
1
+ import { describe, test, expect } from "bun:test";
2
+ import { mkdtempSync, readFileSync, writeFileSync, existsSync } from "fs";
3
+ import { tmpdir } from "os";
4
+ import { join } from "path";
5
+ import { resolveManifestPath, upsertService, listVaultNames, type ServiceEntry } from "./services-manifest.ts";
6
+
7
+ function tmp(): string {
8
+ return join(mkdtempSync(join(tmpdir(), "pc-manifest-")), "services.json");
9
+ }
10
+
11
+ const AGENT: ServiceEntry = {
12
+ name: "parachute-agent",
13
+ port: 1941,
14
+ paths: ["/agent"],
15
+ health: "/health",
16
+ version: "0.1.0",
17
+ displayName: "Agent",
18
+ stripPrefix: true,
19
+ };
20
+
21
+ describe("resolveManifestPath", () => {
22
+ test("honors PARACHUTE_HOME (sandbox for tests/e2e)", () => {
23
+ expect(resolveManifestPath({ PARACHUTE_HOME: "/tmp/sandbox" })).toBe("/tmp/sandbox/services.json");
24
+ });
25
+ test("falls back to HOME/.parachute", () => {
26
+ expect(resolveManifestPath({ HOME: "/home/x" })).toBe("/home/x/.parachute/services.json");
27
+ });
28
+ });
29
+
30
+ describe("upsertService", () => {
31
+ test("creates the manifest with the entry on first write", () => {
32
+ const path = tmp();
33
+ upsertService(AGENT, path);
34
+ const m = JSON.parse(readFileSync(path, "utf8"));
35
+ expect(m.services).toHaveLength(1);
36
+ expect(m.services[0].name).toBe("parachute-agent");
37
+ expect(m.services[0].paths).toEqual(["/agent"]);
38
+ expect(m.services[0].stripPrefix).toBe(true);
39
+ });
40
+
41
+ test("carries startCmd so the hub supervisor can start/restart/adopt the module (agent#34)", () => {
42
+ const path = tmp();
43
+ upsertService({ ...AGENT, startCmd: ["parachute-agent"] }, path);
44
+ const m = JSON.parse(readFileSync(path, "utf8"));
45
+ expect(m.services[0].startCmd).toEqual(["parachute-agent"]);
46
+ });
47
+
48
+ test("is idempotent — re-registering the same name does not duplicate", () => {
49
+ const path = tmp();
50
+ upsertService(AGENT, path);
51
+ upsertService({ ...AGENT, version: "0.1.1" }, path);
52
+ const m = JSON.parse(readFileSync(path, "utf8"));
53
+ expect(m.services).toHaveLength(1);
54
+ expect(m.services[0].version).toBe("0.1.1"); // module wins for fields it owns
55
+ });
56
+
57
+ test("merges — preserves hub-stamped fields the module doesn't author", () => {
58
+ const path = tmp();
59
+ // hub stamped installDir onto the row; module re-registers without it.
60
+ writeFileSync(path, JSON.stringify({ services: [{ ...AGENT, installDir: "/hub/stamped" }] }));
61
+ upsertService(AGENT, path);
62
+ const m = JSON.parse(readFileSync(path, "utf8"));
63
+ expect(m.services[0].installDir).toBe("/hub/stamped");
64
+ });
65
+
66
+ test("preserves other modules' entries", () => {
67
+ const path = tmp();
68
+ writeFileSync(path, JSON.stringify({ services: [{ name: "parachute-vault", port: 1940, paths: ["/vault/default"], health: "/vault/default/health", version: "0.5.2" }] }));
69
+ upsertService(AGENT, path);
70
+ const m = JSON.parse(readFileSync(path, "utf8"));
71
+ expect(m.services).toHaveLength(2);
72
+ expect(m.services.map((s: ServiceEntry) => s.name).sort()).toEqual(["parachute-agent", "parachute-vault"]);
73
+ });
74
+
75
+ test("throws on a malformed manifest rather than clobbering it", () => {
76
+ const path = tmp();
77
+ writeFileSync(path, JSON.stringify({ not_services: true }));
78
+ expect(() => upsertService(AGENT, path)).toThrow(/malformed/);
79
+ expect(existsSync(path)).toBe(true); // original left intact
80
+ expect(JSON.parse(readFileSync(path, "utf8"))).toEqual({ not_services: true }); // content untouched
81
+ });
82
+ });
83
+
84
+ describe("listVaultNames", () => {
85
+ test("extracts vault names from the vault module's /vault/<name> paths, default first", () => {
86
+ const path = tmp();
87
+ writeFileSync(path, JSON.stringify({ services: [
88
+ { name: "parachute-vault", port: 1940, paths: ["/vault/boulder", "/vault/default", "/vault/techne"], health: "x", version: "1" },
89
+ { name: "parachute-agent", port: 1941, paths: ["/agent"], health: "x", version: "1" },
90
+ ] }));
91
+ expect(listVaultNames(path)).toEqual(["default", "boulder", "techne"]);
92
+ });
93
+
94
+ test("dedupes across services and ignores non-vault paths", () => {
95
+ const path = tmp();
96
+ writeFileSync(path, JSON.stringify({ services: [
97
+ { name: "a", port: 1, paths: ["/vault/x", "/other"], health: "x", version: "1" },
98
+ { name: "b", port: 2, paths: ["/vault/x", "/vault/y"], health: "x", version: "1" },
99
+ ] }));
100
+ expect(listVaultNames(path).sort()).toEqual(["x", "y"]);
101
+ });
102
+
103
+ test("returns [] when the manifest is absent or unreadable", () => {
104
+ expect(listVaultNames(join(tmpdir(), "does-not-exist-" + Math.floor(performance.now()), "services.json"))).toEqual([]);
105
+ });
106
+ });
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Self-registration into `~/.parachute/services.json` on daemon boot.
3
+ *
4
+ * Mirrors `parachute-scribe/src/services-manifest.ts` deliberately — the file
5
+ * shape is the contract between every Parachute module and the hub
6
+ * (`parachute-hub/src/services-manifest.ts` is the canonical reader). Hub reads
7
+ * this to know the module's port, the paths it should reverse-proxy
8
+ * (`/agent/*` over the expose → this daemon on loopback), and the version.
9
+ *
10
+ * Best-effort: any write error is logged + swallowed by the caller. The daemon
11
+ * still serves locally even if registration fails. Honors `PARACHUTE_HOME` so
12
+ * sandboxed/test/e2e daemons never touch the operator's real services.json.
13
+ */
14
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
15
+ import { homedir } from "node:os";
16
+ import { dirname, join } from "node:path";
17
+
18
+ export interface ServiceEntry {
19
+ name: string;
20
+ port: number;
21
+ paths: string[];
22
+ health: string;
23
+ version: string;
24
+ displayName?: string;
25
+ tagline?: string;
26
+ installDir?: string;
27
+ stripPrefix?: boolean;
28
+ /** Hub-stamped fields (e.g. installDir) ride along; the upsert merges. */
29
+ [key: string]: unknown;
30
+ }
31
+
32
+ interface ServicesManifest {
33
+ services: ServiceEntry[];
34
+ }
35
+
36
+ /** Canonical services.json path. Honors PARACHUTE_HOME for sandbox/test runs. */
37
+ export function resolveManifestPath(env: Record<string, string | undefined> = process.env): string {
38
+ const base = env.PARACHUTE_HOME ?? join(env.HOME ?? homedir(), ".parachute");
39
+ return join(base, "services.json");
40
+ }
41
+
42
+ function readManifest(path: string): ServicesManifest {
43
+ if (!existsSync(path)) return { services: [] };
44
+ const raw = JSON.parse(readFileSync(path, "utf8"));
45
+ if (!raw || typeof raw !== "object" || !Array.isArray((raw as { services?: unknown }).services)) {
46
+ throw new Error(`services manifest at ${path} is malformed (missing "services" array)`);
47
+ }
48
+ return raw as ServicesManifest;
49
+ }
50
+
51
+ /**
52
+ * List the vault instance names installed on this host, from the vault module's
53
+ * registered `paths` (`/vault/<name>` → `<name>`). Used by the agents page's vault
54
+ * picker so an operator chooses from real vaults instead of typing a name blind.
55
+ * Best-effort: returns `[]` if the manifest is absent/unreadable (the picker then
56
+ * falls back to free text). Deduped + sorted; `default` floated first if present.
57
+ */
58
+ export function listVaultNames(path: string = resolveManifestPath()): string[] {
59
+ let manifest: ServicesManifest;
60
+ try {
61
+ manifest = readManifest(path);
62
+ } catch {
63
+ return [];
64
+ }
65
+ const names = new Set<string>();
66
+ for (const svc of manifest.services) {
67
+ for (const p of svc.paths ?? []) {
68
+ // `paths` are operator-registered route prefixes, not URLs — take the literal
69
+ // segment (no decodeURIComponent: a stray %2F could synthesize a slash-bearing
70
+ // vault name, and real vault names are plain slugs).
71
+ const m = /^\/vault\/([^/]+)/.exec(p);
72
+ if (m && m[1]) names.add(m[1]);
73
+ }
74
+ }
75
+ const sorted = [...names].sort((a, b) => a.localeCompare(b));
76
+ // Float "default" to the front — it's the conventional primary vault.
77
+ return sorted.sort((a, b) => (a === "default" ? -1 : b === "default" ? 1 : 0));
78
+ }
79
+
80
+ /**
81
+ * Idempotent upsert of a service entry. Merges into any existing row rather
82
+ * than replacing it — preserves hub-stamped fields the module doesn't own.
83
+ * Atomic write: stages to a tmp file, then renames over the target so a crash
84
+ * mid-write leaves the prior file intact.
85
+ */
86
+ export function upsertService(entry: ServiceEntry, path: string = resolveManifestPath()): void {
87
+ mkdirSync(dirname(path), { recursive: true });
88
+ const manifest = readManifest(path);
89
+ const idx = manifest.services.findIndex((s) => s.name === entry.name);
90
+ if (idx >= 0) manifest.services[idx] = { ...manifest.services[idx], ...entry };
91
+ else manifest.services.push(entry);
92
+ const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
93
+ writeFileSync(tmp, `${JSON.stringify(manifest, null, 2)}\n`);
94
+ renameSync(tmp, path);
95
+ }