@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
@@ -1,880 +0,0 @@
1
- import { useCallback, useEffect, useState } from 'react';
2
- import { Link, useParams, useSearchParams } from 'react-router-dom';
3
- import { ActivityFeed } from '../components/ActivityFeed.tsx';
4
- import { AgentProviderCards } from '../components/AgentProviderCards.tsx';
5
- import { ScopeGrants, SCOPE_OPTIONS } from '../components/ScopeGrants.tsx';
6
- import { StatusDot, formatRelative } from '../components/StatusDot.tsx';
7
- import { VaultPicker } from '../components/VaultPicker.tsx';
8
- import {
9
- attachVault,
10
- clearGroupAgentProvider,
11
- closeSession,
12
- detachVault,
13
- getGroup,
14
- getGroupAgentProvider,
15
- listGroupInjectableSecrets,
16
- setGroupAgentProvider,
17
- spawnSession,
18
- type AgentGroupView,
19
- type AgentProviderSource,
20
- type GroupAgentProviderView,
21
- type GroupStatus,
22
- type InjectableSecretView,
23
- type SecretInclusionScope,
24
- type VaultScope,
25
- } from '../lib/api.ts';
26
-
27
- const POLL_MS = 7_000;
28
-
29
- type DetailTab = 'overview' | 'activity';
30
-
31
- function parseTab(raw: string | null): DetailTab {
32
- return raw === 'activity' ? 'activity' : 'overview';
33
- }
34
-
35
- export function GroupDetail() {
36
- const { folder } = useParams<{ folder: string }>();
37
- const [searchParams, setSearchParams] = useSearchParams();
38
- const tab = parseTab(searchParams.get('tab'));
39
- const [group, setGroup] = useState<AgentGroupView | null>(null);
40
- const [error, setError] = useState<string | null>(null);
41
- const [loading, setLoading] = useState(true);
42
-
43
- // Attach form state. vaultBaseUrl starts empty — VaultPicker fills it in
44
- // with the first registered vault's URL once /api/vaults resolves, or
45
- // surfaces a free-text input when discovery is empty / errors.
46
- const [scope, setScope] = useState<VaultScope>('vault:read');
47
- const [vaultBaseUrl, setVaultBaseUrl] = useState('');
48
- const [pickedVaultName, setPickedVaultName] = useState<string | null>(null);
49
- const [pasteToken, setPasteToken] = useState('');
50
- const [tokenLabel, setTokenLabel] = useState('');
51
- const [submitting, setSubmitting] = useState(false);
52
- const [spawning, setSpawning] = useState(false);
53
- const [restartingSessionId, setRestartingSessionId] = useState<string | null>(null);
54
- const [flash, setFlash] = useState<{ kind: 'ok' | 'error'; text: string } | null>(null);
55
-
56
- const reload = useCallback(async () => {
57
- if (!folder) return;
58
- try {
59
- setLoading(true);
60
- const g = await getGroup(folder);
61
- setGroup(g);
62
- setError(null);
63
- // Default the token-label field to agent-<folder> when not set.
64
- // The label is opaque to the vault — operators with existing
65
- // `claw-<folder>` tokens keep them; only fresh mints from this UI
66
- // pick up the new prefix.
67
- if (!tokenLabel) setTokenLabel(`agent-${folder}`);
68
- } catch (err) {
69
- setError(err instanceof Error ? err.message : String(err));
70
- } finally {
71
- setLoading(false);
72
- }
73
- // eslint-disable-next-line react-hooks/exhaustive-deps
74
- }, [folder]);
75
-
76
- useEffect(() => {
77
- void reload();
78
- }, [reload]);
79
-
80
- // Background poll for live status — silent on failure.
81
- useEffect(() => {
82
- if (!folder || !group) return;
83
- const t = setInterval(() => {
84
- getGroup(folder)
85
- .then((g) => setGroup(g))
86
- .catch(() => {});
87
- }, POLL_MS);
88
- return () => clearInterval(t);
89
- }, [folder, group]);
90
-
91
- const onAttach = async (e: React.FormEvent) => {
92
- e.preventDefault();
93
- if (!folder) return;
94
- setSubmitting(true);
95
- setFlash(null);
96
- try {
97
- // Thread the picked vault name into re-auth on 403. The server's
98
- // implicit-mint forwards our JWT to the vault, which 403s on missing
99
- // `vault:<name>:admin` — without this hint, beginLogin would re-auth
100
- // with broad scopes only, the new JWT would still lack the narrow
101
- // scope, and we'd loop. (Detach below intentionally omits this — the
102
- // detach path doesn't know its target vault from the call site.)
103
- const result = await attachVault(
104
- folder,
105
- {
106
- scope,
107
- vaultBaseUrl: vaultBaseUrl.trim().replace(/\/+$/, ''),
108
- tokenLabel: tokenLabel.trim() || undefined,
109
- token: pasteToken.trim() || undefined,
110
- },
111
- {
112
- authExtraScopes: pickedVaultName ? [`vault:${pickedVaultName}:admin`] : undefined,
113
- },
114
- );
115
- setGroup(result.group);
116
- setFlash({
117
- kind: 'ok',
118
- text: result.mintedToken
119
- ? `Vault attached (server minted a fresh ${scope} token via parachute CLI).`
120
- : `Vault attached using your pasted token.`,
121
- });
122
- setPasteToken('');
123
- } catch (err) {
124
- setFlash({
125
- kind: 'error',
126
- text: err instanceof Error ? err.message : String(err),
127
- });
128
- } finally {
129
- setSubmitting(false);
130
- }
131
- };
132
-
133
- // Reap a running session's container. The host's session-close path is
134
- // synchronous (kill + mark closed), so by the time `closeSession` resolves
135
- // the container is gone. Next inbound message spawns fresh — that's what
136
- // makes this the canonical way to pick up secrets/env/code changes that
137
- // were baked at spawn time and aren't readable mid-life.
138
- const onRestartSession = async (sessionId: string) => {
139
- if (!folder) return;
140
- if (
141
- !window.confirm(
142
- "Restart this agent's container? The current session ends; the agent's next reply will spawn a fresh container picking up the latest secrets, env, and code. The chat thread is preserved; only the container's in-memory state resets.",
143
- )
144
- ) {
145
- return;
146
- }
147
- setRestartingSessionId(sessionId);
148
- setFlash(null);
149
- try {
150
- await closeSession(sessionId);
151
- await reload();
152
- setFlash({
153
- kind: 'ok',
154
- text: `Session ${sessionId} closed. The agent's next message will spawn a fresh container.`,
155
- });
156
- } catch (err) {
157
- setFlash({ kind: 'error', text: err instanceof Error ? err.message : String(err) });
158
- } finally {
159
- setRestartingSessionId(null);
160
- }
161
- };
162
-
163
- const onSpawn = async () => {
164
- if (!folder) return;
165
- setSpawning(true);
166
- setFlash(null);
167
- try {
168
- const result = await spawnSession(folder);
169
- // Reload immediately so the new session shows up in the live-status
170
- // list before the next 7s poll tick. Container `running` flips on a
171
- // later tick once the heartbeat lands.
172
- await reload();
173
- setFlash({
174
- kind: 'ok',
175
- text: result.created
176
- ? `Session ${result.sessionId} created — container starting…`
177
- : `Session ${result.sessionId} already exists — waking container…`,
178
- });
179
- } catch (err) {
180
- setFlash({
181
- kind: 'error',
182
- text: err instanceof Error ? err.message : String(err),
183
- });
184
- } finally {
185
- setSpawning(false);
186
- }
187
- };
188
-
189
- const onDetach = async () => {
190
- if (!folder) return;
191
- if (!window.confirm("Detach vault from this agent group? Token is NOT revoked — that's a separate action.")) {
192
- return;
193
- }
194
- setSubmitting(true);
195
- setFlash(null);
196
- try {
197
- // No `authExtraScopes` here on purpose — this surface isn't vault-
198
- // scoped (the agent group may be attached to any vault) so we can't
199
- // cleanly thread `vault:<name>:admin`. A 403 falls back to the broad
200
- // re-auth set, which is the pre-paraclaw#56 behavior. The narrow-
201
- // scope path lives on the per-vault detail page (VaultDetail.tsx),
202
- // where the vault name is known at the call site.
203
- const result = await detachVault(folder);
204
- setGroup(result.group);
205
- setFlash({
206
- kind: 'ok',
207
- text: 'Vault detached. To revoke the token: parachute vault tokens revoke <label> (or use the Vault detail page)',
208
- });
209
- } catch (err) {
210
- setFlash({
211
- kind: 'error',
212
- text: err instanceof Error ? err.message : String(err),
213
- });
214
- } finally {
215
- setSubmitting(false);
216
- }
217
- };
218
-
219
- if (loading && !group) {
220
- return (
221
- <div>
222
- <Link to="/" className="muted">
223
- ← All groups
224
- </Link>
225
- <div className="skeleton skeleton-heading" style={{ marginTop: '1rem' }} />
226
- <div className="section">
227
- <div className="skeleton skeleton-line" style={{ width: '30%' }} />
228
- <div className="skeleton skeleton-line" />
229
- <div className="skeleton skeleton-line" style={{ width: '70%' }} />
230
- </div>
231
- </div>
232
- );
233
- }
234
-
235
- if (error) {
236
- return (
237
- <div>
238
- <Link to="/" className="muted">
239
- ← All groups
240
- </Link>
241
- <div className="error-banner" style={{ marginTop: '1rem' }}>
242
- {error}
243
- </div>
244
- <div className="actions" style={{ marginTop: '1rem' }}>
245
- <button onClick={reload} disabled={loading}>
246
- {loading ? 'Retrying…' : 'Retry'}
247
- </button>
248
- </div>
249
- </div>
250
- );
251
- }
252
-
253
- if (!group) {
254
- return (
255
- <div>
256
- <Link to="/" className="muted">
257
- ← All groups
258
- </Link>
259
- <div className="empty">Group not found.</div>
260
- </div>
261
- );
262
- }
263
-
264
- return (
265
- <div>
266
- <Link to="/" className="muted">
267
- ← All groups
268
- </Link>
269
- <h2 style={{ display: 'flex', alignItems: 'center', gap: '0.6rem' }}>
270
- <StatusDot status={group.status} />
271
- {group.name}
272
- {group.vault ? (
273
- <span className="tag">{group.vault.scope}</span>
274
- ) : (
275
- <span className="tag muted">no vault attached</span>
276
- )}
277
- </h2>
278
-
279
- {flash && <div className={flash.kind === 'ok' ? 'status-banner' : 'error-banner'}>{flash.text}</div>}
280
-
281
- <DetailTabs
282
- tab={tab}
283
- onChange={(next) => {
284
- // Preserve any other search params (none today, but future-proof).
285
- // The replace param keeps the tab toggle out of browser-history
286
- // back-stack noise — flipping tabs shouldn't make Back unintuitive.
287
- setSearchParams(
288
- (prev) => {
289
- const p = new URLSearchParams(prev);
290
- if (next === 'overview') p.delete('tab');
291
- else p.set('tab', next);
292
- return p;
293
- },
294
- { replace: true },
295
- );
296
- }}
297
- />
298
-
299
- {tab === 'activity' && folder && <ActivityFeed folder={folder} />}
300
-
301
- {tab === 'overview' && (
302
- <>
303
- <div className="section">
304
- <h3>Agent group</h3>
305
- <div className="kv">
306
- <div>name</div>
307
- <div>{group.name}</div>
308
- <div>folder</div>
309
- <div>
310
- <code>{group.folder}</code>
311
- </div>
312
- <div>id</div>
313
- <div>
314
- <code>{group.id}</code>
315
- </div>
316
- <div>provider</div>
317
- <div>{group.agent_provider ?? <em className="dim">default</em>}</div>
318
- <div>created</div>
319
- <div>{new Date(group.created_at).toLocaleString()}</div>
320
- </div>
321
- </div>
322
-
323
- {group.status && (
324
- <StatusSection
325
- status={group.status}
326
- onSpawn={onSpawn}
327
- spawning={spawning}
328
- onRestartSession={onRestartSession}
329
- restartingSessionId={restartingSessionId}
330
- />
331
- )}
332
-
333
- {folder && <AgentProviderSection folder={folder} />}
334
-
335
- {group.vault ? (
336
- <div className="section">
337
- <h3>Vault attachment</h3>
338
- <div className="kv">
339
- <div>vault url</div>
340
- <div>
341
- <code>{group.vault.vaultBaseUrl}</code>
342
- </div>
343
- <div>scope</div>
344
- <div>
345
- <span className="tag">{group.vault.scope}</span>
346
- </div>
347
- <div>token label</div>
348
- <div>
349
- <code>{group.vault.tokenLabel}</code>
350
- </div>
351
- <div>attached</div>
352
- <div>{new Date(group.vault.attachedAt).toLocaleString()}</div>
353
- </div>
354
- <hr className="sep" />
355
- <div className="dim" style={{ marginBottom: '0.75rem' }}>
356
- The agent's container.json has a <code>parachute-vault</code> MCP entry pointing at this URL with a
357
- Bearer token. Detach removes the entry; the token stays valid until you revoke it via{' '}
358
- <code>parachute vault tokens revoke {group.vault.tokenLabel}</code>.
359
- </div>
360
- <button className="danger" onClick={onDetach} disabled={submitting}>
361
- {submitting ? 'Working…' : 'Detach vault'}
362
- </button>
363
- </div>
364
- ) : (
365
- <div className="section">
366
- <h3>Attach {pickedVaultName ? <code>{pickedVaultName}</code> : null} vault</h3>
367
- <form onSubmit={onAttach}>
368
- <div className="row">
369
- <label htmlFor="vaultBaseUrl">Vault</label>
370
- <VaultPicker
371
- inputId="vaultBaseUrl"
372
- value={vaultBaseUrl}
373
- onChange={setVaultBaseUrl}
374
- onPickedName={setPickedVaultName}
375
- disabled={submitting}
376
- />
377
- </div>
378
-
379
- <div className="row">
380
- <label htmlFor="scope">Scope</label>
381
- <select
382
- id="scope"
383
- value={scope}
384
- onChange={(e) => setScope(e.target.value as VaultScope)}
385
- disabled={submitting}
386
- >
387
- {SCOPE_OPTIONS.map((s) => (
388
- <option key={s.value} value={s.value}>
389
- {s.label}
390
- </option>
391
- ))}
392
- </select>
393
- <p className="dim">
394
- Token capability — the agent literally cannot exceed this. Default <code>vault:read</code>.
395
- </p>
396
- <ScopeGrants scope={scope} />
397
- </div>
398
-
399
- <div className="row">
400
- <label htmlFor="tokenLabel">Token label</label>
401
- <input
402
- id="tokenLabel"
403
- type="text"
404
- value={tokenLabel}
405
- onChange={(e) => setTokenLabel(e.target.value)}
406
- disabled={submitting}
407
- placeholder={`agent-${folder}`}
408
- />
409
- <p className="dim">
410
- Used for revocation. Default: <code>agent-{folder}</code>.
411
- </p>
412
- </div>
413
-
414
- <div className="row">
415
- <label htmlFor="pasteToken">Paste an existing token (optional)</label>
416
- <input
417
- id="pasteToken"
418
- type="text"
419
- value={pasteToken}
420
- onChange={(e) => setPasteToken(e.target.value)}
421
- disabled={submitting}
422
- placeholder="pvt_… (leave blank to mint a fresh one via the parachute CLI)"
423
- />
424
- <p className="dim">
425
- When blank: the server runs{' '}
426
- <code>
427
- parachute vault tokens create --scope {scope} --label {tokenLabel || `agent-${folder}`}
428
- </code>{' '}
429
- for you. (Until vault OAuth is wired in Phase B; then you'll never see <code>pvt_…</code> tokens at
430
- all.)
431
- </p>
432
- </div>
433
-
434
- <div className="actions">
435
- <button type="submit" disabled={submitting}>
436
- {submitting ? 'Attaching…' : 'Attach vault'}
437
- </button>
438
- </div>
439
- </form>
440
- </div>
441
- )}
442
-
443
- {folder && <SecretsSection folder={folder} secretMode={group.secret_mode} />}
444
-
445
- <div className="section">
446
- <h3>What the agent gets</h3>
447
- <p className="muted">
448
- When attached, the agent's container has a <code>parachute-vault</code> MCP server available with the nine
449
- vault tools: <code>query-notes</code>, <code>create-note</code>, <code>update-note</code>,{' '}
450
- <code>delete-note</code>, <code>list-tags</code>, <code>update-tag</code>, <code>delete-tag</code>,{' '}
451
- <code>find-path</code>, <code>vault-info</code>. Constrained by the scope you chose.
452
- </p>
453
- <p className="muted">
454
- Parachute Agent doesn't impose a vault-note layout — the agent decides how to use vault access. (See{' '}
455
- <a
456
- href="https://github.com/ParachuteComputer/parachute-agent/blob/main/docs/parachute-integration.md"
457
- target="_blank"
458
- rel="noreferrer"
459
- >
460
- docs/parachute-integration.md
461
- </a>
462
- .)
463
- </p>
464
- </div>
465
- </>
466
- )}
467
- </div>
468
- );
469
- }
470
-
471
- const SCOPE_LABEL: Record<SecretInclusionScope, string> = {
472
- scoped: 'scoped',
473
- assigned: 'assigned',
474
- global: 'global',
475
- };
476
-
477
- const SCOPE_HINT: Record<SecretInclusionScope, string> = {
478
- scoped: 'Owned by this agent group — never injected into peers.',
479
- assigned: 'Global secret routed here via an explicit assignment row.',
480
- global: "Global secret reaching this group only because secret_mode='all'.",
481
- };
482
-
483
- /**
484
- * "Secrets" panel — the env vars this group will receive at the next session
485
- * spawn. Read-only; mirrors `resolveInjectableSecrets()` on the host. Click a
486
- * row to jump to the SecretEditor (paraclaw#104).
487
- *
488
- * The list is fetched once per mount and on remount-driven reloads — there's
489
- * no live poll because mid-life secret changes don't reach a running
490
- * container anyway (env vars are spawn-time-only). Operators who want a
491
- * fresher view can navigate away and back.
492
- */
493
- function SecretsSection({ folder, secretMode }: { folder: string; secretMode?: 'all' | 'selective' | null }) {
494
- const [state, setState] = useState<
495
- | { kind: 'loading' }
496
- | { kind: 'ok'; secrets: InjectableSecretView[] }
497
- | { kind: 'error'; message: string }
498
- >({ kind: 'loading' });
499
-
500
- const reload = useCallback(async () => {
501
- setState({ kind: 'loading' });
502
- try {
503
- const secrets = await listGroupInjectableSecrets(folder);
504
- setState({ kind: 'ok', secrets });
505
- } catch (err) {
506
- setState({ kind: 'error', message: err instanceof Error ? err.message : String(err) });
507
- }
508
- }, [folder]);
509
-
510
- useEffect(() => {
511
- void reload();
512
- }, [reload]);
513
-
514
- return (
515
- <div className="section">
516
- <h3 style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', margin: 0 }}>
517
- Secrets
518
- {secretMode && <span className="tag muted">mode: {secretMode}</span>}
519
- </h3>
520
- <p className="muted" style={{ marginTop: '0.5rem' }}>
521
- Env vars injected into this group's containers at the next session spawn — same set the agent-runner sees.
522
- Values never leave the encrypted DB; this list is metadata only.
523
- </p>
524
-
525
- {state.kind === 'loading' && (
526
- <ul className="skeleton-list" aria-busy="true" style={{ marginTop: '0.75rem' }}>
527
- <li className="skeleton skeleton-row" />
528
- <li className="skeleton skeleton-row" />
529
- </ul>
530
- )}
531
-
532
- {state.kind === 'error' && (
533
- <>
534
- <div className="error-banner" style={{ marginTop: '0.75rem' }}>
535
- Couldn't load secrets: <code>{state.message}</code>
536
- </div>
537
- <div className="actions" style={{ marginTop: '0.75rem' }}>
538
- <button onClick={reload}>Retry</button>
539
- </div>
540
- </>
541
- )}
542
-
543
- {state.kind === 'ok' && state.secrets.length === 0 && (
544
- <div className="empty" style={{ marginTop: '0.75rem' }}>
545
- No secrets reach this group.{' '}
546
- {secretMode === 'selective' ? (
547
- <>Mode is <code>selective</code> — only globals with an explicit assignment row will land here.</>
548
- ) : (
549
- <>Create a scoped secret or a global one with mode <code>all</code> to populate this list.</>
550
- )}{' '}
551
- <Link to="/secrets">Manage secrets →</Link>
552
- </div>
553
- )}
554
-
555
- {state.kind === 'ok' && state.secrets.length > 0 && (
556
- <div style={{ marginTop: '0.75rem' }}>
557
- {state.secrets.map((s) => (
558
- <Link
559
- key={s.id}
560
- to={`/secrets?edit=${encodeURIComponent(s.id)}`}
561
- className="row clickable"
562
- style={{
563
- display: 'flex',
564
- alignItems: 'center',
565
- gap: '0.6rem',
566
- padding: '0.4rem 0',
567
- borderBottom: '1px solid var(--border-dim)',
568
- textDecoration: 'none',
569
- color: 'inherit',
570
- }}
571
- >
572
- <code style={{ flex: '1 1 auto', minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis' }}>
573
- {s.name}
574
- </code>
575
- <span className="tag muted" title={`Kind: ${s.kind}`}>{s.kind}</span>
576
- <span className="tag" title={SCOPE_HINT[s.scope]}>{SCOPE_LABEL[s.scope]}</span>
577
- <span className="dim" style={{ fontSize: '0.78rem' }} title={new Date(s.updatedAt).toLocaleString()}>
578
- {formatRelative(s.updatedAt)}
579
- </span>
580
- </Link>
581
- ))}
582
- <p className="dim" style={{ marginTop: '0.5rem' }}>
583
- <Link to="/secrets">Manage all secrets →</Link>
584
- </p>
585
- </div>
586
- )}
587
- </div>
588
- );
589
- }
590
-
591
- function describeSource(source: AgentProviderSource | null, serverUrl: string | null): string {
592
- switch (source) {
593
- case 'claude_setup_token':
594
- return 'Claude setup token';
595
- case 'anthropic_api_key':
596
- return 'Anthropic API key';
597
- case 'external_server':
598
- return serverUrl ? `External server (${serverUrl})` : 'External server';
599
- case null:
600
- return 'not configured';
601
- }
602
- }
603
-
604
- function AgentProviderSection({ folder }: { folder: string }) {
605
- const [state, setState] = useState<
606
- { kind: 'loading' } | { kind: 'ok'; view: GroupAgentProviderView } | { kind: 'error'; message: string }
607
- >({ kind: 'loading' });
608
- const [busy, setBusy] = useState(false);
609
- const [showForm, setShowForm] = useState(false);
610
- const [flash, setFlash] = useState<{ kind: 'ok' | 'error'; text: string } | null>(null);
611
-
612
- const reload = useCallback(async () => {
613
- try {
614
- setState({ kind: 'loading' });
615
- const view = await getGroupAgentProvider(folder);
616
- setState({ kind: 'ok', view });
617
- } catch (err) {
618
- setState({ kind: 'error', message: err instanceof Error ? err.message : String(err) });
619
- }
620
- }, [folder]);
621
-
622
- useEffect(() => {
623
- void reload();
624
- }, [reload]);
625
-
626
- const submit = async (input: { source: AgentProviderSource; apiKey?: string; serverUrl?: string }) => {
627
- setBusy(true);
628
- setFlash(null);
629
- try {
630
- const view = await setGroupAgentProvider(folder, input);
631
- setState({ kind: 'ok', view });
632
- setShowForm(false);
633
- setFlash({ kind: 'ok', text: 'Override saved — takes effect on the next session spawn.' });
634
- } catch (err) {
635
- setFlash({ kind: 'error', text: err instanceof Error ? err.message : String(err) });
636
- } finally {
637
- setBusy(false);
638
- }
639
- };
640
-
641
- const onClear = async () => {
642
- if (!window.confirm("Clear this group's override and inherit the install-wide default?")) return;
643
- setBusy(true);
644
- setFlash(null);
645
- try {
646
- const view = await clearGroupAgentProvider(folder);
647
- setState({ kind: 'ok', view });
648
- setShowForm(false);
649
- setFlash({
650
- kind: 'ok',
651
- text: 'Override cleared. Group will inherit the install-wide default on the next spawn.',
652
- });
653
- } catch (err) {
654
- setFlash({ kind: 'error', text: err instanceof Error ? err.message : String(err) });
655
- } finally {
656
- setBusy(false);
657
- }
658
- };
659
-
660
- if (state.kind === 'loading') {
661
- return (
662
- <div className="section">
663
- <h3>Agent provider</h3>
664
- <div className="skeleton skeleton-line" style={{ width: '60%' }} />
665
- </div>
666
- );
667
- }
668
- if (state.kind === 'error') {
669
- return (
670
- <div className="section">
671
- <h3>Agent provider</h3>
672
- <div className="error-banner">
673
- Couldn't load: <code>{state.message}</code>
674
- </div>
675
- <div className="actions" style={{ marginTop: '0.75rem' }}>
676
- <button onClick={reload}>Retry</button>
677
- </div>
678
- </div>
679
- );
680
- }
681
-
682
- const { view } = state;
683
- const effectiveSummary = describeSource(view.effective.source, view.effective.serverUrl);
684
- const overrideSummary = describeSource(view.override.source, view.override.serverUrl);
685
-
686
- return (
687
- <div className="section">
688
- <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '1rem' }}>
689
- <h3 style={{ margin: 0 }}>Agent provider</h3>
690
- {view.overridden ? <span className="tag">override</span> : <span className="tag muted">inheriting</span>}
691
- </div>
692
- <p className="muted" style={{ marginTop: '0.75rem' }}>
693
- {view.overridden ? (
694
- <>
695
- This group uses its own credential source: <strong>{overrideSummary}</strong>. Clear the override to inherit
696
- the install-wide default again.
697
- </>
698
- ) : (
699
- <>
700
- This group inherits the install-wide default — currently <strong>{effectiveSummary}</strong>. Set an
701
- override below to give this group its own credentials. Change at{' '}
702
- <Link to="/settings/agent-provider">Settings · Agent provider</Link> for the install-wide default.
703
- </>
704
- )}
705
- </p>
706
-
707
- {flash && (
708
- <div className={flash.kind === 'ok' ? 'status-banner' : 'error-banner'} style={{ marginBottom: '0.75rem' }}>
709
- {flash.text}
710
- </div>
711
- )}
712
-
713
- {!view.overridden && !showForm && (
714
- <div className="actions">
715
- <button onClick={() => setShowForm(true)}>Override default</button>
716
- </div>
717
- )}
718
-
719
- {(view.overridden || showForm) && (
720
- <>
721
- <AgentProviderCards view={view.override} busy={busy} onSubmit={submit} />
722
- <div className="actions" style={{ display: 'flex', gap: '0.5rem' }}>
723
- {view.overridden && (
724
- <button className="danger" onClick={onClear} disabled={busy}>
725
- {busy ? 'Working…' : 'Clear override'}
726
- </button>
727
- )}
728
- {!view.overridden && showForm && (
729
- <button className="secondary" onClick={() => setShowForm(false)} disabled={busy}>
730
- Cancel
731
- </button>
732
- )}
733
- </div>
734
- </>
735
- )}
736
- </div>
737
- );
738
- }
739
-
740
- function DetailTabs({ tab, onChange }: { tab: DetailTab; onChange: (next: DetailTab) => void }) {
741
- return (
742
- <ol className="detail-tabs">
743
- <li className={`detail-tab${tab === 'overview' ? ' active' : ''}`}>
744
- <button type="button" onClick={() => onChange('overview')}>
745
- Overview
746
- </button>
747
- </li>
748
- <li className={`detail-tab${tab === 'activity' ? ' active' : ''}`}>
749
- <button type="button" onClick={() => onChange('activity')}>
750
- Activity
751
- </button>
752
- </li>
753
- </ol>
754
- );
755
- }
756
-
757
- function StatusSection({
758
- status,
759
- onSpawn,
760
- spawning,
761
- onRestartSession,
762
- restartingSessionId,
763
- }: {
764
- status: GroupStatus;
765
- onSpawn: () => void;
766
- spawning: boolean;
767
- onRestartSession: (sessionId: string) => void;
768
- restartingSessionId: string | null;
769
- }) {
770
- return (
771
- <div className="section">
772
- <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '1rem' }}>
773
- <h3 style={{ margin: 0 }}>Live status</h3>
774
- <button onClick={onSpawn} disabled={spawning}>
775
- {spawning ? 'Spawning…' : '+ New session'}
776
- </button>
777
- </div>
778
- <p className="dim" style={{ marginTop: '0.5rem', marginBottom: 0 }}>
779
- Container env (secrets, agent provider, code) is set at session spawn. To pick up changes, use{' '}
780
- <strong>Restart</strong> on a running session — the agent's next message will spawn a fresh container.
781
- </p>
782
- <div className="kv" style={{ marginTop: '1rem' }}>
783
- <div>container</div>
784
- <div>
785
- {status.containerRunning ? (
786
- <span className="status-text alive">running</span>
787
- ) : (
788
- <span className="status-text idle">idle</span>
789
- )}
790
- </div>
791
- <div>active sessions</div>
792
- <div>
793
- {status.activeSessionCount} of {status.sessionCount}
794
- </div>
795
- <div>last heartbeat</div>
796
- <div>
797
- {status.lastHeartbeatAt ? (
798
- <>
799
- {formatRelative(status.lastHeartbeatAt)}{' '}
800
- <span className="dim">({new Date(status.lastHeartbeatAt).toLocaleString()})</span>
801
- </>
802
- ) : (
803
- <span className="dim">never</span>
804
- )}
805
- </div>
806
- <div>last message in</div>
807
- <div>
808
- {status.lastMessageInAt ? (
809
- <>
810
- {formatRelative(status.lastMessageInAt)}{' '}
811
- <span className="dim">({new Date(status.lastMessageInAt).toLocaleString()})</span>
812
- </>
813
- ) : (
814
- <span className="dim">none</span>
815
- )}
816
- </div>
817
- <div>last message out</div>
818
- <div>
819
- {status.lastMessageOutAt ? (
820
- <>
821
- {formatRelative(status.lastMessageOutAt)}{' '}
822
- <span className="dim">({new Date(status.lastMessageOutAt).toLocaleString()})</span>
823
- </>
824
- ) : (
825
- <span className="dim">none</span>
826
- )}
827
- </div>
828
- </div>
829
- <hr className="sep" />
830
- {status.sessions.length === 0 ? (
831
- <p className="dim" style={{ marginBottom: 0 }}>
832
- No sessions yet — spawn one with the button above to start the agent's container.
833
- </p>
834
- ) : (
835
- <>
836
- <div className="dim" style={{ marginBottom: '0.5rem' }}>
837
- Sessions ({status.sessions.length}):
838
- </div>
839
- <ul className="session-list">
840
- {status.sessions.map((s) => {
841
- const restartable = s.containerStatus === 'running' && s.status === 'active';
842
- const isRestarting = restartingSessionId === s.sessionId;
843
- return (
844
- <li key={s.sessionId}>
845
- <code>{s.sessionId}</code>{' '}
846
- {s.alive ? (
847
- <span className="status-text alive">alive</span>
848
- ) : (
849
- <span className="status-text idle">{s.containerStatus}</span>
850
- )}{' '}
851
- <span className="dim">— {s.status}</span>
852
- {s.lastHeartbeatAt && (
853
- <>
854
- {' '}
855
- <span className="dim">· hb {formatRelative(s.lastHeartbeatAt)}</span>
856
- </>
857
- )}
858
- {restartable && (
859
- <>
860
- {' '}
861
- <button
862
- type="button"
863
- className="secondary"
864
- style={{ marginLeft: '0.5rem', padding: '0.15rem 0.6rem', fontSize: '0.85rem' }}
865
- onClick={() => onRestartSession(s.sessionId)}
866
- disabled={restartingSessionId !== null}
867
- >
868
- {isRestarting ? 'Restarting…' : 'Restart'}
869
- </button>
870
- </>
871
- )}
872
- </li>
873
- );
874
- })}
875
- </ul>
876
- </>
877
- )}
878
- </div>
879
- );
880
- }