@openparachute/agent 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (605) 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 -263
  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 -172
  318. package/scripts/init-first-agent.ts +0 -378
  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 -80
  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/api-translator.test.ts +0 -306
  328. package/src/channels/api-translator.ts +0 -214
  329. package/src/channels/ask-question.ts +0 -46
  330. package/src/channels/channel-registry.test.ts +0 -421
  331. package/src/channels/channel-registry.ts +0 -313
  332. package/src/channels/chat-sdk-bridge.test.ts +0 -84
  333. package/src/channels/chat-sdk-bridge.ts +0 -652
  334. package/src/channels/cli.ts +0 -276
  335. package/src/channels/discord.ts +0 -90
  336. package/src/channels/index.ts +0 -17
  337. package/src/channels/telegram-markdown-sanitize.test.ts +0 -78
  338. package/src/channels/telegram-markdown-sanitize.ts +0 -55
  339. package/src/channels/telegram-pairing.test.ts +0 -254
  340. package/src/channels/telegram-pairing.ts +0 -339
  341. package/src/channels/telegram.ts +0 -279
  342. package/src/channels/trust-hint.test.ts +0 -48
  343. package/src/channels/trust-hint.ts +0 -75
  344. package/src/claude-md-compose.migrate.test.ts +0 -64
  345. package/src/claude-md-compose.ts +0 -205
  346. package/src/command-gate.ts +0 -63
  347. package/src/config.test.ts +0 -93
  348. package/src/config.ts +0 -128
  349. package/src/container-config.ts +0 -167
  350. package/src/container-runner.test.ts +0 -32
  351. package/src/container-runner.ts +0 -576
  352. package/src/container-runtime.test.ts +0 -269
  353. package/src/container-runtime.ts +0 -167
  354. package/src/db/_bun-sqlite-shim.ts +0 -88
  355. package/src/db/agent-activity.test.ts +0 -155
  356. package/src/db/agent-activity.ts +0 -121
  357. package/src/db/agent-groups.ts +0 -77
  358. package/src/db/connection.migrate.test.ts +0 -176
  359. package/src/db/connection.ts +0 -259
  360. package/src/db/db-v2.test.ts +0 -440
  361. package/src/db/dropped-messages.ts +0 -44
  362. package/src/db/index.ts +0 -40
  363. package/src/db/messaging-groups.ts +0 -252
  364. package/src/db/migrations/001-initial.ts +0 -112
  365. package/src/db/migrations/002-chat-sdk-state.ts +0 -36
  366. package/src/db/migrations/008-dropped-messages.ts +0 -27
  367. package/src/db/migrations/009-drop-pending-credentials.ts +0 -13
  368. package/src/db/migrations/010-engage-modes.ts +0 -103
  369. package/src/db/migrations/011-pending-sender-approvals.ts +0 -40
  370. package/src/db/migrations/012-channel-registration.ts +0 -48
  371. package/src/db/migrations/013-approval-render-metadata.ts +0 -27
  372. package/src/db/migrations/014-secrets.ts +0 -44
  373. package/src/db/migrations/015-secrets-drop-host-pattern.ts +0 -18
  374. package/src/db/migrations/016-secret-assignments.ts +0 -30
  375. package/src/db/migrations/017-agent-activity.ts +0 -40
  376. package/src/db/migrations/018-oauth-app-configs.ts +0 -34
  377. package/src/db/migrations/019-oauth-app-connections.ts +0 -48
  378. package/src/db/migrations/020-agent-app-connections.ts +0 -28
  379. package/src/db/migrations/021-pending-oauth-states.ts +0 -35
  380. package/src/db/migrations/022-app-connections-provider.ts +0 -25
  381. package/src/db/migrations/023-agent-group-secret-mode.test.ts +0 -124
  382. package/src/db/migrations/023-agent-group-secret-mode.ts +0 -65
  383. package/src/db/migrations/024-collapse-approvals.test.ts +0 -249
  384. package/src/db/migrations/024-collapse-approvals.ts +0 -182
  385. package/src/db/migrations/025-secret-mode-check.test.ts +0 -155
  386. package/src/db/migrations/025-secret-mode-check.ts +0 -49
  387. package/src/db/migrations/026-user-dms-bot-id.test.ts +0 -116
  388. package/src/db/migrations/026-user-dms-bot-id.ts +0 -54
  389. package/src/db/migrations/027-provider-credentials.ts +0 -41
  390. package/src/db/migrations/_test-helpers.ts +0 -41
  391. package/src/db/migrations/index.ts +0 -127
  392. package/src/db/migrations/module-agent-to-agent-destinations.ts +0 -84
  393. package/src/db/migrations/module-approvals-pending-approvals.ts +0 -42
  394. package/src/db/migrations/module-approvals-title-options.ts +0 -40
  395. package/src/db/schema.ts +0 -258
  396. package/src/db/session-db.test.ts +0 -93
  397. package/src/db/session-db.ts +0 -325
  398. package/src/db/sessions.ts +0 -241
  399. package/src/delivery.test.ts +0 -148
  400. package/src/delivery.ts +0 -445
  401. package/src/env.ts +0 -74
  402. package/src/group-folder.test.ts +0 -35
  403. package/src/group-folder.ts +0 -44
  404. package/src/group-init.ts +0 -92
  405. package/src/host-core.test.ts +0 -456
  406. package/src/host-sweep.test.ts +0 -146
  407. package/src/host-sweep.ts +0 -287
  408. package/src/index.ts +0 -232
  409. package/src/install-slug.ts +0 -33
  410. package/src/log.test.ts +0 -81
  411. package/src/log.ts +0 -117
  412. package/src/mcp/http.ts +0 -72
  413. package/src/mcp/server.ts +0 -92
  414. package/src/mcp/stdio.ts +0 -51
  415. package/src/mcp/tools/activity.ts +0 -88
  416. package/src/mcp/tools/agent-groups.ts +0 -183
  417. package/src/mcp/tools/approvals.ts +0 -122
  418. package/src/mcp/tools/channels.test.ts +0 -126
  419. package/src/mcp/tools/channels.ts +0 -134
  420. package/src/mcp/tools/index.ts +0 -27
  421. package/src/mcp/tools/oauth.ts +0 -48
  422. package/src/mcp/tools/secrets.ts +0 -169
  423. package/src/mcp/tools/sessions.ts +0 -135
  424. package/src/mcp/types.ts +0 -51
  425. package/src/modules/agent-to-agent/agent-route.test.ts +0 -46
  426. package/src/modules/agent-to-agent/agent-route.ts +0 -223
  427. package/src/modules/agent-to-agent/create-agent.ts +0 -127
  428. package/src/modules/agent-to-agent/db/agent-destinations.ts +0 -135
  429. package/src/modules/agent-to-agent/index.ts +0 -22
  430. package/src/modules/agent-to-agent/write-destinations.ts +0 -59
  431. package/src/modules/approvals/agent.md +0 -45
  432. package/src/modules/approvals/index.ts +0 -21
  433. package/src/modules/approvals/picks.test.ts +0 -291
  434. package/src/modules/approvals/primitive.ts +0 -279
  435. package/src/modules/approvals/project.md +0 -27
  436. package/src/modules/approvals/response-handler.ts +0 -87
  437. package/src/modules/index.ts +0 -24
  438. package/src/modules/interactive/agent.md +0 -21
  439. package/src/modules/interactive/index.ts +0 -69
  440. package/src/modules/interactive/project.md +0 -12
  441. package/src/modules/mount-security/expand-path.test.ts +0 -82
  442. package/src/modules/mount-security/index.ts +0 -459
  443. package/src/modules/mount-security/migrate.test.ts +0 -91
  444. package/src/modules/permissions/access.ts +0 -28
  445. package/src/modules/permissions/channel-approval.test.ts +0 -389
  446. package/src/modules/permissions/channel-approval.ts +0 -188
  447. package/src/modules/permissions/db/agent-group-members.ts +0 -44
  448. package/src/modules/permissions/db/pending-channel-approvals.test.ts +0 -86
  449. package/src/modules/permissions/db/pending-channel-approvals.ts +0 -66
  450. package/src/modules/permissions/db/pending-sender-approvals.ts +0 -60
  451. package/src/modules/permissions/db/user-dms.ts +0 -58
  452. package/src/modules/permissions/db/user-roles.ts +0 -85
  453. package/src/modules/permissions/db/users.ts +0 -38
  454. package/src/modules/permissions/index.ts +0 -421
  455. package/src/modules/permissions/permissions.test.ts +0 -358
  456. package/src/modules/permissions/sender-approval.test.ts +0 -641
  457. package/src/modules/permissions/sender-approval.ts +0 -165
  458. package/src/modules/permissions/user-dm.ts +0 -200
  459. package/src/modules/provider-credentials/db.ts +0 -121
  460. package/src/modules/provider-credentials/index.ts +0 -12
  461. package/src/modules/provider-credentials/spawn.test.ts +0 -206
  462. package/src/modules/provider-credentials/spawn.ts +0 -114
  463. package/src/modules/scheduling/actions.ts +0 -113
  464. package/src/modules/scheduling/db.test.ts +0 -282
  465. package/src/modules/scheduling/db.ts +0 -148
  466. package/src/modules/scheduling/index.ts +0 -34
  467. package/src/modules/scheduling/recurrence.test.ts +0 -98
  468. package/src/modules/scheduling/recurrence.ts +0 -54
  469. package/src/modules/self-mod/agent.md +0 -30
  470. package/src/modules/self-mod/apply.ts +0 -85
  471. package/src/modules/self-mod/index.ts +0 -30
  472. package/src/modules/self-mod/project.md +0 -39
  473. package/src/modules/self-mod/request.ts +0 -91
  474. package/src/modules/typing/index.ts +0 -165
  475. package/src/oauth/agent-app-connections.ts +0 -103
  476. package/src/oauth/app-configs.test.ts +0 -64
  477. package/src/oauth/app-configs.ts +0 -114
  478. package/src/oauth/app-connections.test.ts +0 -109
  479. package/src/oauth/app-connections.ts +0 -178
  480. package/src/oauth/crypto.ts +0 -56
  481. package/src/oauth/flow.ts +0 -104
  482. package/src/oauth/providers/google.test.ts +0 -38
  483. package/src/oauth/providers/google.ts +0 -46
  484. package/src/oauth/providers/index.ts +0 -48
  485. package/src/oauth/state-store.test.ts +0 -54
  486. package/src/oauth/state-store.ts +0 -93
  487. package/src/parachute/README.md +0 -27
  488. package/src/parachute/create-agent.test.ts +0 -83
  489. package/src/parachute/create-agent.ts +0 -122
  490. package/src/parachute/group-status.test.ts +0 -165
  491. package/src/parachute/group-status.ts +0 -136
  492. package/src/parachute/types.ts +0 -41
  493. package/src/parachute/vault-mcp.test.ts +0 -251
  494. package/src/parachute/vault-mcp.ts +0 -232
  495. package/src/platform-id.test.ts +0 -104
  496. package/src/platform-id.ts +0 -109
  497. package/src/providers/index.ts +0 -6
  498. package/src/providers/provider-container-registry.ts +0 -58
  499. package/src/response-registry.ts +0 -45
  500. package/src/router.ts +0 -530
  501. package/src/secrets/crypto.test.ts +0 -45
  502. package/src/secrets/crypto.ts +0 -55
  503. package/src/secrets/index.ts +0 -461
  504. package/src/secrets/master-key.ts +0 -70
  505. package/src/secrets/secrets.test.ts +0 -651
  506. package/src/session-manager.attachments.test.ts +0 -171
  507. package/src/session-manager.dup-skip.test.ts +0 -173
  508. package/src/session-manager.migrate.test.ts +0 -59
  509. package/src/session-manager.ts +0 -451
  510. package/src/startup-bootstrap.test.ts +0 -226
  511. package/src/startup-bootstrap.ts +0 -207
  512. package/src/state-sqlite.ts +0 -182
  513. package/src/timezone.test.ts +0 -64
  514. package/src/timezone.ts +0 -37
  515. package/src/types.ts +0 -233
  516. package/src/web/auth.test.ts +0 -335
  517. package/src/web/auth.ts +0 -214
  518. package/src/web/discord-validate.test.ts +0 -77
  519. package/src/web/discord-validate.ts +0 -88
  520. package/src/web/hub-discovery.test.ts +0 -98
  521. package/src/web/hub-discovery.ts +0 -69
  522. package/src/web/routes/activity.ts +0 -106
  523. package/src/web/routes/agent-provider.test.ts +0 -282
  524. package/src/web/routes/agent-provider.ts +0 -309
  525. package/src/web/routes/approvals.ts +0 -185
  526. package/src/web/routes/apps.ts +0 -434
  527. package/src/web/routes/channels-mg-detail.test.ts +0 -324
  528. package/src/web/routes/channels-mga-detail.test.ts +0 -472
  529. package/src/web/routes/channels.ts +0 -311
  530. package/src/web/routes/oauth-providers.ts +0 -42
  531. package/src/web/routes/secrets.test.ts +0 -220
  532. package/src/web/routes/secrets.ts +0 -317
  533. package/src/web/routes/sessions.ts +0 -123
  534. package/src/web/routes/settings.test.ts +0 -106
  535. package/src/web/routes/settings.ts +0 -247
  536. package/src/web/routes/setup-status.ts +0 -205
  537. package/src/web/routes/vaults.test.ts +0 -389
  538. package/src/web/routes/vaults.ts +0 -225
  539. package/src/web/server-version.test.ts +0 -16
  540. package/src/web/server.ts +0 -1024
  541. package/src/web/services-manifest.test.ts +0 -148
  542. package/src/web/services-manifest.ts +0 -66
  543. package/src/web/static-serve.test.ts +0 -255
  544. package/src/web/static-serve.ts +0 -104
  545. package/src/web/telegram-validate.test.ts +0 -116
  546. package/src/web/telegram-validate.ts +0 -107
  547. package/src/web/vault-proxy.test.ts +0 -214
  548. package/src/web/vault-proxy.ts +0 -120
  549. package/src/web/wire-channel.ts +0 -181
  550. package/src/webhook-server.ts +0 -134
  551. package/vitest.config.ts +0 -18
  552. package/web/README.md +0 -63
  553. package/web/ui/index.html +0 -13
  554. package/web/ui/package.json +0 -35
  555. package/web/ui/pnpm-lock.yaml +0 -2164
  556. package/web/ui/scripts/verify-base.mjs +0 -31
  557. package/web/ui/src/App.tsx +0 -88
  558. package/web/ui/src/components/ActivityFeed.tsx +0 -444
  559. package/web/ui/src/components/AgentGroupPicker.tsx +0 -263
  560. package/web/ui/src/components/AgentProviderCards.tsx +0 -220
  561. package/web/ui/src/components/CredentialForm.tsx +0 -214
  562. package/web/ui/src/components/ScopeGrants.tsx +0 -74
  563. package/web/ui/src/components/StatusDot.tsx +0 -43
  564. package/web/ui/src/components/VaultPicker.tsx +0 -127
  565. package/web/ui/src/components/setup/AdapterInstallStep.tsx +0 -178
  566. package/web/ui/src/components/setup/AgentGroupStep.tsx +0 -43
  567. package/web/ui/src/components/setup/ChannelPickStep.tsx +0 -74
  568. package/web/ui/src/components/setup/DoneStep.tsx +0 -49
  569. package/web/ui/src/components/setup/PrereqStep.tsx +0 -129
  570. package/web/ui/src/components/setup/TestConnectionStep.tsx +0 -108
  571. package/web/ui/src/components/setup/TestMessageStep.tsx +0 -104
  572. package/web/ui/src/components/setup/WireChannelStep.tsx +0 -166
  573. package/web/ui/src/components/setup/types.ts +0 -105
  574. package/web/ui/src/lib/api.test.ts +0 -410
  575. package/web/ui/src/lib/api.ts +0 -1248
  576. package/web/ui/src/lib/auth.test.ts +0 -352
  577. package/web/ui/src/lib/auth.ts +0 -405
  578. package/web/ui/src/lib/channel-adapters.ts +0 -136
  579. package/web/ui/src/main.tsx +0 -19
  580. package/web/ui/src/routes/ApprovalsList.tsx +0 -294
  581. package/web/ui/src/routes/Apps.tsx +0 -613
  582. package/web/ui/src/routes/ChannelWireDetail.test.tsx +0 -233
  583. package/web/ui/src/routes/ChannelWireDetail.tsx +0 -403
  584. package/web/ui/src/routes/ChannelsList.tsx +0 -158
  585. package/web/ui/src/routes/GroupDetail.test.tsx +0 -206
  586. package/web/ui/src/routes/GroupDetail.tsx +0 -880
  587. package/web/ui/src/routes/GroupList.tsx +0 -187
  588. package/web/ui/src/routes/MessagingGroupDetail.test.tsx +0 -233
  589. package/web/ui/src/routes/MessagingGroupDetail.tsx +0 -306
  590. package/web/ui/src/routes/NewGroupWizard.tsx +0 -390
  591. package/web/ui/src/routes/OAuthCallback.tsx +0 -56
  592. package/web/ui/src/routes/SecretsList.tsx +0 -942
  593. package/web/ui/src/routes/SessionsList.tsx +0 -220
  594. package/web/ui/src/routes/SettingsAgentProvider.tsx +0 -109
  595. package/web/ui/src/routes/SettingsApprovals.tsx +0 -234
  596. package/web/ui/src/routes/SetupWizard.tsx +0 -219
  597. package/web/ui/src/routes/VaultDetail.test.tsx +0 -363
  598. package/web/ui/src/routes/VaultDetail.tsx +0 -960
  599. package/web/ui/src/routes/VaultsList.tsx +0 -295
  600. package/web/ui/src/routes/WireChannelPage.tsx +0 -413
  601. package/web/ui/src/styles.css +0 -608
  602. package/web/ui/src/test/setup.ts +0 -23
  603. package/web/ui/src/vite-env.d.ts +0 -10
  604. package/web/ui/vite.config.ts +0 -34
  605. 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
+ }