@openparachute/agent 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (605) hide show
  1. package/.parachute/module.json +124 -8
  2. package/LICENSE +2 -16
  3. package/README.md +118 -166
  4. package/package.json +32 -43
  5. package/scripts/spawn-agent.ts +371 -0
  6. package/src/_parked/interactive-spawn.test.ts +324 -0
  7. package/src/_parked/interactive-spawn.ts +701 -0
  8. package/src/agent-defs.test.ts +1504 -0
  9. package/src/agent-defs.ts +1702 -0
  10. package/src/agent-mcp-config.test.ts +115 -0
  11. package/src/agent-mcp-config.ts +115 -0
  12. package/src/agents.test.ts +360 -0
  13. package/src/agents.ts +379 -0
  14. package/src/auth.test.ts +46 -0
  15. package/src/auth.ts +140 -0
  16. package/src/backends/attached-queue.test.ts +376 -0
  17. package/src/backends/attached-queue.ts +372 -0
  18. package/src/backends/programmatic.test.ts +1715 -0
  19. package/src/backends/programmatic.ts +927 -0
  20. package/src/backends/registry.test.ts +1494 -0
  21. package/src/backends/registry.ts +1202 -0
  22. package/src/backends/stream-json.test.ts +570 -0
  23. package/src/backends/stream-json.ts +392 -0
  24. package/src/backends/types.ts +223 -0
  25. package/src/bridge.ts +417 -0
  26. package/src/channel-backend-wiring.test.ts +237 -0
  27. package/src/credentials.test.ts +274 -0
  28. package/src/credentials.ts +380 -0
  29. package/src/cron.test.ts +342 -0
  30. package/src/cron.ts +380 -0
  31. package/src/daemon-agent-def-api.test.ts +166 -0
  32. package/src/daemon-agent-defs-api.test.ts +953 -0
  33. package/src/daemon-agent-env-api.test.ts +338 -0
  34. package/src/daemon-attached-queue-store.test.ts +65 -0
  35. package/src/daemon-config-api.test.ts +962 -0
  36. package/src/daemon-jobs-api.test.ts +271 -0
  37. package/src/daemon-vault-chat.test.ts +250 -0
  38. package/src/daemon.test.ts +746 -0
  39. package/src/daemon.ts +3314 -0
  40. package/src/def-vaults.test.ts +136 -0
  41. package/src/def-vaults.ts +165 -0
  42. package/src/delivery-state.test.ts +110 -0
  43. package/src/delivery-state.ts +154 -0
  44. package/src/effective-env.test.ts +114 -0
  45. package/src/effective-env.ts +184 -0
  46. package/src/env-compat.ts +39 -0
  47. package/src/grants.test.ts +638 -0
  48. package/src/grants.ts +675 -0
  49. package/src/hub-jwt.test.ts +161 -0
  50. package/src/hub-jwt.ts +182 -0
  51. package/src/jobs.test.ts +245 -0
  52. package/src/jobs.ts +266 -0
  53. package/src/mcp-http.test.ts +265 -0
  54. package/src/mcp-http.ts +771 -0
  55. package/src/mint-token.test.ts +152 -0
  56. package/src/mint-token.ts +139 -0
  57. package/src/module-manifest.test.ts +158 -0
  58. package/src/oauth-discovery.ts +134 -0
  59. package/src/programmatic-wiring.test.ts +838 -0
  60. package/src/registry.test.ts +227 -0
  61. package/src/registry.ts +228 -0
  62. package/src/resolve-port.test.ts +64 -0
  63. package/src/routing.test.ts +184 -0
  64. package/src/routing.ts +76 -0
  65. package/src/runner.test.ts +506 -0
  66. package/src/runner.ts +255 -0
  67. package/src/sandbox/config.test.ts +150 -0
  68. package/src/sandbox/config.ts +102 -0
  69. package/src/sandbox/egress.test.ts +113 -0
  70. package/src/sandbox/egress.ts +123 -0
  71. package/src/sandbox/index.ts +180 -0
  72. package/src/sandbox/live-seatbelt.test.ts +277 -0
  73. package/src/sandbox/mounts.test.ts +154 -0
  74. package/src/sandbox/mounts.ts +133 -0
  75. package/src/sandbox/sandbox.test.ts +168 -0
  76. package/src/sandbox/types.ts +382 -0
  77. package/src/services-manifest.test.ts +106 -0
  78. package/src/services-manifest.ts +95 -0
  79. package/src/spa-serve.test.ts +116 -0
  80. package/src/spa-serve.ts +116 -0
  81. package/src/spawn-agent-cli.test.ts +172 -0
  82. package/src/spawn-agent.test.ts +1218 -0
  83. package/src/spawn-agent.ts +569 -0
  84. package/src/spawn-deps.test.ts +54 -0
  85. package/src/spawn-deps.ts +166 -0
  86. package/src/telegram/api.ts +153 -0
  87. package/src/terminal-assets.test.ts +50 -0
  88. package/src/terminal-assets.ts +79 -0
  89. package/src/terminal-ui.ts +305 -0
  90. package/src/terminal.test.ts +530 -0
  91. package/src/terminal.ts +458 -0
  92. package/src/transport.ts +270 -0
  93. package/src/transports/http-ui.test.ts +455 -0
  94. package/src/transports/http-ui.ts +201 -0
  95. package/src/transports/telegram.test.ts +174 -0
  96. package/src/transports/telegram.ts +426 -0
  97. package/src/transports/vault.test.ts +2011 -0
  98. package/src/transports/vault.ts +1790 -0
  99. package/src/ui-kit.test.ts +178 -0
  100. package/src/ui-kit.ts +402 -0
  101. package/tsconfig.json +8 -14
  102. package/web/ui/tsconfig.json +2 -1
  103. package/.claude/scheduled_tasks.lock +0 -1
  104. package/.claude/settings.json +0 -5
  105. package/.claude/skills/add-atomic-chat-tool/SKILL.md +0 -243
  106. package/.claude/skills/add-atomic-chat-tool/atomic-chat-mcp-stdio.ts +0 -229
  107. package/.claude/skills/add-codex/SKILL.md +0 -161
  108. package/.claude/skills/add-dashboard/SKILL.md +0 -138
  109. package/.claude/skills/add-dashboard/resources/dashboard-pusher.ts +0 -495
  110. package/.claude/skills/add-emacs/SKILL.md +0 -296
  111. package/.claude/skills/add-gcal-tool/SKILL.md +0 -210
  112. package/.claude/skills/add-gchat/REMOVE.md +0 -6
  113. package/.claude/skills/add-gchat/SKILL.md +0 -92
  114. package/.claude/skills/add-gchat/VERIFY.md +0 -3
  115. package/.claude/skills/add-github/REMOVE.md +0 -6
  116. package/.claude/skills/add-github/SKILL.md +0 -148
  117. package/.claude/skills/add-github/VERIFY.md +0 -3
  118. package/.claude/skills/add-gmail-tool/SKILL.md +0 -229
  119. package/.claude/skills/add-imessage/REMOVE.md +0 -6
  120. package/.claude/skills/add-imessage/SKILL.md +0 -113
  121. package/.claude/skills/add-imessage/VERIFY.md +0 -3
  122. package/.claude/skills/add-karpathy-llm-wiki/SKILL.md +0 -110
  123. package/.claude/skills/add-karpathy-llm-wiki/llm-wiki.md +0 -75
  124. package/.claude/skills/add-linear/REMOVE.md +0 -6
  125. package/.claude/skills/add-linear/SKILL.md +0 -168
  126. package/.claude/skills/add-linear/VERIFY.md +0 -3
  127. package/.claude/skills/add-macos-statusbar/SKILL.md +0 -133
  128. package/.claude/skills/add-macos-statusbar/add/src/statusbar.swift +0 -147
  129. package/.claude/skills/add-matrix/REMOVE.md +0 -6
  130. package/.claude/skills/add-matrix/SKILL.md +0 -148
  131. package/.claude/skills/add-matrix/VERIFY.md +0 -3
  132. package/.claude/skills/add-ollama-provider/SKILL.md +0 -179
  133. package/.claude/skills/add-ollama-tool/SKILL.md +0 -193
  134. package/.claude/skills/add-opencode/SKILL.md +0 -229
  135. package/.claude/skills/add-parallel/SKILL.md +0 -290
  136. package/.claude/skills/add-resend/REMOVE.md +0 -6
  137. package/.claude/skills/add-resend/SKILL.md +0 -93
  138. package/.claude/skills/add-resend/VERIFY.md +0 -3
  139. package/.claude/skills/add-signal/REMOVE.md +0 -13
  140. package/.claude/skills/add-signal/SKILL.md +0 -318
  141. package/.claude/skills/add-signal/VERIFY.md +0 -5
  142. package/.claude/skills/add-slack/REMOVE.md +0 -6
  143. package/.claude/skills/add-slack/SKILL.md +0 -112
  144. package/.claude/skills/add-slack/VERIFY.md +0 -3
  145. package/.claude/skills/add-teams/REMOVE.md +0 -6
  146. package/.claude/skills/add-teams/SKILL.md +0 -207
  147. package/.claude/skills/add-teams/VERIFY.md +0 -3
  148. package/.claude/skills/add-vercel/SKILL.md +0 -147
  149. package/.claude/skills/add-vercel/container-skills/vercel-cli/SKILL.md +0 -103
  150. package/.claude/skills/add-webex/REMOVE.md +0 -6
  151. package/.claude/skills/add-webex/SKILL.md +0 -88
  152. package/.claude/skills/add-webex/VERIFY.md +0 -3
  153. package/.claude/skills/add-wechat/REMOVE.md +0 -49
  154. package/.claude/skills/add-wechat/SKILL.md +0 -170
  155. package/.claude/skills/add-wechat/scripts/wire-dm.ts +0 -172
  156. package/.claude/skills/add-whatsapp/SKILL.md +0 -264
  157. package/.claude/skills/add-whatsapp-cloud/REMOVE.md +0 -6
  158. package/.claude/skills/add-whatsapp-cloud/SKILL.md +0 -95
  159. package/.claude/skills/add-whatsapp-cloud/VERIFY.md +0 -3
  160. package/.claude/skills/claw/SKILL.md +0 -131
  161. package/.claude/skills/claw/scripts/claw +0 -374
  162. package/.claude/skills/convert-to-apple-container/SKILL.md +0 -212
  163. package/.claude/skills/customize/SKILL.md +0 -110
  164. package/.claude/skills/debug/SKILL.md +0 -349
  165. package/.claude/skills/get-qodo-rules/SKILL.md +0 -122
  166. package/.claude/skills/get-qodo-rules/references/output-format.md +0 -41
  167. package/.claude/skills/get-qodo-rules/references/pagination.md +0 -33
  168. package/.claude/skills/get-qodo-rules/references/repository-scope.md +0 -26
  169. package/.claude/skills/init-first-agent/SKILL.md +0 -120
  170. package/.claude/skills/init-onecli/SKILL.md +0 -270
  171. package/.claude/skills/manage-channels/SKILL.md +0 -87
  172. package/.claude/skills/manage-mounts/SKILL.md +0 -47
  173. package/.claude/skills/migrate-from-openclaw/MIGRATE_CRONS.md +0 -100
  174. package/.claude/skills/migrate-from-openclaw/SKILL.md +0 -447
  175. package/.claude/skills/migrate-from-openclaw/scripts/discover-openclaw.ts +0 -734
  176. package/.claude/skills/migrate-from-openclaw/scripts/extract-channel-credentials.ts +0 -476
  177. package/.claude/skills/migrate-nanoclaw/SKILL.md +0 -484
  178. package/.claude/skills/migrate-nanoclaw/diagnostics.md +0 -51
  179. package/.claude/skills/qodo-pr-resolver/SKILL.md +0 -326
  180. package/.claude/skills/qodo-pr-resolver/resources/providers.md +0 -329
  181. package/.claude/skills/update-nanoclaw/SKILL.md +0 -243
  182. package/.claude/skills/update-nanoclaw/diagnostics.md +0 -48
  183. package/.claude/skills/update-skills/SKILL.md +0 -130
  184. package/.claude/skills/use-native-credential-proxy/SKILL.md +0 -167
  185. package/.claude/skills/x-integration/SKILL.md +0 -417
  186. package/.claude/skills/x-integration/agent.ts +0 -243
  187. package/.claude/skills/x-integration/host.ts +0 -155
  188. package/.claude/skills/x-integration/lib/browser.ts +0 -148
  189. package/.claude/skills/x-integration/lib/config.ts +0 -62
  190. package/.claude/skills/x-integration/scripts/like.ts +0 -56
  191. package/.claude/skills/x-integration/scripts/post.ts +0 -66
  192. package/.claude/skills/x-integration/scripts/quote.ts +0 -80
  193. package/.claude/skills/x-integration/scripts/reply.ts +0 -74
  194. package/.claude/skills/x-integration/scripts/retweet.ts +0 -62
  195. package/.claude/skills/x-integration/scripts/setup.ts +0 -87
  196. package/.github/CODEOWNERS +0 -10
  197. package/.github/PULL_REQUEST_TEMPLATE.md +0 -18
  198. package/.github/workflows/bump-version.yml +0 -35
  199. package/.github/workflows/ci.yml +0 -39
  200. package/.github/workflows/label-pr.yml +0 -40
  201. package/.github/workflows/update-tokens.yml +0 -43
  202. package/.husky/pre-commit +0 -1
  203. package/.mcp.json +0 -3
  204. package/.nvmrc +0 -1
  205. package/.prettierrc +0 -4
  206. package/CHANGELOG.md +0 -263
  207. package/CLAUDE.md +0 -307
  208. package/CODE_OF_CONDUCT.md +0 -128
  209. package/CONTRIBUTING.md +0 -159
  210. package/CONTRIBUTORS.md +0 -26
  211. package/LICENSE-NANOCLAW-MIT +0 -21
  212. package/README_ja.md +0 -194
  213. package/README_zh.md +0 -194
  214. package/assets/nanoclaw-favicon.png +0 -0
  215. package/assets/nanoclaw-icon.png +0 -0
  216. package/assets/nanoclaw-logo-dark.png +0 -0
  217. package/assets/nanoclaw-logo.png +0 -0
  218. package/assets/nanoclaw-profile.jpeg +0 -0
  219. package/assets/nanoclaw-sales.png +0 -0
  220. package/assets/social-preview.jpg +0 -0
  221. package/config-examples/mount-allowlist.json +0 -25
  222. package/container/.dockerignore +0 -2
  223. package/container/CLAUDE.md +0 -21
  224. package/container/Dockerfile +0 -121
  225. package/container/agent-runner/bun.lock +0 -243
  226. package/container/agent-runner/package.json +0 -22
  227. package/container/agent-runner/scripts/sdk-signal-probe.ts +0 -169
  228. package/container/agent-runner/src/config.ts +0 -55
  229. package/container/agent-runner/src/db/connection.ts +0 -267
  230. package/container/agent-runner/src/db/index.ts +0 -20
  231. package/container/agent-runner/src/db/messages-in.ts +0 -138
  232. package/container/agent-runner/src/db/messages-out.ts +0 -143
  233. package/container/agent-runner/src/db/session-routing.ts +0 -30
  234. package/container/agent-runner/src/db/session-state.test.ts +0 -100
  235. package/container/agent-runner/src/db/session-state.ts +0 -79
  236. package/container/agent-runner/src/destinations.ts +0 -135
  237. package/container/agent-runner/src/formatter.test.ts +0 -167
  238. package/container/agent-runner/src/formatter.ts +0 -260
  239. package/container/agent-runner/src/index.ts +0 -110
  240. package/container/agent-runner/src/integration.test.ts +0 -121
  241. package/container/agent-runner/src/mcp-tools/agents.instructions.md +0 -26
  242. package/container/agent-runner/src/mcp-tools/agents.ts +0 -66
  243. package/container/agent-runner/src/mcp-tools/core.instructions.md +0 -27
  244. package/container/agent-runner/src/mcp-tools/core.ts +0 -262
  245. package/container/agent-runner/src/mcp-tools/index.ts +0 -22
  246. package/container/agent-runner/src/mcp-tools/interactive.instructions.md +0 -22
  247. package/container/agent-runner/src/mcp-tools/interactive.ts +0 -169
  248. package/container/agent-runner/src/mcp-tools/scheduling.instructions.md +0 -40
  249. package/container/agent-runner/src/mcp-tools/scheduling.ts +0 -299
  250. package/container/agent-runner/src/mcp-tools/self-mod.instructions.md +0 -25
  251. package/container/agent-runner/src/mcp-tools/self-mod.ts +0 -120
  252. package/container/agent-runner/src/mcp-tools/server.ts +0 -54
  253. package/container/agent-runner/src/mcp-tools/types.ts +0 -6
  254. package/container/agent-runner/src/poll-loop.test.ts +0 -248
  255. package/container/agent-runner/src/poll-loop.ts +0 -437
  256. package/container/agent-runner/src/providers/claude.ts +0 -379
  257. package/container/agent-runner/src/providers/factory.test.ts +0 -19
  258. package/container/agent-runner/src/providers/factory.ts +0 -13
  259. package/container/agent-runner/src/providers/index.ts +0 -6
  260. package/container/agent-runner/src/providers/mock.ts +0 -77
  261. package/container/agent-runner/src/providers/provider-registry.ts +0 -33
  262. package/container/agent-runner/src/providers/types.ts +0 -82
  263. package/container/agent-runner/src/scheduling/task-script.ts +0 -121
  264. package/container/agent-runner/src/timezone.test.ts +0 -93
  265. package/container/agent-runner/src/timezone.ts +0 -107
  266. package/container/agent-runner/tsconfig.json +0 -14
  267. package/container/build.sh +0 -48
  268. package/container/entrypoint.sh +0 -16
  269. package/container/skills/agent-browser/SKILL.md +0 -159
  270. package/container/skills/frontend-engineer/SKILL.md +0 -157
  271. package/container/skills/self-customize/SKILL.md +0 -87
  272. package/container/skills/slack-formatting/SKILL.md +0 -94
  273. package/container/skills/vercel-cli/SKILL.md +0 -111
  274. package/container/skills/welcome/SKILL.md +0 -85
  275. package/docs/APPLE-CONTAINER-NETWORKING.md +0 -90
  276. package/docs/BRANCH-FORK-MAINTENANCE.md +0 -81
  277. package/docs/README.md +0 -25
  278. package/docs/SDK_DEEP_DIVE.md +0 -643
  279. package/docs/SECURITY.md +0 -162
  280. package/docs/agent-runner-details.md +0 -749
  281. package/docs/api-details.md +0 -365
  282. package/docs/architecture-diagram.html +0 -422
  283. package/docs/architecture-diagram.md +0 -215
  284. package/docs/architecture.md +0 -751
  285. package/docs/audit/2026-04-30-channel-endpoint-audit.md +0 -36
  286. package/docs/build-and-runtime.md +0 -80
  287. package/docs/cross-mount-stress/README.md +0 -112
  288. package/docs/cross-mount-stress/container-writer-retry.mjs +0 -55
  289. package/docs/cross-mount-stress/container-writer-slow.mjs +0 -42
  290. package/docs/cross-mount-stress/container-writer.mjs +0 -47
  291. package/docs/cross-mount-stress/host-writer-retry.mjs +0 -55
  292. package/docs/cross-mount-stress/host-writer-slow.mjs +0 -43
  293. package/docs/cross-mount-stress/host-writer.mjs +0 -47
  294. package/docs/db-central.md +0 -316
  295. package/docs/db-session.md +0 -183
  296. package/docs/db.md +0 -119
  297. package/docs/design/2026-04-29-vault-management-ui.md +0 -231
  298. package/docs/design/2026-04-30-channel-wiring-rework.md +0 -234
  299. package/docs/design/2026-05-01-channel-wiring-approvals-deep-dive.md +0 -272
  300. package/docs/design/2026-05-02-channel-policy-and-approval-routing.md +0 -250
  301. package/docs/docker-sandboxes.md +0 -359
  302. package/docs/isolation-model.md +0 -88
  303. package/docs/ollama.md +0 -79
  304. package/docs/parachute-integration.md +0 -109
  305. package/docs/post-night-rebirth-reflections.md +0 -151
  306. package/eslint.config.js +0 -32
  307. package/pnpm-workspace.yaml +0 -8
  308. package/repo-tokens/README.md +0 -113
  309. package/repo-tokens/action.yml +0 -186
  310. package/repo-tokens/badge.svg +0 -23
  311. package/repo-tokens/examples/green.svg +0 -14
  312. package/repo-tokens/examples/red.svg +0 -14
  313. package/repo-tokens/examples/yellow-green.svg +0 -14
  314. package/repo-tokens/examples/yellow.svg +0 -14
  315. package/scripts/chat.ts +0 -101
  316. package/scripts/cleanup-sessions.sh +0 -150
  317. package/scripts/init-cli-agent.ts +0 -172
  318. package/scripts/init-first-agent.ts +0 -378
  319. package/scripts/parachute.ts +0 -158
  320. package/scripts/run-migrations.ts +0 -105
  321. package/scripts/sanity-live-poll.ts +0 -95
  322. package/scripts/seed-discord.ts +0 -80
  323. package/scripts/test-v2-agent.ts +0 -106
  324. package/scripts/test-v2-channel-e2e.ts +0 -265
  325. package/scripts/test-v2-host.ts +0 -184
  326. package/src/channels/adapter.ts +0 -214
  327. package/src/channels/api-translator.test.ts +0 -306
  328. package/src/channels/api-translator.ts +0 -214
  329. package/src/channels/ask-question.ts +0 -46
  330. package/src/channels/channel-registry.test.ts +0 -421
  331. package/src/channels/channel-registry.ts +0 -313
  332. package/src/channels/chat-sdk-bridge.test.ts +0 -84
  333. package/src/channels/chat-sdk-bridge.ts +0 -652
  334. package/src/channels/cli.ts +0 -276
  335. package/src/channels/discord.ts +0 -90
  336. package/src/channels/index.ts +0 -17
  337. package/src/channels/telegram-markdown-sanitize.test.ts +0 -78
  338. package/src/channels/telegram-markdown-sanitize.ts +0 -55
  339. package/src/channels/telegram-pairing.test.ts +0 -254
  340. package/src/channels/telegram-pairing.ts +0 -339
  341. package/src/channels/telegram.ts +0 -279
  342. package/src/channels/trust-hint.test.ts +0 -48
  343. package/src/channels/trust-hint.ts +0 -75
  344. package/src/claude-md-compose.migrate.test.ts +0 -64
  345. package/src/claude-md-compose.ts +0 -205
  346. package/src/command-gate.ts +0 -63
  347. package/src/config.test.ts +0 -93
  348. package/src/config.ts +0 -128
  349. package/src/container-config.ts +0 -167
  350. package/src/container-runner.test.ts +0 -32
  351. package/src/container-runner.ts +0 -576
  352. package/src/container-runtime.test.ts +0 -269
  353. package/src/container-runtime.ts +0 -167
  354. package/src/db/_bun-sqlite-shim.ts +0 -88
  355. package/src/db/agent-activity.test.ts +0 -155
  356. package/src/db/agent-activity.ts +0 -121
  357. package/src/db/agent-groups.ts +0 -77
  358. package/src/db/connection.migrate.test.ts +0 -176
  359. package/src/db/connection.ts +0 -259
  360. package/src/db/db-v2.test.ts +0 -440
  361. package/src/db/dropped-messages.ts +0 -44
  362. package/src/db/index.ts +0 -40
  363. package/src/db/messaging-groups.ts +0 -252
  364. package/src/db/migrations/001-initial.ts +0 -112
  365. package/src/db/migrations/002-chat-sdk-state.ts +0 -36
  366. package/src/db/migrations/008-dropped-messages.ts +0 -27
  367. package/src/db/migrations/009-drop-pending-credentials.ts +0 -13
  368. package/src/db/migrations/010-engage-modes.ts +0 -103
  369. package/src/db/migrations/011-pending-sender-approvals.ts +0 -40
  370. package/src/db/migrations/012-channel-registration.ts +0 -48
  371. package/src/db/migrations/013-approval-render-metadata.ts +0 -27
  372. package/src/db/migrations/014-secrets.ts +0 -44
  373. package/src/db/migrations/015-secrets-drop-host-pattern.ts +0 -18
  374. package/src/db/migrations/016-secret-assignments.ts +0 -30
  375. package/src/db/migrations/017-agent-activity.ts +0 -40
  376. package/src/db/migrations/018-oauth-app-configs.ts +0 -34
  377. package/src/db/migrations/019-oauth-app-connections.ts +0 -48
  378. package/src/db/migrations/020-agent-app-connections.ts +0 -28
  379. package/src/db/migrations/021-pending-oauth-states.ts +0 -35
  380. package/src/db/migrations/022-app-connections-provider.ts +0 -25
  381. package/src/db/migrations/023-agent-group-secret-mode.test.ts +0 -124
  382. package/src/db/migrations/023-agent-group-secret-mode.ts +0 -65
  383. package/src/db/migrations/024-collapse-approvals.test.ts +0 -249
  384. package/src/db/migrations/024-collapse-approvals.ts +0 -182
  385. package/src/db/migrations/025-secret-mode-check.test.ts +0 -155
  386. package/src/db/migrations/025-secret-mode-check.ts +0 -49
  387. package/src/db/migrations/026-user-dms-bot-id.test.ts +0 -116
  388. package/src/db/migrations/026-user-dms-bot-id.ts +0 -54
  389. package/src/db/migrations/027-provider-credentials.ts +0 -41
  390. package/src/db/migrations/_test-helpers.ts +0 -41
  391. package/src/db/migrations/index.ts +0 -127
  392. package/src/db/migrations/module-agent-to-agent-destinations.ts +0 -84
  393. package/src/db/migrations/module-approvals-pending-approvals.ts +0 -42
  394. package/src/db/migrations/module-approvals-title-options.ts +0 -40
  395. package/src/db/schema.ts +0 -258
  396. package/src/db/session-db.test.ts +0 -93
  397. package/src/db/session-db.ts +0 -325
  398. package/src/db/sessions.ts +0 -241
  399. package/src/delivery.test.ts +0 -148
  400. package/src/delivery.ts +0 -445
  401. package/src/env.ts +0 -74
  402. package/src/group-folder.test.ts +0 -35
  403. package/src/group-folder.ts +0 -44
  404. package/src/group-init.ts +0 -92
  405. package/src/host-core.test.ts +0 -456
  406. package/src/host-sweep.test.ts +0 -146
  407. package/src/host-sweep.ts +0 -287
  408. package/src/index.ts +0 -232
  409. package/src/install-slug.ts +0 -33
  410. package/src/log.test.ts +0 -81
  411. package/src/log.ts +0 -117
  412. package/src/mcp/http.ts +0 -72
  413. package/src/mcp/server.ts +0 -92
  414. package/src/mcp/stdio.ts +0 -51
  415. package/src/mcp/tools/activity.ts +0 -88
  416. package/src/mcp/tools/agent-groups.ts +0 -183
  417. package/src/mcp/tools/approvals.ts +0 -122
  418. package/src/mcp/tools/channels.test.ts +0 -126
  419. package/src/mcp/tools/channels.ts +0 -134
  420. package/src/mcp/tools/index.ts +0 -27
  421. package/src/mcp/tools/oauth.ts +0 -48
  422. package/src/mcp/tools/secrets.ts +0 -169
  423. package/src/mcp/tools/sessions.ts +0 -135
  424. package/src/mcp/types.ts +0 -51
  425. package/src/modules/agent-to-agent/agent-route.test.ts +0 -46
  426. package/src/modules/agent-to-agent/agent-route.ts +0 -223
  427. package/src/modules/agent-to-agent/create-agent.ts +0 -127
  428. package/src/modules/agent-to-agent/db/agent-destinations.ts +0 -135
  429. package/src/modules/agent-to-agent/index.ts +0 -22
  430. package/src/modules/agent-to-agent/write-destinations.ts +0 -59
  431. package/src/modules/approvals/agent.md +0 -45
  432. package/src/modules/approvals/index.ts +0 -21
  433. package/src/modules/approvals/picks.test.ts +0 -291
  434. package/src/modules/approvals/primitive.ts +0 -279
  435. package/src/modules/approvals/project.md +0 -27
  436. package/src/modules/approvals/response-handler.ts +0 -87
  437. package/src/modules/index.ts +0 -24
  438. package/src/modules/interactive/agent.md +0 -21
  439. package/src/modules/interactive/index.ts +0 -69
  440. package/src/modules/interactive/project.md +0 -12
  441. package/src/modules/mount-security/expand-path.test.ts +0 -82
  442. package/src/modules/mount-security/index.ts +0 -459
  443. package/src/modules/mount-security/migrate.test.ts +0 -91
  444. package/src/modules/permissions/access.ts +0 -28
  445. package/src/modules/permissions/channel-approval.test.ts +0 -389
  446. package/src/modules/permissions/channel-approval.ts +0 -188
  447. package/src/modules/permissions/db/agent-group-members.ts +0 -44
  448. package/src/modules/permissions/db/pending-channel-approvals.test.ts +0 -86
  449. package/src/modules/permissions/db/pending-channel-approvals.ts +0 -66
  450. package/src/modules/permissions/db/pending-sender-approvals.ts +0 -60
  451. package/src/modules/permissions/db/user-dms.ts +0 -58
  452. package/src/modules/permissions/db/user-roles.ts +0 -85
  453. package/src/modules/permissions/db/users.ts +0 -38
  454. package/src/modules/permissions/index.ts +0 -421
  455. package/src/modules/permissions/permissions.test.ts +0 -358
  456. package/src/modules/permissions/sender-approval.test.ts +0 -641
  457. package/src/modules/permissions/sender-approval.ts +0 -165
  458. package/src/modules/permissions/user-dm.ts +0 -200
  459. package/src/modules/provider-credentials/db.ts +0 -121
  460. package/src/modules/provider-credentials/index.ts +0 -12
  461. package/src/modules/provider-credentials/spawn.test.ts +0 -206
  462. package/src/modules/provider-credentials/spawn.ts +0 -114
  463. package/src/modules/scheduling/actions.ts +0 -113
  464. package/src/modules/scheduling/db.test.ts +0 -282
  465. package/src/modules/scheduling/db.ts +0 -148
  466. package/src/modules/scheduling/index.ts +0 -34
  467. package/src/modules/scheduling/recurrence.test.ts +0 -98
  468. package/src/modules/scheduling/recurrence.ts +0 -54
  469. package/src/modules/self-mod/agent.md +0 -30
  470. package/src/modules/self-mod/apply.ts +0 -85
  471. package/src/modules/self-mod/index.ts +0 -30
  472. package/src/modules/self-mod/project.md +0 -39
  473. package/src/modules/self-mod/request.ts +0 -91
  474. package/src/modules/typing/index.ts +0 -165
  475. package/src/oauth/agent-app-connections.ts +0 -103
  476. package/src/oauth/app-configs.test.ts +0 -64
  477. package/src/oauth/app-configs.ts +0 -114
  478. package/src/oauth/app-connections.test.ts +0 -109
  479. package/src/oauth/app-connections.ts +0 -178
  480. package/src/oauth/crypto.ts +0 -56
  481. package/src/oauth/flow.ts +0 -104
  482. package/src/oauth/providers/google.test.ts +0 -38
  483. package/src/oauth/providers/google.ts +0 -46
  484. package/src/oauth/providers/index.ts +0 -48
  485. package/src/oauth/state-store.test.ts +0 -54
  486. package/src/oauth/state-store.ts +0 -93
  487. package/src/parachute/README.md +0 -27
  488. package/src/parachute/create-agent.test.ts +0 -83
  489. package/src/parachute/create-agent.ts +0 -122
  490. package/src/parachute/group-status.test.ts +0 -165
  491. package/src/parachute/group-status.ts +0 -136
  492. package/src/parachute/types.ts +0 -41
  493. package/src/parachute/vault-mcp.test.ts +0 -251
  494. package/src/parachute/vault-mcp.ts +0 -232
  495. package/src/platform-id.test.ts +0 -104
  496. package/src/platform-id.ts +0 -109
  497. package/src/providers/index.ts +0 -6
  498. package/src/providers/provider-container-registry.ts +0 -58
  499. package/src/response-registry.ts +0 -45
  500. package/src/router.ts +0 -530
  501. package/src/secrets/crypto.test.ts +0 -45
  502. package/src/secrets/crypto.ts +0 -55
  503. package/src/secrets/index.ts +0 -461
  504. package/src/secrets/master-key.ts +0 -70
  505. package/src/secrets/secrets.test.ts +0 -651
  506. package/src/session-manager.attachments.test.ts +0 -171
  507. package/src/session-manager.dup-skip.test.ts +0 -173
  508. package/src/session-manager.migrate.test.ts +0 -59
  509. package/src/session-manager.ts +0 -451
  510. package/src/startup-bootstrap.test.ts +0 -226
  511. package/src/startup-bootstrap.ts +0 -207
  512. package/src/state-sqlite.ts +0 -182
  513. package/src/timezone.test.ts +0 -64
  514. package/src/timezone.ts +0 -37
  515. package/src/types.ts +0 -233
  516. package/src/web/auth.test.ts +0 -335
  517. package/src/web/auth.ts +0 -214
  518. package/src/web/discord-validate.test.ts +0 -77
  519. package/src/web/discord-validate.ts +0 -88
  520. package/src/web/hub-discovery.test.ts +0 -98
  521. package/src/web/hub-discovery.ts +0 -69
  522. package/src/web/routes/activity.ts +0 -106
  523. package/src/web/routes/agent-provider.test.ts +0 -282
  524. package/src/web/routes/agent-provider.ts +0 -309
  525. package/src/web/routes/approvals.ts +0 -185
  526. package/src/web/routes/apps.ts +0 -434
  527. package/src/web/routes/channels-mg-detail.test.ts +0 -324
  528. package/src/web/routes/channels-mga-detail.test.ts +0 -472
  529. package/src/web/routes/channels.ts +0 -311
  530. package/src/web/routes/oauth-providers.ts +0 -42
  531. package/src/web/routes/secrets.test.ts +0 -220
  532. package/src/web/routes/secrets.ts +0 -317
  533. package/src/web/routes/sessions.ts +0 -123
  534. package/src/web/routes/settings.test.ts +0 -106
  535. package/src/web/routes/settings.ts +0 -247
  536. package/src/web/routes/setup-status.ts +0 -205
  537. package/src/web/routes/vaults.test.ts +0 -389
  538. package/src/web/routes/vaults.ts +0 -225
  539. package/src/web/server-version.test.ts +0 -16
  540. package/src/web/server.ts +0 -1024
  541. package/src/web/services-manifest.test.ts +0 -148
  542. package/src/web/services-manifest.ts +0 -66
  543. package/src/web/static-serve.test.ts +0 -255
  544. package/src/web/static-serve.ts +0 -104
  545. package/src/web/telegram-validate.test.ts +0 -116
  546. package/src/web/telegram-validate.ts +0 -107
  547. package/src/web/vault-proxy.test.ts +0 -214
  548. package/src/web/vault-proxy.ts +0 -120
  549. package/src/web/wire-channel.ts +0 -181
  550. package/src/webhook-server.ts +0 -134
  551. package/vitest.config.ts +0 -18
  552. package/web/README.md +0 -63
  553. package/web/ui/index.html +0 -13
  554. package/web/ui/package.json +0 -35
  555. package/web/ui/pnpm-lock.yaml +0 -2164
  556. package/web/ui/scripts/verify-base.mjs +0 -31
  557. package/web/ui/src/App.tsx +0 -88
  558. package/web/ui/src/components/ActivityFeed.tsx +0 -444
  559. package/web/ui/src/components/AgentGroupPicker.tsx +0 -263
  560. package/web/ui/src/components/AgentProviderCards.tsx +0 -220
  561. package/web/ui/src/components/CredentialForm.tsx +0 -214
  562. package/web/ui/src/components/ScopeGrants.tsx +0 -74
  563. package/web/ui/src/components/StatusDot.tsx +0 -43
  564. package/web/ui/src/components/VaultPicker.tsx +0 -127
  565. package/web/ui/src/components/setup/AdapterInstallStep.tsx +0 -178
  566. package/web/ui/src/components/setup/AgentGroupStep.tsx +0 -43
  567. package/web/ui/src/components/setup/ChannelPickStep.tsx +0 -74
  568. package/web/ui/src/components/setup/DoneStep.tsx +0 -49
  569. package/web/ui/src/components/setup/PrereqStep.tsx +0 -129
  570. package/web/ui/src/components/setup/TestConnectionStep.tsx +0 -108
  571. package/web/ui/src/components/setup/TestMessageStep.tsx +0 -104
  572. package/web/ui/src/components/setup/WireChannelStep.tsx +0 -166
  573. package/web/ui/src/components/setup/types.ts +0 -105
  574. package/web/ui/src/lib/api.test.ts +0 -410
  575. package/web/ui/src/lib/api.ts +0 -1248
  576. package/web/ui/src/lib/auth.test.ts +0 -352
  577. package/web/ui/src/lib/auth.ts +0 -405
  578. package/web/ui/src/lib/channel-adapters.ts +0 -136
  579. package/web/ui/src/main.tsx +0 -19
  580. package/web/ui/src/routes/ApprovalsList.tsx +0 -294
  581. package/web/ui/src/routes/Apps.tsx +0 -613
  582. package/web/ui/src/routes/ChannelWireDetail.test.tsx +0 -233
  583. package/web/ui/src/routes/ChannelWireDetail.tsx +0 -403
  584. package/web/ui/src/routes/ChannelsList.tsx +0 -158
  585. package/web/ui/src/routes/GroupDetail.test.tsx +0 -206
  586. package/web/ui/src/routes/GroupDetail.tsx +0 -880
  587. package/web/ui/src/routes/GroupList.tsx +0 -187
  588. package/web/ui/src/routes/MessagingGroupDetail.test.tsx +0 -233
  589. package/web/ui/src/routes/MessagingGroupDetail.tsx +0 -306
  590. package/web/ui/src/routes/NewGroupWizard.tsx +0 -390
  591. package/web/ui/src/routes/OAuthCallback.tsx +0 -56
  592. package/web/ui/src/routes/SecretsList.tsx +0 -942
  593. package/web/ui/src/routes/SessionsList.tsx +0 -220
  594. package/web/ui/src/routes/SettingsAgentProvider.tsx +0 -109
  595. package/web/ui/src/routes/SettingsApprovals.tsx +0 -234
  596. package/web/ui/src/routes/SetupWizard.tsx +0 -219
  597. package/web/ui/src/routes/VaultDetail.test.tsx +0 -363
  598. package/web/ui/src/routes/VaultDetail.tsx +0 -960
  599. package/web/ui/src/routes/VaultsList.tsx +0 -295
  600. package/web/ui/src/routes/WireChannelPage.tsx +0 -413
  601. package/web/ui/src/styles.css +0 -608
  602. package/web/ui/src/test/setup.ts +0 -23
  603. package/web/ui/src/vite-env.d.ts +0 -10
  604. package/web/ui/vite.config.ts +0 -34
  605. package/web/ui/vitest.config.ts +0 -25
package/src/router.ts DELETED
@@ -1,530 +0,0 @@
1
- /**
2
- * Inbound message routing.
3
- *
4
- * Channel adapter event → resolve messaging group → sender resolver →
5
- * resolve/pick agent → access gate → resolve/create session → write
6
- * messages_in → wake container.
7
- *
8
- * Two module hooks (registered by the permissions module):
9
- * - `setSenderResolver` runs BEFORE agent resolution so user rows get
10
- * upserted even if the message ends up dropped by agent wiring.
11
- * Without the module, userId is null and downstream code tolerates it.
12
- * - `setAccessGate` runs AFTER agent resolution so policy decisions can
13
- * branch on the target agent group. Without the module, access is
14
- * allow-all.
15
- *
16
- * `dropped_messages` is core audit infra. Core writes rows for structural
17
- * drops (no agent wired, no trigger match); the access gate writes rows
18
- * for policy refusals.
19
- */
20
- import { getChannelAdapter } from './channels/channel-registry.js';
21
- import { consumeTrustHint } from './channels/trust-hint.js';
22
- import { gateCommand } from './command-gate.js';
23
- import { getAgentGroup, getAllAgentGroups } from './db/agent-groups.js';
24
- import { recordDroppedMessage } from './db/dropped-messages.js';
25
- import {
26
- createMessagingGroup,
27
- getMessagingGroupAgents,
28
- getMessagingGroupWithAgentCount,
29
- updateMessagingGroup,
30
- } from './db/messaging-groups.js';
31
- import { decodePlatformIdAs } from './platform-id.js';
32
- import { wireDmToAgent } from './web/wire-channel.js';
33
- import { findSessionForAgent } from './db/sessions.js';
34
- import { startTypingRefresh } from './modules/typing/index.js';
35
- import { log } from './log.js';
36
- import { resolveSession, writeSessionMessage, writeOutboundDirect } from './session-manager.js';
37
- import { wakeContainer } from './container-runner.js';
38
- import { getSession } from './db/sessions.js';
39
- import type { AgentGroup, MessagingGroup, MessagingGroupAgent } from './types.js';
40
- import type { InboundEvent } from './channels/adapter.js';
41
-
42
- function generateId(): string {
43
- return `msg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
44
- }
45
-
46
- /**
47
- * Sender-resolver hook. Runs before agent resolution.
48
- *
49
- * The permissions module registers this to extract the sender's namespaced
50
- * user id and upsert the users row. Returns null when the payload doesn't
51
- * carry enough info to identify a sender. Without the hook, every message
52
- * arrives at the gate with userId=null.
53
- */
54
- export type SenderResolverFn = (event: InboundEvent) => string | null;
55
-
56
- let senderResolver: SenderResolverFn | null = null;
57
-
58
- export function setSenderResolver(fn: SenderResolverFn): void {
59
- if (senderResolver) {
60
- log.warn('Sender resolver overwritten');
61
- }
62
- senderResolver = fn;
63
- }
64
-
65
- /**
66
- * Access-gate hook. Runs after agent resolution.
67
- *
68
- * The permissions module registers this; without it, core defaults to
69
- * allow-all. The gate receives the raw event so it can extract the sender
70
- * name for audit-trail purposes, and it is responsible for recording its
71
- * own `dropped_messages` row on refusal (structural drops are already
72
- * recorded by core before the gate runs).
73
- */
74
- export type AccessGateResult = { allowed: true } | { allowed: false; reason: string };
75
-
76
- export type AccessGateFn = (
77
- event: InboundEvent,
78
- userId: string | null,
79
- mg: MessagingGroup,
80
- agentGroupId: string,
81
- ) => AccessGateResult;
82
-
83
- let accessGate: AccessGateFn | null = null;
84
-
85
- export function setAccessGate(fn: AccessGateFn): void {
86
- if (accessGate) {
87
- log.warn('Access gate overwritten');
88
- }
89
- accessGate = fn;
90
- }
91
-
92
- /**
93
- * Per-wiring sender-scope hook. Runs alongside the access gate for each
94
- * agent that would otherwise engage — lets the permissions module enforce
95
- * `sender_scope='known'` on wirings that are stricter than the messaging
96
- * group's `unknown_sender_policy`. When the hook isn't registered (module
97
- * not installed), sender_scope is a no-op.
98
- */
99
- export type SenderScopeGateFn = (
100
- event: InboundEvent,
101
- userId: string | null,
102
- mg: MessagingGroup,
103
- agent: MessagingGroupAgent,
104
- ) => AccessGateResult;
105
-
106
- let senderScopeGate: SenderScopeGateFn | null = null;
107
-
108
- export function setSenderScopeGate(fn: SenderScopeGateFn): void {
109
- if (senderScopeGate) {
110
- log.warn('Sender-scope gate overwritten');
111
- }
112
- senderScopeGate = fn;
113
- }
114
-
115
- /**
116
- * Channel-registration hook. Runs when the router sees a mention/DM on a
117
- * messaging group that has no wirings AND hasn't been denied. The hook is
118
- * expected to escalate to an owner (card, etc.) and arrange for future
119
- * replay via routeInbound after approval. Fire-and-forget from the
120
- * router's perspective.
121
- *
122
- * Registered by the permissions module. Without the module the router
123
- * silently records the drop with reason='no_agent_wired' and moves on.
124
- */
125
- export type ChannelRequestGateFn = (mg: MessagingGroup, event: InboundEvent) => Promise<void>;
126
-
127
- let channelRequestGate: ChannelRequestGateFn | null = null;
128
-
129
- export function setChannelRequestGate(fn: ChannelRequestGateFn): void {
130
- if (channelRequestGate) {
131
- log.warn('Channel-request gate overwritten');
132
- }
133
- channelRequestGate = fn;
134
- }
135
-
136
- function safeParseContent(raw: string): { text?: string; sender?: string; senderId?: string } {
137
- try {
138
- return JSON.parse(raw);
139
- } catch {
140
- return { text: raw };
141
- }
142
- }
143
-
144
- /**
145
- * Route an inbound message from a channel adapter to the correct session.
146
- * Creates messaging group + session if they don't exist yet.
147
- */
148
- export async function routeInbound(event: InboundEvent): Promise<void> {
149
- // 0. Apply the adapter's thread policy. Non-threaded adapters (Telegram,
150
- // WhatsApp, iMessage, email) collapse threads to the channel.
151
- // By-channel-type (not by-bot) lookup is correct here: we only read
152
- // `supportsThreads`, which is a property of the channel itself, not of
153
- // a specific bot identity. Per-bot resolution (`getChannelAdapterForPlatformId`)
154
- // is reserved for delivery, where the outbound adapter must match the
155
- // bot dimension encoded in the v2 platform_id.
156
- const adapter = getChannelAdapter(event.channelType);
157
- if (adapter && !adapter.supportsThreads) {
158
- event = { ...event, threadId: null };
159
- }
160
-
161
- const isMention = event.message.isMention === true;
162
-
163
- // 1. Combined lookup: messaging_group row + count of wired agents in a
164
- // single query. Cheap short-circuit for the common "unwired channel"
165
- // case — one DB read and we're out, no auto-create, no sender
166
- // resolution, no log spam.
167
- const found = getMessagingGroupWithAgentCount(event.channelType, event.platformId);
168
-
169
- let mg: MessagingGroup;
170
- let agentCount: number;
171
- if (!found) {
172
- // No messaging_groups row. Auto-create only when the message warrants
173
- // attention (the bot was addressed — @mention or DM). Plain chatter in
174
- // channels we merely sit in stays silent — no row, no DB writes.
175
- if (!isMention) return;
176
- const mgId = `mg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
177
- mg = {
178
- id: mgId,
179
- channel_type: event.channelType,
180
- platform_id: event.platformId,
181
- name: null,
182
- is_group: event.message.isGroup ? 1 : 0,
183
- unknown_sender_policy: 'request_approval',
184
- denied_at: null,
185
- created_at: new Date().toISOString(),
186
- };
187
- createMessagingGroup(mg);
188
- log.info('Auto-created messaging group', {
189
- id: mgId,
190
- channelType: event.channelType,
191
- platformId: event.platformId,
192
- });
193
- agentCount = 0;
194
- } else {
195
- mg = found.mg;
196
- agentCount = found.agentCount;
197
- }
198
-
199
- // 1b. No wirings — either silent drop (plain chatter / denied channel) or
200
- // escalate to owner for channel-registration approval.
201
- if (agentCount === 0) {
202
- if (!isMention) return;
203
- if (mg.denied_at) {
204
- log.debug('Message dropped — channel was denied by owner', {
205
- messagingGroupId: mg.id,
206
- deniedAt: mg.denied_at,
207
- });
208
- return;
209
- }
210
-
211
- const parsedUnwired = safeParseContent(event.message.content);
212
-
213
- // Trust hint: if the operator just wired this bot at /channels/new and
214
- // is now DM'ing it, treat the message as trusted self-traffic instead
215
- // of escalating through the channel-registration approval flow. The
216
- // hint is single-use and bound to (channelType, botId, operatorUserId)
217
- // — Discord wires record no hint (no operator user id captured), so
218
- // this branch only fires for Telegram self-DMs in the trust window.
219
- const decoded = decodePlatformIdAs(event.platformId, 'v2');
220
- const senderId = parsedUnwired.senderId;
221
- if (decoded.botId && senderId && consumeTrustHint(event.channelType, decoded.botId, senderId)) {
222
- const targetGroups = getAllAgentGroups();
223
- if (targetGroups.length === 0) {
224
- // Hint already consumed (single-use) but there's no agent group to
225
- // wire to. Falls through to the approval cascade below; surface a
226
- // warn so an operator who hits this can correlate it with their
227
- // missing agent-group setup.
228
- log.warn('Trust hint consumed but no agent groups — operator message dropped', {
229
- messagingGroupId: mg.id,
230
- channelType: event.channelType,
231
- botId: decoded.botId,
232
- });
233
- }
234
- if (targetGroups.length > 0) {
235
- // Multi-agent-group installs: this picks the first group by DB
236
- // insert order. Good enough for the trust-hint use case (operator
237
- // just wired a bot and is DM'ing it now — usually the install only
238
- // has one group anyway), but a richer "wire to the most recently
239
- // wired group" or operator-prompted pick is a follow-up if needed.
240
- const target = targetGroups[0]!;
241
- log.info('Channel inbound auto-wired via operator trust hint', {
242
- messagingGroupId: mg.id,
243
- channelType: event.channelType,
244
- botId: decoded.botId,
245
- targetAgentGroupId: target.id,
246
- });
247
- // Drop the auto-created request_approval row in favor of a fresh
248
- // wire built with the trusted defaults (strict policy, all-senders
249
- // MGA). wireDmToAgent is idempotent — if we beat it to a wire that
250
- // already exists, it returns the existing rows.
251
- updateMessagingGroup(mg.id, { unknown_sender_policy: 'strict' });
252
- wireDmToAgent({
253
- channelType: event.channelType as 'discord' | 'telegram',
254
- agentGroup: target,
255
- botId: decoded.botId,
256
- botUserId: decoded.native,
257
- });
258
- // Re-run the standard route from the now-wired path so engage
259
- // checks, sender resolution, and session creation behave exactly
260
- // as if this were a normal message on a pre-wired channel.
261
- await routeInbound(event);
262
- return;
263
- }
264
- }
265
-
266
- recordDroppedMessage({
267
- channel_type: event.channelType,
268
- platform_id: event.platformId,
269
- user_id: null,
270
- sender_name: parsedUnwired.sender ?? null,
271
- reason: 'no_agent_wired',
272
- messaging_group_id: mg.id,
273
- agent_group_id: null,
274
- });
275
-
276
- if (channelRequestGate) {
277
- // Fire-and-forget escalation. The gate is expected to build a card,
278
- // persist pending_channel_approvals, and replay the event via
279
- // routeInbound after approval. Errors are logged internally — the
280
- // user's message still stays dropped here either way.
281
- void channelRequestGate(mg, event).catch((err) =>
282
- log.error('Channel-request gate threw', { messagingGroupId: mg.id, err }),
283
- );
284
- } else {
285
- log.warn('MESSAGE DROPPED — no agent groups wired and no channel-request gate registered', {
286
- messagingGroupId: mg.id,
287
- channelType: event.channelType,
288
- platformId: event.platformId,
289
- });
290
- }
291
- return;
292
- }
293
-
294
- // 2. Sender resolution (permissions module upserts the users row as a
295
- // side effect so later role/access lookups find a real record).
296
- // Without the module, userId is null — downstream tolerates it.
297
- const userId: string | null = senderResolver ? senderResolver(event) : null;
298
-
299
- // 3. Fetch wired agents in full (we already know the count is > 0; now
300
- // we need their actual rows for fan-out).
301
- const agents = getMessagingGroupAgents(mg.id);
302
-
303
- // 4. Fan-out: evaluate each wired agent independently against engage_mode,
304
- // sender_scope, and access gate. An agent that engages gets its own
305
- // session and container wake. An agent that declines but has
306
- // ignored_message_policy='accumulate' still gets the message stored in
307
- // its session (trigger=0) so the context is available when it does
308
- // engage later. Drop policy = skip silently.
309
- //
310
- // Subscribe (for mention-sticky wirings on threaded platforms) fires
311
- // once per message from this loop — the first engaging mention-sticky
312
- // wiring triggers adapter.subscribe(...); subsequent wirings don't
313
- // re-subscribe (chat.subscribe is idempotent anyway, but the flag
314
- // avoids the extra await).
315
- const parsed = safeParseContent(event.message.content);
316
- const messageText = parsed.text ?? '';
317
-
318
- let engagedCount = 0;
319
- let accumulatedCount = 0;
320
- let subscribed = false;
321
-
322
- for (const agent of agents) {
323
- const agentGroup = getAgentGroup(agent.agent_group_id);
324
- if (!agentGroup) continue;
325
-
326
- const engages = evaluateEngage(agent, messageText, isMention, mg, event.threadId);
327
-
328
- const accessOk = engages && (!accessGate || accessGate(event, userId, mg, agent.agent_group_id).allowed);
329
- const scopeOk = engages && (!senderScopeGate || senderScopeGate(event, userId, mg, agent).allowed);
330
-
331
- if (engages && accessOk && scopeOk) {
332
- await deliverToAgent(agent, agentGroup, mg, event, userId, adapter?.supportsThreads === true, true);
333
- engagedCount++;
334
-
335
- // Mention-sticky: ask the adapter to subscribe the thread so the
336
- // platform's subscribed-message path carries follow-ups without
337
- // requiring another @mention. Threaded-adapter only; DMs and
338
- // non-threaded platforms skip.
339
- if (
340
- !subscribed &&
341
- agent.engage_mode === 'mention-sticky' &&
342
- adapter?.supportsThreads &&
343
- adapter.subscribe &&
344
- event.threadId !== null &&
345
- mg.is_group !== 0
346
- ) {
347
- subscribed = true;
348
- // Fire-and-forget — subscribe is platform-side bookkeeping and
349
- // shouldn't block message routing. Errors are logged inside the
350
- // adapter (or by the promise rejection handler below).
351
- void adapter.subscribe(event.platformId, event.threadId).catch((err) => {
352
- log.warn('adapter.subscribe failed', { channelType: event.channelType, threadId: event.threadId, err });
353
- });
354
- }
355
- } else if (agent.ignored_message_policy === 'accumulate') {
356
- await deliverToAgent(agent, agentGroup, mg, event, userId, adapter?.supportsThreads === true, false);
357
- accumulatedCount++;
358
- } else {
359
- log.debug('Message not engaged for agent (drop policy)', {
360
- agentGroupId: agent.agent_group_id,
361
- engage_mode: agent.engage_mode,
362
- engages,
363
- accessOk,
364
- scopeOk,
365
- });
366
- }
367
- }
368
-
369
- if (engagedCount + accumulatedCount === 0) {
370
- recordDroppedMessage({
371
- channel_type: event.channelType,
372
- platform_id: event.platformId,
373
- user_id: userId,
374
- sender_name: parsed.sender ?? null,
375
- reason: 'no_agent_engaged',
376
- messaging_group_id: mg.id,
377
- agent_group_id: null,
378
- });
379
- }
380
- }
381
-
382
- /**
383
- * Decide whether a given wired agent should engage on this message.
384
- *
385
- * 'pattern' — regex test on text; '.' = always
386
- * 'mention' — bot must be mentioned on the platform. Resolved by
387
- * the adapter (SDK-level) and forwarded as
388
- * `event.message.isMention`. Agent display name
389
- * (`agent_group.name`) is irrelevant — users address
390
- * the bot via its platform username (@botname on
391
- * Telegram, user-id mention on Slack/Discord), not
392
- * via the agent's Paraclaw-side display name. If a
393
- * user wants to disambiguate between multiple agents
394
- * wired to one chat, use engage_mode='pattern' with
395
- * the disambiguator as the regex.
396
- * 'mention-sticky' — platform mention OR an active per-thread session
397
- * already exists for this (agent, mg, thread). The
398
- * session existence IS our subscription state; once
399
- * a thread has engaged us once, follow-ups arrive
400
- * with no mention and should still fire.
401
- */
402
- function evaluateEngage(
403
- agent: MessagingGroupAgent,
404
- text: string,
405
- isMention: boolean,
406
- mg: MessagingGroup,
407
- threadId: string | null,
408
- ): boolean {
409
- switch (agent.engage_mode) {
410
- case 'pattern': {
411
- const pat = agent.engage_pattern ?? '.';
412
- if (pat === '.') return true;
413
- try {
414
- return new RegExp(pat).test(text);
415
- } catch {
416
- // Bad regex: fail open so admin sees the agent responding + can fix.
417
- return true;
418
- }
419
- }
420
- case 'mention':
421
- return isMention;
422
- case 'mention-sticky': {
423
- if (isMention) return true;
424
- // Sticky follow-up: session already exists for this (agent, mg, thread)
425
- // — the thread was activated before, keep firing.
426
- if (mg.is_group === 0) return false; // DMs never use mention-sticky sensibly
427
- const existing = findSessionForAgent(agent.agent_group_id, mg.id, threadId);
428
- return existing !== undefined;
429
- }
430
- default:
431
- return false;
432
- }
433
- }
434
-
435
- async function deliverToAgent(
436
- agent: MessagingGroupAgent,
437
- agentGroup: AgentGroup,
438
- mg: MessagingGroup,
439
- event: InboundEvent,
440
- userId: string | null,
441
- adapterSupportsThreads: boolean,
442
- wake: boolean,
443
- ): Promise<void> {
444
- // Apply the adapter thread policy: threaded adapter in a group chat →
445
- // per-thread session regardless of wiring. agent-shared preserved (it's
446
- // a cross-channel directive the adapter doesn't know about). DMs collapse
447
- // sub-threads to one session (is_group=0 short-circuit).
448
- let effectiveSessionMode = agent.session_mode;
449
- if (adapterSupportsThreads && effectiveSessionMode !== 'agent-shared' && mg.is_group !== 0) {
450
- effectiveSessionMode = 'per-thread';
451
- }
452
-
453
- const { session, created } = resolveSession(agent.agent_group_id, mg.id, event.threadId, effectiveSessionMode);
454
-
455
- // The inbound row's (channel_type, platform_id, thread_id) is the address
456
- // the agent's reply will be delivered to. Normally it mirrors the source
457
- // (stamped from the event). When the caller supplied `replyTo` (CLI admin
458
- // transport acting on operator intent), the reply is redirected there.
459
- const deliveryAddr = event.replyTo ?? {
460
- channelType: event.channelType,
461
- platformId: event.platformId,
462
- threadId: event.threadId,
463
- };
464
-
465
- // Command gate: classify slash commands before they reach the container.
466
- // Filtered commands are dropped silently. Denied admin commands get a
467
- // permission-denied response written directly to messages_out.
468
- if (event.message.kind === 'chat' || event.message.kind === 'chat-sdk') {
469
- const gate = gateCommand(event.message.content, userId, agent.agent_group_id);
470
- if (gate.action === 'filter') {
471
- log.debug('Filtered command dropped by gate', { agentGroupId: agent.agent_group_id });
472
- return;
473
- }
474
- if (gate.action === 'deny') {
475
- writeOutboundDirect(session.agent_group_id, session.id, {
476
- id: `deny-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
477
- kind: 'chat',
478
- platformId: deliveryAddr.platformId,
479
- channelType: deliveryAddr.channelType,
480
- threadId: deliveryAddr.threadId,
481
- content: JSON.stringify({ text: `Permission denied: ${gate.command} requires admin access.` }),
482
- });
483
- log.info('Admin command denied by gate', { command: gate.command, userId, agentGroupId: agent.agent_group_id });
484
- return;
485
- }
486
- }
487
-
488
- writeSessionMessage(session.agent_group_id, session.id, {
489
- id: messageIdForAgent(event.message.id, agent.agent_group_id),
490
- kind: event.message.kind,
491
- timestamp: event.message.timestamp,
492
- platformId: deliveryAddr.platformId,
493
- channelType: deliveryAddr.channelType,
494
- threadId: deliveryAddr.threadId,
495
- content: event.message.content,
496
- trigger: wake ? 1 : 0,
497
- });
498
-
499
- log.info('Message routed', {
500
- sessionId: session.id,
501
- agentGroup: agent.agent_group_id,
502
- engage_mode: agent.engage_mode,
503
- kind: event.message.kind,
504
- userId,
505
- wake,
506
- created,
507
- agentGroupName: agentGroup.name,
508
- });
509
-
510
- if (wake) {
511
- // Typing indicator + wake are only for the engaged branch; accumulated
512
- // messages sit silently until a real trigger fires.
513
- startTypingRefresh(session.id, session.agent_group_id, event.channelType, event.platformId, event.threadId);
514
- const freshSession = getSession(session.id);
515
- if (freshSession) {
516
- await wakeContainer(freshSession);
517
- }
518
- }
519
- }
520
-
521
- /**
522
- * When fanning out, the same inbound message lands in multiple per-agent
523
- * session DBs. messages_in.id is PRIMARY KEY, so reuse of the raw id would
524
- * collide across sessions (or, more subtly, within one session if re-routed
525
- * after a retry). Namespace by agent_group_id to keep ids unique per session.
526
- */
527
- function messageIdForAgent(baseId: string | undefined, agentGroupId: string): string {
528
- const id = baseId && baseId.length > 0 ? baseId : generateId();
529
- return `${id}:${agentGroupId}`;
530
- }
@@ -1,45 +0,0 @@
1
- import crypto from 'crypto';
2
- import { describe, expect, it } from 'vitest';
3
-
4
- import { decryptSecret, encryptSecret } from './crypto.js';
5
-
6
- describe('secret crypto', () => {
7
- const key = crypto.randomBytes(32);
8
-
9
- it('round-trips a plaintext value', () => {
10
- const ct = encryptSecret('xoxb-1234-secret', key);
11
- expect(decryptSecret(ct, key)).toBe('xoxb-1234-secret');
12
- });
13
-
14
- it('produces a different ciphertext each call (random IV)', () => {
15
- const a = encryptSecret('same-value', key);
16
- const b = encryptSecret('same-value', key);
17
- expect(a).not.toBe(b);
18
- expect(decryptSecret(a, key)).toBe('same-value');
19
- expect(decryptSecret(b, key)).toBe('same-value');
20
- });
21
-
22
- it('rejects tampered ciphertext', () => {
23
- const ct = encryptSecret('original', key);
24
- const buf = Buffer.from(ct, 'base64');
25
- buf[buf.length - 1] ^= 0x01;
26
- const tampered = buf.toString('base64');
27
- expect(() => decryptSecret(tampered, key)).toThrow();
28
- });
29
-
30
- it('rejects ciphertext encrypted under a different key', () => {
31
- const otherKey = crypto.randomBytes(32);
32
- const ct = encryptSecret('only-original-key', key);
33
- expect(() => decryptSecret(ct, otherKey)).toThrow();
34
- });
35
-
36
- it('handles empty strings', () => {
37
- const ct = encryptSecret('', key);
38
- expect(decryptSecret(ct, key)).toBe('');
39
- });
40
-
41
- it('handles unicode', () => {
42
- const v = '🔐 résumé — 秘密';
43
- expect(decryptSecret(encryptSecret(v, key), key)).toBe(v);
44
- });
45
- });
@@ -1,55 +0,0 @@
1
- /**
2
- * AES-256-GCM encryption for secret values.
3
- *
4
- * Wire format (base64-encoded):
5
- * 12-byte IV || ciphertext || 16-byte auth tag
6
- *
7
- * Each call generates a fresh random IV — never reuse an IV with the same
8
- * key (catastrophic for GCM). The auth tag is appended so decryption fails
9
- * loudly on tampering.
10
- *
11
- * Domain separation: encryptSecret/decryptSecret accept a 32-byte key. Callers
12
- * MUST NOT pass the raw master key — they pass a per-domain HKDF derivation
13
- * (see `deriveKey` below). That way if a future subsystem (e.g. an outbox
14
- * cookie signer) needs symmetric crypto from the same master, its key is
15
- * cryptographically separated and a bug in one domain can't decrypt the other.
16
- */
17
- import crypto from 'crypto';
18
-
19
- const ALGO = 'aes-256-gcm';
20
- const IV_LEN = 12;
21
- const TAG_LEN = 16;
22
- const KEY_LEN = 32;
23
-
24
- /**
25
- * HKDF-SHA256 with an empty salt and a domain-specific `info` string. The
26
- * empty salt is fine — the master key is already 256 bits of CSPRNG output,
27
- * so HKDF degenerates to HKDF-Expand and the domain-tag in `info` does the
28
- * real work. Use `paraclaw.<subsystem>.v<n>`; bumping `v` is a key rotation
29
- * for that subsystem only.
30
- */
31
- export function deriveKey(masterKey: Buffer, info: string): Buffer {
32
- if (masterKey.length !== KEY_LEN) {
33
- throw new Error(`master key must be ${KEY_LEN} bytes, got ${masterKey.length}`);
34
- }
35
- return Buffer.from(crypto.hkdfSync('sha256', masterKey, Buffer.alloc(0), info, KEY_LEN));
36
- }
37
-
38
- export function encryptSecret(plaintext: string, key: Buffer): string {
39
- const iv = crypto.randomBytes(IV_LEN);
40
- const cipher = crypto.createCipheriv(ALGO, key, iv);
41
- const ct = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
42
- const tag = cipher.getAuthTag();
43
- return Buffer.concat([iv, ct, tag]).toString('base64');
44
- }
45
-
46
- export function decryptSecret(encoded: string, key: Buffer): string {
47
- const buf = Buffer.from(encoded, 'base64');
48
- if (buf.length < IV_LEN + TAG_LEN) throw new Error('ciphertext too short');
49
- const iv = buf.subarray(0, IV_LEN);
50
- const tag = buf.subarray(buf.length - TAG_LEN);
51
- const ct = buf.subarray(IV_LEN, buf.length - TAG_LEN);
52
- const decipher = crypto.createDecipheriv(ALGO, key, iv);
53
- decipher.setAuthTag(tag);
54
- return Buffer.concat([decipher.update(ct), decipher.final()]).toString('utf8');
55
- }