@vellumai/assistant 0.3.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 (1068) hide show
  1. package/.dockerignore +27 -0
  2. package/.env.example +22 -0
  3. package/Dockerfile +99 -0
  4. package/Dockerfile.sandbox +5 -0
  5. package/README.md +248 -0
  6. package/bun.lock +1723 -0
  7. package/bunfig.toml +2 -0
  8. package/docs/skills.md +158 -0
  9. package/drizzle/0000_dizzy_maggott.sql +301 -0
  10. package/drizzle/meta/0000_snapshot.json +1999 -0
  11. package/drizzle/meta/_journal.json +13 -0
  12. package/drizzle.config.ts +7 -0
  13. package/eslint.config.mjs +17 -0
  14. package/hook-templates/debug-prompt-logger/hook.json +7 -0
  15. package/hook-templates/debug-prompt-logger/run.sh +68 -0
  16. package/knip.json +9 -0
  17. package/package.json +70 -0
  18. package/scripts/capture-x-graphql.ts +545 -0
  19. package/scripts/ipc/check-contract-inventory.ts +104 -0
  20. package/scripts/ipc/check-swift-decoder-drift.ts +166 -0
  21. package/scripts/ipc/generate-swift.ts +492 -0
  22. package/scripts/test-filesystem-tools.sh +48 -0
  23. package/scripts/test.sh +127 -0
  24. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +2485 -0
  25. package/src/__tests__/account-registry.test.ts +245 -0
  26. package/src/__tests__/active-skill-tools.test.ts +378 -0
  27. package/src/__tests__/agent-heartbeat-service.test.ts +250 -0
  28. package/src/__tests__/agent-loop-thinking.test.ts +81 -0
  29. package/src/__tests__/agent-loop.test.ts +1135 -0
  30. package/src/__tests__/anthropic-provider.test.ts +778 -0
  31. package/src/__tests__/app-builder-tool-scripts.test.ts +290 -0
  32. package/src/__tests__/app-bundler.test.ts +292 -0
  33. package/src/__tests__/app-executors.test.ts +613 -0
  34. package/src/__tests__/app-git-history.test.ts +176 -0
  35. package/src/__tests__/app-git-service.test.ts +169 -0
  36. package/src/__tests__/app-open-proxy.test.ts +62 -0
  37. package/src/__tests__/asset-materialize-tool.test.ts +452 -0
  38. package/src/__tests__/asset-search-tool.test.ts +477 -0
  39. package/src/__tests__/assistant-attachment-directive.test.ts +401 -0
  40. package/src/__tests__/assistant-attachments.test.ts +437 -0
  41. package/src/__tests__/assistant-event-hub.test.ts +226 -0
  42. package/src/__tests__/assistant-event.test.ts +123 -0
  43. package/src/__tests__/assistant-events-sse-hardening.test.ts +315 -0
  44. package/src/__tests__/attachments-store.test.ts +476 -0
  45. package/src/__tests__/attachments.test.ts +134 -0
  46. package/src/__tests__/audit-log-rotation.test.ts +154 -0
  47. package/src/__tests__/browser-fill-credential.test.ts +309 -0
  48. package/src/__tests__/browser-manager.test.ts +203 -0
  49. package/src/__tests__/browser-runtime-check.test.ts +55 -0
  50. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +68 -0
  51. package/src/__tests__/browser-skill-endstate.test.ts +195 -0
  52. package/src/__tests__/bundle-scanner.test.ts +313 -0
  53. package/src/__tests__/call-bridge.test.ts +517 -0
  54. package/src/__tests__/call-constants.test.ts +40 -0
  55. package/src/__tests__/call-domain.test.ts +163 -0
  56. package/src/__tests__/call-orchestrator.test.ts +625 -0
  57. package/src/__tests__/call-recovery.test.ts +518 -0
  58. package/src/__tests__/call-routes-http.test.ts +699 -0
  59. package/src/__tests__/call-state-machine.test.ts +143 -0
  60. package/src/__tests__/call-state.test.ts +174 -0
  61. package/src/__tests__/call-store.test.ts +691 -0
  62. package/src/__tests__/channel-approval-routes.test.ts +2356 -0
  63. package/src/__tests__/channel-approval.test.ts +299 -0
  64. package/src/__tests__/channel-approvals.test.ts +521 -0
  65. package/src/__tests__/channel-delivery-store.test.ts +447 -0
  66. package/src/__tests__/channel-guardian.test.ts +1005 -0
  67. package/src/__tests__/checker.test.ts +3519 -0
  68. package/src/__tests__/clarification-resolver.test.ts +159 -0
  69. package/src/__tests__/classifier.test.ts +67 -0
  70. package/src/__tests__/claude-code-skill-regression.test.ts +127 -0
  71. package/src/__tests__/claude-code-tool-profiles.test.ts +88 -0
  72. package/src/__tests__/cli-discover.test.ts +85 -0
  73. package/src/__tests__/cli.test.ts +26 -0
  74. package/src/__tests__/clipboard.test.ts +80 -0
  75. package/src/__tests__/commit-guarantee.test.ts +335 -0
  76. package/src/__tests__/commit-message-enrichment-service.test.ts +550 -0
  77. package/src/__tests__/compaction.benchmark.test.ts +176 -0
  78. package/src/__tests__/computer-use-session-compaction.test.ts +132 -0
  79. package/src/__tests__/computer-use-session-lifecycle.test.ts +293 -0
  80. package/src/__tests__/computer-use-session-working-dir.test.ts +117 -0
  81. package/src/__tests__/computer-use-skill-baseline.test.ts +74 -0
  82. package/src/__tests__/computer-use-skill-endstate.test.ts +89 -0
  83. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +217 -0
  84. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +107 -0
  85. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +54 -0
  86. package/src/__tests__/computer-use-tools.test.ts +250 -0
  87. package/src/__tests__/config-schema.test.ts +1462 -0
  88. package/src/__tests__/conflict-intent-tokenization.test.ts +141 -0
  89. package/src/__tests__/conflict-policy.test.ts +121 -0
  90. package/src/__tests__/conflict-store.test.ts +332 -0
  91. package/src/__tests__/connection-policy.test.ts +102 -0
  92. package/src/__tests__/contacts-tools.test.ts +331 -0
  93. package/src/__tests__/context-memory-e2e.test.ts +434 -0
  94. package/src/__tests__/context-token-estimator.test.ts +135 -0
  95. package/src/__tests__/context-window-manager.test.ts +376 -0
  96. package/src/__tests__/contradiction-checker.test.ts +314 -0
  97. package/src/__tests__/conversation-store.test.ts +612 -0
  98. package/src/__tests__/credential-broker-browser-fill.test.ts +517 -0
  99. package/src/__tests__/credential-broker-server-use.test.ts +554 -0
  100. package/src/__tests__/credential-broker.test.ts +167 -0
  101. package/src/__tests__/credential-host-pattern-match.test.ts +104 -0
  102. package/src/__tests__/credential-metadata-store.test.ts +779 -0
  103. package/src/__tests__/credential-policy-validate.test.ts +121 -0
  104. package/src/__tests__/credential-resolve.test.ts +328 -0
  105. package/src/__tests__/credential-security-e2e.test.ts +352 -0
  106. package/src/__tests__/credential-security-invariants.test.ts +583 -0
  107. package/src/__tests__/credential-selection.test.ts +354 -0
  108. package/src/__tests__/credential-vault-unit.test.ts +780 -0
  109. package/src/__tests__/credential-vault.test.ts +852 -0
  110. package/src/__tests__/daemon-assistant-events.test.ts +164 -0
  111. package/src/__tests__/daemon-server-session-init.test.ts +522 -0
  112. package/src/__tests__/date-context.test.ts +373 -0
  113. package/src/__tests__/db-schedule-syntax-migration.test.ts +129 -0
  114. package/src/__tests__/delete-managed-skill-tool.test.ts +97 -0
  115. package/src/__tests__/diff.test.ts +121 -0
  116. package/src/__tests__/domain-normalize.test.ts +112 -0
  117. package/src/__tests__/domain-policy.test.ts +124 -0
  118. package/src/__tests__/doordash-client.test.ts +186 -0
  119. package/src/__tests__/doordash-session.test.ts +152 -0
  120. package/src/__tests__/dynamic-page-surface.test.ts +91 -0
  121. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +132 -0
  122. package/src/__tests__/edit-engine.test.ts +180 -0
  123. package/src/__tests__/elevenlabs-client.test.ts +271 -0
  124. package/src/__tests__/email-cli.test.ts +283 -0
  125. package/src/__tests__/encrypted-store.test.ts +332 -0
  126. package/src/__tests__/entity-extractor.test.ts +190 -0
  127. package/src/__tests__/ephemeral-permissions.test.ts +362 -0
  128. package/src/__tests__/evaluate-typescript-tool.test.ts +286 -0
  129. package/src/__tests__/event-bus.test.ts +222 -0
  130. package/src/__tests__/file-edit-tool.test.ts +122 -0
  131. package/src/__tests__/file-ops-service.test.ts +330 -0
  132. package/src/__tests__/file-read-tool.test.ts +75 -0
  133. package/src/__tests__/file-write-tool.test.ts +113 -0
  134. package/src/__tests__/filesystem-tools.test.ts +579 -0
  135. package/src/__tests__/fixtures/credential-security-fixtures.ts +181 -0
  136. package/src/__tests__/fixtures/media-reuse-fixtures.ts +126 -0
  137. package/src/__tests__/fixtures/mock-signup-server.ts +387 -0
  138. package/src/__tests__/fixtures/proxy-fixtures.ts +147 -0
  139. package/src/__tests__/followup-tools.test.ts +303 -0
  140. package/src/__tests__/forbidden-legacy-symbols.test.ts +71 -0
  141. package/src/__tests__/fuzzy-match-property.test.ts +216 -0
  142. package/src/__tests__/fuzzy-match.test.ts +138 -0
  143. package/src/__tests__/gateway-only-enforcement.test.ts +631 -0
  144. package/src/__tests__/gemini-image-service.test.ts +261 -0
  145. package/src/__tests__/gemini-provider.test.ts +651 -0
  146. package/src/__tests__/get-weather.test.ts +318 -0
  147. package/src/__tests__/gmail-integration.test.ts +73 -0
  148. package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +202 -0
  149. package/src/__tests__/handlers-cu-observation-blob.test.ts +352 -0
  150. package/src/__tests__/handlers-ipc-blob-probe.test.ts +191 -0
  151. package/src/__tests__/handlers-slack-config.test.ts +200 -0
  152. package/src/__tests__/handlers-task-submit-slash.test.ts +38 -0
  153. package/src/__tests__/handlers-telegram-config.test.ts +968 -0
  154. package/src/__tests__/handlers-twilio-config.test.ts +659 -0
  155. package/src/__tests__/handlers-twitter-config.test.ts +858 -0
  156. package/src/__tests__/headless-browser-interactions.test.ts +536 -0
  157. package/src/__tests__/headless-browser-navigate.test.ts +211 -0
  158. package/src/__tests__/headless-browser-read-tools.test.ts +261 -0
  159. package/src/__tests__/headless-browser-snapshot.test.ts +185 -0
  160. package/src/__tests__/history-repair-observability.test.ts +56 -0
  161. package/src/__tests__/history-repair.test.ts +510 -0
  162. package/src/__tests__/home-base-bootstrap.test.ts +82 -0
  163. package/src/__tests__/hooks-blocking.test.ts +128 -0
  164. package/src/__tests__/hooks-cli.test.ts +144 -0
  165. package/src/__tests__/hooks-config.test.ts +93 -0
  166. package/src/__tests__/hooks-discovery.test.ts +199 -0
  167. package/src/__tests__/hooks-integration.test.ts +189 -0
  168. package/src/__tests__/hooks-manager.test.ts +187 -0
  169. package/src/__tests__/hooks-runner.test.ts +182 -0
  170. package/src/__tests__/hooks-settings.test.ts +154 -0
  171. package/src/__tests__/hooks-templates.test.ts +137 -0
  172. package/src/__tests__/hooks-ts-runner.test.ts +125 -0
  173. package/src/__tests__/hooks-watch.test.ts +100 -0
  174. package/src/__tests__/host-file-edit-tool.test.ts +228 -0
  175. package/src/__tests__/host-file-read-tool.test.ts +123 -0
  176. package/src/__tests__/host-file-write-tool.test.ts +136 -0
  177. package/src/__tests__/host-shell-tool.test.ts +562 -0
  178. package/src/__tests__/ingress-reconcile.test.ts +581 -0
  179. package/src/__tests__/ingress-url-consistency.test.ts +214 -0
  180. package/src/__tests__/intent-routing.test.ts +259 -0
  181. package/src/__tests__/ipc-blob-store.test.ts +315 -0
  182. package/src/__tests__/ipc-contract-inventory.test.ts +54 -0
  183. package/src/__tests__/ipc-contract.test.ts +74 -0
  184. package/src/__tests__/ipc-protocol.test.ts +113 -0
  185. package/src/__tests__/ipc-roundtrip.benchmark.test.ts +237 -0
  186. package/src/__tests__/ipc-snapshot.test.ts +1769 -0
  187. package/src/__tests__/ipc-validate.test.ts +407 -0
  188. package/src/__tests__/key-migration.test.ts +206 -0
  189. package/src/__tests__/keychain.test.ts +258 -0
  190. package/src/__tests__/llm-usage-store.test.ts +221 -0
  191. package/src/__tests__/managed-skill-lifecycle.test.ts +257 -0
  192. package/src/__tests__/managed-store.test.ts +608 -0
  193. package/src/__tests__/media-generate-image.test.ts +238 -0
  194. package/src/__tests__/media-reuse-story.e2e.test.ts +676 -0
  195. package/src/__tests__/media-visibility-policy.test.ts +141 -0
  196. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +235 -0
  197. package/src/__tests__/memory-lifecycle-e2e.test.ts +481 -0
  198. package/src/__tests__/memory-query-builder.test.ts +59 -0
  199. package/src/__tests__/memory-recall-quality.test.ts +846 -0
  200. package/src/__tests__/memory-regressions.experimental.test.ts +538 -0
  201. package/src/__tests__/memory-regressions.test.ts +4435 -0
  202. package/src/__tests__/memory-retrieval-budget.test.ts +49 -0
  203. package/src/__tests__/memory-retrieval.benchmark.test.ts +430 -0
  204. package/src/__tests__/migration-cli-flows.test.ts +169 -0
  205. package/src/__tests__/migration-ordering.test.ts +249 -0
  206. package/src/__tests__/mock-signup-server.test.ts +528 -0
  207. package/src/__tests__/oauth-callback-registry.test.ts +92 -0
  208. package/src/__tests__/oauth2-gateway-transport.test.ts +285 -0
  209. package/src/__tests__/onboarding-starter-tasks.test.ts +176 -0
  210. package/src/__tests__/onboarding-template-contract.test.ts +58 -0
  211. package/src/__tests__/openai-provider.test.ts +753 -0
  212. package/src/__tests__/parallel-tool.benchmark.test.ts +294 -0
  213. package/src/__tests__/parser.test.ts +472 -0
  214. package/src/__tests__/path-classifier.test.ts +73 -0
  215. package/src/__tests__/path-policy.test.ts +435 -0
  216. package/src/__tests__/platform-move-helper.test.ts +99 -0
  217. package/src/__tests__/platform-socket-path.test.ts +52 -0
  218. package/src/__tests__/platform-workspace-migration.test.ts +1000 -0
  219. package/src/__tests__/platform.test.ts +131 -0
  220. package/src/__tests__/playbook-execution.test.ts +502 -0
  221. package/src/__tests__/playbook-tools.test.ts +340 -0
  222. package/src/__tests__/prebuilt-home-base-seed.test.ts +75 -0
  223. package/src/__tests__/pricing.test.ts +256 -0
  224. package/src/__tests__/profile-compiler.test.ts +374 -0
  225. package/src/__tests__/provider-commit-message-generator.test.ts +342 -0
  226. package/src/__tests__/provider-registry-ollama.test.ts +16 -0
  227. package/src/__tests__/provider-streaming.benchmark.test.ts +773 -0
  228. package/src/__tests__/proxy-approval-callback.test.ts +601 -0
  229. package/src/__tests__/public-ingress-urls.test.ts +256 -0
  230. package/src/__tests__/qdrant-manager.test.ts +267 -0
  231. package/src/__tests__/ratelimit.test.ts +297 -0
  232. package/src/__tests__/recurrence-engine-rruleset.test.ts +175 -0
  233. package/src/__tests__/recurrence-engine.test.ts +78 -0
  234. package/src/__tests__/recurrence-types.test.ts +79 -0
  235. package/src/__tests__/registry.test.ts +494 -0
  236. package/src/__tests__/relay-server.test.ts +688 -0
  237. package/src/__tests__/reminder-store.test.ts +223 -0
  238. package/src/__tests__/reminder.test.ts +229 -0
  239. package/src/__tests__/request-file-tool.test.ts +158 -0
  240. package/src/__tests__/run-orchestrator-assistant-events.test.ts +227 -0
  241. package/src/__tests__/run-orchestrator.test.ts +425 -0
  242. package/src/__tests__/runtime-attachment-metadata.test.ts +189 -0
  243. package/src/__tests__/runtime-events-sse-parity.test.ts +343 -0
  244. package/src/__tests__/runtime-events-sse.test.ts +162 -0
  245. package/src/__tests__/runtime-runs-http.test.ts +438 -0
  246. package/src/__tests__/runtime-runs.test.ts +260 -0
  247. package/src/__tests__/sandbox-diagnostics.test.ts +408 -0
  248. package/src/__tests__/sandbox-host-parity.test.ts +950 -0
  249. package/src/__tests__/scaffold-managed-skill-tool.test.ts +253 -0
  250. package/src/__tests__/schedule-store.test.ts +484 -0
  251. package/src/__tests__/schedule-tools.test.ts +783 -0
  252. package/src/__tests__/scheduler-recurrence.test.ts +430 -0
  253. package/src/__tests__/script-proxy-certs.test.ts +90 -0
  254. package/src/__tests__/script-proxy-connect-tunnel.test.ts +177 -0
  255. package/src/__tests__/script-proxy-decision-trace.test.ts +156 -0
  256. package/src/__tests__/script-proxy-http-forwarder.test.ts +281 -0
  257. package/src/__tests__/script-proxy-injection-runtime.test.ts +401 -0
  258. package/src/__tests__/script-proxy-mitm-handler.test.ts +407 -0
  259. package/src/__tests__/script-proxy-policy-runtime.test.ts +287 -0
  260. package/src/__tests__/script-proxy-policy.test.ts +310 -0
  261. package/src/__tests__/script-proxy-rewrite-specificity.test.ts +135 -0
  262. package/src/__tests__/script-proxy-router.test.ts +180 -0
  263. package/src/__tests__/script-proxy-session-manager.test.ts +382 -0
  264. package/src/__tests__/script-proxy-session-runtime.test.ts +113 -0
  265. package/src/__tests__/secret-allowlist.test.ts +230 -0
  266. package/src/__tests__/secret-ingress-handler.test.ts +110 -0
  267. package/src/__tests__/secret-onetime-send.test.ts +130 -0
  268. package/src/__tests__/secret-prompt-log-hygiene.test.ts +106 -0
  269. package/src/__tests__/secret-response-routing.test.ts +93 -0
  270. package/src/__tests__/secret-scanner-executor.test.ts +348 -0
  271. package/src/__tests__/secret-scanner.test.ts +900 -0
  272. package/src/__tests__/secure-keys.test.ts +323 -0
  273. package/src/__tests__/server-history-render.test.ts +431 -0
  274. package/src/__tests__/session-abort-tool-results.test.ts +240 -0
  275. package/src/__tests__/session-conflict-gate.test.ts +1136 -0
  276. package/src/__tests__/session-error.test.ts +369 -0
  277. package/src/__tests__/session-evictor.test.ts +188 -0
  278. package/src/__tests__/session-init.benchmark.test.ts +465 -0
  279. package/src/__tests__/session-load-history-repair.test.ts +222 -0
  280. package/src/__tests__/session-pre-run-repair.test.ts +213 -0
  281. package/src/__tests__/session-process-bridge.test.ts +242 -0
  282. package/src/__tests__/session-profile-injection.test.ts +444 -0
  283. package/src/__tests__/session-provider-retry-repair.test.ts +306 -0
  284. package/src/__tests__/session-queue.test.ts +1535 -0
  285. package/src/__tests__/session-runtime-assembly.test.ts +476 -0
  286. package/src/__tests__/session-runtime-workspace.test.ts +183 -0
  287. package/src/__tests__/session-skill-tools.test.ts +2431 -0
  288. package/src/__tests__/session-slash-known.test.ts +368 -0
  289. package/src/__tests__/session-slash-queue.test.ts +288 -0
  290. package/src/__tests__/session-slash-unknown.test.ts +271 -0
  291. package/src/__tests__/session-surfaces-task-progress.test.ts +104 -0
  292. package/src/__tests__/session-tool-setup-app-refresh.test.ts +473 -0
  293. package/src/__tests__/session-tool-setup-memory-scope.test.ts +140 -0
  294. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +140 -0
  295. package/src/__tests__/session-undo.test.ts +75 -0
  296. package/src/__tests__/session-workspace-cache-state.test.ts +246 -0
  297. package/src/__tests__/session-workspace-injection.test.ts +327 -0
  298. package/src/__tests__/session-workspace-tool-tracking.test.ts +240 -0
  299. package/src/__tests__/shared-filesystem-errors.test.ts +78 -0
  300. package/src/__tests__/shell-credential-ref.test.ts +187 -0
  301. package/src/__tests__/shell-identity.test.ts +256 -0
  302. package/src/__tests__/shell-parser-fuzz.test.ts +544 -0
  303. package/src/__tests__/shell-parser-property.test.ts +433 -0
  304. package/src/__tests__/shell-tool-proxy-mode.test.ts +272 -0
  305. package/src/__tests__/signup-e2e.test.ts +353 -0
  306. package/src/__tests__/size-guard.test.ts +117 -0
  307. package/src/__tests__/skill-include-graph.test.ts +303 -0
  308. package/src/__tests__/skill-load-tool.test.ts +409 -0
  309. package/src/__tests__/skill-projection.benchmark.test.ts +338 -0
  310. package/src/__tests__/skill-script-runner-host.test.ts +489 -0
  311. package/src/__tests__/skill-script-runner-sandbox.test.ts +349 -0
  312. package/src/__tests__/skill-script-runner.test.ts +159 -0
  313. package/src/__tests__/skill-tool-factory.test.ts +252 -0
  314. package/src/__tests__/skill-tool-manifest.test.ts +658 -0
  315. package/src/__tests__/skill-version-hash.test.ts +182 -0
  316. package/src/__tests__/skills.test.ts +680 -0
  317. package/src/__tests__/slash-commands-catalog.test.ts +86 -0
  318. package/src/__tests__/slash-commands-parser.test.ts +119 -0
  319. package/src/__tests__/slash-commands-resolver.test.ts +193 -0
  320. package/src/__tests__/slash-commands-rewrite.test.ts +39 -0
  321. package/src/__tests__/speaker-identification.test.ts +52 -0
  322. package/src/__tests__/starter-bundle.test.ts +136 -0
  323. package/src/__tests__/starter-task-flow.test.ts +143 -0
  324. package/src/__tests__/subagent-manager-notify.test.ts +404 -0
  325. package/src/__tests__/subagent-tools.test.ts +801 -0
  326. package/src/__tests__/subagent-types.test.ts +78 -0
  327. package/src/__tests__/swarm-orchestrator.test.ts +428 -0
  328. package/src/__tests__/swarm-plan-validator.test.ts +330 -0
  329. package/src/__tests__/swarm-recursion.test.ts +165 -0
  330. package/src/__tests__/swarm-router-planner.test.ts +208 -0
  331. package/src/__tests__/swarm-session-integration.test.ts +274 -0
  332. package/src/__tests__/swarm-tool.test.ts +145 -0
  333. package/src/__tests__/swarm-worker-backend.test.ts +129 -0
  334. package/src/__tests__/swarm-worker-runner.test.ts +272 -0
  335. package/src/__tests__/system-prompt.test.ts +439 -0
  336. package/src/__tests__/task-compiler.test.ts +284 -0
  337. package/src/__tests__/task-management-tools.test.ts +936 -0
  338. package/src/__tests__/task-runner.test.ts +216 -0
  339. package/src/__tests__/task-scheduler.test.ts +217 -0
  340. package/src/__tests__/task-tools.test.ts +595 -0
  341. package/src/__tests__/terminal-sandbox-docker.test.ts +1064 -0
  342. package/src/__tests__/terminal-sandbox.integration.test.ts +178 -0
  343. package/src/__tests__/terminal-sandbox.test.ts +202 -0
  344. package/src/__tests__/terminal-tools.test.ts +840 -0
  345. package/src/__tests__/test-support/browser-skill-harness.ts +90 -0
  346. package/src/__tests__/test-support/computer-use-skill-harness.ts +45 -0
  347. package/src/__tests__/tool-audit-listener.test.ts +113 -0
  348. package/src/__tests__/tool-domain-event-publisher.test.ts +253 -0
  349. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +500 -0
  350. package/src/__tests__/tool-executor-lifecycle-events.test.ts +516 -0
  351. package/src/__tests__/tool-executor-redaction.test.ts +289 -0
  352. package/src/__tests__/tool-executor-shell-integration.test.ts +301 -0
  353. package/src/__tests__/tool-executor.test.ts +1989 -0
  354. package/src/__tests__/tool-metrics-listener.test.ts +225 -0
  355. package/src/__tests__/tool-notification-listener.test.ts +49 -0
  356. package/src/__tests__/tool-permission-simulate-handler.test.ts +336 -0
  357. package/src/__tests__/tool-policy.test.ts +54 -0
  358. package/src/__tests__/tool-profiling-listener.test.ts +268 -0
  359. package/src/__tests__/tool-result-truncation.test.ts +217 -0
  360. package/src/__tests__/tool-trace-listener.test.ts +226 -0
  361. package/src/__tests__/top-level-renderer.test.ts +121 -0
  362. package/src/__tests__/top-level-scanner.test.ts +141 -0
  363. package/src/__tests__/trace-emitter.test.ts +173 -0
  364. package/src/__tests__/trust-store.test.ts +1605 -0
  365. package/src/__tests__/turn-commit.test.ts +554 -0
  366. package/src/__tests__/twilio-provider.test.ts +329 -0
  367. package/src/__tests__/twilio-routes-elevenlabs.test.ts +375 -0
  368. package/src/__tests__/twilio-routes-twiml.test.ts +127 -0
  369. package/src/__tests__/twilio-routes.test.ts +577 -0
  370. package/src/__tests__/twitter-auth-handler.test.ts +667 -0
  371. package/src/__tests__/twitter-cli-error-shaping.test.ts +208 -0
  372. package/src/__tests__/twitter-cli-routing.test.ts +252 -0
  373. package/src/__tests__/twitter-oauth-client.test.ts +209 -0
  374. package/src/__tests__/url-safety.test.ts +418 -0
  375. package/src/__tests__/view-image-tool.test.ts +217 -0
  376. package/src/__tests__/weather-skill-regression.test.ts +225 -0
  377. package/src/__tests__/web-fetch.test.ts +869 -0
  378. package/src/__tests__/web-search.test.ts +584 -0
  379. package/src/__tests__/workspace-git-service.test.ts +1153 -0
  380. package/src/__tests__/workspace-heartbeat-service.test.ts +486 -0
  381. package/src/__tests__/workspace-lifecycle.test.ts +292 -0
  382. package/src/__tests__/workspace-policy.test.ts +213 -0
  383. package/src/agent/attachments.ts +35 -0
  384. package/src/agent/loop.ts +500 -0
  385. package/src/agent/message-types.ts +17 -0
  386. package/src/agent-heartbeat/agent-heartbeat-service.ts +155 -0
  387. package/src/autonomy/autonomy-resolver.ts +60 -0
  388. package/src/autonomy/autonomy-store.ts +122 -0
  389. package/src/autonomy/disposition-mapper.ts +31 -0
  390. package/src/autonomy/index.ts +11 -0
  391. package/src/autonomy/types.ts +39 -0
  392. package/src/bundler/app-bundler.ts +295 -0
  393. package/src/bundler/bundle-scanner.ts +535 -0
  394. package/src/bundler/bundle-signer.ts +124 -0
  395. package/src/bundler/manifest.ts +21 -0
  396. package/src/bundler/signature-verifier.ts +184 -0
  397. package/src/calls/call-bridge.ts +168 -0
  398. package/src/calls/call-constants.ts +48 -0
  399. package/src/calls/call-domain.ts +430 -0
  400. package/src/calls/call-orchestrator.ts +498 -0
  401. package/src/calls/call-recovery.ts +207 -0
  402. package/src/calls/call-state-machine.ts +68 -0
  403. package/src/calls/call-state.ts +87 -0
  404. package/src/calls/call-store.ts +422 -0
  405. package/src/calls/elevenlabs-client.ts +97 -0
  406. package/src/calls/elevenlabs-config.ts +31 -0
  407. package/src/calls/relay-server.ts +390 -0
  408. package/src/calls/speaker-identification.ts +213 -0
  409. package/src/calls/twilio-config.ts +45 -0
  410. package/src/calls/twilio-provider.ts +263 -0
  411. package/src/calls/twilio-rest.ts +156 -0
  412. package/src/calls/twilio-routes.ts +311 -0
  413. package/src/calls/types.ts +39 -0
  414. package/src/calls/voice-provider.ts +14 -0
  415. package/src/calls/voice-quality.ts +114 -0
  416. package/src/cli/autonomy.ts +188 -0
  417. package/src/cli/config-commands.ts +334 -0
  418. package/src/cli/contacts.ts +149 -0
  419. package/src/cli/core-commands.ts +784 -0
  420. package/src/cli/doordash.ts +1055 -0
  421. package/src/cli/email-guardrails.ts +200 -0
  422. package/src/cli/email.ts +405 -0
  423. package/src/cli/ipc-client.ts +82 -0
  424. package/src/cli/main-screen.tsx +53 -0
  425. package/src/cli/map.ts +270 -0
  426. package/src/cli/twitter.ts +754 -0
  427. package/src/cli.ts +918 -0
  428. package/src/commands/__tests__/cc-command-registry.test.ts +319 -0
  429. package/src/commands/cc-command-registry.ts +209 -0
  430. package/src/config/bundled-skills/.gitkeep +0 -0
  431. package/src/config/bundled-skills/agentmail/SKILL.md +128 -0
  432. package/src/config/bundled-skills/agentmail/icon.svg +21 -0
  433. package/src/config/bundled-skills/app-builder/SKILL.md +1404 -0
  434. package/src/config/bundled-skills/app-builder/TOOLS.json +279 -0
  435. package/src/config/bundled-skills/app-builder/icon.svg +9 -0
  436. package/src/config/bundled-skills/app-builder/tools/app-create.ts +15 -0
  437. package/src/config/bundled-skills/app-builder/tools/app-delete.ts +10 -0
  438. package/src/config/bundled-skills/app-builder/tools/app-file-edit.ts +11 -0
  439. package/src/config/bundled-skills/app-builder/tools/app-file-list.ts +10 -0
  440. package/src/config/bundled-skills/app-builder/tools/app-file-read.ts +18 -0
  441. package/src/config/bundled-skills/app-builder/tools/app-file-write.ts +11 -0
  442. package/src/config/bundled-skills/app-builder/tools/app-list.ts +10 -0
  443. package/src/config/bundled-skills/app-builder/tools/app-query.ts +10 -0
  444. package/src/config/bundled-skills/app-builder/tools/app-update.ts +20 -0
  445. package/src/config/bundled-skills/browser/SKILL.md +28 -0
  446. package/src/config/bundled-skills/browser/TOOLS.json +234 -0
  447. package/src/config/bundled-skills/browser/tools/browser-click.ts +9 -0
  448. package/src/config/bundled-skills/browser/tools/browser-close.ts +9 -0
  449. package/src/config/bundled-skills/browser/tools/browser-extract.ts +9 -0
  450. package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +9 -0
  451. package/src/config/bundled-skills/browser/tools/browser-navigate.ts +9 -0
  452. package/src/config/bundled-skills/browser/tools/browser-press-key.ts +9 -0
  453. package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +9 -0
  454. package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +9 -0
  455. package/src/config/bundled-skills/browser/tools/browser-type.ts +9 -0
  456. package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +9 -0
  457. package/src/config/bundled-skills/claude-code/SKILL.md +50 -0
  458. package/src/config/bundled-skills/claude-code/TOOLS.json +40 -0
  459. package/src/config/bundled-skills/claude-code/tools/claude-code.ts +9 -0
  460. package/src/config/bundled-skills/computer-use/SKILL.md +17 -0
  461. package/src/config/bundled-skills/computer-use/TOOLS.json +326 -0
  462. package/src/config/bundled-skills/computer-use/tools/computer-use-click.ts +9 -0
  463. package/src/config/bundled-skills/computer-use/tools/computer-use-done.ts +9 -0
  464. package/src/config/bundled-skills/computer-use/tools/computer-use-double-click.ts +9 -0
  465. package/src/config/bundled-skills/computer-use/tools/computer-use-drag.ts +9 -0
  466. package/src/config/bundled-skills/computer-use/tools/computer-use-key.ts +9 -0
  467. package/src/config/bundled-skills/computer-use/tools/computer-use-open-app.ts +9 -0
  468. package/src/config/bundled-skills/computer-use/tools/computer-use-request-control.ts +9 -0
  469. package/src/config/bundled-skills/computer-use/tools/computer-use-respond.ts +9 -0
  470. package/src/config/bundled-skills/computer-use/tools/computer-use-right-click.ts +9 -0
  471. package/src/config/bundled-skills/computer-use/tools/computer-use-run-applescript.ts +9 -0
  472. package/src/config/bundled-skills/computer-use/tools/computer-use-scroll.ts +9 -0
  473. package/src/config/bundled-skills/computer-use/tools/computer-use-type-text.ts +9 -0
  474. package/src/config/bundled-skills/computer-use/tools/computer-use-wait.ts +9 -0
  475. package/src/config/bundled-skills/contacts/SKILL.md +39 -0
  476. package/src/config/bundled-skills/contacts/TOOLS.json +122 -0
  477. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +57 -0
  478. package/src/config/bundled-skills/contacts/tools/contact-search.ts +60 -0
  479. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +66 -0
  480. package/src/config/bundled-skills/document/SKILL.md +26 -0
  481. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  482. package/src/config/bundled-skills/document/tools/document-create.ts +9 -0
  483. package/src/config/bundled-skills/document/tools/document-update.ts +9 -0
  484. package/src/config/bundled-skills/doordash/SKILL.md +163 -0
  485. package/src/config/bundled-skills/followups/SKILL.md +32 -0
  486. package/src/config/bundled-skills/followups/TOOLS.json +100 -0
  487. package/src/config/bundled-skills/followups/icon.svg +24 -0
  488. package/src/config/bundled-skills/followups/tools/followup-create.ts +9 -0
  489. package/src/config/bundled-skills/followups/tools/followup-list.ts +9 -0
  490. package/src/config/bundled-skills/followups/tools/followup-resolve.ts +9 -0
  491. package/src/config/bundled-skills/google-calendar/SKILL.md +51 -0
  492. package/src/config/bundled-skills/google-calendar/TOOLS.json +108 -0
  493. package/src/config/bundled-skills/google-calendar/calendar-client.ts +165 -0
  494. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +21 -0
  495. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +42 -0
  496. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +13 -0
  497. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +30 -0
  498. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +41 -0
  499. package/src/config/bundled-skills/google-calendar/tools/shared.ts +18 -0
  500. package/src/config/bundled-skills/google-calendar/types.ts +97 -0
  501. package/src/config/bundled-skills/image-studio/SKILL.md +32 -0
  502. package/src/config/bundled-skills/image-studio/TOOLS.json +42 -0
  503. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +115 -0
  504. package/src/config/bundled-skills/macos-automation/SKILL.md +66 -0
  505. package/src/config/bundled-skills/messaging/SKILL.md +153 -0
  506. package/src/config/bundled-skills/messaging/TOOLS.json +357 -0
  507. package/src/config/bundled-skills/messaging/tools/gmail-archive.ts +23 -0
  508. package/src/config/bundled-skills/messaging/tools/gmail-batch-archive.ts +23 -0
  509. package/src/config/bundled-skills/messaging/tools/gmail-batch-label.ts +25 -0
  510. package/src/config/bundled-skills/messaging/tools/gmail-draft.ts +26 -0
  511. package/src/config/bundled-skills/messaging/tools/gmail-label.ts +25 -0
  512. package/src/config/bundled-skills/messaging/tools/gmail-trash.ts +23 -0
  513. package/src/config/bundled-skills/messaging/tools/gmail-unsubscribe.ts +84 -0
  514. package/src/config/bundled-skills/messaging/tools/messaging-analyze-activity.ts +18 -0
  515. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +125 -0
  516. package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +16 -0
  517. package/src/config/bundled-skills/messaging/tools/messaging-draft.ts +49 -0
  518. package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +21 -0
  519. package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +25 -0
  520. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +28 -0
  521. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +32 -0
  522. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +22 -0
  523. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +31 -0
  524. package/src/config/bundled-skills/messaging/tools/shared.ts +76 -0
  525. package/src/config/bundled-skills/messaging/tools/slack-add-reaction.ts +25 -0
  526. package/src/config/bundled-skills/messaging/tools/slack-leave-channel.ts +23 -0
  527. package/src/config/bundled-skills/phone-calls/SKILL.md +533 -0
  528. package/src/config/bundled-skills/playbooks/SKILL.md +31 -0
  529. package/src/config/bundled-skills/playbooks/TOOLS.json +126 -0
  530. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +98 -0
  531. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +54 -0
  532. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +76 -0
  533. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +113 -0
  534. package/src/config/bundled-skills/public-ingress/SKILL.md +200 -0
  535. package/src/config/bundled-skills/reminder/SKILL.md +20 -0
  536. package/src/config/bundled-skills/reminder/TOOLS.json +67 -0
  537. package/src/config/bundled-skills/reminder/tools/reminder-cancel.ts +9 -0
  538. package/src/config/bundled-skills/reminder/tools/reminder-create.ts +9 -0
  539. package/src/config/bundled-skills/reminder/tools/reminder-list.ts +9 -0
  540. package/src/config/bundled-skills/schedule/SKILL.md +74 -0
  541. package/src/config/bundled-skills/schedule/TOOLS.json +135 -0
  542. package/src/config/bundled-skills/schedule/tools/schedule-create.ts +9 -0
  543. package/src/config/bundled-skills/schedule/tools/schedule-delete.ts +9 -0
  544. package/src/config/bundled-skills/schedule/tools/schedule-list.ts +9 -0
  545. package/src/config/bundled-skills/schedule/tools/schedule-update.ts +9 -0
  546. package/src/config/bundled-skills/self-upgrade/SKILL.md +68 -0
  547. package/src/config/bundled-skills/start-the-day/SKILL.md +70 -0
  548. package/src/config/bundled-skills/start-the-day/icon.svg +13 -0
  549. package/src/config/bundled-skills/subagent/SKILL.md +25 -0
  550. package/src/config/bundled-skills/subagent/TOOLS.json +107 -0
  551. package/src/config/bundled-skills/subagent/tools/subagent-abort.ts +9 -0
  552. package/src/config/bundled-skills/subagent/tools/subagent-message.ts +9 -0
  553. package/src/config/bundled-skills/subagent/tools/subagent-read.ts +9 -0
  554. package/src/config/bundled-skills/subagent/tools/subagent-spawn.ts +9 -0
  555. package/src/config/bundled-skills/subagent/tools/subagent-status.ts +9 -0
  556. package/src/config/bundled-skills/tasks/SKILL.md +28 -0
  557. package/src/config/bundled-skills/tasks/TOOLS.json +281 -0
  558. package/src/config/bundled-skills/tasks/tools/task-delete.ts +9 -0
  559. package/src/config/bundled-skills/tasks/tools/task-list-add.ts +9 -0
  560. package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +9 -0
  561. package/src/config/bundled-skills/tasks/tools/task-list-show.ts +9 -0
  562. package/src/config/bundled-skills/tasks/tools/task-list-update.ts +9 -0
  563. package/src/config/bundled-skills/tasks/tools/task-list.ts +9 -0
  564. package/src/config/bundled-skills/tasks/tools/task-queue-run.ts +9 -0
  565. package/src/config/bundled-skills/tasks/tools/task-run.ts +9 -0
  566. package/src/config/bundled-skills/tasks/tools/task-save.ts +9 -0
  567. package/src/config/bundled-skills/transcribe/SKILL.md +25 -0
  568. package/src/config/bundled-skills/transcribe/TOOLS.json +32 -0
  569. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +370 -0
  570. package/src/config/bundled-skills/twitter/SKILL.md +220 -0
  571. package/src/config/bundled-skills/watcher/SKILL.md +27 -0
  572. package/src/config/bundled-skills/watcher/TOOLS.json +147 -0
  573. package/src/config/bundled-skills/watcher/tools/watcher-create.ts +9 -0
  574. package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +9 -0
  575. package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +9 -0
  576. package/src/config/bundled-skills/watcher/tools/watcher-list.ts +9 -0
  577. package/src/config/bundled-skills/watcher/tools/watcher-update.ts +9 -0
  578. package/src/config/bundled-skills/weather/SKILL.md +37 -0
  579. package/src/config/bundled-skills/weather/TOOLS.json +32 -0
  580. package/src/config/bundled-skills/weather/icon.svg +24 -0
  581. package/src/config/bundled-skills/weather/tools/get-weather.ts +9 -0
  582. package/src/config/computer-use-prompt.ts +97 -0
  583. package/src/config/defaults.ts +263 -0
  584. package/src/config/loader.ts +339 -0
  585. package/src/config/schema.ts +1436 -0
  586. package/src/config/skill-state.ts +95 -0
  587. package/src/config/skills.ts +972 -0
  588. package/src/config/system-prompt.ts +675 -0
  589. package/src/config/templates/BOOTSTRAP.md +70 -0
  590. package/src/config/templates/IDENTITY.md +25 -0
  591. package/src/config/templates/LOOKS.md +25 -0
  592. package/src/config/templates/SOUL.md +37 -0
  593. package/src/config/templates/USER.md +19 -0
  594. package/src/config/types.ts +42 -0
  595. package/src/config/vellum-skills/chatgpt-import/SKILL.md +24 -0
  596. package/src/config/vellum-skills/chatgpt-import/TOOLS.json +23 -0
  597. package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +284 -0
  598. package/src/config/vellum-skills/deploy-fullstack-vercel/SKILL.md +179 -0
  599. package/src/config/vellum-skills/document-writer/SKILL.md +195 -0
  600. package/src/config/vellum-skills/google-oauth-setup/SKILL.md +199 -0
  601. package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +153 -0
  602. package/src/config/vellum-skills/telegram-setup/SKILL.md +143 -0
  603. package/src/config/vellum-skills/twilio-setup/SKILL.md +213 -0
  604. package/src/contacts/contact-store.ts +410 -0
  605. package/src/contacts/index.ts +11 -0
  606. package/src/contacts/types.ts +28 -0
  607. package/src/context/token-estimator.ts +108 -0
  608. package/src/context/tool-result-truncation.ts +128 -0
  609. package/src/context/window-manager.ts +531 -0
  610. package/src/daemon/assistant-attachments.ts +691 -0
  611. package/src/daemon/classifier.ts +110 -0
  612. package/src/daemon/computer-use-session.ts +903 -0
  613. package/src/daemon/connection-policy.ts +41 -0
  614. package/src/daemon/date-context.ts +136 -0
  615. package/src/daemon/handlers/apps.ts +530 -0
  616. package/src/daemon/handlers/browser.ts +54 -0
  617. package/src/daemon/handlers/computer-use.ts +187 -0
  618. package/src/daemon/handlers/config.ts +1517 -0
  619. package/src/daemon/handlers/diagnostics.ts +338 -0
  620. package/src/daemon/handlers/documents.ts +173 -0
  621. package/src/daemon/handlers/home-base.ts +78 -0
  622. package/src/daemon/handlers/identity.ts +127 -0
  623. package/src/daemon/handlers/index.ts +129 -0
  624. package/src/daemon/handlers/misc.ts +331 -0
  625. package/src/daemon/handlers/open-bundle-handler.ts +80 -0
  626. package/src/daemon/handlers/publish.ts +187 -0
  627. package/src/daemon/handlers/sessions.ts +555 -0
  628. package/src/daemon/handlers/shared.ts +570 -0
  629. package/src/daemon/handlers/signing.ts +37 -0
  630. package/src/daemon/handlers/skills.ts +486 -0
  631. package/src/daemon/handlers/subagents.ts +210 -0
  632. package/src/daemon/handlers/twitter-auth.ts +198 -0
  633. package/src/daemon/handlers/work-items.ts +632 -0
  634. package/src/daemon/handlers/workspace-files.ts +75 -0
  635. package/src/daemon/handlers.ts +17 -0
  636. package/src/daemon/history-repair.ts +214 -0
  637. package/src/daemon/ipc-blob-store.ts +231 -0
  638. package/src/daemon/ipc-contract-inventory.json +495 -0
  639. package/src/daemon/ipc-contract-inventory.ts +126 -0
  640. package/src/daemon/ipc-contract.ts +2551 -0
  641. package/src/daemon/ipc-protocol.ts +75 -0
  642. package/src/daemon/ipc-validate.ts +188 -0
  643. package/src/daemon/lifecycle.ts +582 -0
  644. package/src/daemon/main.ts +21 -0
  645. package/src/daemon/media-visibility-policy.ts +57 -0
  646. package/src/daemon/ride-shotgun-handler.ts +309 -0
  647. package/src/daemon/server.ts +1215 -0
  648. package/src/daemon/session-agent-loop.ts +922 -0
  649. package/src/daemon/session-attachments.ts +196 -0
  650. package/src/daemon/session-conflict-gate.ts +184 -0
  651. package/src/daemon/session-dynamic-profile.ts +63 -0
  652. package/src/daemon/session-error.ts +290 -0
  653. package/src/daemon/session-evictor.ts +196 -0
  654. package/src/daemon/session-history.ts +437 -0
  655. package/src/daemon/session-lifecycle.ts +147 -0
  656. package/src/daemon/session-media-retry.ts +147 -0
  657. package/src/daemon/session-memory.ts +212 -0
  658. package/src/daemon/session-messaging.ts +145 -0
  659. package/src/daemon/session-notifiers.ts +193 -0
  660. package/src/daemon/session-process.ts +323 -0
  661. package/src/daemon/session-queue-manager.ts +82 -0
  662. package/src/daemon/session-runtime-assembly.ts +447 -0
  663. package/src/daemon/session-skill-tools.ts +356 -0
  664. package/src/daemon/session-slash.ts +305 -0
  665. package/src/daemon/session-surfaces.ts +702 -0
  666. package/src/daemon/session-tool-setup.ts +523 -0
  667. package/src/daemon/session-usage.ts +72 -0
  668. package/src/daemon/session-workspace.ts +19 -0
  669. package/src/daemon/session.ts +400 -0
  670. package/src/daemon/tls-certs.ts +189 -0
  671. package/src/daemon/trace-emitter.ts +82 -0
  672. package/src/daemon/video-thumbnail.ts +62 -0
  673. package/src/daemon/watch-handler.ts +274 -0
  674. package/src/doordash/client.ts +999 -0
  675. package/src/doordash/queries.ts +1311 -0
  676. package/src/doordash/query-extractor.ts +93 -0
  677. package/src/doordash/session.ts +82 -0
  678. package/src/email/provider.ts +117 -0
  679. package/src/email/providers/agentmail.ts +317 -0
  680. package/src/email/providers/index.ts +58 -0
  681. package/src/email/service.ts +303 -0
  682. package/src/email/types.ts +126 -0
  683. package/src/events/bus.ts +157 -0
  684. package/src/events/domain-events.ts +83 -0
  685. package/src/events/index.ts +18 -0
  686. package/src/events/tool-audit-listener.ts +80 -0
  687. package/src/events/tool-domain-event-publisher.ts +111 -0
  688. package/src/events/tool-metrics-listener.ts +159 -0
  689. package/src/events/tool-notification-listener.ts +17 -0
  690. package/src/events/tool-profiling-listener.ts +158 -0
  691. package/src/events/tool-trace-listener.ts +75 -0
  692. package/src/export/formatter.ts +98 -0
  693. package/src/followups/followup-store.ts +168 -0
  694. package/src/followups/index.ts +10 -0
  695. package/src/followups/types.ts +29 -0
  696. package/src/gallery/default-gallery.ts +795 -0
  697. package/src/gallery/gallery-manifest.ts +24 -0
  698. package/src/home-base/app-link-store.ts +82 -0
  699. package/src/home-base/bootstrap.ts +68 -0
  700. package/src/home-base/prebuilt/index.html +662 -0
  701. package/src/home-base/prebuilt/seed-metadata.json +21 -0
  702. package/src/home-base/prebuilt/seed.ts +112 -0
  703. package/src/home-base/prebuilt-home-base-updater.ts +30 -0
  704. package/src/hooks/cli.ts +163 -0
  705. package/src/hooks/config.ts +88 -0
  706. package/src/hooks/discovery.ts +110 -0
  707. package/src/hooks/manager.ts +124 -0
  708. package/src/hooks/runner.ts +123 -0
  709. package/src/hooks/templates.ts +52 -0
  710. package/src/hooks/types.ts +72 -0
  711. package/src/inbound/public-ingress-urls.ts +123 -0
  712. package/src/index.ts +81 -0
  713. package/src/instrument.ts +60 -0
  714. package/src/logfire.ts +99 -0
  715. package/src/media/gemini-image-service.ts +136 -0
  716. package/src/memory/account-store.ts +108 -0
  717. package/src/memory/admin.ts +211 -0
  718. package/src/memory/app-git-service.ts +295 -0
  719. package/src/memory/app-store.ts +577 -0
  720. package/src/memory/attachments-store.ts +397 -0
  721. package/src/memory/channel-delivery-store.ts +353 -0
  722. package/src/memory/channel-guardian-store.ts +669 -0
  723. package/src/memory/checkpoints.ts +52 -0
  724. package/src/memory/clarification-resolver.ts +298 -0
  725. package/src/memory/conflict-intent.ts +157 -0
  726. package/src/memory/conflict-policy.ts +73 -0
  727. package/src/memory/conflict-store.ts +350 -0
  728. package/src/memory/contradiction-checker.ts +358 -0
  729. package/src/memory/conversation-key-store.ts +122 -0
  730. package/src/memory/conversation-store.ts +470 -0
  731. package/src/memory/db.ts +1991 -0
  732. package/src/memory/embedding-backend.ts +229 -0
  733. package/src/memory/embedding-gemini.ts +52 -0
  734. package/src/memory/embedding-local.ts +65 -0
  735. package/src/memory/embedding-ollama.ts +55 -0
  736. package/src/memory/embedding-openai.ts +25 -0
  737. package/src/memory/entity-extractor.ts +474 -0
  738. package/src/memory/external-conversation-store.ts +234 -0
  739. package/src/memory/fingerprint.ts +20 -0
  740. package/src/memory/indexer.ts +156 -0
  741. package/src/memory/items-extractor.ts +461 -0
  742. package/src/memory/job-handlers/backfill.ts +139 -0
  743. package/src/memory/job-handlers/cleanup.ts +58 -0
  744. package/src/memory/job-handlers/conflict.ts +141 -0
  745. package/src/memory/job-handlers/embedding.ts +61 -0
  746. package/src/memory/job-handlers/extraction.ts +123 -0
  747. package/src/memory/job-handlers/index-maintenance.ts +54 -0
  748. package/src/memory/job-handlers/summarization.ts +286 -0
  749. package/src/memory/job-utils.ts +170 -0
  750. package/src/memory/jobs-store.ts +401 -0
  751. package/src/memory/jobs-worker.ts +313 -0
  752. package/src/memory/llm-request-log-store.ts +45 -0
  753. package/src/memory/llm-usage-store.ts +60 -0
  754. package/src/memory/message-content.ts +54 -0
  755. package/src/memory/profile-compiler.ts +160 -0
  756. package/src/memory/published-pages-store.ts +137 -0
  757. package/src/memory/qdrant-client.ts +366 -0
  758. package/src/memory/qdrant-manager.ts +242 -0
  759. package/src/memory/query-builder.ts +45 -0
  760. package/src/memory/retrieval-budget.ts +30 -0
  761. package/src/memory/retriever.ts +653 -0
  762. package/src/memory/runs-store.ts +305 -0
  763. package/src/memory/schema.ts +677 -0
  764. package/src/memory/search/entity.ts +298 -0
  765. package/src/memory/search/formatting.ts +207 -0
  766. package/src/memory/search/lexical.ts +227 -0
  767. package/src/memory/search/ranking.ts +401 -0
  768. package/src/memory/search/semantic.ts +121 -0
  769. package/src/memory/search/types.ts +137 -0
  770. package/src/memory/segmenter.ts +68 -0
  771. package/src/memory/shared-app-links-store.ts +138 -0
  772. package/src/memory/tool-usage-store.ts +62 -0
  773. package/src/messaging/activity-analyzer.ts +76 -0
  774. package/src/messaging/draft-store.ts +88 -0
  775. package/src/messaging/index.ts +3 -0
  776. package/src/messaging/provider-types.ts +80 -0
  777. package/src/messaging/provider.ts +52 -0
  778. package/src/messaging/providers/gmail/adapter.ts +193 -0
  779. package/src/messaging/providers/gmail/client.ts +204 -0
  780. package/src/messaging/providers/gmail/types.ts +90 -0
  781. package/src/messaging/providers/slack/adapter.ts +202 -0
  782. package/src/messaging/providers/slack/client.ts +198 -0
  783. package/src/messaging/providers/slack/types.ts +119 -0
  784. package/src/messaging/providers/telegram-bot/adapter.ts +162 -0
  785. package/src/messaging/providers/telegram-bot/client.ts +104 -0
  786. package/src/messaging/providers/telegram-bot/types.ts +15 -0
  787. package/src/messaging/registry.ts +35 -0
  788. package/src/messaging/style-analyzer.ts +159 -0
  789. package/src/messaging/thread-summarizer.ts +306 -0
  790. package/src/messaging/triage-engine.ts +323 -0
  791. package/src/messaging/types.ts +55 -0
  792. package/src/permissions/checker.ts +640 -0
  793. package/src/permissions/defaults.ts +254 -0
  794. package/src/permissions/prompter.ts +98 -0
  795. package/src/permissions/secret-prompter.ts +114 -0
  796. package/src/permissions/shell-identity.ts +227 -0
  797. package/src/permissions/trust-store.ts +607 -0
  798. package/src/permissions/types.ts +43 -0
  799. package/src/permissions/workspace-policy.ts +114 -0
  800. package/src/playbooks/index.ts +2 -0
  801. package/src/playbooks/playbook-compiler.ts +90 -0
  802. package/src/playbooks/types.ts +55 -0
  803. package/src/providers/anthropic/client.ts +751 -0
  804. package/src/providers/failover.ts +129 -0
  805. package/src/providers/fireworks/client.ts +20 -0
  806. package/src/providers/gemini/client.ts +285 -0
  807. package/src/providers/ollama/client.ts +30 -0
  808. package/src/providers/openai/client.ts +337 -0
  809. package/src/providers/openrouter/client.ts +20 -0
  810. package/src/providers/ratelimit.ts +93 -0
  811. package/src/providers/registry.ts +146 -0
  812. package/src/providers/retry.ts +81 -0
  813. package/src/providers/stream-timeout.ts +38 -0
  814. package/src/providers/types.ts +109 -0
  815. package/src/runtime/assistant-event-hub.ts +157 -0
  816. package/src/runtime/assistant-event.ts +82 -0
  817. package/src/runtime/channel-approval-parser.ts +60 -0
  818. package/src/runtime/channel-approval-types.ts +73 -0
  819. package/src/runtime/channel-approvals.ts +206 -0
  820. package/src/runtime/channel-guardian-service.ts +212 -0
  821. package/src/runtime/gateway-client.ts +58 -0
  822. package/src/runtime/http-server.ts +1076 -0
  823. package/src/runtime/http-types.ts +66 -0
  824. package/src/runtime/routes/app-routes.ts +174 -0
  825. package/src/runtime/routes/attachment-routes.ts +133 -0
  826. package/src/runtime/routes/call-routes.ts +190 -0
  827. package/src/runtime/routes/channel-routes.ts +1404 -0
  828. package/src/runtime/routes/conversation-routes.ts +352 -0
  829. package/src/runtime/routes/events-routes.ts +148 -0
  830. package/src/runtime/routes/run-routes.ts +257 -0
  831. package/src/runtime/routes/secret-routes.ts +76 -0
  832. package/src/runtime/run-orchestrator.ts +330 -0
  833. package/src/schedule/recurrence-engine.ts +162 -0
  834. package/src/schedule/recurrence-types.ts +67 -0
  835. package/src/schedule/schedule-store.ts +506 -0
  836. package/src/schedule/scheduler.ts +171 -0
  837. package/src/security/encrypted-store.ts +238 -0
  838. package/src/security/keychain.ts +252 -0
  839. package/src/security/oauth-callback-registry.ts +66 -0
  840. package/src/security/oauth2.ts +274 -0
  841. package/src/security/redaction.ts +89 -0
  842. package/src/security/secret-allowlist.ts +164 -0
  843. package/src/security/secret-ingress.ts +57 -0
  844. package/src/security/secret-scanner.ts +550 -0
  845. package/src/security/secure-keys.ts +180 -0
  846. package/src/security/token-manager.ts +141 -0
  847. package/src/services/published-app-updater.ts +69 -0
  848. package/src/services/vercel-deploy.ts +73 -0
  849. package/src/skills/active-skill-tools.ts +81 -0
  850. package/src/skills/clawhub.ts +414 -0
  851. package/src/skills/include-graph.ts +146 -0
  852. package/src/skills/managed-store.ts +233 -0
  853. package/src/skills/path-classifier.ts +128 -0
  854. package/src/skills/slash-commands.ts +174 -0
  855. package/src/skills/tool-manifest.ts +165 -0
  856. package/src/skills/version-hash.ts +110 -0
  857. package/src/slack/slack-webhook.ts +61 -0
  858. package/src/subagent/index.ts +19 -0
  859. package/src/subagent/manager.ts +511 -0
  860. package/src/subagent/types.ts +69 -0
  861. package/src/swarm/backend-claude-code.ts +145 -0
  862. package/src/swarm/index.ts +44 -0
  863. package/src/swarm/limits.ts +37 -0
  864. package/src/swarm/orchestrator.ts +279 -0
  865. package/src/swarm/plan-validator.ts +151 -0
  866. package/src/swarm/router-planner.ts +100 -0
  867. package/src/swarm/router-prompts.ts +36 -0
  868. package/src/swarm/synthesizer.ts +62 -0
  869. package/src/swarm/types.ts +62 -0
  870. package/src/swarm/worker-backend.ts +121 -0
  871. package/src/swarm/worker-prompts.ts +79 -0
  872. package/src/swarm/worker-runner.ts +164 -0
  873. package/src/tasks/SPEC.md +139 -0
  874. package/src/tasks/candidate-store.ts +86 -0
  875. package/src/tasks/ephemeral-permissions.ts +48 -0
  876. package/src/tasks/task-compiler.ts +199 -0
  877. package/src/tasks/task-runner.ts +90 -0
  878. package/src/tasks/task-scheduler.ts +21 -0
  879. package/src/tasks/task-store.ts +127 -0
  880. package/src/tasks/tool-sanitizer.ts +36 -0
  881. package/src/tools/apps/definitions.ts +59 -0
  882. package/src/tools/apps/executors.ts +313 -0
  883. package/src/tools/apps/open-proxy.ts +43 -0
  884. package/src/tools/apps/registry.ts +16 -0
  885. package/src/tools/assets/materialize.ts +218 -0
  886. package/src/tools/assets/search.ts +361 -0
  887. package/src/tools/browser/__tests__/auth-cache.test.ts +219 -0
  888. package/src/tools/browser/__tests__/auth-detector.test.ts +362 -0
  889. package/src/tools/browser/__tests__/jit-auth.test.ts +189 -0
  890. package/src/tools/browser/api-map.ts +293 -0
  891. package/src/tools/browser/auth-cache.ts +149 -0
  892. package/src/tools/browser/auth-detector.ts +347 -0
  893. package/src/tools/browser/auto-navigate.ts +270 -0
  894. package/src/tools/browser/browser-execution.ts +980 -0
  895. package/src/tools/browser/browser-handoff.ts +79 -0
  896. package/src/tools/browser/browser-manager.ts +715 -0
  897. package/src/tools/browser/browser-screencast.ts +217 -0
  898. package/src/tools/browser/headless-browser.ts +450 -0
  899. package/src/tools/browser/jit-auth.ts +51 -0
  900. package/src/tools/browser/network-recorder.ts +349 -0
  901. package/src/tools/browser/network-recording-types.ts +49 -0
  902. package/src/tools/browser/recording-store.ts +49 -0
  903. package/src/tools/browser/runtime-check.ts +43 -0
  904. package/src/tools/browser/x-auto-navigate.ts +207 -0
  905. package/src/tools/calls/call-end.ts +67 -0
  906. package/src/tools/calls/call-start.ts +81 -0
  907. package/src/tools/calls/call-status.ts +81 -0
  908. package/src/tools/claude-code/claude-code.ts +428 -0
  909. package/src/tools/computer-use/definitions.ts +443 -0
  910. package/src/tools/computer-use/registry.ts +22 -0
  911. package/src/tools/computer-use/request-computer-control.ts +53 -0
  912. package/src/tools/computer-use/skill-proxy-bridge.ts +28 -0
  913. package/src/tools/credentials/account-registry.ts +127 -0
  914. package/src/tools/credentials/broker-types.ts +107 -0
  915. package/src/tools/credentials/broker.ts +372 -0
  916. package/src/tools/credentials/domain-policy.ts +51 -0
  917. package/src/tools/credentials/host-pattern-match.ts +60 -0
  918. package/src/tools/credentials/metadata-store.ts +335 -0
  919. package/src/tools/credentials/policy-types.ts +52 -0
  920. package/src/tools/credentials/policy-validate.ts +80 -0
  921. package/src/tools/credentials/resolve.ts +122 -0
  922. package/src/tools/credentials/selection.ts +159 -0
  923. package/src/tools/credentials/tool-policy.ts +25 -0
  924. package/src/tools/credentials/vault.ts +657 -0
  925. package/src/tools/document/document-tool.ts +92 -0
  926. package/src/tools/document/editor-template.ts +237 -0
  927. package/src/tools/execution-target.ts +21 -0
  928. package/src/tools/execution-timeout.ts +49 -0
  929. package/src/tools/executor.ts +815 -0
  930. package/src/tools/filesystem/edit.ts +127 -0
  931. package/src/tools/filesystem/fuzzy-match.ts +202 -0
  932. package/src/tools/filesystem/read.ts +71 -0
  933. package/src/tools/filesystem/view-image.ts +199 -0
  934. package/src/tools/filesystem/write.ts +79 -0
  935. package/src/tools/followups/followup_create.ts +76 -0
  936. package/src/tools/followups/followup_list.ts +60 -0
  937. package/src/tools/followups/followup_resolve.ts +56 -0
  938. package/src/tools/host-filesystem/edit.ts +125 -0
  939. package/src/tools/host-filesystem/read.ts +80 -0
  940. package/src/tools/host-filesystem/write.ts +76 -0
  941. package/src/tools/host-terminal/cli-discover.ts +180 -0
  942. package/src/tools/host-terminal/host-shell.ts +191 -0
  943. package/src/tools/memory/definitions.ts +69 -0
  944. package/src/tools/memory/handlers.ts +246 -0
  945. package/src/tools/memory/register.ts +66 -0
  946. package/src/tools/network/__tests__/web-search.test.ts +427 -0
  947. package/src/tools/network/domain-normalize.ts +85 -0
  948. package/src/tools/network/script-proxy/__tests__/logging.test.ts +248 -0
  949. package/src/tools/network/script-proxy/__tests__/policy.test.ts +234 -0
  950. package/src/tools/network/script-proxy/__tests__/router.test.ts +76 -0
  951. package/src/tools/network/script-proxy/certs.ts +237 -0
  952. package/src/tools/network/script-proxy/connect-tunnel.ts +82 -0
  953. package/src/tools/network/script-proxy/http-forwarder.ts +151 -0
  954. package/src/tools/network/script-proxy/index.ts +28 -0
  955. package/src/tools/network/script-proxy/logging.ts +196 -0
  956. package/src/tools/network/script-proxy/mitm-handler.ts +269 -0
  957. package/src/tools/network/script-proxy/policy.ts +152 -0
  958. package/src/tools/network/script-proxy/router.ts +60 -0
  959. package/src/tools/network/script-proxy/server.ts +136 -0
  960. package/src/tools/network/script-proxy/session-manager.ts +534 -0
  961. package/src/tools/network/script-proxy/types.ts +125 -0
  962. package/src/tools/network/url-safety.ts +227 -0
  963. package/src/tools/network/web-fetch.ts +713 -0
  964. package/src/tools/network/web-search.ts +296 -0
  965. package/src/tools/policy-context.ts +29 -0
  966. package/src/tools/registry.ts +295 -0
  967. package/src/tools/reminder/reminder-store.ts +148 -0
  968. package/src/tools/reminder/reminder.ts +80 -0
  969. package/src/tools/schedule/create.ts +81 -0
  970. package/src/tools/schedule/delete.ts +28 -0
  971. package/src/tools/schedule/list.ts +69 -0
  972. package/src/tools/schedule/update.ts +97 -0
  973. package/src/tools/shared/filesystem/edit-engine.ts +56 -0
  974. package/src/tools/shared/filesystem/errors.ts +85 -0
  975. package/src/tools/shared/filesystem/file-ops-service.ts +215 -0
  976. package/src/tools/shared/filesystem/format-diff.ts +35 -0
  977. package/src/tools/shared/filesystem/path-policy.ts +125 -0
  978. package/src/tools/shared/filesystem/size-guard.ts +41 -0
  979. package/src/tools/shared/filesystem/types.ts +80 -0
  980. package/src/tools/shared/shell-output.ts +52 -0
  981. package/src/tools/skills/delete-managed.ts +60 -0
  982. package/src/tools/skills/load.ts +139 -0
  983. package/src/tools/skills/sandbox-runner.ts +279 -0
  984. package/src/tools/skills/scaffold-managed.ts +150 -0
  985. package/src/tools/skills/script-contract.ts +6 -0
  986. package/src/tools/skills/skill-script-runner.ts +86 -0
  987. package/src/tools/skills/skill-tool-factory.ts +64 -0
  988. package/src/tools/skills/vellum-catalog.ts +217 -0
  989. package/src/tools/subagent/abort.ts +33 -0
  990. package/src/tools/subagent/message.ts +39 -0
  991. package/src/tools/subagent/read.ts +67 -0
  992. package/src/tools/subagent/spawn.ts +46 -0
  993. package/src/tools/subagent/status.ts +45 -0
  994. package/src/tools/swarm/delegate.ts +183 -0
  995. package/src/tools/system/request-permission.ts +98 -0
  996. package/src/tools/system/version.ts +43 -0
  997. package/src/tools/tasks/index.ts +27 -0
  998. package/src/tools/tasks/task-delete.ts +82 -0
  999. package/src/tools/tasks/task-list.ts +44 -0
  1000. package/src/tools/tasks/task-run.ts +97 -0
  1001. package/src/tools/tasks/task-save.ts +47 -0
  1002. package/src/tools/tasks/work-item-enqueue.ts +234 -0
  1003. package/src/tools/tasks/work-item-list.ts +55 -0
  1004. package/src/tools/tasks/work-item-remove.ts +60 -0
  1005. package/src/tools/tasks/work-item-run.ts +78 -0
  1006. package/src/tools/tasks/work-item-update.ts +114 -0
  1007. package/src/tools/terminal/backends/docker.ts +372 -0
  1008. package/src/tools/terminal/backends/native.ts +190 -0
  1009. package/src/tools/terminal/backends/types.ts +26 -0
  1010. package/src/tools/terminal/evaluate-typescript.ts +275 -0
  1011. package/src/tools/terminal/parser.ts +413 -0
  1012. package/src/tools/terminal/safe-env.ts +37 -0
  1013. package/src/tools/terminal/sandbox-diagnostics.ts +149 -0
  1014. package/src/tools/terminal/sandbox.ts +44 -0
  1015. package/src/tools/terminal/shell.ts +257 -0
  1016. package/src/tools/tool-manifest.ts +198 -0
  1017. package/src/tools/types.ts +176 -0
  1018. package/src/tools/ui-surface/definitions.ts +244 -0
  1019. package/src/tools/ui-surface/registry.ts +14 -0
  1020. package/src/tools/watch/screen-watch.ts +130 -0
  1021. package/src/tools/watch/watch-state.ts +119 -0
  1022. package/src/tools/watcher/create.ts +64 -0
  1023. package/src/tools/watcher/delete.ts +27 -0
  1024. package/src/tools/watcher/digest.ts +50 -0
  1025. package/src/tools/watcher/list.ts +60 -0
  1026. package/src/tools/watcher/update.ts +56 -0
  1027. package/src/tools/weather/service.ts +551 -0
  1028. package/src/twitter/client.ts +690 -0
  1029. package/src/twitter/oauth-client.ts +102 -0
  1030. package/src/twitter/router.ts +101 -0
  1031. package/src/twitter/session.ts +91 -0
  1032. package/src/usage/actors.ts +24 -0
  1033. package/src/usage/types.ts +37 -0
  1034. package/src/util/clipboard.ts +33 -0
  1035. package/src/util/content-id.ts +16 -0
  1036. package/src/util/debounce.ts +88 -0
  1037. package/src/util/diff.ts +181 -0
  1038. package/src/util/errors.ts +129 -0
  1039. package/src/util/logger.ts +243 -0
  1040. package/src/util/network-info.ts +47 -0
  1041. package/src/util/platform.ts +632 -0
  1042. package/src/util/pricing.ts +150 -0
  1043. package/src/util/promise-guard.ts +37 -0
  1044. package/src/util/retry.ts +98 -0
  1045. package/src/util/spinner.ts +51 -0
  1046. package/src/util/time.ts +16 -0
  1047. package/src/util/truncate.ts +6 -0
  1048. package/src/util/xml.ts +4 -0
  1049. package/src/version.ts +3 -0
  1050. package/src/watcher/constants.ts +11 -0
  1051. package/src/watcher/engine.ts +199 -0
  1052. package/src/watcher/provider-registry.ts +15 -0
  1053. package/src/watcher/provider-types.ts +48 -0
  1054. package/src/watcher/providers/gmail.ts +198 -0
  1055. package/src/watcher/providers/google-calendar.ts +228 -0
  1056. package/src/watcher/providers/slack.ts +129 -0
  1057. package/src/watcher/watcher-store.ts +419 -0
  1058. package/src/work-items/work-item-runner.ts +171 -0
  1059. package/src/work-items/work-item-store.ts +325 -0
  1060. package/src/workspace/commit-message-enrichment-service.ts +284 -0
  1061. package/src/workspace/commit-message-provider.ts +95 -0
  1062. package/src/workspace/git-service.ts +857 -0
  1063. package/src/workspace/heartbeat-service.ts +345 -0
  1064. package/src/workspace/provider-commit-message-generator.ts +285 -0
  1065. package/src/workspace/top-level-renderer.ts +19 -0
  1066. package/src/workspace/top-level-scanner.ts +41 -0
  1067. package/src/workspace/turn-commit.ts +175 -0
  1068. package/tsconfig.json +21 -0
@@ -0,0 +1,1517 @@
1
+ import * as net from 'node:net';
2
+ import { getConfig, loadRawConfig, saveRawConfig } from '../../config/loader.js';
3
+ import { initializeProviders } from '../../providers/registry.js';
4
+ import { addRule, removeRule, updateRule, getAllRules, acceptStarterBundle } from '../../permissions/trust-store.js';
5
+ import { classifyRisk, check, generateAllowlistOptions, generateScopeOptions } from '../../permissions/checker.js';
6
+ import { isSideEffectTool } from '../../tools/executor.js';
7
+ import { resolveExecutionTarget } from '../../tools/execution-target.js';
8
+ import { getAllTools, getTool } from '../../tools/registry.js';
9
+ import { listSchedules, updateSchedule, deleteSchedule, describeCronExpression } from '../../schedule/schedule-store.js';
10
+ import { listReminders, cancelReminder } from '../../tools/reminder/reminder-store.js';
11
+ import { getSecureKey, setSecureKey, deleteSecureKey } from '../../security/secure-keys.js';
12
+ import { upsertCredentialMetadata, deleteCredentialMetadata, getCredentialMetadata } from '../../tools/credentials/metadata-store.js';
13
+ import { postToSlackWebhook } from '../../slack/slack-webhook.js';
14
+ import { getApp } from '../../memory/app-store.js';
15
+ import { readHttpToken } from '../../util/platform.js';
16
+ import type {
17
+ ModelSetRequest,
18
+ ImageGenModelSetRequest,
19
+ AddTrustRule,
20
+ RemoveTrustRule,
21
+ UpdateTrustRule,
22
+ ScheduleToggle,
23
+ ScheduleRemove,
24
+ ReminderCancel,
25
+ ShareToSlackRequest,
26
+ SlackWebhookConfigRequest,
27
+ IngressConfigRequest,
28
+ VercelApiConfigRequest,
29
+ TwitterIntegrationConfigRequest,
30
+ TelegramConfigRequest,
31
+ TwilioConfigRequest,
32
+ GuardianVerificationRequest,
33
+ ToolPermissionSimulateRequest,
34
+ } from '../ipc-protocol.js';
35
+ import {
36
+ hasTwilioCredentials,
37
+ listIncomingPhoneNumbers,
38
+ searchAvailableNumbers,
39
+ provisionPhoneNumber,
40
+ } from '../../calls/twilio-rest.js';
41
+ import { createVerificationChallenge, getGuardianBinding, revokeBinding as revokeGuardianBinding } from '../../runtime/channel-guardian-service.js';
42
+ import { log, CONFIG_RELOAD_DEBOUNCE_MS, defineHandlers, type HandlerContext } from './shared.js';
43
+ import { MODEL_TO_PROVIDER } from '../session-slash.js';
44
+
45
+ // Lazily capture the env-provided INGRESS_PUBLIC_BASE_URL on first access
46
+ // rather than at module load time. The daemon loads ~/.vellum/.env inside
47
+ // runDaemon() (see lifecycle.ts), which runs AFTER static ES module imports
48
+ // resolve. A module-level snapshot would miss dotenv-provided values.
49
+ let _originalIngressEnvCaptured = false;
50
+ let _originalIngressEnv: string | undefined;
51
+ function getOriginalIngressEnv(): string | undefined {
52
+ if (!_originalIngressEnvCaptured) {
53
+ _originalIngressEnv = process.env.INGRESS_PUBLIC_BASE_URL;
54
+ _originalIngressEnvCaptured = true;
55
+ }
56
+ return _originalIngressEnv;
57
+ }
58
+
59
+ const TELEGRAM_BOT_TOKEN_IN_URL_PATTERN = /\/bot\d{8,10}:[A-Za-z0-9_-]{30,120}\//g;
60
+ const TELEGRAM_BOT_TOKEN_PATTERN = /(?<![A-Za-z0-9_])\d{8,10}:[A-Za-z0-9_-]{30,120}(?![A-Za-z0-9_])/g;
61
+
62
+ function redactTelegramBotTokens(value: string): string {
63
+ return value
64
+ .replace(TELEGRAM_BOT_TOKEN_IN_URL_PATTERN, '/bot[REDACTED]/')
65
+ .replace(TELEGRAM_BOT_TOKEN_PATTERN, '[REDACTED]');
66
+ }
67
+
68
+ function summarizeTelegramError(err: unknown): string {
69
+ const parts: string[] = [];
70
+ if (err instanceof Error) {
71
+ parts.push(err.message);
72
+ } else {
73
+ parts.push(String(err));
74
+ }
75
+ const path = (err as { path?: unknown })?.path;
76
+ if (typeof path === 'string' && path.length > 0) {
77
+ parts.push(`path=${path}`);
78
+ }
79
+ const code = (err as { code?: unknown })?.code;
80
+ if (typeof code === 'string' && code.length > 0) {
81
+ parts.push(`code=${code}`);
82
+ }
83
+ return redactTelegramBotTokens(parts.join(' '));
84
+ }
85
+
86
+ export function handleModelGet(socket: net.Socket, ctx: HandlerContext): void {
87
+ const config = getConfig();
88
+ const configured = Object.keys(config.apiKeys).filter((k) => !!config.apiKeys[k]);
89
+ if (!configured.includes('ollama')) configured.push('ollama');
90
+ ctx.send(socket, {
91
+ type: 'model_info',
92
+ model: config.model,
93
+ provider: config.provider,
94
+ configuredProviders: configured,
95
+ });
96
+ }
97
+
98
+ export function handleModelSet(
99
+ msg: ModelSetRequest,
100
+ socket: net.Socket,
101
+ ctx: HandlerContext,
102
+ ): void {
103
+ try {
104
+ // If the requested model is already the current model AND the provider
105
+ // is already aligned with what MODEL_TO_PROVIDER expects, skip expensive
106
+ // reinitialization but still send model_info so the client confirms.
107
+ // If the provider has drifted (e.g. manual config edit), fall through
108
+ // so the full reinit path can repair it.
109
+ {
110
+ const current = getConfig();
111
+ const expectedProvider = MODEL_TO_PROVIDER[msg.model];
112
+ const providerAligned = !expectedProvider || current.provider === expectedProvider;
113
+ if (msg.model === current.model && providerAligned) {
114
+ const configured = Object.keys(current.apiKeys).filter((k) => !!current.apiKeys[k]);
115
+ if (!configured.includes('ollama')) configured.push('ollama');
116
+ ctx.send(socket, {
117
+ type: 'model_info',
118
+ model: current.model,
119
+ provider: current.provider,
120
+ configuredProviders: configured,
121
+ });
122
+ return;
123
+ }
124
+ }
125
+
126
+ // Validate API key before switching
127
+ const provider = MODEL_TO_PROVIDER[msg.model];
128
+ if (provider && provider !== 'ollama') {
129
+ const currentConfig = getConfig();
130
+ if (!currentConfig.apiKeys[provider]) {
131
+ // Send current model_info so the client resyncs its optimistic state
132
+ // (don't use generic 'error' type — it would interrupt in-flight chat)
133
+ const configured = Object.keys(currentConfig.apiKeys).filter((k) => !!currentConfig.apiKeys[k]);
134
+ if (!configured.includes('ollama')) configured.push('ollama');
135
+ ctx.send(socket, { type: 'model_info', model: currentConfig.model, provider: currentConfig.provider, configuredProviders: configured });
136
+ return;
137
+ }
138
+ }
139
+
140
+ // Use raw config to avoid persisting env-var API keys to disk
141
+ const raw = loadRawConfig();
142
+ raw.model = msg.model;
143
+ // Infer provider from model ID to keep provider and model in sync
144
+ raw.provider = provider ?? raw.provider;
145
+
146
+ // Suppress the file watcher callback — handleModelSet already does
147
+ // the full reload sequence; a redundant watcher-triggered reload
148
+ // would incorrectly evict sessions created after this method returns.
149
+ const wasSuppressed = ctx.suppressConfigReload;
150
+ ctx.setSuppressConfigReload(true);
151
+ try {
152
+ saveRawConfig(raw);
153
+ } catch (err) {
154
+ ctx.setSuppressConfigReload(wasSuppressed);
155
+ throw err;
156
+ }
157
+ ctx.debounceTimers.schedule('__suppress_reset__', () => { ctx.setSuppressConfigReload(false); }, CONFIG_RELOAD_DEBOUNCE_MS);
158
+
159
+ // Re-initialize provider with the new model so LLM calls use it
160
+ const config = getConfig();
161
+ initializeProviders(config);
162
+
163
+ // Evict idle sessions immediately; mark busy ones as stale so they
164
+ // get recreated with the new provider once they finish processing.
165
+ for (const [id, session] of ctx.sessions) {
166
+ if (!session.isProcessing()) {
167
+ session.dispose();
168
+ ctx.sessions.delete(id);
169
+ } else {
170
+ session.markStale();
171
+ }
172
+ }
173
+
174
+ ctx.updateConfigFingerprint();
175
+
176
+ ctx.send(socket, {
177
+ type: 'model_info',
178
+ model: config.model,
179
+ provider: config.provider,
180
+ });
181
+ } catch (err) {
182
+ const message = err instanceof Error ? err.message : String(err);
183
+ ctx.send(socket, { type: 'error', message: `Failed to set model: ${message}` });
184
+ }
185
+ }
186
+
187
+ export function handleImageGenModelSet(
188
+ msg: ImageGenModelSetRequest,
189
+ _socket: net.Socket,
190
+ ctx: HandlerContext,
191
+ ): void {
192
+ try {
193
+ const raw = loadRawConfig();
194
+ raw.imageGenModel = msg.model;
195
+
196
+ const wasSuppressed = ctx.suppressConfigReload;
197
+ ctx.setSuppressConfigReload(true);
198
+ try {
199
+ saveRawConfig(raw);
200
+ } catch (err) {
201
+ ctx.setSuppressConfigReload(wasSuppressed);
202
+ throw err;
203
+ }
204
+ ctx.debounceTimers.schedule('__suppress_reset__', () => { ctx.setSuppressConfigReload(false); }, CONFIG_RELOAD_DEBOUNCE_MS);
205
+
206
+ ctx.updateConfigFingerprint();
207
+ log.info({ model: msg.model }, 'Image generation model updated');
208
+ } catch (err) {
209
+ const message = err instanceof Error ? err.message : String(err);
210
+ log.error({ err }, `Failed to set image gen model: ${message}`);
211
+ }
212
+ }
213
+
214
+ export function handleAddTrustRule(
215
+ msg: AddTrustRule,
216
+ _socket: net.Socket,
217
+ _ctx: HandlerContext,
218
+ ): void {
219
+ try {
220
+ const hasMetadata = msg.allowHighRisk != null
221
+ || msg.executionTarget != null;
222
+
223
+ addRule(
224
+ msg.toolName,
225
+ msg.pattern,
226
+ msg.scope,
227
+ msg.decision,
228
+ undefined, // priority — use default
229
+ hasMetadata
230
+ ? {
231
+ allowHighRisk: msg.allowHighRisk,
232
+ executionTarget: msg.executionTarget,
233
+ }
234
+ : undefined,
235
+ );
236
+ log.info({ toolName: msg.toolName, pattern: msg.pattern, scope: msg.scope, decision: msg.decision }, 'Trust rule added via client');
237
+ } catch (err) {
238
+ log.error({ err, toolName: msg.toolName, pattern: msg.pattern, scope: msg.scope }, 'Failed to add trust rule via client');
239
+ }
240
+ }
241
+
242
+ export function handleTrustRulesList(socket: net.Socket, ctx: HandlerContext): void {
243
+ const rules = getAllRules();
244
+ ctx.send(socket, { type: 'trust_rules_list_response', rules });
245
+ }
246
+
247
+ export function handleRemoveTrustRule(
248
+ msg: RemoveTrustRule,
249
+ _socket: net.Socket,
250
+ _ctx: HandlerContext,
251
+ ): void {
252
+ try {
253
+ const removed = removeRule(msg.id);
254
+ if (!removed) {
255
+ log.warn({ id: msg.id }, 'Trust rule not found for removal');
256
+ } else {
257
+ log.info({ id: msg.id }, 'Trust rule removed via client');
258
+ }
259
+ } catch (err) {
260
+ log.error({ err }, 'Failed to remove trust rule');
261
+ }
262
+ }
263
+
264
+ export function handleUpdateTrustRule(
265
+ msg: UpdateTrustRule,
266
+ _socket: net.Socket,
267
+ _ctx: HandlerContext,
268
+ ): void {
269
+ try {
270
+ updateRule(msg.id, {
271
+ tool: msg.tool,
272
+ pattern: msg.pattern,
273
+ scope: msg.scope,
274
+ decision: msg.decision,
275
+ priority: msg.priority,
276
+ });
277
+ log.info({ id: msg.id }, 'Trust rule updated via client');
278
+ } catch (err) {
279
+ log.error({ err }, 'Failed to update trust rule');
280
+ }
281
+ }
282
+
283
+ export function handleAcceptStarterBundle(
284
+ socket: net.Socket,
285
+ ctx: HandlerContext,
286
+ ): void {
287
+ try {
288
+ const result = acceptStarterBundle();
289
+ ctx.send(socket, {
290
+ type: 'accept_starter_bundle_response',
291
+ accepted: result.accepted,
292
+ rulesAdded: result.rulesAdded,
293
+ alreadyAccepted: result.alreadyAccepted,
294
+ });
295
+ log.info({ rulesAdded: result.rulesAdded, alreadyAccepted: result.alreadyAccepted }, 'Starter bundle accepted via client');
296
+ } catch (err) {
297
+ log.error({ err }, 'Failed to accept starter bundle');
298
+ ctx.send(socket, { type: 'error', message: 'Failed to accept starter bundle' });
299
+ }
300
+ }
301
+
302
+ export function handleSchedulesList(socket: net.Socket, ctx: HandlerContext): void {
303
+ const jobs = listSchedules();
304
+ ctx.send(socket, {
305
+ type: 'schedules_list_response',
306
+ schedules: jobs.map((j) => ({
307
+ id: j.id,
308
+ name: j.name,
309
+ enabled: j.enabled,
310
+ syntax: j.syntax,
311
+ expression: j.expression,
312
+ cronExpression: j.cronExpression,
313
+ timezone: j.timezone,
314
+ message: j.message,
315
+ nextRunAt: j.nextRunAt,
316
+ lastRunAt: j.lastRunAt,
317
+ lastStatus: j.lastStatus,
318
+ description: j.syntax === 'cron' ? describeCronExpression(j.cronExpression) : j.expression,
319
+ })),
320
+ });
321
+ }
322
+
323
+ export function handleScheduleToggle(
324
+ msg: ScheduleToggle,
325
+ socket: net.Socket,
326
+ ctx: HandlerContext,
327
+ ): void {
328
+ try {
329
+ updateSchedule(msg.id, { enabled: msg.enabled });
330
+ log.info({ id: msg.id, enabled: msg.enabled }, 'Schedule toggled via client');
331
+ } catch (err) {
332
+ log.error({ err }, 'Failed to toggle schedule');
333
+ }
334
+ handleSchedulesList(socket, ctx);
335
+ }
336
+
337
+ export function handleScheduleRemove(
338
+ msg: ScheduleRemove,
339
+ socket: net.Socket,
340
+ ctx: HandlerContext,
341
+ ): void {
342
+ try {
343
+ const removed = deleteSchedule(msg.id);
344
+ if (!removed) {
345
+ log.warn({ id: msg.id }, 'Schedule not found for removal');
346
+ } else {
347
+ log.info({ id: msg.id }, 'Schedule removed via client');
348
+ }
349
+ } catch (err) {
350
+ log.error({ err }, 'Failed to remove schedule');
351
+ }
352
+ handleSchedulesList(socket, ctx);
353
+ }
354
+
355
+ export function handleRemindersList(socket: net.Socket, ctx: HandlerContext): void {
356
+ const items = listReminders();
357
+ ctx.send(socket, {
358
+ type: 'reminders_list_response',
359
+ reminders: items.map((r) => ({
360
+ id: r.id,
361
+ label: r.label,
362
+ message: r.message,
363
+ fireAt: r.fireAt,
364
+ mode: r.mode,
365
+ status: r.status,
366
+ firedAt: r.firedAt,
367
+ createdAt: r.createdAt,
368
+ })),
369
+ });
370
+ }
371
+
372
+ export function handleReminderCancel(
373
+ msg: ReminderCancel,
374
+ socket: net.Socket,
375
+ ctx: HandlerContext,
376
+ ): void {
377
+ try {
378
+ const cancelled = cancelReminder(msg.id);
379
+ if (!cancelled) {
380
+ log.warn({ id: msg.id }, 'Reminder not found or already fired/cancelled');
381
+ } else {
382
+ log.info({ id: msg.id }, 'Reminder cancelled via client');
383
+ }
384
+ } catch (err) {
385
+ log.error({ err }, 'Failed to cancel reminder');
386
+ }
387
+ handleRemindersList(socket, ctx);
388
+ }
389
+
390
+ export async function handleShareToSlack(
391
+ msg: ShareToSlackRequest,
392
+ socket: net.Socket,
393
+ ctx: HandlerContext,
394
+ ): Promise<void> {
395
+ try {
396
+ const config = loadRawConfig();
397
+ const webhookUrl = config.slackWebhookUrl as string | undefined;
398
+ if (!webhookUrl) {
399
+ ctx.send(socket, {
400
+ type: 'share_to_slack_response',
401
+ success: false,
402
+ error: 'No Slack webhook URL configured. Set one in Settings.',
403
+ });
404
+ return;
405
+ }
406
+
407
+ const app = getApp(msg.appId);
408
+ if (!app) {
409
+ ctx.send(socket, {
410
+ type: 'share_to_slack_response',
411
+ success: false,
412
+ error: `App not found: ${msg.appId}`,
413
+ });
414
+ return;
415
+ }
416
+
417
+ await postToSlackWebhook(
418
+ webhookUrl,
419
+ app.name,
420
+ app.description ?? '',
421
+ '\u{1F4F1}',
422
+ );
423
+
424
+ ctx.send(socket, { type: 'share_to_slack_response', success: true });
425
+ } catch (err) {
426
+ const message = err instanceof Error ? err.message : String(err);
427
+ log.error({ err, appId: msg.appId }, 'Failed to share app to Slack');
428
+ ctx.send(socket, {
429
+ type: 'share_to_slack_response',
430
+ success: false,
431
+ error: message,
432
+ });
433
+ }
434
+ }
435
+
436
+ export function handleSlackWebhookConfig(
437
+ msg: SlackWebhookConfigRequest,
438
+ socket: net.Socket,
439
+ ctx: HandlerContext,
440
+ ): void {
441
+ try {
442
+ const config = loadRawConfig();
443
+ if (msg.action === 'get') {
444
+ ctx.send(socket, {
445
+ type: 'slack_webhook_config_response',
446
+ webhookUrl: (config.slackWebhookUrl as string) ?? undefined,
447
+ success: true,
448
+ });
449
+ } else {
450
+ config.slackWebhookUrl = msg.webhookUrl ?? '';
451
+ saveRawConfig(config);
452
+ ctx.send(socket, {
453
+ type: 'slack_webhook_config_response',
454
+ success: true,
455
+ });
456
+ }
457
+ } catch (err) {
458
+ const message = err instanceof Error ? err.message : String(err);
459
+ log.error({ err }, 'Failed to handle Slack webhook config');
460
+ ctx.send(socket, {
461
+ type: 'slack_webhook_config_response',
462
+ success: false,
463
+ error: message,
464
+ });
465
+ }
466
+ }
467
+
468
+ function computeGatewayTarget(): string {
469
+ if (process.env.GATEWAY_INTERNAL_BASE_URL) {
470
+ return process.env.GATEWAY_INTERNAL_BASE_URL.replace(/\/+$/, '');
471
+ }
472
+ const portRaw = process.env.GATEWAY_PORT || '7830';
473
+ const port = Number(portRaw) || 7830;
474
+ return `http://127.0.0.1:${port}`;
475
+ }
476
+
477
+ /**
478
+ * Best-effort call to the gateway's internal reconcile endpoint so that
479
+ * Telegram webhook registration is updated immediately when the ingress
480
+ * URL changes, without requiring a gateway restart.
481
+ */
482
+ function triggerGatewayReconcile(ingressPublicBaseUrl: string | undefined): void {
483
+ const gatewayBase = computeGatewayTarget();
484
+ const token = readHttpToken();
485
+ if (!token) {
486
+ log.debug('Skipping gateway reconcile trigger: no HTTP bearer token available');
487
+ return;
488
+ }
489
+
490
+ const url = `${gatewayBase}/internal/telegram/reconcile`;
491
+ const body = JSON.stringify({ ingressPublicBaseUrl: ingressPublicBaseUrl ?? '' });
492
+
493
+ fetch(url, {
494
+ method: 'POST',
495
+ headers: {
496
+ 'Content-Type': 'application/json',
497
+ 'Authorization': `Bearer ${token}`,
498
+ },
499
+ body,
500
+ signal: AbortSignal.timeout(5_000),
501
+ }).then((res) => {
502
+ if (res.ok) {
503
+ log.info('Gateway Telegram webhook reconcile triggered successfully');
504
+ } else {
505
+ log.warn({ status: res.status }, 'Gateway Telegram webhook reconcile returned non-OK status');
506
+ }
507
+ }).catch((err) => {
508
+ log.debug({ err }, 'Gateway Telegram webhook reconcile failed (gateway may not be running)');
509
+ });
510
+ }
511
+
512
+ export function handleIngressConfig(
513
+ msg: IngressConfigRequest,
514
+ socket: net.Socket,
515
+ ctx: HandlerContext,
516
+ ): void {
517
+ const localGatewayTarget = computeGatewayTarget();
518
+ try {
519
+ if (msg.action === 'get') {
520
+ const raw = loadRawConfig();
521
+ const ingress = (raw?.ingress ?? {}) as Record<string, unknown>;
522
+ const publicBaseUrl = (ingress.publicBaseUrl as string) ?? '';
523
+ // Backward compatibility: if `enabled` was never explicitly set,
524
+ // infer from whether a publicBaseUrl is configured so existing users
525
+ // who predate the toggle aren't silently disabled.
526
+ const enabled = (ingress.enabled as boolean | undefined) ?? (publicBaseUrl ? true : false);
527
+ ctx.send(socket, { type: 'ingress_config_response', enabled, publicBaseUrl, localGatewayTarget, success: true });
528
+ } else if (msg.action === 'set') {
529
+ const value = (msg.publicBaseUrl ?? '').trim().replace(/\/+$/, '');
530
+ // Ensure we capture the original env value before any mutation below
531
+ getOriginalIngressEnv();
532
+ const raw = loadRawConfig();
533
+
534
+ // Update ingress.publicBaseUrl — this is the single source of truth for
535
+ // the canonical public ingress URL. The gateway receives this value via
536
+ // the INGRESS_PUBLIC_BASE_URL env var at spawn time (see hatch.ts).
537
+ // The gateway also validates Twilio signatures against forwarded public
538
+ // URL headers, so local tunnel updates generally apply without restarts.
539
+ const ingress = (raw?.ingress ?? {}) as Record<string, unknown>;
540
+ ingress.publicBaseUrl = value || undefined;
541
+ if (msg.enabled !== undefined) {
542
+ ingress.enabled = msg.enabled;
543
+ }
544
+
545
+ const wasSuppressed = ctx.suppressConfigReload;
546
+ ctx.setSuppressConfigReload(true);
547
+ try {
548
+ saveRawConfig({ ...raw, ingress });
549
+ } catch (err) {
550
+ ctx.setSuppressConfigReload(wasSuppressed);
551
+ throw err;
552
+ }
553
+ ctx.debounceTimers.schedule('__suppress_reset__', () => { ctx.setSuppressConfigReload(false); }, CONFIG_RELOAD_DEBOUNCE_MS);
554
+
555
+ // Propagate to the gateway's process environment so it picks up the
556
+ // new URL when it is restarted. For the local-deployment path the
557
+ // gateway runs as a child process that inherited the assistant's env,
558
+ // so updating process.env here ensures the value is visible when the
559
+ // gateway is restarted (e.g. by the self-upgrade skill or a manual
560
+ // `pkill -f gateway`).
561
+ // Only export the URL when ingress is enabled; clearing it when
562
+ // disabled ensures the gateway stops accepting inbound webhooks.
563
+ const isEnabled = (ingress.enabled as boolean | undefined) ?? (value ? true : false);
564
+ if (value && isEnabled) {
565
+ process.env.INGRESS_PUBLIC_BASE_URL = value;
566
+ } else if (isEnabled && getOriginalIngressEnv() !== undefined) {
567
+ // Ingress is enabled but the user cleared the URL — fall back to the
568
+ // env var that was present when the process started.
569
+ process.env.INGRESS_PUBLIC_BASE_URL = getOriginalIngressEnv()!;
570
+ } else {
571
+ // Ingress is disabled or no URL is configured and no startup env var
572
+ // exists — remove the env var so the gateway stops accepting webhooks.
573
+ delete process.env.INGRESS_PUBLIC_BASE_URL;
574
+ }
575
+
576
+ ctx.send(socket, { type: 'ingress_config_response', enabled: isEnabled, publicBaseUrl: value, localGatewayTarget, success: true });
577
+
578
+ // Trigger immediate Telegram webhook reconcile on the gateway so
579
+ // that changing the ingress URL takes effect without a restart.
580
+ // Called unconditionally so the gateway clears its in-memory URL
581
+ // when ingress is disabled, preventing stale re-registration on
582
+ // credential rotation.
583
+ // Use the effective URL from process.env (which accounts for the
584
+ // fallback branch above) rather than the raw `value` from the UI.
585
+ const effectiveUrl = isEnabled ? process.env.INGRESS_PUBLIC_BASE_URL : undefined;
586
+ triggerGatewayReconcile(effectiveUrl);
587
+ } else {
588
+ ctx.send(socket, { type: 'ingress_config_response', enabled: false, publicBaseUrl: '', localGatewayTarget, success: false, error: `Unknown action: ${String((msg as unknown as Record<string, unknown>).action)}` });
589
+ }
590
+ } catch (err) {
591
+ const message = err instanceof Error ? err.message : String(err);
592
+ ctx.send(socket, { type: 'ingress_config_response', enabled: false, publicBaseUrl: '', localGatewayTarget, success: false, error: message });
593
+ }
594
+ }
595
+
596
+ export function handleVercelApiConfig(
597
+ msg: VercelApiConfigRequest,
598
+ socket: net.Socket,
599
+ ctx: HandlerContext,
600
+ ): void {
601
+ try {
602
+ if (msg.action === 'get') {
603
+ const existing = getSecureKey('credential:vercel:api_token');
604
+ ctx.send(socket, {
605
+ type: 'vercel_api_config_response',
606
+ hasToken: !!existing,
607
+ success: true,
608
+ });
609
+ } else if (msg.action === 'set') {
610
+ if (!msg.apiToken) {
611
+ ctx.send(socket, {
612
+ type: 'vercel_api_config_response',
613
+ hasToken: false,
614
+ success: false,
615
+ error: 'apiToken is required for set action',
616
+ });
617
+ return;
618
+ }
619
+ const stored = setSecureKey('credential:vercel:api_token', msg.apiToken);
620
+ if (!stored) {
621
+ ctx.send(socket, {
622
+ type: 'vercel_api_config_response',
623
+ hasToken: false,
624
+ success: false,
625
+ error: 'Failed to store API token in secure storage',
626
+ });
627
+ return;
628
+ }
629
+ upsertCredentialMetadata('vercel', 'api_token', {
630
+ allowedTools: ['publish_page', 'unpublish_page'],
631
+ });
632
+ ctx.send(socket, {
633
+ type: 'vercel_api_config_response',
634
+ hasToken: true,
635
+ success: true,
636
+ });
637
+ } else {
638
+ deleteSecureKey('credential:vercel:api_token');
639
+ deleteCredentialMetadata('vercel', 'api_token');
640
+ ctx.send(socket, {
641
+ type: 'vercel_api_config_response',
642
+ hasToken: false,
643
+ success: true,
644
+ });
645
+ }
646
+ } catch (err) {
647
+ const message = err instanceof Error ? err.message : String(err);
648
+ log.error({ err }, 'Failed to handle Vercel API config');
649
+ ctx.send(socket, {
650
+ type: 'vercel_api_config_response',
651
+ hasToken: false,
652
+ success: false,
653
+ error: message,
654
+ });
655
+ }
656
+ }
657
+
658
+ export function handleTwitterIntegrationConfig(
659
+ msg: TwitterIntegrationConfigRequest,
660
+ socket: net.Socket,
661
+ ctx: HandlerContext,
662
+ ): void {
663
+ try {
664
+ if (msg.action === 'get') {
665
+ const raw = loadRawConfig();
666
+ const mode = (raw.twitterIntegrationMode as 'local_byo' | 'managed' | undefined) ?? 'local_byo';
667
+ const strategy = (raw.twitterOperationStrategy as 'oauth' | 'browser' | 'auto' | undefined) ?? 'auto';
668
+ const strategyConfigured = Object.prototype.hasOwnProperty.call(raw, 'twitterOperationStrategy');
669
+ const localClientConfigured = !!getSecureKey('credential:integration:twitter:oauth_client_id');
670
+ const connected = !!getSecureKey('credential:integration:twitter:access_token');
671
+ const meta = getCredentialMetadata('integration:twitter', 'access_token');
672
+ ctx.send(socket, {
673
+ type: 'twitter_integration_config_response',
674
+ success: true,
675
+ mode,
676
+ managedAvailable: false,
677
+ localClientConfigured,
678
+ connected,
679
+ accountInfo: meta?.accountInfo ?? undefined,
680
+ strategy,
681
+ strategyConfigured,
682
+ });
683
+ } else if (msg.action === 'get_strategy') {
684
+ const raw = loadRawConfig();
685
+ const strategy = (raw.twitterOperationStrategy as 'oauth' | 'browser' | 'auto' | undefined) ?? 'auto';
686
+ const strategyConfigured = Object.prototype.hasOwnProperty.call(raw, 'twitterOperationStrategy');
687
+ ctx.send(socket, {
688
+ type: 'twitter_integration_config_response',
689
+ success: true,
690
+ managedAvailable: false,
691
+ localClientConfigured: !!getSecureKey('credential:integration:twitter:oauth_client_id'),
692
+ connected: !!getSecureKey('credential:integration:twitter:access_token'),
693
+ strategy,
694
+ strategyConfigured,
695
+ });
696
+ } else if (msg.action === 'set_strategy') {
697
+ const valid = ['oauth', 'browser', 'auto'];
698
+ const value = msg.strategy;
699
+ if (!value || !valid.includes(value)) {
700
+ ctx.send(socket, {
701
+ type: 'twitter_integration_config_response',
702
+ success: false,
703
+ managedAvailable: false,
704
+ localClientConfigured: false,
705
+ connected: false,
706
+ error: `Invalid strategy value: ${String(value)}. Must be one of: ${valid.join(', ')}`,
707
+ });
708
+ return;
709
+ }
710
+ const raw = loadRawConfig();
711
+ raw.twitterOperationStrategy = value;
712
+ saveRawConfig(raw);
713
+ ctx.send(socket, {
714
+ type: 'twitter_integration_config_response',
715
+ success: true,
716
+ managedAvailable: false,
717
+ localClientConfigured: !!getSecureKey('credential:integration:twitter:oauth_client_id'),
718
+ connected: !!getSecureKey('credential:integration:twitter:access_token'),
719
+ strategy: value as 'oauth' | 'browser' | 'auto',
720
+ strategyConfigured: true,
721
+ });
722
+ } else if (msg.action === 'set_mode') {
723
+ const raw = loadRawConfig();
724
+ raw.twitterIntegrationMode = msg.mode ?? 'local_byo';
725
+ saveRawConfig(raw);
726
+ ctx.send(socket, {
727
+ type: 'twitter_integration_config_response',
728
+ success: true,
729
+ mode: msg.mode ?? 'local_byo',
730
+ managedAvailable: false,
731
+ localClientConfigured: !!getSecureKey('credential:integration:twitter:oauth_client_id'),
732
+ connected: !!getSecureKey('credential:integration:twitter:access_token'),
733
+ });
734
+ } else if (msg.action === 'set_local_client') {
735
+ if (!msg.clientId) {
736
+ ctx.send(socket, {
737
+ type: 'twitter_integration_config_response',
738
+ success: false,
739
+ managedAvailable: false,
740
+ localClientConfigured: false,
741
+ connected: false,
742
+ error: 'clientId is required for set_local_client action',
743
+ });
744
+ return;
745
+ }
746
+ const previousClientId = getSecureKey('credential:integration:twitter:oauth_client_id');
747
+ const storedId = setSecureKey('credential:integration:twitter:oauth_client_id', msg.clientId);
748
+ if (!storedId) {
749
+ ctx.send(socket, {
750
+ type: 'twitter_integration_config_response',
751
+ success: false,
752
+ managedAvailable: false,
753
+ localClientConfigured: false,
754
+ connected: false,
755
+ error: 'Failed to store client ID in secure storage',
756
+ });
757
+ return;
758
+ }
759
+ if (msg.clientSecret) {
760
+ const storedSecret = setSecureKey('credential:integration:twitter:oauth_client_secret', msg.clientSecret);
761
+ if (!storedSecret) {
762
+ // Roll back the client ID to its previous value to avoid inconsistent OAuth state
763
+ if (previousClientId) {
764
+ setSecureKey('credential:integration:twitter:oauth_client_id', previousClientId);
765
+ } else {
766
+ deleteSecureKey('credential:integration:twitter:oauth_client_id');
767
+ }
768
+ ctx.send(socket, {
769
+ type: 'twitter_integration_config_response',
770
+ success: false,
771
+ managedAvailable: false,
772
+ localClientConfigured: !!previousClientId,
773
+ connected: false,
774
+ error: 'Failed to store client secret in secure storage',
775
+ });
776
+ return;
777
+ }
778
+ } else {
779
+ // Clear any stale secret when updating client without a secret (e.g. switching to PKCE)
780
+ deleteSecureKey('credential:integration:twitter:oauth_client_secret');
781
+ }
782
+ ctx.send(socket, {
783
+ type: 'twitter_integration_config_response',
784
+ success: true,
785
+ managedAvailable: false,
786
+ localClientConfigured: true,
787
+ connected: !!getSecureKey('credential:integration:twitter:access_token'),
788
+ });
789
+ } else if (msg.action === 'clear_local_client') {
790
+ // If connected, disconnect first
791
+ if (getSecureKey('credential:integration:twitter:access_token')) {
792
+ deleteSecureKey('credential:integration:twitter:access_token');
793
+ deleteSecureKey('credential:integration:twitter:refresh_token');
794
+ deleteCredentialMetadata('integration:twitter', 'access_token');
795
+ }
796
+ deleteSecureKey('credential:integration:twitter:oauth_client_id');
797
+ deleteSecureKey('credential:integration:twitter:oauth_client_secret');
798
+ ctx.send(socket, {
799
+ type: 'twitter_integration_config_response',
800
+ success: true,
801
+ managedAvailable: false,
802
+ localClientConfigured: false,
803
+ connected: false,
804
+ });
805
+ } else if (msg.action === 'disconnect') {
806
+ deleteSecureKey('credential:integration:twitter:access_token');
807
+ deleteSecureKey('credential:integration:twitter:refresh_token');
808
+ deleteCredentialMetadata('integration:twitter', 'access_token');
809
+ ctx.send(socket, {
810
+ type: 'twitter_integration_config_response',
811
+ success: true,
812
+ managedAvailable: false,
813
+ localClientConfigured: !!getSecureKey('credential:integration:twitter:oauth_client_id'),
814
+ connected: false,
815
+ });
816
+ } else {
817
+ ctx.send(socket, {
818
+ type: 'twitter_integration_config_response',
819
+ success: false,
820
+ managedAvailable: false,
821
+ localClientConfigured: false,
822
+ connected: false,
823
+ error: `Unknown action: ${String((msg as unknown as Record<string, unknown>).action)}`,
824
+ });
825
+ }
826
+ } catch (err) {
827
+ const message = err instanceof Error ? err.message : String(err);
828
+ log.error({ err }, 'Failed to handle Twitter integration config');
829
+ ctx.send(socket, {
830
+ type: 'twitter_integration_config_response',
831
+ success: false,
832
+ managedAvailable: false,
833
+ localClientConfigured: false,
834
+ connected: false,
835
+ error: message,
836
+ });
837
+ }
838
+ }
839
+
840
+ export async function handleTelegramConfig(
841
+ msg: TelegramConfigRequest,
842
+ socket: net.Socket,
843
+ ctx: HandlerContext,
844
+ ): Promise<void> {
845
+ try {
846
+ if (msg.action === 'get') {
847
+ const hasBotToken = !!getSecureKey('credential:telegram:bot_token');
848
+ const hasWebhookSecret = !!getSecureKey('credential:telegram:webhook_secret');
849
+ const meta = getCredentialMetadata('telegram', 'bot_token');
850
+ const botUsername = meta?.accountInfo ?? undefined;
851
+ ctx.send(socket, {
852
+ type: 'telegram_config_response',
853
+ success: true,
854
+ hasBotToken,
855
+ botUsername,
856
+ connected: hasBotToken && hasWebhookSecret,
857
+ hasWebhookSecret,
858
+ });
859
+ } else if (msg.action === 'set') {
860
+ // Resolve token: prefer explicit msg.botToken, fall back to secure storage.
861
+ // Track provenance so we only rollback tokens that were freshly provided.
862
+ const isNewToken = !!msg.botToken;
863
+ const botToken = msg.botToken || getSecureKey('credential:telegram:bot_token');
864
+ if (!botToken) {
865
+ ctx.send(socket, {
866
+ type: 'telegram_config_response',
867
+ success: false,
868
+ hasBotToken: false,
869
+ connected: false,
870
+ hasWebhookSecret: false,
871
+ error: 'botToken is required for set action',
872
+ });
873
+ return;
874
+ }
875
+
876
+ // Validate token via Telegram getMe API
877
+ let botUsername: string;
878
+ try {
879
+ const res = await fetch(`https://api.telegram.org/bot${botToken}/getMe`);
880
+ if (!res.ok) {
881
+ const body = await res.text();
882
+ ctx.send(socket, {
883
+ type: 'telegram_config_response',
884
+ success: false,
885
+ hasBotToken: false,
886
+ connected: false,
887
+ hasWebhookSecret: false,
888
+ error: `Telegram API validation failed: ${body}`,
889
+ });
890
+ return;
891
+ }
892
+ const data = await res.json() as { ok: boolean; result?: { username?: string } };
893
+ if (!data.ok || !data.result?.username) {
894
+ ctx.send(socket, {
895
+ type: 'telegram_config_response',
896
+ success: false,
897
+ hasBotToken: false,
898
+ connected: false,
899
+ hasWebhookSecret: false,
900
+ error: 'Telegram API returned unexpected response',
901
+ });
902
+ return;
903
+ }
904
+ botUsername = data.result.username;
905
+ } catch (err) {
906
+ const message = summarizeTelegramError(err);
907
+ ctx.send(socket, {
908
+ type: 'telegram_config_response',
909
+ success: false,
910
+ hasBotToken: false,
911
+ connected: false,
912
+ hasWebhookSecret: false,
913
+ error: `Failed to validate bot token: ${message}`,
914
+ });
915
+ return;
916
+ }
917
+
918
+ // Store bot token securely
919
+ const stored = setSecureKey('credential:telegram:bot_token', botToken);
920
+ if (!stored) {
921
+ ctx.send(socket, {
922
+ type: 'telegram_config_response',
923
+ success: false,
924
+ hasBotToken: false,
925
+ connected: false,
926
+ hasWebhookSecret: false,
927
+ error: 'Failed to store bot token in secure storage',
928
+ });
929
+ return;
930
+ }
931
+
932
+ // Store metadata with bot username
933
+ upsertCredentialMetadata('telegram', 'bot_token', {
934
+ accountInfo: botUsername,
935
+ });
936
+
937
+ // Ensure webhook secret exists (generate if missing)
938
+ let hasWebhookSecret = !!getSecureKey('credential:telegram:webhook_secret');
939
+ if (!hasWebhookSecret) {
940
+ const { randomUUID } = await import('node:crypto');
941
+ const webhookSecret = randomUUID();
942
+ const secretStored = setSecureKey('credential:telegram:webhook_secret', webhookSecret);
943
+ if (secretStored) {
944
+ upsertCredentialMetadata('telegram', 'webhook_secret', {});
945
+ hasWebhookSecret = true;
946
+ } else {
947
+ // Only roll back the bot token if it was freshly provided.
948
+ // When the token came from secure storage it was already valid
949
+ // configuration; deleting it would destroy working state.
950
+ if (isNewToken) {
951
+ deleteSecureKey('credential:telegram:bot_token');
952
+ deleteCredentialMetadata('telegram', 'bot_token');
953
+ }
954
+ ctx.send(socket, {
955
+ type: 'telegram_config_response',
956
+ success: false,
957
+ hasBotToken: !isNewToken,
958
+ connected: false,
959
+ hasWebhookSecret: false,
960
+ error: 'Failed to store webhook secret',
961
+ });
962
+ return;
963
+ }
964
+ } else {
965
+ // Self-heal: ensure metadata exists even when the secret was
966
+ // already present (covers previously lost/corrupted metadata).
967
+ upsertCredentialMetadata('telegram', 'webhook_secret', {});
968
+ }
969
+
970
+ ctx.send(socket, {
971
+ type: 'telegram_config_response',
972
+ success: true,
973
+ hasBotToken: true,
974
+ botUsername,
975
+ connected: true,
976
+ hasWebhookSecret,
977
+ });
978
+
979
+ // Trigger gateway reconcile so the webhook registration updates immediately
980
+ const effectiveUrl = process.env.INGRESS_PUBLIC_BASE_URL;
981
+ if (effectiveUrl) {
982
+ triggerGatewayReconcile(effectiveUrl);
983
+ }
984
+ } else if (msg.action === 'clear') {
985
+ // Deregister the Telegram webhook before deleting credentials.
986
+ // The gateway reconcile short-circuits when credentials are absent,
987
+ // so we must call the Telegram API directly while the token is still
988
+ // available.
989
+ const botToken = getSecureKey('credential:telegram:bot_token');
990
+ if (botToken) {
991
+ try {
992
+ await fetch(`https://api.telegram.org/bot${botToken}/deleteWebhook`);
993
+ } catch (err) {
994
+ log.warn(
995
+ { error: summarizeTelegramError(err) },
996
+ 'Failed to deregister Telegram webhook (proceeding with credential cleanup)',
997
+ );
998
+ }
999
+ }
1000
+
1001
+ deleteSecureKey('credential:telegram:bot_token');
1002
+ deleteCredentialMetadata('telegram', 'bot_token');
1003
+ deleteSecureKey('credential:telegram:webhook_secret');
1004
+ deleteCredentialMetadata('telegram', 'webhook_secret');
1005
+
1006
+ ctx.send(socket, {
1007
+ type: 'telegram_config_response',
1008
+ success: true,
1009
+ hasBotToken: false,
1010
+ connected: false,
1011
+ hasWebhookSecret: false,
1012
+ });
1013
+
1014
+ // Trigger reconcile to deregister webhook
1015
+ const effectiveUrl = process.env.INGRESS_PUBLIC_BASE_URL;
1016
+ if (effectiveUrl) {
1017
+ triggerGatewayReconcile(effectiveUrl);
1018
+ }
1019
+ } else if (msg.action === 'set_commands') {
1020
+ const storedToken = getSecureKey('credential:telegram:bot_token');
1021
+ if (!storedToken) {
1022
+ ctx.send(socket, {
1023
+ type: 'telegram_config_response',
1024
+ success: false,
1025
+ hasBotToken: false,
1026
+ connected: false,
1027
+ hasWebhookSecret: false,
1028
+ error: 'Bot token not configured. Run set action first.',
1029
+ });
1030
+ return;
1031
+ }
1032
+
1033
+ const commands = msg.commands ?? [
1034
+ { command: 'new', description: 'Start a new conversation' },
1035
+ { command: 'guardian_verify', description: 'Verify your guardian identity' },
1036
+ ];
1037
+
1038
+ try {
1039
+ const res = await fetch(`https://api.telegram.org/bot${storedToken}/setMyCommands`, {
1040
+ method: 'POST',
1041
+ headers: { 'Content-Type': 'application/json' },
1042
+ body: JSON.stringify({ commands }),
1043
+ });
1044
+ if (!res.ok) {
1045
+ const body = await res.text();
1046
+ ctx.send(socket, {
1047
+ type: 'telegram_config_response',
1048
+ success: false,
1049
+ hasBotToken: true,
1050
+ connected: !!getSecureKey('credential:telegram:webhook_secret'),
1051
+ hasWebhookSecret: !!getSecureKey('credential:telegram:webhook_secret'),
1052
+ error: `Failed to set bot commands: ${body}`,
1053
+ });
1054
+ return;
1055
+ }
1056
+ } catch (err) {
1057
+ const message = summarizeTelegramError(err);
1058
+ ctx.send(socket, {
1059
+ type: 'telegram_config_response',
1060
+ success: false,
1061
+ hasBotToken: true,
1062
+ connected: !!getSecureKey('credential:telegram:webhook_secret'),
1063
+ hasWebhookSecret: !!getSecureKey('credential:telegram:webhook_secret'),
1064
+ error: `Failed to set bot commands: ${message}`,
1065
+ });
1066
+ return;
1067
+ }
1068
+
1069
+ const hasBotToken = !!getSecureKey('credential:telegram:bot_token');
1070
+ const hasWebhookSecret = !!getSecureKey('credential:telegram:webhook_secret');
1071
+ ctx.send(socket, {
1072
+ type: 'telegram_config_response',
1073
+ success: true,
1074
+ hasBotToken,
1075
+ connected: hasBotToken && hasWebhookSecret,
1076
+ hasWebhookSecret,
1077
+ });
1078
+ } else {
1079
+ ctx.send(socket, {
1080
+ type: 'telegram_config_response',
1081
+ success: false,
1082
+ hasBotToken: false,
1083
+ connected: false,
1084
+ hasWebhookSecret: false,
1085
+ error: `Unknown action: ${String((msg as unknown as Record<string, unknown>).action)}`,
1086
+ });
1087
+ }
1088
+ } catch (err) {
1089
+ const message = err instanceof Error ? err.message : String(err);
1090
+ log.error({ err }, 'Failed to handle Telegram config');
1091
+ ctx.send(socket, {
1092
+ type: 'telegram_config_response',
1093
+ success: false,
1094
+ hasBotToken: false,
1095
+ connected: false,
1096
+ hasWebhookSecret: false,
1097
+ error: message,
1098
+ });
1099
+ }
1100
+ }
1101
+
1102
+ export async function handleTwilioConfig(
1103
+ msg: TwilioConfigRequest,
1104
+ socket: net.Socket,
1105
+ ctx: HandlerContext,
1106
+ ): Promise<void> {
1107
+ try {
1108
+ if (msg.action === 'get') {
1109
+ const hasCredentials = hasTwilioCredentials();
1110
+ const raw = loadRawConfig();
1111
+ const sms = (raw?.sms ?? {}) as Record<string, unknown>;
1112
+ const phoneNumber = (sms.phoneNumber as string) ?? '';
1113
+ ctx.send(socket, {
1114
+ type: 'twilio_config_response',
1115
+ success: true,
1116
+ hasCredentials,
1117
+ phoneNumber: phoneNumber || undefined,
1118
+ });
1119
+ } else if (msg.action === 'set_credentials') {
1120
+ if (!msg.accountSid || !msg.authToken) {
1121
+ ctx.send(socket, {
1122
+ type: 'twilio_config_response',
1123
+ success: false,
1124
+ hasCredentials: hasTwilioCredentials(),
1125
+ error: 'accountSid and authToken are required for set_credentials action',
1126
+ });
1127
+ return;
1128
+ }
1129
+
1130
+ // Validate credentials by calling the Twilio API
1131
+ const authHeader = 'Basic ' + Buffer.from(`${msg.accountSid}:${msg.authToken}`).toString('base64');
1132
+ try {
1133
+ const res = await fetch(
1134
+ `https://api.twilio.com/2010-04-01/Accounts/${msg.accountSid}.json`,
1135
+ {
1136
+ method: 'GET',
1137
+ headers: { Authorization: authHeader },
1138
+ },
1139
+ );
1140
+ if (!res.ok) {
1141
+ const body = await res.text();
1142
+ ctx.send(socket, {
1143
+ type: 'twilio_config_response',
1144
+ success: false,
1145
+ hasCredentials: hasTwilioCredentials(),
1146
+ error: `Twilio API validation failed (${res.status}): ${body}`,
1147
+ });
1148
+ return;
1149
+ }
1150
+ } catch (err) {
1151
+ const message = err instanceof Error ? err.message : String(err);
1152
+ ctx.send(socket, {
1153
+ type: 'twilio_config_response',
1154
+ success: false,
1155
+ hasCredentials: hasTwilioCredentials(),
1156
+ error: `Failed to validate Twilio credentials: ${message}`,
1157
+ });
1158
+ return;
1159
+ }
1160
+
1161
+ // Store credentials securely
1162
+ const sidStored = setSecureKey('credential:twilio:account_sid', msg.accountSid);
1163
+ if (!sidStored) {
1164
+ ctx.send(socket, {
1165
+ type: 'twilio_config_response',
1166
+ success: false,
1167
+ hasCredentials: false,
1168
+ error: 'Failed to store Account SID in secure storage',
1169
+ });
1170
+ return;
1171
+ }
1172
+
1173
+ const tokenStored = setSecureKey('credential:twilio:auth_token', msg.authToken);
1174
+ if (!tokenStored) {
1175
+ // Roll back the Account SID
1176
+ deleteSecureKey('credential:twilio:account_sid');
1177
+ ctx.send(socket, {
1178
+ type: 'twilio_config_response',
1179
+ success: false,
1180
+ hasCredentials: false,
1181
+ error: 'Failed to store Auth Token in secure storage',
1182
+ });
1183
+ return;
1184
+ }
1185
+
1186
+ upsertCredentialMetadata('twilio', 'account_sid', {});
1187
+ upsertCredentialMetadata('twilio', 'auth_token', {});
1188
+
1189
+ ctx.send(socket, {
1190
+ type: 'twilio_config_response',
1191
+ success: true,
1192
+ hasCredentials: true,
1193
+ });
1194
+ } else if (msg.action === 'clear_credentials') {
1195
+ deleteSecureKey('credential:twilio:account_sid');
1196
+ deleteSecureKey('credential:twilio:auth_token');
1197
+ deleteSecureKey('credential:twilio:phone_number');
1198
+ deleteCredentialMetadata('twilio', 'account_sid');
1199
+ deleteCredentialMetadata('twilio', 'auth_token');
1200
+
1201
+ ctx.send(socket, {
1202
+ type: 'twilio_config_response',
1203
+ success: true,
1204
+ hasCredentials: false,
1205
+ });
1206
+ } else if (msg.action === 'provision_number') {
1207
+ if (!hasTwilioCredentials()) {
1208
+ ctx.send(socket, {
1209
+ type: 'twilio_config_response',
1210
+ success: false,
1211
+ hasCredentials: false,
1212
+ error: 'Twilio credentials not configured. Set credentials first.',
1213
+ });
1214
+ return;
1215
+ }
1216
+
1217
+ const accountSid = getSecureKey('credential:twilio:account_sid')!;
1218
+ const authToken = getSecureKey('credential:twilio:auth_token')!;
1219
+ const country = msg.country ?? 'US';
1220
+
1221
+ // Search for an available number
1222
+ const available = await searchAvailableNumbers(accountSid, authToken, country, msg.areaCode);
1223
+ if (available.length === 0) {
1224
+ ctx.send(socket, {
1225
+ type: 'twilio_config_response',
1226
+ success: false,
1227
+ hasCredentials: true,
1228
+ error: `No available phone numbers found for country=${country}${msg.areaCode ? ` areaCode=${msg.areaCode}` : ''}`,
1229
+ });
1230
+ return;
1231
+ }
1232
+
1233
+ // Purchase the first available number
1234
+ const purchased = await provisionPhoneNumber(accountSid, authToken, available[0].phoneNumber);
1235
+
1236
+ ctx.send(socket, {
1237
+ type: 'twilio_config_response',
1238
+ success: true,
1239
+ hasCredentials: true,
1240
+ phoneNumber: purchased.phoneNumber,
1241
+ });
1242
+ } else if (msg.action === 'assign_number') {
1243
+ if (!msg.phoneNumber) {
1244
+ ctx.send(socket, {
1245
+ type: 'twilio_config_response',
1246
+ success: false,
1247
+ hasCredentials: hasTwilioCredentials(),
1248
+ error: 'phoneNumber is required for assign_number action',
1249
+ });
1250
+ return;
1251
+ }
1252
+
1253
+ // Persist the phone number in the secure credential store so the
1254
+ // active Twilio runtime can read it via credential:twilio:phone_number
1255
+ const phoneStored = setSecureKey('credential:twilio:phone_number', msg.phoneNumber);
1256
+ if (!phoneStored) {
1257
+ ctx.send(socket, {
1258
+ type: 'twilio_config_response',
1259
+ success: false,
1260
+ hasCredentials: hasTwilioCredentials(),
1261
+ error: 'Failed to store phone number in secure storage',
1262
+ });
1263
+ return;
1264
+ }
1265
+
1266
+ // Also persist in assistant config (non-secret) for the UI
1267
+ const raw = loadRawConfig();
1268
+ const sms = (raw?.sms ?? {}) as Record<string, unknown>;
1269
+ sms.phoneNumber = msg.phoneNumber;
1270
+
1271
+ const wasSuppressed = ctx.suppressConfigReload;
1272
+ ctx.setSuppressConfigReload(true);
1273
+ try {
1274
+ saveRawConfig({ ...raw, sms });
1275
+ } catch (err) {
1276
+ ctx.setSuppressConfigReload(wasSuppressed);
1277
+ throw err;
1278
+ }
1279
+ ctx.debounceTimers.schedule('__suppress_reset__', () => { ctx.setSuppressConfigReload(false); }, CONFIG_RELOAD_DEBOUNCE_MS);
1280
+
1281
+ ctx.send(socket, {
1282
+ type: 'twilio_config_response',
1283
+ success: true,
1284
+ hasCredentials: hasTwilioCredentials(),
1285
+ phoneNumber: msg.phoneNumber,
1286
+ });
1287
+ } else if (msg.action === 'list_numbers') {
1288
+ if (!hasTwilioCredentials()) {
1289
+ ctx.send(socket, {
1290
+ type: 'twilio_config_response',
1291
+ success: false,
1292
+ hasCredentials: false,
1293
+ error: 'Twilio credentials not configured. Set credentials first.',
1294
+ });
1295
+ return;
1296
+ }
1297
+
1298
+ const accountSid = getSecureKey('credential:twilio:account_sid')!;
1299
+ const authToken = getSecureKey('credential:twilio:auth_token')!;
1300
+ const numbers = await listIncomingPhoneNumbers(accountSid, authToken);
1301
+
1302
+ ctx.send(socket, {
1303
+ type: 'twilio_config_response',
1304
+ success: true,
1305
+ hasCredentials: true,
1306
+ numbers,
1307
+ });
1308
+ } else {
1309
+ ctx.send(socket, {
1310
+ type: 'twilio_config_response',
1311
+ success: false,
1312
+ hasCredentials: hasTwilioCredentials(),
1313
+ error: `Unknown action: ${String((msg as unknown as Record<string, unknown>).action)}`,
1314
+ });
1315
+ }
1316
+ } catch (err) {
1317
+ const message = err instanceof Error ? err.message : String(err);
1318
+ log.error({ err }, 'Failed to handle Twilio config');
1319
+ ctx.send(socket, {
1320
+ type: 'twilio_config_response',
1321
+ success: false,
1322
+ hasCredentials: hasTwilioCredentials(),
1323
+ error: message,
1324
+ });
1325
+ }
1326
+ }
1327
+
1328
+ export function handleGuardianVerification(
1329
+ msg: GuardianVerificationRequest,
1330
+ socket: net.Socket,
1331
+ ctx: HandlerContext,
1332
+ ): void {
1333
+ try {
1334
+ // In single-assistant mode, 'self' is the canonical assistant ID used
1335
+ // by channel routes when validating challenges on the inbound path.
1336
+ const assistantId = 'self';
1337
+ const channel = msg.channel ?? 'telegram';
1338
+
1339
+ if (msg.action === 'create_challenge') {
1340
+ const result = createVerificationChallenge(assistantId, channel, msg.sessionId);
1341
+
1342
+ ctx.send(socket, {
1343
+ type: 'guardian_verification_response',
1344
+ success: true,
1345
+ secret: result.secret,
1346
+ instruction: result.instruction,
1347
+ });
1348
+ } else if (msg.action === 'status') {
1349
+ const binding = getGuardianBinding(assistantId, channel);
1350
+ ctx.send(socket, {
1351
+ type: 'guardian_verification_response',
1352
+ success: true,
1353
+ bound: binding !== null,
1354
+ guardianExternalUserId: binding?.guardianExternalUserId,
1355
+ });
1356
+ } else if (msg.action === 'revoke') {
1357
+ const revoked = revokeGuardianBinding(assistantId, channel);
1358
+ ctx.send(socket, {
1359
+ type: 'guardian_verification_response',
1360
+ success: true,
1361
+ bound: !revoked,
1362
+ });
1363
+ } else {
1364
+ ctx.send(socket, {
1365
+ type: 'guardian_verification_response',
1366
+ success: false,
1367
+ error: `Unknown action: ${String(msg.action)}`,
1368
+ });
1369
+ }
1370
+ } catch (err) {
1371
+ const message = err instanceof Error ? err.message : String(err);
1372
+ log.error({ err }, 'Failed to handle guardian verification');
1373
+ ctx.send(socket, {
1374
+ type: 'guardian_verification_response',
1375
+ success: false,
1376
+ error: message,
1377
+ });
1378
+ }
1379
+ }
1380
+
1381
+ export function handleEnvVarsRequest(socket: net.Socket, ctx: HandlerContext): void {
1382
+ const vars: Record<string, string> = {};
1383
+ for (const [key, value] of Object.entries(process.env)) {
1384
+ if (value !== undefined) vars[key] = value;
1385
+ }
1386
+ ctx.send(socket, { type: 'env_vars_response', vars });
1387
+ }
1388
+
1389
+ export async function handleToolPermissionSimulate(
1390
+ msg: ToolPermissionSimulateRequest,
1391
+ socket: net.Socket,
1392
+ ctx: HandlerContext,
1393
+ ): Promise<void> {
1394
+ try {
1395
+ if (!msg.toolName || typeof msg.toolName !== 'string') {
1396
+ ctx.send(socket, {
1397
+ type: 'tool_permission_simulate_response',
1398
+ success: false,
1399
+ error: 'toolName is required',
1400
+ });
1401
+ return;
1402
+ }
1403
+ if (!msg.input || typeof msg.input !== 'object') {
1404
+ ctx.send(socket, {
1405
+ type: 'tool_permission_simulate_response',
1406
+ success: false,
1407
+ error: 'input is required and must be an object',
1408
+ });
1409
+ return;
1410
+ }
1411
+
1412
+ const workingDir = msg.workingDir ?? process.cwd();
1413
+
1414
+ // Only infer execution target when the tool is actually registered;
1415
+ // for unresolved tools, leave it undefined so trust rules are unscoped.
1416
+ const isRegistered = getTool(msg.toolName) !== undefined;
1417
+ const executionTarget = isRegistered ? resolveExecutionTarget(msg.toolName) : undefined;
1418
+ const policyContext = executionTarget ? { executionTarget } : undefined;
1419
+
1420
+ const riskLevel = await classifyRisk(msg.toolName, msg.input, workingDir);
1421
+ const result = await check(msg.toolName, msg.input, workingDir, policyContext);
1422
+
1423
+ // Private-thread override: promote allow → prompt for side-effect tools
1424
+ if (
1425
+ msg.forcePromptSideEffects
1426
+ && result.decision === 'allow'
1427
+ && isSideEffectTool(msg.toolName, msg.input)
1428
+ ) {
1429
+ result.decision = 'prompt';
1430
+ result.reason = 'Private thread: side-effect tools require explicit approval';
1431
+ }
1432
+
1433
+ // Non-interactive override: convert prompt → deny
1434
+ if (msg.isInteractive === false && result.decision === 'prompt') {
1435
+ result.decision = 'deny';
1436
+ result.reason = 'Non-interactive session: no client to approve prompt';
1437
+ }
1438
+
1439
+ // When decision is prompt, generate the full payload the UI needs
1440
+ let promptPayload: {
1441
+ allowlistOptions: Array<{ label: string; description: string; pattern: string }>;
1442
+ scopeOptions: Array<{ label: string; scope: string }>;
1443
+ persistentDecisionsAllowed: boolean;
1444
+ } | undefined;
1445
+
1446
+ if (result.decision === 'prompt') {
1447
+ const allowlistOptions = await generateAllowlistOptions(msg.toolName, msg.input);
1448
+ const scopeOptions = generateScopeOptions(workingDir, msg.toolName);
1449
+ const persistentDecisionsAllowed = !(
1450
+ msg.toolName === 'bash'
1451
+ && msg.input.network_mode === 'proxied'
1452
+ );
1453
+ promptPayload = { allowlistOptions, scopeOptions, persistentDecisionsAllowed };
1454
+ }
1455
+
1456
+ ctx.send(socket, {
1457
+ type: 'tool_permission_simulate_response',
1458
+ success: true,
1459
+ decision: result.decision,
1460
+ riskLevel,
1461
+ reason: result.reason,
1462
+ executionTarget,
1463
+ matchedRuleId: result.matchedRule?.id,
1464
+ promptPayload,
1465
+ });
1466
+ } catch (err) {
1467
+ const message = err instanceof Error ? err.message : String(err);
1468
+ log.error({ err }, 'Failed to simulate tool permission');
1469
+ ctx.send(socket, {
1470
+ type: 'tool_permission_simulate_response',
1471
+ success: false,
1472
+ error: message,
1473
+ });
1474
+ }
1475
+ }
1476
+
1477
+ export function handleToolNamesList(socket: net.Socket, ctx: HandlerContext): void {
1478
+ const tools = getAllTools();
1479
+ const names = tools.map((t) => t.name).sort((a, b) => a.localeCompare(b));
1480
+ const schemas: Record<string, import('../ipc-contract.js').ToolInputSchema> = {};
1481
+ for (const tool of tools) {
1482
+ try {
1483
+ const def = tool.getDefinition();
1484
+ schemas[tool.name] = def.input_schema as import('../ipc-contract.js').ToolInputSchema;
1485
+ } catch {
1486
+ // Skip tools whose definitions can't be resolved
1487
+ }
1488
+ }
1489
+ ctx.send(socket, { type: 'tool_names_list_response', names, schemas });
1490
+ }
1491
+
1492
+ export const configHandlers = defineHandlers({
1493
+ model_get: (_msg, socket, ctx) => handleModelGet(socket, ctx),
1494
+ model_set: handleModelSet,
1495
+ image_gen_model_set: handleImageGenModelSet,
1496
+ add_trust_rule: handleAddTrustRule,
1497
+ trust_rules_list: (_msg, socket, ctx) => handleTrustRulesList(socket, ctx),
1498
+ remove_trust_rule: handleRemoveTrustRule,
1499
+ update_trust_rule: handleUpdateTrustRule,
1500
+ accept_starter_bundle: (_msg, socket, ctx) => handleAcceptStarterBundle(socket, ctx),
1501
+ schedules_list: (_msg, socket, ctx) => handleSchedulesList(socket, ctx),
1502
+ schedule_toggle: handleScheduleToggle,
1503
+ schedule_remove: handleScheduleRemove,
1504
+ reminders_list: (_msg, socket, ctx) => handleRemindersList(socket, ctx),
1505
+ reminder_cancel: handleReminderCancel,
1506
+ share_to_slack: handleShareToSlack,
1507
+ slack_webhook_config: handleSlackWebhookConfig,
1508
+ ingress_config: handleIngressConfig,
1509
+ vercel_api_config: handleVercelApiConfig,
1510
+ twitter_integration_config: handleTwitterIntegrationConfig,
1511
+ telegram_config: handleTelegramConfig,
1512
+ twilio_config: handleTwilioConfig,
1513
+ guardian_verification: handleGuardianVerification,
1514
+ env_vars_request: (_msg, socket, ctx) => handleEnvVarsRequest(socket, ctx),
1515
+ tool_permission_simulate: handleToolPermissionSimulate,
1516
+ tool_names_list: (_msg, socket, ctx) => handleToolNamesList(socket, ctx),
1517
+ });