@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,651 +0,0 @@
1
- import crypto from 'crypto';
2
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
3
-
4
- import type { SecretMode } from '../types.js';
5
- import { closeDb, initTestDb, runMigrations } from '../db/index.js';
6
- import { _setMasterKeyForTest } from './master-key.js';
7
- import {
8
- addAssignment,
9
- deleteSecret,
10
- findStaleSessionsForSecret,
11
- getSecret,
12
- getSecretById,
13
- listAssignments,
14
- listInjectableSecretsForGroup,
15
- listSecrets,
16
- putSecret,
17
- removeAssignment,
18
- replaceAssignments,
19
- resolveInjectableSecrets,
20
- } from './index.js';
21
-
22
- beforeEach(() => {
23
- const db = initTestDb();
24
- runMigrations(db);
25
- _setMasterKeyForTest(crypto.randomBytes(32));
26
- });
27
-
28
- afterEach(() => {
29
- closeDb();
30
- });
31
-
32
- function seedAgentGroup(db: ReturnType<typeof initTestDb>, id: string, mode: SecretMode = 'selective') {
33
- db.prepare(
34
- `INSERT INTO agent_groups (id, folder, name, secret_mode, created_at)
35
- VALUES (?, ?, ?, ?, datetime('now'))`,
36
- ).run(id, id, id, mode);
37
- }
38
-
39
- describe('secrets store', () => {
40
- it('round-trips a global secret', () => {
41
- putSecret('SLACK_BOT_TOKEN', 'xoxb-1234');
42
- expect(getSecret('SLACK_BOT_TOKEN')).toBe('xoxb-1234');
43
- });
44
-
45
- it('returns undefined for missing names', () => {
46
- expect(getSecret('NOT_THERE')).toBeUndefined();
47
- });
48
-
49
- it('updates an existing secret in place', () => {
50
- const id1 = putSecret('NAME', 'v1');
51
- const id2 = putSecret('NAME', 'v2');
52
- expect(id1).toBe(id2);
53
- expect(getSecret('NAME')).toBe('v2');
54
- });
55
-
56
- it('lists secret metadata without exposing values', () => {
57
- putSecret('A', 'aaa');
58
- putSecret('B', 'bbb', { kind: 'channel-token' });
59
- const rows = listSecrets();
60
- expect(rows.map((r) => r.name).sort()).toEqual(['A', 'B']);
61
- expect(rows.find((r) => r.name === 'B')?.kind).toBe('channel-token');
62
- expect(rows[0]).not.toHaveProperty('value_encrypted');
63
- });
64
-
65
- it('agent-scoped secret beats a global one with the same name', () => {
66
- const db = initTestDb();
67
- runMigrations(db);
68
- _setMasterKeyForTest(crypto.randomBytes(32));
69
- seedAgentGroup(db, 'g1');
70
-
71
- putSecret('TOKEN', 'global-value');
72
- putSecret('TOKEN', 'g1-value', { agent_group_id: 'g1' });
73
-
74
- expect(getSecret('TOKEN')).toBe('global-value');
75
- expect(getSecret('TOKEN', 'g1')).toBe('g1-value');
76
- });
77
-
78
- it('falls back to global when no scoped row exists', () => {
79
- const db = initTestDb();
80
- runMigrations(db);
81
- _setMasterKeyForTest(crypto.randomBytes(32));
82
- seedAgentGroup(db, 'g1');
83
-
84
- putSecret('SHARED', 'global-only');
85
- expect(getSecret('SHARED', 'g1')).toBe('global-only');
86
- });
87
-
88
- it('deletes by id', () => {
89
- const id = putSecret('ZAP', 'value');
90
- expect(deleteSecret(id)).toBe(true);
91
- expect(getSecret('ZAP')).toBeUndefined();
92
- expect(deleteSecret(id)).toBe(false);
93
- });
94
-
95
- it('resolveInjectableSecrets in mode=all unions global + scoped, scoped wins', () => {
96
- const db = initTestDb();
97
- runMigrations(db);
98
- _setMasterKeyForTest(crypto.randomBytes(32));
99
- seedAgentGroup(db, 'g1', 'all');
100
-
101
- putSecret('G', 'global-only');
102
- putSecret('S', 'scoped-only', { agent_group_id: 'g1' });
103
- putSecret('B', 'global-B', {});
104
- putSecret('B', 'scoped-B', { agent_group_id: 'g1' });
105
-
106
- const env = resolveInjectableSecrets('g1');
107
- expect(env.get('G')).toBe('global-only');
108
- expect(env.get('S')).toBe('scoped-only');
109
- expect(env.get('B')).toBe('scoped-B');
110
- });
111
-
112
- it('mode=selective hides unassigned globals, but scoped secrets are auto-seeded into the owner', () => {
113
- const db = initTestDb();
114
- runMigrations(db);
115
- _setMasterKeyForTest(crypto.randomBytes(32));
116
- seedAgentGroup(db, 'g1', 'selective');
117
-
118
- // Unassigned global → invisible under selective mode.
119
- putSecret('GLOBAL', 'global-v');
120
- // Scoped secret → putSecret auto-seeds the (id, g1) assignment row, so
121
- // the resolver injects it even under selective mode (paraclaw#127).
122
- putSecret('SCOPED', 'scoped-v', { agent_group_id: 'g1' });
123
-
124
- const env = resolveInjectableSecrets('g1');
125
- expect([...env.keys()]).toEqual(['SCOPED']);
126
- expect(env.get('SCOPED')).toBe('scoped-v');
127
- });
128
-
129
- it('unknown agent_group_id resolves as no-secrets (selective default)', () => {
130
- putSecret('GLOBAL', 'v');
131
- expect(resolveInjectableSecrets('does-not-exist').size).toBe(0);
132
- });
133
- });
134
-
135
- describe('putSecret auto-seeds the owner assignment for scoped creates (paraclaw#127)', () => {
136
- /**
137
- * The default `agent_groups.secret_mode` is `selective` (migration 023).
138
- * Before the auto-seed, calling `putSecret(name, value, { agent_group_id })`
139
- * inserted a `secrets` row but never the matching `secret_assignments` row,
140
- * leaving the secret silently invisible to `resolveInjectableSecrets` —
141
- * the row predicate `(s.agent_group_id = g.id OR s.agent_group_id IS NULL)`
142
- * matches but the gate `(g.secret_mode='all' OR a.secret_id IS NOT NULL)`
143
- * rejects. UI layers had a follow-up `setSecretAssignments` that papered
144
- * over this on edits, but the create-side `CredentialForm` "free" mode
145
- * called only `putSecret` — so the standard "+ New secret" flow produced
146
- * orphan rows.
147
- *
148
- * Fix: `putSecret` writes the (id, owning_group) assignment row in the
149
- * same transaction on INSERT. UPDATE/rotate leaves the assignment set
150
- * alone (operator may have deliberately revoked).
151
- */
152
-
153
- it('scoped create writes a matching secret_assignments row', () => {
154
- const db = initTestDb();
155
- runMigrations(db);
156
- _setMasterKeyForTest(crypto.randomBytes(32));
157
- seedAgentGroup(db, 'A', 'selective');
158
-
159
- const id = putSecret('TOKEN', 'v', { agent_group_id: 'A' });
160
-
161
- expect(listAssignments(id)).toEqual(['A']);
162
- });
163
-
164
- it('scoped create in selective mode is visible via resolveInjectableSecrets without an explicit assign call', () => {
165
- const db = initTestDb();
166
- runMigrations(db);
167
- _setMasterKeyForTest(crypto.randomBytes(32));
168
- seedAgentGroup(db, 'A', 'selective');
169
-
170
- putSecret('TOKEN', 'scoped-v', { agent_group_id: 'A' });
171
-
172
- expect(resolveInjectableSecrets('A').get('TOKEN')).toBe('scoped-v');
173
- });
174
-
175
- it('scoped create in selective mode is visible via listInjectableSecretsForGroup, tagged scope=scoped', () => {
176
- const db = initTestDb();
177
- runMigrations(db);
178
- _setMasterKeyForTest(crypto.randomBytes(32));
179
- seedAgentGroup(db, 'A', 'selective');
180
-
181
- putSecret('TOKEN', 'scoped-v', { agent_group_id: 'A' });
182
-
183
- const rows = listInjectableSecretsForGroup('A');
184
- expect(rows).toHaveLength(1);
185
- expect(rows[0].name).toBe('TOKEN');
186
- expect(rows[0].scope).toBe('scoped');
187
- });
188
-
189
- it('global create does NOT write any secret_assignments row', () => {
190
- const db = initTestDb();
191
- runMigrations(db);
192
- _setMasterKeyForTest(crypto.randomBytes(32));
193
-
194
- const id = putSecret('GLOBAL_TOKEN', 'v');
195
-
196
- expect(listAssignments(id)).toEqual([]);
197
- const total = db.prepare<{ n: number }>(`SELECT COUNT(*) AS n FROM secret_assignments`).get();
198
- expect(total?.n).toBe(0);
199
- });
200
-
201
- it('rotate (UPDATE path) does NOT touch the assignment set', () => {
202
- // Operator may have deliberately revoked the auto-seeded assignment —
203
- // a rotate must not re-seed it. The auto-seed is INSERT-only.
204
- const db = initTestDb();
205
- runMigrations(db);
206
- _setMasterKeyForTest(crypto.randomBytes(32));
207
- seedAgentGroup(db, 'A', 'selective');
208
-
209
- const id = putSecret('TOKEN', 'v1', { agent_group_id: 'A' });
210
- expect(listAssignments(id)).toEqual(['A']);
211
-
212
- // Operator revokes.
213
- removeAssignment(id, 'A');
214
- expect(listAssignments(id)).toEqual([]);
215
-
216
- // Rotate plaintext — assignment set stays empty.
217
- const id2 = putSecret('TOKEN', 'v2', { agent_group_id: 'A' });
218
- expect(id2).toBe(id);
219
- expect(listAssignments(id)).toEqual([]);
220
- });
221
-
222
- it('two scoped creates with different owners each seed their own row', () => {
223
- const db = initTestDb();
224
- runMigrations(db);
225
- _setMasterKeyForTest(crypto.randomBytes(32));
226
- seedAgentGroup(db, 'A', 'selective');
227
- seedAgentGroup(db, 'B', 'selective');
228
-
229
- const aId = putSecret('TOKEN', 'va', { agent_group_id: 'A' });
230
- const bId = putSecret('TOKEN', 'vb', { agent_group_id: 'B' });
231
-
232
- expect(aId).not.toBe(bId);
233
- expect(listAssignments(aId)).toEqual(['A']);
234
- expect(listAssignments(bId)).toEqual(['B']);
235
- // Each scoped create is visible only in its own group, never across.
236
- expect(resolveInjectableSecrets('A').get('TOKEN')).toBe('va');
237
- expect(resolveInjectableSecrets('B').get('TOKEN')).toBe('vb');
238
- });
239
- });
240
-
241
- describe('secret assignments (selective mode)', () => {
242
- it('round-trips: assignment to A injects into A, not B', () => {
243
- const db = initTestDb();
244
- runMigrations(db);
245
- _setMasterKeyForTest(crypto.randomBytes(32));
246
- seedAgentGroup(db, 'A', 'selective');
247
- seedAgentGroup(db, 'B', 'selective');
248
-
249
- const secretId = putSecret('SHARED_KEY', 'top-secret');
250
- addAssignment(secretId, 'A');
251
-
252
- expect(resolveInjectableSecrets('A').get('SHARED_KEY')).toBe('top-secret');
253
- expect(resolveInjectableSecrets('B').has('SHARED_KEY')).toBe(false);
254
- });
255
-
256
- it('list/replace/add/remove cycle', () => {
257
- const db = initTestDb();
258
- runMigrations(db);
259
- _setMasterKeyForTest(crypto.randomBytes(32));
260
- seedAgentGroup(db, 'A');
261
- seedAgentGroup(db, 'B');
262
- seedAgentGroup(db, 'C');
263
-
264
- const id = putSecret('K', 'v');
265
- expect(listAssignments(id)).toEqual([]);
266
-
267
- replaceAssignments(id, ['A', 'B']);
268
- expect(listAssignments(id)).toEqual(['A', 'B']);
269
-
270
- addAssignment(id, 'C');
271
- expect(listAssignments(id)).toEqual(['A', 'B', 'C']);
272
-
273
- // re-add is a no-op (composite PK)
274
- expect(addAssignment(id, 'C')).toBe(false);
275
-
276
- removeAssignment(id, 'A');
277
- expect(listAssignments(id)).toEqual(['B', 'C']);
278
-
279
- replaceAssignments(id, []);
280
- expect(listAssignments(id)).toEqual([]);
281
- });
282
-
283
- it('replaceAssignments throws on unknown secret', () => {
284
- const db = initTestDb();
285
- runMigrations(db);
286
- _setMasterKeyForTest(crypto.randomBytes(32));
287
- expect(() => replaceAssignments('does-not-exist', [])).toThrow(/secret not found/);
288
- });
289
-
290
- it('deleting a secret cascades its assignments', () => {
291
- const db = initTestDb();
292
- runMigrations(db);
293
- _setMasterKeyForTest(crypto.randomBytes(32));
294
- seedAgentGroup(db, 'A');
295
-
296
- const id = putSecret('K', 'v');
297
- addAssignment(id, 'A');
298
- expect(listAssignments(id)).toEqual(['A']);
299
-
300
- deleteSecret(id);
301
-
302
- const remaining = db.prepare<{ n: number }>(`SELECT COUNT(*) AS n FROM secret_assignments`).get();
303
- expect(remaining?.n).toBe(0);
304
- });
305
-
306
- it('selective group + assignment + scoped secret in mode=all peer group', () => {
307
- const db = initTestDb();
308
- runMigrations(db);
309
- _setMasterKeyForTest(crypto.randomBytes(32));
310
- seedAgentGroup(db, 'A', 'selective');
311
- seedAgentGroup(db, 'B', 'all');
312
-
313
- // Global with explicit assignment to A only — A sees it via assignment,
314
- // B sees its own scoped row instead (scoped wins on name collision).
315
- const globalId = putSecret('TOKEN', 'shared-via-allowlist');
316
- addAssignment(globalId, 'A');
317
- putSecret('TOKEN', 'b-only', { agent_group_id: 'B' });
318
-
319
- expect(resolveInjectableSecrets('A').get('TOKEN')).toBe('shared-via-allowlist');
320
- expect(resolveInjectableSecrets('B').get('TOKEN')).toBe('b-only');
321
- });
322
- });
323
-
324
- describe('findStaleSessionsForSecret', () => {
325
- function seedSession(
326
- db: ReturnType<typeof initTestDb>,
327
- sessionId: string,
328
- agentGroupId: string,
329
- createdAt: string,
330
- containerStatus: 'running' | 'idle' | 'stopped' = 'running',
331
- ) {
332
- db.prepare(
333
- `INSERT INTO sessions
334
- (id, agent_group_id, messaging_group_id, thread_id, agent_provider, status, container_status, last_active, created_at)
335
- VALUES (?, ?, NULL, NULL, NULL, 'active', ?, NULL, ?)`,
336
- ).run(sessionId, agentGroupId, containerStatus, createdAt);
337
- }
338
-
339
- function bumpSecretUpdatedAt(db: ReturnType<typeof initTestDb>, secretId: string, updatedAt: string) {
340
- db.prepare(`UPDATE secrets SET updated_at = ? WHERE id = ?`).run(updatedAt, secretId);
341
- }
342
-
343
- it('returns sessions spawned before a global secret was updated, when assigned', () => {
344
- const db = initTestDb();
345
- runMigrations(db);
346
- _setMasterKeyForTest(crypto.randomBytes(32));
347
- seedAgentGroup(db, 'A', 'selective');
348
-
349
- // Session created at t=10
350
- seedSession(db, 'sess-A', 'A', '2026-01-01T00:00:10.000Z');
351
-
352
- // Global secret with assignment to A; secret updated at t=20 (after spawn)
353
- const sid = putSecret('TOKEN', 'v');
354
- addAssignment(sid, 'A');
355
- bumpSecretUpdatedAt(db, sid, '2026-01-01T00:00:20.000Z');
356
-
357
- const stale = findStaleSessionsForSecret(sid);
358
- expect(stale).toHaveLength(1);
359
- expect(stale[0].sessionId).toBe('sess-A');
360
- expect(stale[0].agentGroupId).toBe('A');
361
- expect(stale[0].secretUpdatedAt).toBe('2026-01-01T00:00:20.000Z');
362
- expect(stale[0].sessionCreatedAt).toBe('2026-01-01T00:00:10.000Z');
363
- });
364
-
365
- it('skips sessions spawned AFTER the secret update', () => {
366
- const db = initTestDb();
367
- runMigrations(db);
368
- _setMasterKeyForTest(crypto.randomBytes(32));
369
- seedAgentGroup(db, 'A', 'selective');
370
-
371
- const sid = putSecret('TOKEN', 'v');
372
- addAssignment(sid, 'A');
373
- bumpSecretUpdatedAt(db, sid, '2026-01-01T00:00:10.000Z');
374
-
375
- // Session spawned at t=20 — after the secret update — already has env.
376
- seedSession(db, 'sess-A', 'A', '2026-01-01T00:00:20.000Z');
377
-
378
- expect(findStaleSessionsForSecret(sid)).toEqual([]);
379
- });
380
-
381
- it('skips non-running sessions (idle and stopped)', () => {
382
- const db = initTestDb();
383
- runMigrations(db);
384
- _setMasterKeyForTest(crypto.randomBytes(32));
385
- seedAgentGroup(db, 'A', 'all');
386
-
387
- const sid = putSecret('TOKEN', 'v');
388
- bumpSecretUpdatedAt(db, sid, '2026-01-01T00:00:20.000Z');
389
-
390
- seedSession(db, 'sess-running', 'A', '2026-01-01T00:00:10.000Z', 'running');
391
- seedSession(db, 'sess-idle', 'A', '2026-01-01T00:00:10.000Z', 'idle');
392
- seedSession(db, 'sess-stopped', 'A', '2026-01-01T00:00:10.000Z', 'stopped');
393
-
394
- const stale = findStaleSessionsForSecret(sid);
395
- expect(stale.map((s) => s.sessionId)).toEqual(['sess-running']);
396
- });
397
-
398
- it('skips groups that would not inject the global (selective + no assignment)', () => {
399
- const db = initTestDb();
400
- runMigrations(db);
401
- _setMasterKeyForTest(crypto.randomBytes(32));
402
- seedAgentGroup(db, 'A', 'selective');
403
- seedAgentGroup(db, 'B', 'selective');
404
-
405
- const sid = putSecret('TOKEN', 'v');
406
- addAssignment(sid, 'A'); // only A is assigned
407
- bumpSecretUpdatedAt(db, sid, '2026-01-01T00:00:20.000Z');
408
-
409
- seedSession(db, 'sess-A', 'A', '2026-01-01T00:00:10.000Z');
410
- seedSession(db, 'sess-B', 'B', '2026-01-01T00:00:10.000Z');
411
-
412
- const stale = findStaleSessionsForSecret(sid);
413
- expect(stale.map((s) => s.sessionId)).toEqual(['sess-A']);
414
- });
415
-
416
- it('includes mode=all groups even without an explicit assignment', () => {
417
- const db = initTestDb();
418
- runMigrations(db);
419
- _setMasterKeyForTest(crypto.randomBytes(32));
420
- seedAgentGroup(db, 'A', 'all');
421
-
422
- const sid = putSecret('TOKEN', 'v');
423
- bumpSecretUpdatedAt(db, sid, '2026-01-01T00:00:20.000Z');
424
-
425
- seedSession(db, 'sess-A', 'A', '2026-01-01T00:00:10.000Z');
426
- expect(findStaleSessionsForSecret(sid).map((s) => s.sessionId)).toEqual(['sess-A']);
427
- });
428
-
429
- it('scoped secret only marks its own group stale', () => {
430
- const db = initTestDb();
431
- runMigrations(db);
432
- _setMasterKeyForTest(crypto.randomBytes(32));
433
- seedAgentGroup(db, 'A', 'all');
434
- seedAgentGroup(db, 'B', 'all');
435
-
436
- const sid = putSecret('TOKEN', 'v', { agent_group_id: 'A' });
437
- bumpSecretUpdatedAt(db, sid, '2026-01-01T00:00:20.000Z');
438
-
439
- seedSession(db, 'sess-A', 'A', '2026-01-01T00:00:10.000Z');
440
- seedSession(db, 'sess-B', 'B', '2026-01-01T00:00:10.000Z');
441
-
442
- expect(findStaleSessionsForSecret(sid).map((s) => s.sessionId)).toEqual(['sess-A']);
443
- });
444
-
445
- it('returns [] for a missing secret id', () => {
446
- const db = initTestDb();
447
- runMigrations(db);
448
- _setMasterKeyForTest(crypto.randomBytes(32));
449
- expect(findStaleSessionsForSecret('does-not-exist')).toEqual([]);
450
- });
451
- });
452
-
453
- describe('getSecretById', () => {
454
- it('returns the metadata row, never the value', () => {
455
- const id = putSecret('NAME', 'plaintext');
456
- const row = getSecretById(id);
457
- expect(row?.id).toBe(id);
458
- expect(row?.name).toBe('NAME');
459
- expect(row).not.toHaveProperty('value_encrypted');
460
- });
461
-
462
- it('returns undefined for a missing id', () => {
463
- expect(getSecretById('does-not-exist')).toBeUndefined();
464
- });
465
- });
466
-
467
- describe('listInjectableSecretsForGroup', () => {
468
- it('tags scoped, assigned, and global rows correctly', () => {
469
- const db = initTestDb();
470
- runMigrations(db);
471
- _setMasterKeyForTest(crypto.randomBytes(32));
472
- seedAgentGroup(db, 'A', 'all');
473
-
474
- // Three inclusion paths:
475
- // SCOPED — owned by group A
476
- // ASSIGNED — global, with explicit assignment row → A
477
- // GLOBAL — global, included only because A is mode='all'
478
- const scopedId = putSecret('SCOPED_TOKEN', 'v', { agent_group_id: 'A' });
479
- const assignedId = putSecret('ASSIGNED_TOKEN', 'v');
480
- addAssignment(assignedId, 'A');
481
- const globalId = putSecret('GLOBAL_TOKEN', 'v');
482
-
483
- const rows = listInjectableSecretsForGroup('A');
484
- const byName = new Map(rows.map((r) => [r.name, r]));
485
-
486
- expect(byName.get('SCOPED_TOKEN')?.scope).toBe('scoped');
487
- expect(byName.get('SCOPED_TOKEN')?.id).toBe(scopedId);
488
- expect(byName.get('ASSIGNED_TOKEN')?.scope).toBe('assigned');
489
- expect(byName.get('ASSIGNED_TOKEN')?.id).toBe(assignedId);
490
- expect(byName.get('GLOBAL_TOKEN')?.scope).toBe('global');
491
- expect(byName.get('GLOBAL_TOKEN')?.id).toBe(globalId);
492
- });
493
-
494
- it('selective mode hides globals that have no assignment row', () => {
495
- const db = initTestDb();
496
- runMigrations(db);
497
- _setMasterKeyForTest(crypto.randomBytes(32));
498
- seedAgentGroup(db, 'A', 'selective');
499
-
500
- putSecret('UNREACHABLE_GLOBAL', 'v');
501
- const assignedId = putSecret('ASSIGNED', 'v');
502
- addAssignment(assignedId, 'A');
503
-
504
- const rows = listInjectableSecretsForGroup('A');
505
- expect(rows.map((r) => r.name).sort()).toEqual(['ASSIGNED']);
506
- expect(rows[0].scope).toBe('assigned');
507
- });
508
-
509
- it('assignment row + mode=all → assigned wins (more specific wins)', () => {
510
- const db = initTestDb();
511
- runMigrations(db);
512
- _setMasterKeyForTest(crypto.randomBytes(32));
513
- seedAgentGroup(db, 'A', 'all');
514
-
515
- const id = putSecret('SHARED', 'v');
516
- addAssignment(id, 'A');
517
-
518
- const rows = listInjectableSecretsForGroup('A');
519
- expect(rows).toHaveLength(1);
520
- expect(rows[0].scope).toBe('assigned');
521
- });
522
-
523
- it('on name collision, scoped row wins and reports scope=scoped', () => {
524
- const db = initTestDb();
525
- runMigrations(db);
526
- _setMasterKeyForTest(crypto.randomBytes(32));
527
- seedAgentGroup(db, 'A', 'all');
528
-
529
- putSecret('TOKEN', 'global-v');
530
- putSecret('TOKEN', 'scoped-v', { agent_group_id: 'A' });
531
-
532
- const rows = listInjectableSecretsForGroup('A');
533
- expect(rows).toHaveLength(1);
534
- expect(rows[0].name).toBe('TOKEN');
535
- expect(rows[0].scope).toBe('scoped');
536
- });
537
-
538
- it('returns an empty list for an unknown agent group', () => {
539
- putSecret('GLOBAL', 'v');
540
- expect(listInjectableSecretsForGroup('does-not-exist')).toEqual([]);
541
- });
542
-
543
- it('never carries the encrypted value', () => {
544
- const db = initTestDb();
545
- runMigrations(db);
546
- _setMasterKeyForTest(crypto.randomBytes(32));
547
- seedAgentGroup(db, 'A', 'all');
548
- putSecret('K', 'super-secret');
549
- const rows = listInjectableSecretsForGroup('A');
550
- expect(rows[0]).not.toHaveProperty('value_encrypted');
551
- });
552
- });
553
-
554
- describe('resolveInjectableSecrets ↔ listInjectableSecretsForGroup lockstep (paraclaw#129)', () => {
555
- /**
556
- * Mechanical guard against drift between the two SQL-identical functions in
557
- * src/secrets/index.ts. Both walk identical row predicates + gate clauses;
558
- * any future SQL edit must touch both. Today the invariant is preserved by
559
- * careful reading + a load-bearing doc-comment. This block tests it.
560
- *
561
- * For each fixture, calls both functions and asserts:
562
- * - same set of names (the row gate matches)
563
- * - per-name plaintext from resolveInjectableSecrets matches the value
564
- * getSecret returns when scoped to the same group (the dedup-by-name
565
- * `ORDER BY s.agent_group_id IS NULL` scoped-wins ordering matches)
566
- *
567
- * If a future SQL change makes one function accept a row the other rejects,
568
- * the name-set assertion fails. If the ORDER BY drifts so dedup picks the
569
- * wrong row on collision, the per-name plaintext assertion fails.
570
- */
571
- function expectLockstep(agentGroupId: string, expectedNames: string[]): void {
572
- const resolved = resolveInjectableSecrets(agentGroupId);
573
- const listed = listInjectableSecretsForGroup(agentGroupId);
574
-
575
- const sortedExpected = expectedNames.slice().sort();
576
- expect([...resolved.keys()].sort()).toEqual(sortedExpected);
577
- expect(listed.map((r) => r.name).sort()).toEqual(sortedExpected);
578
-
579
- for (const view of listed) {
580
- expect(resolved.get(view.name)).toBe(getSecret(view.name, agentGroupId));
581
- }
582
- }
583
-
584
- it('rich mix: scoped+all + global+assigned + global+mode=all + name collision', () => {
585
- const db = initTestDb();
586
- runMigrations(db);
587
- _setMasterKeyForTest(crypto.randomBytes(32));
588
- seedAgentGroup(db, 'A', 'all');
589
-
590
- putSecret('SCOPED_ONLY', 'sv', { agent_group_id: 'A' });
591
- const assignedId = putSecret('GLOBAL_ASSIGNED', 'gv-assigned');
592
- addAssignment(assignedId, 'A');
593
- putSecret('GLOBAL_MODE_ALL', 'gv-mode-all');
594
- putSecret('TOKEN', 'global-token');
595
- putSecret('TOKEN', 'scoped-token', { agent_group_id: 'A' });
596
-
597
- expectLockstep('A', ['SCOPED_ONLY', 'GLOBAL_ASSIGNED', 'GLOBAL_MODE_ALL', 'TOKEN']);
598
-
599
- // Spot-check the collision picked the scoped row in BOTH views.
600
- expect(resolveInjectableSecrets('A').get('TOKEN')).toBe('scoped-token');
601
- expect(listInjectableSecretsForGroup('A').find((r) => r.name === 'TOKEN')?.scope).toBe('scoped');
602
- });
603
-
604
- it('mode=selective: mixed reachable + unreachable globals + scoped-with-assignment', () => {
605
- const db = initTestDb();
606
- runMigrations(db);
607
- _setMasterKeyForTest(crypto.randomBytes(32));
608
- seedAgentGroup(db, 'B', 'selective');
609
-
610
- putSecret('UNREACHABLE_GLOBAL', 'gv'); // mode=selective + no assignment → excluded
611
- const reachableId = putSecret('REACHABLE', 'gv2');
612
- addAssignment(reachableId, 'B');
613
- const scopedAssignedId = putSecret('SCOPED_AND_ASSIGNED', 'sv', { agent_group_id: 'B' });
614
- addAssignment(scopedAssignedId, 'B');
615
-
616
- expectLockstep('B', ['REACHABLE', 'SCOPED_AND_ASSIGNED']);
617
- });
618
-
619
- it('orphaned-scoped (selective + scoped + no assignment): both exclude', () => {
620
- // The "structurally unreachable via putSecret" config — selective mode +
621
- // scoped secret + no assignment row. paraclaw#127 closed the create path
622
- // (putSecret auto-seeds the matching assignment), so to exercise the
623
- // shared gate `(g.secret_mode='all' OR a.secret_id IS NOT NULL)` we
624
- // construct the orphan directly. If a future SQL change makes one of the
625
- // two functions accept it, this catches the drift.
626
- const db = initTestDb();
627
- runMigrations(db);
628
- _setMasterKeyForTest(crypto.randomBytes(32));
629
- seedAgentGroup(db, 'C', 'selective');
630
-
631
- db.prepare(
632
- `INSERT INTO secrets (id, name, value_encrypted, kind, agent_group_id, created_at, updated_at)
633
- VALUES ('orphan-id', 'ORPHAN', 'ct', 'generic', 'C', datetime('now'), datetime('now'))`,
634
- ).run();
635
-
636
- expectLockstep('C', []);
637
- });
638
-
639
- it('unknown agent_group_id: both return empty (selective default)', () => {
640
- putSecret('GLOBAL', 'v');
641
- expectLockstep('does-not-exist', []);
642
- });
643
-
644
- it('empty secret store + mode=all: both return empty', () => {
645
- const db = initTestDb();
646
- runMigrations(db);
647
- _setMasterKeyForTest(crypto.randomBytes(32));
648
- seedAgentGroup(db, 'D', 'all');
649
- expectLockstep('D', []);
650
- });
651
- });