@openparachute/agent 0.1.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 (501) hide show
  1. package/.claude/scheduled_tasks.lock +1 -0
  2. package/.claude/settings.json +5 -0
  3. package/.claude/skills/add-atomic-chat-tool/SKILL.md +243 -0
  4. package/.claude/skills/add-atomic-chat-tool/atomic-chat-mcp-stdio.ts +229 -0
  5. package/.claude/skills/add-codex/SKILL.md +161 -0
  6. package/.claude/skills/add-dashboard/SKILL.md +138 -0
  7. package/.claude/skills/add-dashboard/resources/dashboard-pusher.ts +495 -0
  8. package/.claude/skills/add-emacs/SKILL.md +296 -0
  9. package/.claude/skills/add-gcal-tool/SKILL.md +210 -0
  10. package/.claude/skills/add-gchat/REMOVE.md +6 -0
  11. package/.claude/skills/add-gchat/SKILL.md +92 -0
  12. package/.claude/skills/add-gchat/VERIFY.md +3 -0
  13. package/.claude/skills/add-github/REMOVE.md +6 -0
  14. package/.claude/skills/add-github/SKILL.md +148 -0
  15. package/.claude/skills/add-github/VERIFY.md +3 -0
  16. package/.claude/skills/add-gmail-tool/SKILL.md +229 -0
  17. package/.claude/skills/add-imessage/REMOVE.md +6 -0
  18. package/.claude/skills/add-imessage/SKILL.md +113 -0
  19. package/.claude/skills/add-imessage/VERIFY.md +3 -0
  20. package/.claude/skills/add-karpathy-llm-wiki/SKILL.md +110 -0
  21. package/.claude/skills/add-karpathy-llm-wiki/llm-wiki.md +75 -0
  22. package/.claude/skills/add-linear/REMOVE.md +6 -0
  23. package/.claude/skills/add-linear/SKILL.md +168 -0
  24. package/.claude/skills/add-linear/VERIFY.md +3 -0
  25. package/.claude/skills/add-macos-statusbar/SKILL.md +133 -0
  26. package/.claude/skills/add-macos-statusbar/add/src/statusbar.swift +147 -0
  27. package/.claude/skills/add-matrix/REMOVE.md +6 -0
  28. package/.claude/skills/add-matrix/SKILL.md +148 -0
  29. package/.claude/skills/add-matrix/VERIFY.md +3 -0
  30. package/.claude/skills/add-ollama-provider/SKILL.md +179 -0
  31. package/.claude/skills/add-ollama-tool/SKILL.md +193 -0
  32. package/.claude/skills/add-opencode/SKILL.md +229 -0
  33. package/.claude/skills/add-parallel/SKILL.md +290 -0
  34. package/.claude/skills/add-resend/REMOVE.md +6 -0
  35. package/.claude/skills/add-resend/SKILL.md +93 -0
  36. package/.claude/skills/add-resend/VERIFY.md +3 -0
  37. package/.claude/skills/add-signal/REMOVE.md +13 -0
  38. package/.claude/skills/add-signal/SKILL.md +318 -0
  39. package/.claude/skills/add-signal/VERIFY.md +5 -0
  40. package/.claude/skills/add-slack/REMOVE.md +6 -0
  41. package/.claude/skills/add-slack/SKILL.md +112 -0
  42. package/.claude/skills/add-slack/VERIFY.md +3 -0
  43. package/.claude/skills/add-teams/REMOVE.md +6 -0
  44. package/.claude/skills/add-teams/SKILL.md +207 -0
  45. package/.claude/skills/add-teams/VERIFY.md +3 -0
  46. package/.claude/skills/add-vercel/SKILL.md +147 -0
  47. package/.claude/skills/add-vercel/container-skills/vercel-cli/SKILL.md +103 -0
  48. package/.claude/skills/add-webex/REMOVE.md +6 -0
  49. package/.claude/skills/add-webex/SKILL.md +88 -0
  50. package/.claude/skills/add-webex/VERIFY.md +3 -0
  51. package/.claude/skills/add-wechat/REMOVE.md +49 -0
  52. package/.claude/skills/add-wechat/SKILL.md +170 -0
  53. package/.claude/skills/add-wechat/scripts/wire-dm.ts +172 -0
  54. package/.claude/skills/add-whatsapp/SKILL.md +264 -0
  55. package/.claude/skills/add-whatsapp-cloud/REMOVE.md +6 -0
  56. package/.claude/skills/add-whatsapp-cloud/SKILL.md +95 -0
  57. package/.claude/skills/add-whatsapp-cloud/VERIFY.md +3 -0
  58. package/.claude/skills/claw/SKILL.md +131 -0
  59. package/.claude/skills/claw/scripts/claw +374 -0
  60. package/.claude/skills/convert-to-apple-container/SKILL.md +212 -0
  61. package/.claude/skills/customize/SKILL.md +110 -0
  62. package/.claude/skills/debug/SKILL.md +349 -0
  63. package/.claude/skills/get-qodo-rules/SKILL.md +122 -0
  64. package/.claude/skills/get-qodo-rules/references/output-format.md +41 -0
  65. package/.claude/skills/get-qodo-rules/references/pagination.md +33 -0
  66. package/.claude/skills/get-qodo-rules/references/repository-scope.md +26 -0
  67. package/.claude/skills/init-first-agent/SKILL.md +120 -0
  68. package/.claude/skills/init-onecli/SKILL.md +270 -0
  69. package/.claude/skills/manage-channels/SKILL.md +87 -0
  70. package/.claude/skills/manage-mounts/SKILL.md +47 -0
  71. package/.claude/skills/migrate-from-openclaw/MIGRATE_CRONS.md +100 -0
  72. package/.claude/skills/migrate-from-openclaw/SKILL.md +447 -0
  73. package/.claude/skills/migrate-from-openclaw/scripts/discover-openclaw.ts +734 -0
  74. package/.claude/skills/migrate-from-openclaw/scripts/extract-channel-credentials.ts +476 -0
  75. package/.claude/skills/migrate-nanoclaw/SKILL.md +484 -0
  76. package/.claude/skills/migrate-nanoclaw/diagnostics.md +51 -0
  77. package/.claude/skills/qodo-pr-resolver/SKILL.md +326 -0
  78. package/.claude/skills/qodo-pr-resolver/resources/providers.md +329 -0
  79. package/.claude/skills/update-nanoclaw/SKILL.md +243 -0
  80. package/.claude/skills/update-nanoclaw/diagnostics.md +48 -0
  81. package/.claude/skills/update-skills/SKILL.md +130 -0
  82. package/.claude/skills/use-native-credential-proxy/SKILL.md +167 -0
  83. package/.claude/skills/x-integration/SKILL.md +417 -0
  84. package/.claude/skills/x-integration/agent.ts +243 -0
  85. package/.claude/skills/x-integration/host.ts +155 -0
  86. package/.claude/skills/x-integration/lib/browser.ts +148 -0
  87. package/.claude/skills/x-integration/lib/config.ts +62 -0
  88. package/.claude/skills/x-integration/scripts/like.ts +56 -0
  89. package/.claude/skills/x-integration/scripts/post.ts +66 -0
  90. package/.claude/skills/x-integration/scripts/quote.ts +80 -0
  91. package/.claude/skills/x-integration/scripts/reply.ts +74 -0
  92. package/.claude/skills/x-integration/scripts/retweet.ts +62 -0
  93. package/.claude/skills/x-integration/scripts/setup.ts +87 -0
  94. package/.github/CODEOWNERS +10 -0
  95. package/.github/PULL_REQUEST_TEMPLATE.md +18 -0
  96. package/.github/workflows/bump-version.yml +35 -0
  97. package/.github/workflows/ci.yml +39 -0
  98. package/.github/workflows/label-pr.yml +40 -0
  99. package/.github/workflows/update-tokens.yml +43 -0
  100. package/.husky/pre-commit +1 -0
  101. package/.mcp.json +3 -0
  102. package/.nvmrc +1 -0
  103. package/.parachute/module.json +14 -0
  104. package/.prettierrc +4 -0
  105. package/CHANGELOG.md +215 -0
  106. package/CLAUDE.md +307 -0
  107. package/CODE_OF_CONDUCT.md +128 -0
  108. package/CONTRIBUTING.md +159 -0
  109. package/CONTRIBUTORS.md +26 -0
  110. package/LICENSE +21 -0
  111. package/README.md +190 -0
  112. package/README_ja.md +194 -0
  113. package/README_zh.md +194 -0
  114. package/assets/nanoclaw-favicon.png +0 -0
  115. package/assets/nanoclaw-icon.png +0 -0
  116. package/assets/nanoclaw-logo-dark.png +0 -0
  117. package/assets/nanoclaw-logo.png +0 -0
  118. package/assets/nanoclaw-profile.jpeg +0 -0
  119. package/assets/nanoclaw-sales.png +0 -0
  120. package/assets/social-preview.jpg +0 -0
  121. package/config-examples/mount-allowlist.json +25 -0
  122. package/container/.dockerignore +2 -0
  123. package/container/CLAUDE.md +21 -0
  124. package/container/Dockerfile +121 -0
  125. package/container/agent-runner/bun.lock +243 -0
  126. package/container/agent-runner/package.json +22 -0
  127. package/container/agent-runner/scripts/sdk-signal-probe.ts +169 -0
  128. package/container/agent-runner/src/config.ts +55 -0
  129. package/container/agent-runner/src/db/connection.ts +267 -0
  130. package/container/agent-runner/src/db/index.ts +20 -0
  131. package/container/agent-runner/src/db/messages-in.ts +138 -0
  132. package/container/agent-runner/src/db/messages-out.ts +143 -0
  133. package/container/agent-runner/src/db/session-routing.ts +30 -0
  134. package/container/agent-runner/src/db/session-state.test.ts +100 -0
  135. package/container/agent-runner/src/db/session-state.ts +79 -0
  136. package/container/agent-runner/src/destinations.ts +135 -0
  137. package/container/agent-runner/src/formatter.test.ts +167 -0
  138. package/container/agent-runner/src/formatter.ts +260 -0
  139. package/container/agent-runner/src/index.ts +110 -0
  140. package/container/agent-runner/src/integration.test.ts +121 -0
  141. package/container/agent-runner/src/mcp-tools/agents.instructions.md +26 -0
  142. package/container/agent-runner/src/mcp-tools/agents.ts +66 -0
  143. package/container/agent-runner/src/mcp-tools/core.instructions.md +27 -0
  144. package/container/agent-runner/src/mcp-tools/core.ts +262 -0
  145. package/container/agent-runner/src/mcp-tools/index.ts +22 -0
  146. package/container/agent-runner/src/mcp-tools/interactive.instructions.md +22 -0
  147. package/container/agent-runner/src/mcp-tools/interactive.ts +169 -0
  148. package/container/agent-runner/src/mcp-tools/scheduling.instructions.md +40 -0
  149. package/container/agent-runner/src/mcp-tools/scheduling.ts +299 -0
  150. package/container/agent-runner/src/mcp-tools/self-mod.instructions.md +25 -0
  151. package/container/agent-runner/src/mcp-tools/self-mod.ts +120 -0
  152. package/container/agent-runner/src/mcp-tools/server.ts +54 -0
  153. package/container/agent-runner/src/mcp-tools/types.ts +6 -0
  154. package/container/agent-runner/src/poll-loop.test.ts +248 -0
  155. package/container/agent-runner/src/poll-loop.ts +437 -0
  156. package/container/agent-runner/src/providers/claude.ts +379 -0
  157. package/container/agent-runner/src/providers/factory.test.ts +19 -0
  158. package/container/agent-runner/src/providers/factory.ts +13 -0
  159. package/container/agent-runner/src/providers/index.ts +6 -0
  160. package/container/agent-runner/src/providers/mock.ts +77 -0
  161. package/container/agent-runner/src/providers/provider-registry.ts +33 -0
  162. package/container/agent-runner/src/providers/types.ts +82 -0
  163. package/container/agent-runner/src/scheduling/task-script.ts +121 -0
  164. package/container/agent-runner/src/timezone.test.ts +93 -0
  165. package/container/agent-runner/src/timezone.ts +107 -0
  166. package/container/agent-runner/tsconfig.json +14 -0
  167. package/container/build.sh +48 -0
  168. package/container/entrypoint.sh +16 -0
  169. package/container/skills/agent-browser/SKILL.md +159 -0
  170. package/container/skills/frontend-engineer/SKILL.md +157 -0
  171. package/container/skills/self-customize/SKILL.md +87 -0
  172. package/container/skills/slack-formatting/SKILL.md +94 -0
  173. package/container/skills/vercel-cli/SKILL.md +111 -0
  174. package/container/skills/welcome/SKILL.md +85 -0
  175. package/docs/APPLE-CONTAINER-NETWORKING.md +90 -0
  176. package/docs/BRANCH-FORK-MAINTENANCE.md +81 -0
  177. package/docs/README.md +25 -0
  178. package/docs/SDK_DEEP_DIVE.md +643 -0
  179. package/docs/SECURITY.md +162 -0
  180. package/docs/agent-runner-details.md +749 -0
  181. package/docs/api-details.md +365 -0
  182. package/docs/architecture-diagram.html +422 -0
  183. package/docs/architecture-diagram.md +215 -0
  184. package/docs/architecture.md +751 -0
  185. package/docs/audit/2026-04-30-channel-endpoint-audit.md +36 -0
  186. package/docs/build-and-runtime.md +80 -0
  187. package/docs/cross-mount-stress/README.md +112 -0
  188. package/docs/cross-mount-stress/container-writer-retry.mjs +55 -0
  189. package/docs/cross-mount-stress/container-writer-slow.mjs +42 -0
  190. package/docs/cross-mount-stress/container-writer.mjs +47 -0
  191. package/docs/cross-mount-stress/host-writer-retry.mjs +55 -0
  192. package/docs/cross-mount-stress/host-writer-slow.mjs +43 -0
  193. package/docs/cross-mount-stress/host-writer.mjs +47 -0
  194. package/docs/db-central.md +316 -0
  195. package/docs/db-session.md +183 -0
  196. package/docs/db.md +119 -0
  197. package/docs/design/2026-04-29-vault-management-ui.md +231 -0
  198. package/docs/design/2026-04-30-channel-wiring-rework.md +234 -0
  199. package/docs/design/2026-05-01-channel-wiring-approvals-deep-dive.md +272 -0
  200. package/docs/design/2026-05-02-channel-policy-and-approval-routing.md +250 -0
  201. package/docs/docker-sandboxes.md +359 -0
  202. package/docs/isolation-model.md +88 -0
  203. package/docs/ollama.md +79 -0
  204. package/docs/parachute-integration.md +109 -0
  205. package/docs/post-night-rebirth-reflections.md +151 -0
  206. package/eslint.config.js +32 -0
  207. package/package.json +54 -0
  208. package/pnpm-workspace.yaml +8 -0
  209. package/repo-tokens/README.md +113 -0
  210. package/repo-tokens/action.yml +186 -0
  211. package/repo-tokens/badge.svg +23 -0
  212. package/repo-tokens/examples/green.svg +14 -0
  213. package/repo-tokens/examples/red.svg +14 -0
  214. package/repo-tokens/examples/yellow-green.svg +14 -0
  215. package/repo-tokens/examples/yellow.svg +14 -0
  216. package/scripts/chat.ts +101 -0
  217. package/scripts/cleanup-sessions.sh +150 -0
  218. package/scripts/init-cli-agent.ts +171 -0
  219. package/scripts/init-first-agent.ts +377 -0
  220. package/scripts/parachute.ts +158 -0
  221. package/scripts/run-migrations.ts +105 -0
  222. package/scripts/sanity-live-poll.ts +95 -0
  223. package/scripts/seed-discord.ts +79 -0
  224. package/scripts/test-v2-agent.ts +106 -0
  225. package/scripts/test-v2-channel-e2e.ts +265 -0
  226. package/scripts/test-v2-host.ts +184 -0
  227. package/src/channels/adapter.ts +214 -0
  228. package/src/channels/ask-question.ts +46 -0
  229. package/src/channels/channel-registry.test.ts +421 -0
  230. package/src/channels/channel-registry.ts +313 -0
  231. package/src/channels/chat-sdk-bridge.test.ts +84 -0
  232. package/src/channels/chat-sdk-bridge.ts +652 -0
  233. package/src/channels/cli.ts +276 -0
  234. package/src/channels/discord.ts +90 -0
  235. package/src/channels/index.ts +17 -0
  236. package/src/channels/telegram-markdown-sanitize.test.ts +78 -0
  237. package/src/channels/telegram-markdown-sanitize.ts +55 -0
  238. package/src/channels/telegram-pairing.test.ts +254 -0
  239. package/src/channels/telegram-pairing.ts +339 -0
  240. package/src/channels/telegram.ts +279 -0
  241. package/src/channels/trust-hint.test.ts +48 -0
  242. package/src/channels/trust-hint.ts +75 -0
  243. package/src/claude-md-compose.migrate.test.ts +64 -0
  244. package/src/claude-md-compose.ts +205 -0
  245. package/src/command-gate.ts +63 -0
  246. package/src/config.test.ts +93 -0
  247. package/src/config.ts +108 -0
  248. package/src/container-config.ts +167 -0
  249. package/src/container-runner.test.ts +32 -0
  250. package/src/container-runner.ts +576 -0
  251. package/src/container-runtime.test.ts +169 -0
  252. package/src/container-runtime.ts +92 -0
  253. package/src/db/_bun-sqlite-shim.ts +88 -0
  254. package/src/db/agent-activity.test.ts +155 -0
  255. package/src/db/agent-activity.ts +121 -0
  256. package/src/db/agent-groups.ts +77 -0
  257. package/src/db/connection.migrate.test.ts +143 -0
  258. package/src/db/connection.ts +224 -0
  259. package/src/db/db-v2.test.ts +440 -0
  260. package/src/db/dropped-messages.ts +44 -0
  261. package/src/db/index.ts +40 -0
  262. package/src/db/messaging-groups.ts +252 -0
  263. package/src/db/migrations/001-initial.ts +112 -0
  264. package/src/db/migrations/002-chat-sdk-state.ts +36 -0
  265. package/src/db/migrations/008-dropped-messages.ts +27 -0
  266. package/src/db/migrations/009-drop-pending-credentials.ts +13 -0
  267. package/src/db/migrations/010-engage-modes.ts +103 -0
  268. package/src/db/migrations/011-pending-sender-approvals.ts +40 -0
  269. package/src/db/migrations/012-channel-registration.ts +48 -0
  270. package/src/db/migrations/013-approval-render-metadata.ts +27 -0
  271. package/src/db/migrations/014-secrets.ts +44 -0
  272. package/src/db/migrations/015-secrets-drop-host-pattern.ts +18 -0
  273. package/src/db/migrations/016-secret-assignments.ts +30 -0
  274. package/src/db/migrations/017-agent-activity.ts +40 -0
  275. package/src/db/migrations/018-oauth-app-configs.ts +34 -0
  276. package/src/db/migrations/019-oauth-app-connections.ts +48 -0
  277. package/src/db/migrations/020-agent-app-connections.ts +28 -0
  278. package/src/db/migrations/021-pending-oauth-states.ts +35 -0
  279. package/src/db/migrations/022-app-connections-provider.ts +25 -0
  280. package/src/db/migrations/023-agent-group-secret-mode.test.ts +124 -0
  281. package/src/db/migrations/023-agent-group-secret-mode.ts +65 -0
  282. package/src/db/migrations/024-collapse-approvals.test.ts +249 -0
  283. package/src/db/migrations/024-collapse-approvals.ts +182 -0
  284. package/src/db/migrations/025-secret-mode-check.test.ts +155 -0
  285. package/src/db/migrations/025-secret-mode-check.ts +49 -0
  286. package/src/db/migrations/026-user-dms-bot-id.test.ts +116 -0
  287. package/src/db/migrations/026-user-dms-bot-id.ts +54 -0
  288. package/src/db/migrations/027-provider-credentials.ts +41 -0
  289. package/src/db/migrations/_test-helpers.ts +41 -0
  290. package/src/db/migrations/index.ts +127 -0
  291. package/src/db/migrations/module-agent-to-agent-destinations.ts +84 -0
  292. package/src/db/migrations/module-approvals-pending-approvals.ts +42 -0
  293. package/src/db/migrations/module-approvals-title-options.ts +40 -0
  294. package/src/db/schema.ts +258 -0
  295. package/src/db/session-db.test.ts +93 -0
  296. package/src/db/session-db.ts +325 -0
  297. package/src/db/sessions.ts +241 -0
  298. package/src/delivery.test.ts +148 -0
  299. package/src/delivery.ts +445 -0
  300. package/src/env.ts +74 -0
  301. package/src/group-folder.test.ts +35 -0
  302. package/src/group-folder.ts +44 -0
  303. package/src/group-init.ts +92 -0
  304. package/src/host-core.test.ts +456 -0
  305. package/src/host-sweep.test.ts +146 -0
  306. package/src/host-sweep.ts +287 -0
  307. package/src/index.ts +227 -0
  308. package/src/install-slug.ts +33 -0
  309. package/src/log.test.ts +81 -0
  310. package/src/log.ts +117 -0
  311. package/src/mcp/http.ts +72 -0
  312. package/src/mcp/server.ts +92 -0
  313. package/src/mcp/stdio.ts +51 -0
  314. package/src/mcp/tools/activity.ts +88 -0
  315. package/src/mcp/tools/agent-groups.ts +183 -0
  316. package/src/mcp/tools/approvals.ts +122 -0
  317. package/src/mcp/tools/channels.ts +199 -0
  318. package/src/mcp/tools/index.ts +27 -0
  319. package/src/mcp/tools/oauth.ts +48 -0
  320. package/src/mcp/tools/secrets.ts +169 -0
  321. package/src/mcp/tools/sessions.ts +135 -0
  322. package/src/mcp/types.ts +51 -0
  323. package/src/modules/agent-to-agent/agent-route.test.ts +46 -0
  324. package/src/modules/agent-to-agent/agent-route.ts +223 -0
  325. package/src/modules/agent-to-agent/create-agent.ts +127 -0
  326. package/src/modules/agent-to-agent/db/agent-destinations.ts +135 -0
  327. package/src/modules/agent-to-agent/index.ts +22 -0
  328. package/src/modules/agent-to-agent/write-destinations.ts +59 -0
  329. package/src/modules/approvals/agent.md +45 -0
  330. package/src/modules/approvals/index.ts +21 -0
  331. package/src/modules/approvals/picks.test.ts +291 -0
  332. package/src/modules/approvals/primitive.ts +279 -0
  333. package/src/modules/approvals/project.md +27 -0
  334. package/src/modules/approvals/response-handler.ts +87 -0
  335. package/src/modules/index.ts +24 -0
  336. package/src/modules/interactive/agent.md +21 -0
  337. package/src/modules/interactive/index.ts +69 -0
  338. package/src/modules/interactive/project.md +12 -0
  339. package/src/modules/mount-security/index.ts +448 -0
  340. package/src/modules/mount-security/migrate.test.ts +91 -0
  341. package/src/modules/permissions/access.ts +28 -0
  342. package/src/modules/permissions/channel-approval.test.ts +389 -0
  343. package/src/modules/permissions/channel-approval.ts +188 -0
  344. package/src/modules/permissions/db/agent-group-members.ts +44 -0
  345. package/src/modules/permissions/db/pending-channel-approvals.test.ts +86 -0
  346. package/src/modules/permissions/db/pending-channel-approvals.ts +66 -0
  347. package/src/modules/permissions/db/pending-sender-approvals.ts +60 -0
  348. package/src/modules/permissions/db/user-dms.ts +58 -0
  349. package/src/modules/permissions/db/user-roles.ts +85 -0
  350. package/src/modules/permissions/db/users.ts +38 -0
  351. package/src/modules/permissions/index.ts +421 -0
  352. package/src/modules/permissions/permissions.test.ts +358 -0
  353. package/src/modules/permissions/sender-approval.test.ts +470 -0
  354. package/src/modules/permissions/sender-approval.ts +165 -0
  355. package/src/modules/permissions/user-dm.ts +200 -0
  356. package/src/modules/provider-credentials/db.ts +121 -0
  357. package/src/modules/provider-credentials/index.ts +12 -0
  358. package/src/modules/provider-credentials/spawn.test.ts +206 -0
  359. package/src/modules/provider-credentials/spawn.ts +114 -0
  360. package/src/modules/scheduling/actions.ts +113 -0
  361. package/src/modules/scheduling/db.test.ts +282 -0
  362. package/src/modules/scheduling/db.ts +148 -0
  363. package/src/modules/scheduling/index.ts +34 -0
  364. package/src/modules/scheduling/recurrence.test.ts +98 -0
  365. package/src/modules/scheduling/recurrence.ts +54 -0
  366. package/src/modules/self-mod/agent.md +30 -0
  367. package/src/modules/self-mod/apply.ts +85 -0
  368. package/src/modules/self-mod/index.ts +30 -0
  369. package/src/modules/self-mod/project.md +39 -0
  370. package/src/modules/self-mod/request.ts +91 -0
  371. package/src/modules/typing/index.ts +165 -0
  372. package/src/oauth/agent-app-connections.ts +103 -0
  373. package/src/oauth/app-configs.test.ts +64 -0
  374. package/src/oauth/app-configs.ts +114 -0
  375. package/src/oauth/app-connections.test.ts +109 -0
  376. package/src/oauth/app-connections.ts +178 -0
  377. package/src/oauth/crypto.ts +56 -0
  378. package/src/oauth/flow.ts +104 -0
  379. package/src/oauth/providers/google.test.ts +38 -0
  380. package/src/oauth/providers/google.ts +46 -0
  381. package/src/oauth/providers/index.ts +48 -0
  382. package/src/oauth/state-store.test.ts +54 -0
  383. package/src/oauth/state-store.ts +93 -0
  384. package/src/parachute/README.md +27 -0
  385. package/src/parachute/create-agent.test.ts +83 -0
  386. package/src/parachute/create-agent.ts +122 -0
  387. package/src/parachute/group-status.test.ts +165 -0
  388. package/src/parachute/group-status.ts +136 -0
  389. package/src/parachute/types.ts +41 -0
  390. package/src/parachute/vault-mcp.test.ts +251 -0
  391. package/src/parachute/vault-mcp.ts +232 -0
  392. package/src/platform-id.test.ts +104 -0
  393. package/src/platform-id.ts +109 -0
  394. package/src/providers/index.ts +6 -0
  395. package/src/providers/provider-container-registry.ts +58 -0
  396. package/src/response-registry.ts +45 -0
  397. package/src/router.ts +530 -0
  398. package/src/secrets/crypto.test.ts +45 -0
  399. package/src/secrets/crypto.ts +55 -0
  400. package/src/secrets/index.ts +355 -0
  401. package/src/secrets/master-key.ts +70 -0
  402. package/src/secrets/secrets.test.ts +354 -0
  403. package/src/session-manager.migrate.test.ts +59 -0
  404. package/src/session-manager.ts +433 -0
  405. package/src/startup-bootstrap.test.ts +226 -0
  406. package/src/startup-bootstrap.ts +207 -0
  407. package/src/state-sqlite.ts +182 -0
  408. package/src/timezone.test.ts +64 -0
  409. package/src/timezone.ts +37 -0
  410. package/src/types.ts +230 -0
  411. package/src/web/auth.test.ts +335 -0
  412. package/src/web/auth.ts +214 -0
  413. package/src/web/discord-validate.test.ts +77 -0
  414. package/src/web/discord-validate.ts +88 -0
  415. package/src/web/hub-discovery.test.ts +98 -0
  416. package/src/web/hub-discovery.ts +69 -0
  417. package/src/web/routes/activity.ts +106 -0
  418. package/src/web/routes/agent-provider.test.ts +282 -0
  419. package/src/web/routes/agent-provider.ts +309 -0
  420. package/src/web/routes/approvals.ts +185 -0
  421. package/src/web/routes/apps.ts +434 -0
  422. package/src/web/routes/channels-mg-detail.test.ts +324 -0
  423. package/src/web/routes/channels-mga-detail.test.ts +425 -0
  424. package/src/web/routes/channels.ts +489 -0
  425. package/src/web/routes/oauth-providers.ts +42 -0
  426. package/src/web/routes/secrets.test.ts +175 -0
  427. package/src/web/routes/secrets.ts +282 -0
  428. package/src/web/routes/sessions.ts +123 -0
  429. package/src/web/routes/settings.test.ts +106 -0
  430. package/src/web/routes/settings.ts +247 -0
  431. package/src/web/routes/setup-status.ts +205 -0
  432. package/src/web/routes/vaults.test.ts +389 -0
  433. package/src/web/routes/vaults.ts +225 -0
  434. package/src/web/server-version.test.ts +16 -0
  435. package/src/web/server.ts +1003 -0
  436. package/src/web/services-manifest.test.ts +120 -0
  437. package/src/web/services-manifest.ts +61 -0
  438. package/src/web/static-serve.test.ts +255 -0
  439. package/src/web/static-serve.ts +104 -0
  440. package/src/web/telegram-validate.test.ts +116 -0
  441. package/src/web/telegram-validate.ts +107 -0
  442. package/src/web/vault-proxy.test.ts +214 -0
  443. package/src/web/vault-proxy.ts +120 -0
  444. package/src/web/wire-channel.ts +181 -0
  445. package/src/webhook-server.ts +134 -0
  446. package/tsconfig.json +21 -0
  447. package/vitest.config.ts +18 -0
  448. package/web/README.md +63 -0
  449. package/web/ui/index.html +13 -0
  450. package/web/ui/package.json +35 -0
  451. package/web/ui/pnpm-lock.yaml +2164 -0
  452. package/web/ui/scripts/verify-base.mjs +31 -0
  453. package/web/ui/src/App.tsx +88 -0
  454. package/web/ui/src/components/ActivityFeed.tsx +444 -0
  455. package/web/ui/src/components/AgentGroupPicker.tsx +263 -0
  456. package/web/ui/src/components/AgentProviderCards.tsx +220 -0
  457. package/web/ui/src/components/CredentialForm.tsx +214 -0
  458. package/web/ui/src/components/ScopeGrants.tsx +74 -0
  459. package/web/ui/src/components/StatusDot.tsx +43 -0
  460. package/web/ui/src/components/VaultPicker.tsx +127 -0
  461. package/web/ui/src/components/setup/AdapterInstallStep.tsx +178 -0
  462. package/web/ui/src/components/setup/AgentGroupStep.tsx +43 -0
  463. package/web/ui/src/components/setup/ChannelPickStep.tsx +74 -0
  464. package/web/ui/src/components/setup/DoneStep.tsx +49 -0
  465. package/web/ui/src/components/setup/PrereqStep.tsx +129 -0
  466. package/web/ui/src/components/setup/TestConnectionStep.tsx +108 -0
  467. package/web/ui/src/components/setup/TestMessageStep.tsx +104 -0
  468. package/web/ui/src/components/setup/WireChannelStep.tsx +166 -0
  469. package/web/ui/src/components/setup/types.ts +105 -0
  470. package/web/ui/src/lib/api.test.ts +410 -0
  471. package/web/ui/src/lib/api.ts +1210 -0
  472. package/web/ui/src/lib/auth.test.ts +139 -0
  473. package/web/ui/src/lib/auth.ts +348 -0
  474. package/web/ui/src/lib/channel-adapters.ts +136 -0
  475. package/web/ui/src/main.tsx +19 -0
  476. package/web/ui/src/routes/ApprovalsList.tsx +294 -0
  477. package/web/ui/src/routes/Apps.tsx +613 -0
  478. package/web/ui/src/routes/ChannelWireDetail.test.tsx +233 -0
  479. package/web/ui/src/routes/ChannelWireDetail.tsx +403 -0
  480. package/web/ui/src/routes/ChannelsList.tsx +158 -0
  481. package/web/ui/src/routes/GroupDetail.tsx +755 -0
  482. package/web/ui/src/routes/GroupList.tsx +187 -0
  483. package/web/ui/src/routes/MessagingGroupDetail.test.tsx +233 -0
  484. package/web/ui/src/routes/MessagingGroupDetail.tsx +306 -0
  485. package/web/ui/src/routes/NewGroupWizard.tsx +390 -0
  486. package/web/ui/src/routes/OAuthCallback.tsx +56 -0
  487. package/web/ui/src/routes/SecretsList.tsx +921 -0
  488. package/web/ui/src/routes/SessionsList.tsx +220 -0
  489. package/web/ui/src/routes/SettingsAgentProvider.tsx +109 -0
  490. package/web/ui/src/routes/SettingsApprovals.tsx +234 -0
  491. package/web/ui/src/routes/SetupWizard.tsx +219 -0
  492. package/web/ui/src/routes/VaultDetail.test.tsx +361 -0
  493. package/web/ui/src/routes/VaultDetail.tsx +960 -0
  494. package/web/ui/src/routes/VaultsList.tsx +295 -0
  495. package/web/ui/src/routes/WireChannelPage.tsx +413 -0
  496. package/web/ui/src/styles.css +608 -0
  497. package/web/ui/src/test/setup.ts +23 -0
  498. package/web/ui/src/vite-env.d.ts +10 -0
  499. package/web/ui/tsconfig.json +20 -0
  500. package/web/ui/vite.config.ts +34 -0
  501. package/web/ui/vitest.config.ts +25 -0
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Real end-to-end test of v2: host router → Docker container → agent-runner → delivery.
3
+ *
4
+ * 1. Init central DB with agent group + messaging group + wiring
5
+ * 2. Route an inbound message (creates session, writes inbound.db, spawns container)
6
+ * 3. Container runs v2 agent-runner, polls inbound.db, queries Claude, writes outbound.db
7
+ * 4. Poll outbound.db for messages_out response
8
+ *
9
+ * Usage: pnpm exec tsx scripts/test-v2-host.ts
10
+ */
11
+ import Database from 'better-sqlite3';
12
+ import fs from 'fs';
13
+ import path from 'path';
14
+
15
+ const TEST_DIR = '/tmp/paraclaw-v2-e2e';
16
+ if (fs.existsSync(TEST_DIR)) fs.rmSync(TEST_DIR, { recursive: true });
17
+ fs.mkdirSync(TEST_DIR, { recursive: true });
18
+
19
+ // --- Step 1: Init central DB ---
20
+ console.log('\n=== Step 1: Init central DB ===');
21
+
22
+ import { initDb } from '../src/db/connection.js';
23
+ import { runMigrations } from '../src/db/migrations/index.js';
24
+ import { createAgentGroup } from '../src/db/agent-groups.js';
25
+ import { createMessagingGroup, createMessagingGroupAgent } from '../src/db/messaging-groups.js';
26
+
27
+ const centralDb = initDb(path.join(TEST_DIR, 'v2.db'));
28
+ runMigrations(centralDb);
29
+
30
+ // Create groups dir for agent folder mount
31
+ const groupsDir = path.resolve(process.cwd(), 'groups');
32
+ const testGroupDir = path.join(groupsDir, 'test-agent-e2e');
33
+ fs.mkdirSync(testGroupDir, { recursive: true });
34
+ fs.writeFileSync(path.join(testGroupDir, 'CLAUDE.md'), '# Test Agent\nYou are a test agent. Be brief.\n');
35
+
36
+ createAgentGroup({
37
+ id: 'ag-e2e',
38
+ name: 'E2E Test Agent',
39
+ folder: 'test-agent-e2e',
40
+ agent_provider: 'claude',
41
+ created_at: new Date().toISOString(),
42
+ });
43
+
44
+ createMessagingGroup({
45
+ id: 'mg-e2e',
46
+ channel_type: 'test',
47
+ platform_id: 'e2e-channel',
48
+ name: 'E2E Test Channel',
49
+ is_group: 0,
50
+ unknown_sender_policy: 'public',
51
+ created_at: new Date().toISOString(),
52
+ });
53
+
54
+ createMessagingGroupAgent({
55
+ id: 'mga-e2e',
56
+ messaging_group_id: 'mg-e2e',
57
+ agent_group_id: 'ag-e2e',
58
+ engage_mode: 'pattern',
59
+ engage_pattern: '.',
60
+ sender_scope: 'all',
61
+ ignored_message_policy: 'drop',
62
+ session_mode: 'shared',
63
+ priority: 0,
64
+ created_at: new Date().toISOString(),
65
+ });
66
+
67
+ console.log('✓ Central DB initialized');
68
+
69
+ // --- Step 2: Route inbound message (spawns container) ---
70
+ console.log('\n=== Step 2: Route inbound message ===');
71
+
72
+ import { routeInbound } from '../src/router.js';
73
+ import { findSession } from '../src/db/sessions.js';
74
+ import { inboundDbPath, outboundDbPath } from '../src/session-manager.js';
75
+
76
+ await routeInbound({
77
+ channelType: 'test',
78
+ platformId: 'e2e-channel',
79
+ threadId: null,
80
+ message: {
81
+ id: 'msg-e2e-1',
82
+ kind: 'chat',
83
+ content: JSON.stringify({
84
+ sender: 'Gavriel',
85
+ text: 'Say "E2E works!" and nothing else. Do not use any tools.',
86
+ }),
87
+ timestamp: new Date().toISOString(),
88
+ },
89
+ });
90
+
91
+ const session = findSession('mg-e2e', null);
92
+ if (!session) {
93
+ console.log('✗ No session created!');
94
+ process.exit(1);
95
+ }
96
+ console.log(`✓ Session: ${session.id}`);
97
+ console.log(`✓ Container status: ${session.container_status}`);
98
+
99
+ const inDbPath = inboundDbPath('ag-e2e', session.id);
100
+ const outDbPath = outboundDbPath('ag-e2e', session.id);
101
+ console.log(`✓ Inbound DB: ${inDbPath}`);
102
+ console.log(`✓ Outbound DB: ${outDbPath}`);
103
+
104
+ // --- Step 3: Wait for response ---
105
+ console.log('\n=== Step 3: Waiting for Claude response... ===');
106
+
107
+ const startTime = Date.now();
108
+ const TIMEOUT_MS = 120_000;
109
+
110
+ const checkForResponse = (): boolean => {
111
+ try {
112
+ const db = new Database(outDbPath, { readonly: true });
113
+ const out = db.prepare('SELECT * FROM messages_out').all() as Array<Record<string, unknown>>;
114
+ db.close();
115
+ return out.length > 0;
116
+ } catch {
117
+ return false;
118
+ }
119
+ };
120
+
121
+ await new Promise<void>((resolve) => {
122
+ const poll = () => {
123
+ if (checkForResponse()) {
124
+ resolve();
125
+ return;
126
+ }
127
+ if (Date.now() - startTime > TIMEOUT_MS) {
128
+ console.log(`\n✗ Timed out after ${TIMEOUT_MS / 1000}s`);
129
+ printState();
130
+ process.exit(1);
131
+ }
132
+ const elapsed = Math.floor((Date.now() - startTime) / 1000);
133
+ if (elapsed > 0 && elapsed % 10 === 0) {
134
+ process.stdout.write(` ${elapsed}s...`);
135
+ }
136
+ setTimeout(poll, 1000);
137
+ };
138
+ poll();
139
+ });
140
+
141
+ // --- Step 4: Print results ---
142
+ console.log('\n\n=== Results ===');
143
+ printState();
144
+
145
+ // Clean up test group dir
146
+ fs.rmSync(testGroupDir, { recursive: true, force: true });
147
+
148
+ process.exit(0);
149
+
150
+ function printState() {
151
+ try {
152
+ const inDb = new Database(inDbPath, { readonly: true });
153
+ const inRows = inDb.prepare('SELECT * FROM messages_in').all() as Array<Record<string, unknown>>;
154
+ inDb.close();
155
+
156
+ console.log('\nmessages_in (inbound.db):');
157
+ for (const r of inRows) {
158
+ console.log(` [${r.id}] status=${r.status} kind=${r.kind}`);
159
+ }
160
+ } catch (err) {
161
+ console.log(` (could not read inbound DB: ${err})`);
162
+ }
163
+
164
+ try {
165
+ const outDb = new Database(outDbPath, { readonly: true });
166
+ const outRows = outDb.prepare('SELECT * FROM messages_out').all() as Array<Record<string, unknown>>;
167
+ const ackRows = outDb.prepare('SELECT * FROM processing_ack').all() as Array<Record<string, unknown>>;
168
+ outDb.close();
169
+
170
+ console.log('\nmessages_out (outbound.db):');
171
+ for (const r of outRows) {
172
+ const content = JSON.parse(r.content as string);
173
+ console.log(` [${r.id}] kind=${r.kind}`);
174
+ console.log(` → ${content.text}`);
175
+ }
176
+
177
+ console.log('\nprocessing_ack (outbound.db):');
178
+ for (const r of ackRows) {
179
+ console.log(` [${r.message_id}] status=${r.status} changed=${r.status_changed}`);
180
+ }
181
+ } catch (err) {
182
+ console.log(` (could not read outbound DB: ${err})`);
183
+ }
184
+ }
@@ -0,0 +1,214 @@
1
+ /**
2
+ * v2 Channel Adapter interface.
3
+ *
4
+ * Channel adapters bridge Paraclaw with messaging platforms (Discord, Slack, etc.).
5
+ * Two patterns: native adapters (implement directly) or Chat SDK bridge (wrap a Chat SDK adapter).
6
+ */
7
+
8
+ /** Passed to the adapter at setup time. */
9
+ export interface ChannelSetup {
10
+ /** Called when an inbound message arrives from the platform. */
11
+ onInbound(platformId: string, threadId: string | null, message: InboundMessage): void | Promise<void>;
12
+
13
+ /**
14
+ * Called by admin-transport adapters (CLI) that want to route a message to
15
+ * an arbitrary channel/platform and optionally redirect replies elsewhere.
16
+ * Regular chat adapters should use `onInbound`; `onInboundEvent` skips the
17
+ * adapter-channel-type injection so the caller can target any wired mg.
18
+ */
19
+ onInboundEvent(event: InboundEvent): void | Promise<void>;
20
+
21
+ /** Called when the adapter discovers metadata about a conversation. */
22
+ onMetadata(platformId: string, name?: string, isGroup?: boolean): void;
23
+
24
+ /** Called when a user clicks a button/action in a card (e.g., ask_user_question response). */
25
+ onAction(questionId: string, selectedOption: string, userId: string): void;
26
+ }
27
+
28
+ /** Delivery address used for reply-to overrides and (normally) the inbound's own origin. */
29
+ export interface DeliveryAddress {
30
+ channelType: string;
31
+ platformId: string;
32
+ threadId: string | null;
33
+ }
34
+
35
+ /**
36
+ * Full inbound event handed to the router.
37
+ *
38
+ * `channelType` + `platformId` + `threadId` identify which messaging group /
39
+ * session receives the message. `replyTo`, when set, overrides where the
40
+ * agent's reply is delivered — used by the CLI admin transport when the
41
+ * operator wants a message routed to one channel but replies echoed back to
42
+ * their terminal. Agents cannot set `replyTo`; it is a router-layer concept
43
+ * set only by external adapters carrying operator intent.
44
+ */
45
+ export interface InboundEvent {
46
+ channelType: string;
47
+ platformId: string;
48
+ threadId: string | null;
49
+ message: {
50
+ id: string;
51
+ kind: 'chat' | 'chat-sdk';
52
+ content: string; // JSON blob
53
+ timestamp: string;
54
+ /**
55
+ * Platform-confirmed bot-mention signal forwarded from the adapter.
56
+ * See InboundMessage.isMention for the full explanation.
57
+ */
58
+ isMention?: boolean;
59
+ /** True when the source is a group/channel thread, false for DMs. */
60
+ isGroup?: boolean;
61
+ };
62
+ replyTo?: DeliveryAddress;
63
+ }
64
+
65
+ /** Inbound message from adapter to host. */
66
+ export interface InboundMessage {
67
+ id: string;
68
+ kind: 'chat' | 'chat-sdk';
69
+ content: unknown; // JS object — host will JSON.stringify before writing to session DB
70
+ timestamp: string;
71
+ /**
72
+ * Platform-confirmed signal that this message is a mention of the bot.
73
+ *
74
+ * Set by adapters that know the platform's own mention semantics — e.g.
75
+ * the Chat SDK bridge sets it true from `onNewMention` / `onDirectMessage`
76
+ * and forwards `message.isMention` from `onSubscribedMessage`. Use this
77
+ * in the router instead of agent-name regex matching, which breaks on
78
+ * platforms where the mention text is the bot's platform username (e.g.
79
+ * Telegram's `@paraclaw_refactr_bot`) rather than the agent_group
80
+ * display name (e.g. `@Andy`).
81
+ *
82
+ * Adapters that don't set it (native / legacy) leave it undefined — the
83
+ * router falls back to text-match against agent_group_name.
84
+ */
85
+ isMention?: boolean;
86
+ /** True when the source is a group/channel thread, false for DMs. */
87
+ isGroup?: boolean;
88
+ }
89
+
90
+ /** A file attachment to deliver alongside a message. */
91
+ export interface OutboundFile {
92
+ filename: string;
93
+ data: Buffer;
94
+ }
95
+
96
+ /** Outbound message from host to adapter. */
97
+ export interface OutboundMessage {
98
+ kind: string;
99
+ content: unknown; // parsed JSON from messages_out
100
+ files?: OutboundFile[]; // file attachments from the session outbox
101
+ }
102
+
103
+ /** Discovered conversation info (from syncConversations). */
104
+ export interface ConversationInfo {
105
+ platformId: string;
106
+ name: string;
107
+ isGroup: boolean;
108
+ }
109
+
110
+ /** The v2 channel adapter contract. */
111
+ export interface ChannelAdapter {
112
+ name: string;
113
+ channelType: string;
114
+
115
+ /**
116
+ * Stable per-bot identity used as the second segment of v2 platform_ids
117
+ * (`<channel>:<botId>:<native>`). Null/undefined for adapters that have
118
+ * no bot dimension at all (CLI admin transport). Resolved at adapter
119
+ * factory time — Telegram via `getMe.id`, Discord via
120
+ * `DISCORD_APPLICATION_ID`. Used by the registry to look up the right
121
+ * adapter when delivering a v2 platform_id back out.
122
+ */
123
+ botId?: string | null;
124
+
125
+ /**
126
+ * Whether this adapter models conversations as threads.
127
+ *
128
+ * true — adapter's platform uses threads as the primary conversation unit
129
+ * (Discord, Slack, Linear, GitHub). One thread = one session; the
130
+ * agent replies into the originating thread.
131
+ * false — adapter's platform treats the channel itself as the conversation
132
+ * (Telegram, WhatsApp, iMessage). Thread ids are stripped at the
133
+ * router; agent replies go to the channel.
134
+ */
135
+ supportsThreads: boolean;
136
+
137
+ // Lifecycle
138
+ setup(config: ChannelSetup): Promise<void>;
139
+ teardown(): Promise<void>;
140
+ isConnected(): boolean;
141
+
142
+ // Outbound delivery — returns the platform message ID if available
143
+ deliver(platformId: string, threadId: string | null, message: OutboundMessage): Promise<string | undefined>;
144
+
145
+ // Optional
146
+ setTyping?(platformId: string, threadId: string | null): Promise<void>;
147
+ syncConversations?(): Promise<ConversationInfo[]>;
148
+
149
+ /**
150
+ * Subscribe the bot to a thread so follow-up messages route via the
151
+ * platform's "subscribed message" path (onSubscribedMessage in Chat SDK).
152
+ * Called by the router when a mention-sticky wiring first engages in a
153
+ * thread. Idempotent: calling twice on the same thread is a no-op.
154
+ *
155
+ * Platforms without a subscription concept can omit this; the router
156
+ * treats absence as a no-op.
157
+ */
158
+ subscribe?(platformId: string, threadId: string): Promise<void>;
159
+
160
+ /**
161
+ * Open (or fetch) a DM with this user, returning the platform_id of the
162
+ * resulting DM channel. Called by the host on demand to initiate cold
163
+ * DMs — approvals, pairing handshakes, host-initiated notifications — to
164
+ * users who may never have messaged the bot themselves.
165
+ *
166
+ * Omit this method on channels where the user handle IS already the DM
167
+ * chat id (Telegram, WhatsApp, iMessage, email, Matrix). Callers will
168
+ * fall through to using the handle directly.
169
+ *
170
+ * For channels that distinguish user id from DM channel id (Discord,
171
+ * Slack, Teams, Webex, gChat): implement by delegating to Chat SDK's
172
+ * chat.openDM, which hits the platform's idempotent open-DM endpoint.
173
+ * Returning the same platform_id on repeated calls is expected.
174
+ */
175
+ openDM?(userHandle: string): Promise<string>;
176
+ }
177
+
178
+ /** Factory function that creates a channel adapter (returns null if credentials missing). */
179
+ export type ChannelAdapterFactory = () => ChannelAdapter | null | Promise<ChannelAdapter | null>;
180
+
181
+ /**
182
+ * Build an adapter from a stored secret. Channel modules implement this so
183
+ * the registry's secrets-backed startup scan and the dynamic register-bot
184
+ * endpoint can spawn additional bot adapters beyond the `.env`-defined
185
+ * primary, without channel-registry needing to know per-channel internals.
186
+ *
187
+ * Receives the full secret name (e.g. `CHANNEL_BOT_TOKEN:discord:9128…`)
188
+ * and its plaintext value (the bot token). Adapters that need more than
189
+ * just a token (Discord needs `applicationId`) parse it out of the name's
190
+ * trailing segment, which by convention is the bot's identity.
191
+ *
192
+ * Returns the adapter ready for `setup()`; the caller wires up host
193
+ * callbacks. Returns null if the secret can't produce a viable adapter
194
+ * (e.g. token rejected by the platform); callers log + skip.
195
+ */
196
+ export type ChannelSpawnFromSecret = (secretName: string, secretValue: string) => Promise<ChannelAdapter | null>;
197
+
198
+ /** Registration entry for a channel adapter. */
199
+ export interface ChannelRegistration {
200
+ factory: ChannelAdapterFactory;
201
+ /**
202
+ * Optional. Channels that support multi-bot operation expose this so the
203
+ * registry can spawn additional bots from `CHANNEL_BOT_TOKEN:<channel>:*`
204
+ * secrets at boot time and on-demand via the register-bot HTTP endpoint.
205
+ *
206
+ * Channels without it support exactly one bot per install (the
207
+ * `.env`-defined primary).
208
+ */
209
+ spawnFromSecret?: ChannelSpawnFromSecret;
210
+ containerConfig?: {
211
+ mounts?: Array<{ hostPath: string; containerPath: string; readonly: boolean }>;
212
+ env?: Record<string, string>;
213
+ };
214
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Shared ask_question payload schema + normalization.
3
+ *
4
+ * Producers (host-side approvals, container-side ask_user_question MCP tool)
5
+ * emit an `ask_question` payload. Options may be bare strings for ergonomics,
6
+ * but are normalized here into a consistent shape before delivery, persistence,
7
+ * and rendering.
8
+ */
9
+
10
+ export interface OptionInput {
11
+ label: string;
12
+ selectedLabel?: string;
13
+ value?: string;
14
+ }
15
+
16
+ export type RawOption = string | OptionInput;
17
+
18
+ export interface NormalizedOption {
19
+ label: string;
20
+ selectedLabel: string;
21
+ value: string;
22
+ }
23
+
24
+ export function normalizeOption(raw: RawOption): NormalizedOption {
25
+ if (typeof raw === 'string') {
26
+ return { label: raw, selectedLabel: raw, value: raw };
27
+ }
28
+ const label = raw.label;
29
+ return {
30
+ label,
31
+ selectedLabel: raw.selectedLabel ?? label,
32
+ value: raw.value ?? label,
33
+ };
34
+ }
35
+
36
+ export function normalizeOptions(raws: RawOption[]): NormalizedOption[] {
37
+ return raws.map(normalizeOption);
38
+ }
39
+
40
+ export interface AskQuestionPayload {
41
+ type: 'ask_question';
42
+ questionId: string;
43
+ title: string;
44
+ question: string;
45
+ options: NormalizedOption[];
46
+ }