@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
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
- }