@vellumai/assistant 0.10.4 → 0.10.5-dev.202607022329.d89bf2f

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 (866) hide show
  1. package/ARCHITECTURE.md +94 -91
  2. package/Dockerfile +5 -16
  3. package/README.md +7 -10
  4. package/bun.lock +0 -4
  5. package/docs/architecture/security.md +20 -20
  6. package/docs/stt-provider-onboarding.md +4 -5
  7. package/docs/trusted-contact-access.md +7 -7
  8. package/eslint-rules/cli-no-daemon-internals.js +4 -0
  9. package/eslint.config.mjs +7 -1
  10. package/knip.json +0 -1
  11. package/node_modules/@vellumai/gateway-client/src/__tests__/contact-read-contracts.test.ts +36 -0
  12. package/node_modules/@vellumai/gateway-client/src/__tests__/invite-contract.test.ts +233 -0
  13. package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +40 -0
  14. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +20 -8
  15. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
  16. package/node_modules/@vellumai/gateway-client/src/index.ts +33 -0
  17. package/node_modules/@vellumai/gateway-client/src/invite-contract.ts +216 -0
  18. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +91 -0
  19. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +39 -3
  20. package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +0 -8
  21. package/node_modules/@vellumai/service-contracts/src/__tests__/package-boundary.test.ts +1 -5
  22. package/node_modules/@vellumai/service-contracts/src/twilio-ingress.ts +3 -14
  23. package/openapi.yaml +1073 -225
  24. package/package.json +1 -4
  25. package/scripts/test.sh +35 -13
  26. package/src/__tests__/access-request-seed-content-blocks.test.ts +60 -4
  27. package/src/__tests__/acp-session.test.ts +4 -1
  28. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +0 -6
  29. package/src/__tests__/add-message-preassigned-id.test.ts +56 -0
  30. package/src/__tests__/agent-loop-set-system-prompt.test.ts +139 -0
  31. package/src/__tests__/app-conversation-ids-backfill.test.ts +3 -0
  32. package/src/__tests__/app-source-watcher.test.ts +11 -0
  33. package/src/__tests__/approval-interception-trust-gates.test.ts +148 -3
  34. package/src/__tests__/assistant-attachments.test.ts +62 -4
  35. package/src/__tests__/assistant-id-boundary-guard.test.ts +0 -1
  36. package/src/__tests__/attachments-store-heic-normalize.test.ts +230 -0
  37. package/src/__tests__/attachments-store.test.ts +3 -0
  38. package/src/__tests__/background-shell-bash.test.ts +34 -0
  39. package/src/__tests__/background-shell-host-bash.test.ts +12 -0
  40. package/src/__tests__/background-tool-registry.test.ts +97 -0
  41. package/src/__tests__/background-tool-routes.test.ts +72 -1
  42. package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
  43. package/src/__tests__/btw-routes.test.ts +57 -3
  44. package/src/__tests__/call-controller.test.ts +474 -84
  45. package/src/__tests__/call-domain.test.ts +48 -1
  46. package/src/__tests__/call-pointer-messages.test.ts +57 -19
  47. package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +2 -6
  48. package/src/__tests__/call-routes-http.test.ts +15 -14
  49. package/src/__tests__/call-setup-flow-invite.test.ts +568 -0
  50. package/src/__tests__/call-setup-flow-midcall-trust.test.ts +231 -0
  51. package/src/__tests__/call-setup-flow-name-capture.test.ts +754 -0
  52. package/src/__tests__/call-setup-flow-verification.test.ts +925 -0
  53. package/src/__tests__/call-setup-flow.test.ts +311 -0
  54. package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -1
  55. package/src/__tests__/channel-guardian.test.ts +4 -7
  56. package/src/__tests__/channel-readiness-service.test.ts +0 -6
  57. package/src/__tests__/channel-readiness-slack-remote.test.ts +0 -4
  58. package/src/__tests__/channel-retry-sweep.test.ts +76 -64
  59. package/src/__tests__/channel-setup-panel-ack.test.ts +184 -0
  60. package/src/__tests__/clear-all-lexical.test.ts +90 -0
  61. package/src/__tests__/compactor-media-strip.test.ts +177 -0
  62. package/src/__tests__/config-loader-backfill.test.ts +5 -5
  63. package/src/__tests__/config-schema.test.ts +17 -0
  64. package/src/__tests__/contact-store-interaction-info.test.ts +27 -70
  65. package/src/__tests__/contacts-relay-reads.test.ts +188 -23
  66. package/src/__tests__/contacts-tools.test.ts +42 -1
  67. package/src/__tests__/contacts-write.test.ts +181 -0
  68. package/src/__tests__/context-search-conversations-source.test.ts +220 -81
  69. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -0
  70. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  71. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  72. package/src/__tests__/conversation-agent-loop.test.ts +39 -0
  73. package/src/__tests__/conversation-analysis-routes.test.ts +1 -1
  74. package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -0
  75. package/src/__tests__/conversation-clear-safety.test.ts +0 -1
  76. package/src/__tests__/conversation-crud-enabled-plugins.test.ts +60 -0
  77. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -1
  78. package/src/__tests__/conversation-disk-view.test.ts +1 -0
  79. package/src/__tests__/conversation-enabledplugins-route.test.ts +184 -0
  80. package/src/__tests__/conversation-error.test.ts +0 -18
  81. package/src/__tests__/conversation-fork-crud.test.ts +67 -0
  82. package/src/__tests__/conversation-history-web-search.test.ts +7 -137
  83. package/src/__tests__/conversation-initial-prompt.test.ts +110 -0
  84. package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +191 -0
  85. package/src/__tests__/conversation-process-callsite.test.ts +37 -0
  86. package/src/__tests__/conversation-queue.test.ts +149 -0
  87. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +392 -0
  88. package/src/__tests__/conversation-routes-guardian-reply.test.ts +84 -0
  89. package/src/__tests__/conversation-routes-hidden-queue.test.ts +407 -0
  90. package/src/__tests__/conversation-runtime-assembly.test.ts +36 -16
  91. package/src/__tests__/conversation-skill-tools.test.ts +73 -0
  92. package/src/__tests__/conversation-wipe.test.ts +20 -5
  93. package/src/__tests__/credential-prompt-route.test.ts +69 -0
  94. package/src/__tests__/credential-security-invariants.test.ts +1 -3
  95. package/src/__tests__/db-drop-messages-fts.test.ts +115 -0
  96. package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -62
  97. package/src/__tests__/edit-propagation.test.ts +56 -2
  98. package/src/__tests__/empty-state-greeting-cache.test.ts +9 -0
  99. package/src/__tests__/gateway-only-enforcement.test.ts +15 -110
  100. package/src/__tests__/guardian-expiry-notifier.test.ts +1 -0
  101. package/src/__tests__/guardian-question-mode.test.ts +57 -0
  102. package/src/__tests__/guardian-verification-voice-binding.test.ts +1 -1
  103. package/src/__tests__/guardian-wait-controller.test.ts +619 -0
  104. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +33 -2
  105. package/src/__tests__/heic-fixture.ts +55 -0
  106. package/src/__tests__/helpers/seed-contact-channel.ts +0 -2
  107. package/src/__tests__/http-conversation-lineage.test.ts +6 -1
  108. package/src/__tests__/image-conversion.test.ts +170 -0
  109. package/src/__tests__/inbound-invite-redemption.test.ts +72 -341
  110. package/src/__tests__/introduction-card-resolver.test.ts +354 -0
  111. package/src/__tests__/invite-redeemed-ipc.test.ts +97 -0
  112. package/src/__tests__/invite-routes-http.test.ts +246 -420
  113. package/src/__tests__/job-handler-registration-guard.test.ts +104 -0
  114. package/src/__tests__/lexical-index-dual-write.test.ts +426 -0
  115. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +2 -2
  116. package/src/__tests__/list-messages-attachments.test.ts +159 -0
  117. package/src/__tests__/list-messages-background-tool-completion.test.ts +141 -0
  118. package/src/__tests__/list-messages-queued.test.ts +25 -0
  119. package/src/__tests__/managed-store.test.ts +129 -1
  120. package/src/__tests__/media-stream-audio-transcode.test.ts +113 -0
  121. package/src/__tests__/media-stream-output.test.ts +350 -19
  122. package/src/__tests__/media-stream-server-integration.test.ts +943 -169
  123. package/src/__tests__/media-stream-stt-session.test.ts +498 -1
  124. package/src/__tests__/memory-jobs-worker-lanes.test.ts +2 -2
  125. package/src/__tests__/model-intents.test.ts +1 -1
  126. package/src/__tests__/mtime-cache.test.ts +19 -0
  127. package/src/__tests__/non-member-access-request.test.ts +349 -3
  128. package/src/__tests__/notification-decision-fallback.test.ts +104 -8
  129. package/src/__tests__/notification-decision-strategy.test.ts +57 -8
  130. package/src/__tests__/onboarding-template-contract.test.ts +27 -43
  131. package/src/__tests__/openai-provider.test.ts +33 -0
  132. package/src/__tests__/openai-responses-provider.test.ts +40 -0
  133. package/src/__tests__/persistence-layering-guard.test.ts +29 -20
  134. package/src/__tests__/persona-resolver.test.ts +56 -2
  135. package/src/__tests__/personal-memory-auth-bypass.test.ts +62 -0
  136. package/src/__tests__/phone.test.ts +1 -15
  137. package/src/__tests__/plugin-bootstrap.test.ts +25 -196
  138. package/src/__tests__/plugin-disabled-state.test.ts +33 -0
  139. package/src/__tests__/plugin-effective-enabled-set.test.ts +227 -0
  140. package/src/__tests__/plugin-event-hub-facade.test.ts +323 -0
  141. package/src/__tests__/plugin-import-boundary-guard.test.ts +156 -42
  142. package/src/__tests__/plugin-injector-contribution.test.ts +4 -20
  143. package/src/__tests__/plugin-pipeline.test.ts +63 -1
  144. package/src/__tests__/plugin-route-contribution.test.ts +62 -115
  145. package/src/__tests__/plugin-tool-contribution.test.ts +0 -22
  146. package/src/__tests__/plugin-types.test.ts +0 -1
  147. package/src/__tests__/pointer-conversation-trust.test.ts +38 -6
  148. package/src/__tests__/pre-model-call-sanitize.test.ts +1 -1
  149. package/src/__tests__/prune-old-tool-invocations-job.test.ts +97 -0
  150. package/src/__tests__/public-ingress-urls.test.ts +0 -48
  151. package/src/__tests__/qdrant-collection-migration.test.ts +145 -2
  152. package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +0 -1
  153. package/src/__tests__/resolve-trust-class.test.ts +28 -10
  154. package/src/__tests__/scaffold-managed-skill-tool.test.ts +137 -0
  155. package/src/__tests__/schedule-retry.test.ts +38 -14
  156. package/src/__tests__/schedule-routes-workflow-validation.test.ts +3 -3
  157. package/src/__tests__/schedule-routes.test.ts +14 -6
  158. package/src/__tests__/schedule-store.test.ts +3 -1
  159. package/src/__tests__/scheduler-disk-pressure.test.ts +17 -6
  160. package/src/__tests__/scheduler-recurrence.test.ts +57 -107
  161. package/src/__tests__/scheduler-reuse-conversation.test.ts +51 -46
  162. package/src/__tests__/scheduler-wake.test.ts +13 -23
  163. package/src/__tests__/scheduler-worker-standdown.test.ts +220 -0
  164. package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
  165. package/src/__tests__/shell-observability.test.ts +1 -0
  166. package/src/__tests__/skill-boundary-guard.test.ts +3 -4
  167. package/src/__tests__/skill-load-plugin-scope.test.ts +238 -0
  168. package/src/__tests__/skill-projection-feature-flag.test.ts +2 -0
  169. package/src/__tests__/skill-script-runner-host.test.ts +53 -0
  170. package/src/__tests__/skill-script-runner-sandbox.test.ts +5 -2
  171. package/src/__tests__/skill-tool-factory.test.ts +73 -1
  172. package/src/__tests__/slack-app-setup-skill-regression.test.ts +37 -14
  173. package/src/__tests__/slack-inbound-verification.test.ts +36 -0
  174. package/src/__tests__/slack-notification-approval-card.test.ts +89 -2
  175. package/src/__tests__/slack-reaction-canonical-approval.test.ts +51 -1
  176. package/src/__tests__/stt-hints.test.ts +2 -256
  177. package/src/__tests__/subagent-allowlist-validation.test.ts +47 -0
  178. package/src/__tests__/subagent-call-site-routing.test.ts +72 -0
  179. package/src/__tests__/subagent-fork-notifications.test.ts +14 -2
  180. package/src/__tests__/subagent-persistence.test.ts +122 -0
  181. package/src/__tests__/subagent-spawn-and-await.test.ts +1 -0
  182. package/src/__tests__/subagent-terminal-message.test.ts +99 -0
  183. package/src/__tests__/subagent-tool-gate-mode.test.ts +78 -1
  184. package/src/__tests__/system-prompt.test.ts +17 -118
  185. package/src/__tests__/task-scheduler.test.ts +68 -89
  186. package/src/__tests__/telephony-credential-preflight.test.ts +312 -0
  187. package/src/__tests__/telephony-tts-guard.test.ts +538 -0
  188. package/src/__tests__/title-generate-hook.test.ts +16 -0
  189. package/src/__tests__/tool-approval-handler.test.ts +87 -1
  190. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +45 -20
  191. package/src/__tests__/tool-grant-request-escalation.test.ts +39 -0
  192. package/src/__tests__/trust-context-guards.test.ts +21 -10
  193. package/src/__tests__/trusted-contact-multichannel.test.ts +4 -0
  194. package/src/__tests__/twilio-provider.test.ts +17 -17
  195. package/src/__tests__/twilio-routes-twiml.test.ts +6 -543
  196. package/src/__tests__/twilio-routes.test.ts +257 -387
  197. package/src/__tests__/twilio-validation.test.ts +1 -1
  198. package/src/__tests__/unified-turn-context-client-os.test.ts +45 -0
  199. package/src/__tests__/usage-cache-backfill-migration.test.ts +5 -0
  200. package/src/__tests__/voice-approval-resolver-relay.test.ts +150 -0
  201. package/src/__tests__/voice-ingress-preflight.test.ts +61 -0
  202. package/src/__tests__/voice-quality.test.ts +2 -2
  203. package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -7
  204. package/src/__tests__/voice-session-bridge-processing-wait.test.ts +27 -0
  205. package/src/__tests__/voice-session-bridge.test.ts +1 -10
  206. package/src/__tests__/workspace-git-service.test.ts +1 -1
  207. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -0
  208. package/src/__tests__/workspace-migration-118-seed-now-md.test.ts +120 -0
  209. package/src/__tests__/workspace-migration-120-revise-onboarding-threads.test.ts +175 -0
  210. package/src/__tests__/workspace-migration-121-seed-default-user-guardrails.test.ts +118 -0
  211. package/src/__tests__/workspace-migration-122-relocate-default-user-boundary.test.ts +119 -0
  212. package/src/__tests__/workspace-migration-123-swap-quality-profile-to-fable.test.ts +191 -0
  213. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +235 -241
  214. package/src/acp/__tests__/prepare-agent-env.test.ts +0 -97
  215. package/src/acp/__tests__/session-manager-persistence.test.ts +2 -0
  216. package/src/acp/__tests__/session-manager-resume.test.ts +8 -0
  217. package/src/acp/__tests__/session-manager-usage.test.ts +6 -0
  218. package/src/acp/__tests__/session-manager.test.ts +6 -0
  219. package/src/acp/agent-process.test.ts +116 -0
  220. package/src/acp/agent-process.ts +71 -0
  221. package/src/acp/auto-install.ts +12 -8
  222. package/src/acp/failure-error.test.ts +109 -0
  223. package/src/acp/failure-error.ts +111 -0
  224. package/src/acp/prepare-agent-env.ts +0 -12
  225. package/src/acp/resolve-agent.test.ts +6 -45
  226. package/src/acp/resolve-agent.ts +2 -4
  227. package/src/acp/session-manager.test.ts +312 -0
  228. package/src/acp/session-manager.ts +102 -46
  229. package/src/agent/ax-tree-compaction.test.ts +4 -1
  230. package/src/agent/image-optimize.ts +17 -127
  231. package/src/agent/loop.ts +45 -192
  232. package/src/api/events/background-tool-completed.test.ts +51 -0
  233. package/src/api/events/background-tool-completed.ts +29 -0
  234. package/src/api/events/background-tool-started.test.ts +36 -0
  235. package/src/api/events/background-tool-started.ts +28 -0
  236. package/src/api/events/conversation-error.ts +0 -1
  237. package/src/api/events/open-panel.ts +33 -0
  238. package/src/api/events/subagent-status-changed.ts +4 -1
  239. package/src/api/index.ts +19 -0
  240. package/src/api/responses/conversation-message.ts +34 -0
  241. package/src/approvals/guardian-decision-primitive.ts +74 -24
  242. package/src/approvals/guardian-request-resolvers.ts +450 -122
  243. package/src/apps/app-store.ts +1 -0
  244. package/src/apps/shared-app-links-store.ts +1 -0
  245. package/src/avatar/identity-avatar.ts +3 -7
  246. package/src/background-wake/wake-intent-hooks.test.ts +1 -1
  247. package/src/calls/__tests__/fish-audio-client.test.ts +73 -0
  248. package/src/calls/__tests__/gateway-invite-reader.test.ts +170 -0
  249. package/src/calls/__tests__/relay-setup-router.test.ts +126 -102
  250. package/src/calls/call-controller.ts +134 -48
  251. package/src/calls/call-domain.ts +18 -42
  252. package/src/calls/call-pointer-messages.ts +51 -67
  253. package/src/calls/call-setup-flow-types.ts +76 -0
  254. package/src/calls/call-setup-flow.ts +1743 -0
  255. package/src/calls/call-speech-output.ts +87 -23
  256. package/src/calls/call-store.ts +4 -0
  257. package/src/calls/call-transport.ts +22 -36
  258. package/src/calls/fish-audio-client.ts +3 -0
  259. package/src/calls/gateway-invite-reader.ts +98 -0
  260. package/src/calls/guardian-wait-controller.ts +451 -0
  261. package/src/calls/media-stream-audio-transcode.ts +82 -2
  262. package/src/calls/media-stream-output.ts +142 -36
  263. package/src/calls/media-stream-server.ts +297 -178
  264. package/src/calls/media-stream-stt-session.ts +393 -50
  265. package/src/calls/media-turn-detector.ts +4 -6
  266. package/src/calls/relay-access-wait.ts +3 -4
  267. package/src/calls/relay-setup-router.ts +22 -43
  268. package/src/calls/relay-verification.ts +17 -24
  269. package/src/calls/resolve-call-tts-provider.ts +96 -29
  270. package/src/calls/stt-hints.ts +24 -87
  271. package/src/calls/telephony-credential-preflight.ts +159 -0
  272. package/src/calls/telephony-tts-capability.ts +183 -0
  273. package/src/calls/tts-call-strategy.ts +18 -20
  274. package/src/calls/twilio-provider.ts +3 -2
  275. package/src/calls/twilio-routes.ts +47 -351
  276. package/src/calls/types.ts +5 -3
  277. package/src/calls/voice-ingress-preflight.ts +9 -0
  278. package/src/calls/voice-quality.ts +13 -18
  279. package/src/calls/voice-session-bridge.ts +25 -35
  280. package/src/channels/__tests__/types.test.ts +28 -0
  281. package/src/channels/channel-verification-sessions.ts +7 -2
  282. package/src/channels/config.ts +0 -42
  283. package/src/channels/slack-thread-store.ts +12 -0
  284. package/src/channels/types.ts +23 -0
  285. package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -1
  286. package/src/cli/commands/__tests__/schedules.test.ts +83 -34
  287. package/src/cli/commands/__tests__/status.test.ts +42 -2
  288. package/src/cli/commands/contacts.ts +2 -2
  289. package/src/cli/commands/credentials.ts +17 -0
  290. package/src/cli/commands/monitoring.ts +191 -0
  291. package/src/cli/commands/schedules-worker.ts +175 -0
  292. package/src/cli/commands/schedules.ts +169 -43
  293. package/src/cli/commands/status.ts +12 -1
  294. package/src/cli/commands/tools.ts +5 -1
  295. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +1 -0
  296. package/src/cli/lib/__tests__/search-plugins.test.ts +29 -1
  297. package/src/cli/lib/__tests__/toggle-plugin.test.ts +7 -0
  298. package/src/cli/lib/search-plugins.ts +6 -0
  299. package/src/cli/lib/toggle-plugin.ts +21 -2
  300. package/src/cli/program.ts +3 -1
  301. package/src/config/__tests__/deployment-context-defaults.test.ts +23 -0
  302. package/src/config/acp-defaults.test.ts +0 -10
  303. package/src/config/acp-defaults.ts +0 -6
  304. package/src/config/bundled-skills/acp/SKILL.md +13 -45
  305. package/src/config/bundled-skills/acp/TOOLS.json +3 -3
  306. package/src/config/bundled-skills/app-builder/SKILL.md +3 -3
  307. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +9 -9
  308. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +1 -1
  309. package/src/config/bundled-skills/contacts/tools/contact-search.ts +1 -1
  310. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
  311. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -9
  312. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +10 -7
  313. package/src/config/bundled-skills/skill-management/TOOLS.json +10 -0
  314. package/src/config/call-site-defaults.ts +0 -2
  315. package/src/config/feature-flag-registry.json +16 -8
  316. package/src/config/loader.ts +11 -0
  317. package/src/config/schema.ts +9 -1
  318. package/src/config/schemas/__tests__/memory-v2.test.ts +2 -2
  319. package/src/config/schemas/__tests__/memory-v3.test.ts +64 -0
  320. package/src/config/schemas/call-site-catalog.ts +0 -12
  321. package/src/config/schemas/calls.ts +8 -0
  322. package/src/config/schemas/elevenlabs.ts +1 -1
  323. package/src/config/schemas/llm.ts +0 -2
  324. package/src/config/schemas/memory-v2.ts +1 -1
  325. package/src/config/schemas/memory-v3.ts +82 -0
  326. package/src/config/schemas/monitoring.ts +61 -0
  327. package/src/config/schemas/schedules.ts +35 -0
  328. package/src/config/seed-inference-profiles.ts +2 -2
  329. package/src/config/skills-plugin-filter.test.ts +96 -0
  330. package/src/config/skills.ts +26 -0
  331. package/src/contacts/__tests__/member-write-relay.test.ts +156 -4
  332. package/src/contacts/canonical-guardian-store.ts +5 -0
  333. package/src/contacts/contact-store.ts +81 -35
  334. package/src/contacts/contacts-write.ts +50 -25
  335. package/src/contacts/member-write-relay.ts +115 -31
  336. package/src/contacts/types.ts +0 -8
  337. package/src/context/compactor.ts +29 -15
  338. package/src/context/outbound-sanitize.ts +202 -0
  339. package/src/daemon/__tests__/abort-null-controller.test.ts +167 -0
  340. package/src/daemon/__tests__/conversation-tool-setup-plugin-scope.test.ts +122 -0
  341. package/src/daemon/__tests__/embedding-reconcile.test.ts +99 -16
  342. package/src/daemon/__tests__/wake-conversation-ops-completion.test.ts +107 -0
  343. package/src/daemon/abort-watchdog.ts +7 -0
  344. package/src/daemon/app-source-watcher.ts +9 -0
  345. package/src/daemon/assistant-attachments.ts +42 -0
  346. package/src/daemon/conversation-agent-loop-handlers.ts +256 -147
  347. package/src/daemon/conversation-agent-loop.ts +61 -68
  348. package/src/daemon/conversation-error.ts +1 -15
  349. package/src/daemon/conversation-history.ts +32 -236
  350. package/src/daemon/conversation-initial-prompt.ts +77 -0
  351. package/src/daemon/conversation-lifecycle.ts +25 -1
  352. package/src/daemon/conversation-messaging.ts +2 -2
  353. package/src/daemon/conversation-plugin-scope.ts +38 -0
  354. package/src/daemon/conversation-process.ts +50 -14
  355. package/src/daemon/conversation-runtime-assembly.ts +36 -6
  356. package/src/daemon/conversation-skill-tools.ts +19 -3
  357. package/src/daemon/conversation-store.ts +3 -3
  358. package/src/daemon/conversation-surfaces.ts +171 -0
  359. package/src/daemon/conversation-tool-setup.test.ts +95 -0
  360. package/src/daemon/conversation-tool-setup.ts +116 -1
  361. package/src/daemon/conversation-turn-finalize.ts +211 -0
  362. package/src/daemon/conversation.ts +92 -1
  363. package/src/daemon/disk-pressure-policy.ts +2 -1
  364. package/src/daemon/event-loop-watchdog.ts +22 -6
  365. package/src/daemon/external-plugins-bootstrap.ts +47 -134
  366. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +1 -1
  367. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +26 -14
  368. package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +27 -14
  369. package/src/daemon/handlers/__tests__/config-channels.test.ts +0 -5
  370. package/src/daemon/handlers/config-a2a.ts +7 -14
  371. package/src/daemon/handlers/config-channels.ts +2 -10
  372. package/src/daemon/handlers/conversation-history.ts +3 -1
  373. package/src/daemon/handlers/conversations.ts +0 -52
  374. package/src/daemon/handlers/shared.ts +4 -24
  375. package/src/daemon/handlers/skills.ts +4 -1
  376. package/src/daemon/host-cu-proxy.ts +1 -1
  377. package/src/daemon/lifecycle.ts +59 -472
  378. package/src/daemon/message-protocol.ts +3 -3
  379. package/src/daemon/message-provenance.ts +12 -14
  380. package/src/daemon/message-types/background-tools.ts +28 -0
  381. package/src/daemon/message-types/contacts.ts +2 -10
  382. package/src/daemon/message-types/conversations.ts +10 -0
  383. package/src/daemon/message-types/integrations.ts +2 -0
  384. package/src/daemon/message-types/surfaces.ts +1 -0
  385. package/src/daemon/message-types/sync.ts +1 -0
  386. package/src/daemon/pointer-conversation-trust.ts +23 -9
  387. package/src/daemon/pointer-turn-runner.ts +174 -0
  388. package/src/daemon/providers-setup.ts +16 -0
  389. package/src/daemon/shutdown-handlers.ts +139 -47
  390. package/src/daemon/skill-memory-refresh.ts +2 -2
  391. package/src/daemon/sqlite-corruption-watchdog.test.ts +217 -0
  392. package/src/daemon/sqlite-corruption-watchdog.ts +155 -0
  393. package/src/daemon/tool-setup-types.ts +8 -0
  394. package/src/daemon/trust-context.ts +21 -6
  395. package/src/daemon/turn-latency-tracker.test.ts +93 -0
  396. package/src/daemon/turn-latency-tracker.ts +38 -0
  397. package/src/daemon/wake-conversation-ops.ts +10 -8
  398. package/src/documents/document-comments-store.test.ts +7 -1
  399. package/src/documents/document-comments-store.ts +8 -0
  400. package/src/documents/document-store.ts +15 -0
  401. package/src/export/__tests__/transcript-formatter.test.ts +11 -0
  402. package/src/export/transcript-formatter.ts +49 -67
  403. package/src/home/__tests__/feed-writer.test.ts +247 -1
  404. package/src/home/conversation-starter-checkpoints.ts +1 -0
  405. package/src/home/feed-writer.ts +61 -0
  406. package/src/home/job-handlers/conversation-starters.ts +1 -0
  407. package/src/hooks/hook-loader.ts +12 -0
  408. package/src/hooks/registry.ts +32 -4
  409. package/src/inbound/public-ingress-urls.ts +0 -17
  410. package/src/ipc/__tests__/contacts-info-ipc.test.ts +285 -0
  411. package/src/ipc/assistant-server.ts +28 -7
  412. package/src/ipc/routes/__tests__/contacts-mirror-apply-atomicity.test.ts +62 -0
  413. package/src/ipc/routes/__tests__/contacts-mirror-apply.test.ts +272 -0
  414. package/src/ipc/routes/__tests__/contacts-mirror-ipc-routes.test.ts +322 -0
  415. package/src/ipc/routes/__tests__/invite-ipc-routes.test.ts +60 -4
  416. package/src/ipc/routes/contacts-info-ipc-routes.ts +239 -0
  417. package/src/ipc/routes/contacts-mirror-ipc-routes.ts +202 -0
  418. package/src/ipc/routes/db-proxy.ts +5 -2
  419. package/src/ipc/routes/invite-ipc-routes.ts +53 -39
  420. package/src/jobs/worker.ts +9 -2
  421. package/src/live-voice/__tests__/live-voice-archive.test.ts +10 -1
  422. package/src/live-voice/live-voice-archive.ts +5 -0
  423. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +22 -6
  424. package/src/messaging/providers/channel-transport.ts +6 -0
  425. package/src/messaging/providers/index.ts +3 -0
  426. package/src/messaging/providers/slack/api.test.ts +49 -1
  427. package/src/messaging/providers/slack/api.ts +110 -0
  428. package/src/messaging/providers/slack/send.test.ts +85 -0
  429. package/src/messaging/providers/slack/send.ts +100 -28
  430. package/src/messaging/providers/slack/transport.ts +7 -13
  431. package/src/messaging/providers/whatsapp/send.ts +29 -9
  432. package/src/monitoring/__tests__/control.test.ts +235 -0
  433. package/src/monitoring/__tests__/sample-ring-buffer.test.ts +96 -0
  434. package/src/monitoring/control.ts +105 -0
  435. package/src/monitoring/resource-sampler.ts +258 -0
  436. package/src/monitoring/sample-ring-buffer.ts +108 -0
  437. package/src/monitoring/worker.ts +94 -0
  438. package/src/notifications/README.md +1 -1
  439. package/src/notifications/access-request-copy.ts +94 -23
  440. package/src/notifications/adapters/slack.ts +89 -21
  441. package/src/notifications/approval-card-builder.ts +33 -18
  442. package/src/notifications/approval-card-data.ts +48 -15
  443. package/src/notifications/broadcaster.ts +25 -16
  444. package/src/notifications/decision-engine.ts +131 -32
  445. package/src/notifications/guardian-question-mode.ts +58 -10
  446. package/src/permissions/approval-policy.ts +8 -1
  447. package/src/permissions/checker.ts +2 -0
  448. package/src/permissions/gateway-threshold-reader.ts +7 -6
  449. package/src/permissions/ipc-risk-types.ts +1 -0
  450. package/src/permissions/secret-prompter.ts +11 -3
  451. package/src/permissions/types.ts +2 -2
  452. package/src/persistence/__tests__/conversation-queries-search.test.ts +397 -0
  453. package/src/persistence/__tests__/conversation-search-lexical.test.ts +103 -0
  454. package/src/persistence/__tests__/jobs-worker-memory-disabled.test.ts +95 -0
  455. package/src/persistence/__tests__/slow-query-log.test.ts +266 -0
  456. package/src/persistence/__tests__/slow-sync-log.test.ts +155 -0
  457. package/src/persistence/a2a-invite-store.ts +205 -0
  458. package/src/persistence/attachments-store.ts +94 -10
  459. package/src/persistence/checkpoints.ts +45 -0
  460. package/src/persistence/conversation-crud.ts +453 -260
  461. package/src/persistence/conversation-display-order-migration.ts +8 -2
  462. package/src/persistence/conversation-group-migration.test.ts +8 -0
  463. package/src/persistence/conversation-group-migration.ts +9 -0
  464. package/src/persistence/conversation-queries.ts +244 -125
  465. package/src/persistence/conversation-row-batch-delete.ts +9 -5
  466. package/src/persistence/conversation-search-lexical.ts +49 -0
  467. package/src/persistence/db-async-query.ts +42 -0
  468. package/src/persistence/db-connection.ts +13 -0
  469. package/src/persistence/delivery-channels.ts +0 -82
  470. package/src/persistence/delivery-crud.ts +27 -6
  471. package/src/persistence/embeddings/__tests__/embedding-identity.test.ts +18 -5
  472. package/src/persistence/embeddings/__tests__/messages-lexical-index.test.ts +334 -0
  473. package/src/persistence/embeddings/embedding-identity.ts +17 -5
  474. package/src/persistence/embeddings/embedding-runtime-manager.ts +31 -0
  475. package/src/persistence/embeddings/messages-lexical-index.ts +396 -0
  476. package/src/persistence/embeddings/qdrant-client.ts +81 -5
  477. package/src/persistence/group-crud.ts +13 -1
  478. package/src/persistence/job-handlers/__tests__/message-lexical-backfill.test.ts +506 -0
  479. package/src/persistence/job-handlers/__tests__/message-lexical-enqueue.test.ts +164 -0
  480. package/src/persistence/job-handlers/__tests__/message-lexical.test.ts +259 -0
  481. package/src/persistence/job-handlers/cleanup.ts +57 -9
  482. package/src/persistence/job-handlers/message-lexical-backfill.ts +187 -0
  483. package/src/persistence/job-handlers/message-lexical.ts +251 -0
  484. package/src/persistence/jobs-store.ts +102 -8
  485. package/src/persistence/jobs-worker.ts +68 -28
  486. package/src/persistence/llm-request-log-store.ts +35 -19
  487. package/src/persistence/llm-usage-store.ts +11 -0
  488. package/src/persistence/memory-lifecycle-hooks.test.ts +65 -0
  489. package/src/persistence/memory-lifecycle-hooks.ts +55 -1
  490. package/src/persistence/migrations/028-call-session-mode.ts +1 -1
  491. package/src/persistence/migrations/116-messages-fts.ts +8 -1
  492. package/src/persistence/migrations/311-create-subagents-table.ts +50 -0
  493. package/src/persistence/migrations/312-drop-inbox-conversation-state-table.ts +15 -0
  494. package/src/persistence/migrations/313-drop-messages-fts.ts +17 -0
  495. package/src/persistence/migrations/314-add-conversation-enabled-plugins.ts +25 -0
  496. package/src/persistence/migrations/315-create-a2a-invites.ts +58 -0
  497. package/src/persistence/migrations/316-drop-contact-channels-invite-id.ts +28 -0
  498. package/src/persistence/migrations/317-canonical-guardian-requester-signals.ts +24 -0
  499. package/src/persistence/migrations/318-drop-contact-channel-telemetry.ts +32 -0
  500. package/src/persistence/migrations/__tests__/315-create-a2a-invites.test.ts +161 -0
  501. package/src/persistence/migrations/__tests__/316-drop-contact-channels-invite-id.test.ts +156 -0
  502. package/src/persistence/migrations/__tests__/318-drop-contact-channel-telemetry.test.ts +134 -0
  503. package/src/persistence/migrations/__tests__/migration-prefix-guard.test.ts +99 -0
  504. package/src/persistence/raw-query.ts +50 -11
  505. package/src/persistence/schema/a2a.ts +14 -0
  506. package/src/persistence/schema/contacts.ts +0 -62
  507. package/src/persistence/schema/conversation-groups.ts +12 -0
  508. package/src/persistence/schema/conversations.ts +2 -0
  509. package/src/persistence/schema/documents.ts +65 -0
  510. package/src/persistence/schema/guardian.ts +4 -0
  511. package/src/persistence/schema/index.ts +4 -0
  512. package/src/persistence/schema/memory-injection.ts +62 -0
  513. package/src/persistence/schema/workflows.ts +55 -0
  514. package/src/persistence/slow-query-log.ts +437 -0
  515. package/src/persistence/slow-sync-log.ts +204 -0
  516. package/src/persistence/steps.ts +16 -0
  517. package/src/persistence/subagent-store.ts +125 -0
  518. package/src/persistence/worker-control.ts +45 -238
  519. package/src/plugin-api/event-hub-facade.ts +172 -0
  520. package/src/plugin-api/index.ts +9 -1
  521. package/src/plugin-api/types.ts +40 -9
  522. package/src/plugins/defaults/default-plugin-names.ts +60 -0
  523. package/src/plugins/defaults/index.ts +19 -29
  524. package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +46 -67
  525. package/src/plugins/defaults/memory/__tests__/db-async-query.test.ts +36 -1
  526. package/src/plugins/defaults/memory/__tests__/find-most-recent-retrospective-for.test.ts +2 -4
  527. package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +2 -2
  528. package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-schedule.test.ts +9 -0
  529. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +7 -4
  530. package/src/plugins/defaults/memory/config.ts +11 -0
  531. package/src/plugins/defaults/memory/context-search/agent-runner.ts +1 -1
  532. package/src/plugins/defaults/memory/context-search/sources/conversations.ts +124 -113
  533. package/src/plugins/defaults/memory/embeddings.ts +77 -0
  534. package/src/plugins/defaults/memory/find-most-recent-retrospective-for.ts +66 -0
  535. package/src/plugins/defaults/memory/frontmatter.ts +50 -0
  536. package/src/plugins/defaults/memory/graph/bootstrap.test.ts +36 -10
  537. package/src/plugins/defaults/memory/graph/bootstrap.ts +6 -5
  538. package/src/plugins/defaults/memory/graph/consolidation.ts +3 -5
  539. package/src/plugins/defaults/memory/graph/extraction.ts +3 -6
  540. package/src/plugins/defaults/memory/graph/graph-search.ts +12 -23
  541. package/src/plugins/defaults/memory/graph/narrative.ts +3 -5
  542. package/src/plugins/defaults/memory/graph/pattern-scan.ts +3 -5
  543. package/src/plugins/defaults/memory/graph/retriever.test.ts +1 -6
  544. package/src/plugins/defaults/memory/graph/retriever.ts +5 -10
  545. package/src/plugins/defaults/memory/graph/tools.ts +1 -1
  546. package/src/plugins/defaults/memory/hooks/init.ts +31 -5
  547. package/src/plugins/defaults/memory/indexer.ts +2 -3
  548. package/src/plugins/defaults/memory/injectors.ts +2 -2
  549. package/src/plugins/defaults/memory/job-handler-registration.ts +96 -0
  550. package/src/plugins/defaults/memory/job-handlers/backfill.ts +4 -26
  551. package/src/plugins/defaults/memory/job-handlers/embedding.test.ts +6 -19
  552. package/src/plugins/defaults/memory/job-handlers/embedding.ts +9 -22
  553. package/src/plugins/defaults/memory/job-handlers/index-maintenance.ts +2 -3
  554. package/src/plugins/defaults/memory/job-handlers.ts +13 -12
  555. package/src/plugins/defaults/memory/jobs/embed-concept-page.ts +1 -1
  556. package/src/plugins/defaults/memory/llm-helpers.ts +76 -0
  557. package/src/plugins/defaults/memory/memory-retrospective-job.ts +8 -5
  558. package/src/plugins/defaults/memory/memory-retrospective-startup-cleanup.ts +2 -2
  559. package/src/plugins/defaults/memory/memory-v2-concept-frequency.ts +2 -0
  560. package/src/plugins/defaults/memory/persistence-hooks.ts +46 -1
  561. package/src/plugins/defaults/memory/pkb/pkb-index.ts +1 -5
  562. package/src/plugins/defaults/memory/pkb/pkb-search.ts +2 -2
  563. package/src/plugins/defaults/memory/routes/__tests__/memory-v2-simulate-route.test.ts +4 -6
  564. package/src/plugins/defaults/memory/routes/memory-item-routes.ts +1 -1
  565. package/src/plugins/defaults/memory/search/semantic.ts +2 -2
  566. package/src/plugins/defaults/memory/startup.ts +244 -0
  567. package/src/plugins/defaults/memory/task-memory-cleanup.ts +8 -0
  568. package/src/{daemon → plugins/defaults/memory/v2}/__tests__/memory-v2-startup.test.ts +6 -6
  569. package/src/plugins/defaults/memory/v2/__tests__/migration.test.ts +2 -13
  570. package/src/plugins/defaults/memory/v2/__tests__/router.test.ts +4 -5
  571. package/src/plugins/defaults/memory/v2/__tests__/sweep-job.test.ts +1 -8
  572. package/src/plugins/defaults/memory/v2/activation.ts +2 -4
  573. package/src/plugins/defaults/memory/v2/cli-command-store.ts +2 -4
  574. package/src/plugins/defaults/memory/v2/frontmatter-sweep.ts +1 -1
  575. package/src/{daemon → plugins/defaults/memory/v2}/memory-v2-startup.test.ts +8 -8
  576. package/src/{daemon → plugins/defaults/memory/v2}/memory-v2-startup.ts +20 -22
  577. package/src/plugins/defaults/memory/v2/migration.ts +2 -5
  578. package/src/plugins/defaults/memory/v2/page-store.ts +1 -1
  579. package/src/plugins/defaults/memory/v2/qdrant.ts +2 -3
  580. package/src/plugins/defaults/memory/v2/router.ts +5 -5
  581. package/src/plugins/defaults/memory/v2/sim.ts +2 -4
  582. package/src/plugins/defaults/memory/v2/skill-store.ts +2 -4
  583. package/src/plugins/defaults/memory/v2/sweep-job.ts +6 -6
  584. package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +11 -9
  585. package/src/plugins/defaults/memory/v3/__tests__/dense.test.ts +107 -2
  586. package/src/plugins/defaults/memory/v3/__tests__/gate-flag.test.ts +69 -0
  587. package/src/plugins/defaults/memory/v3/__tests__/gate.test.ts +181 -0
  588. package/src/plugins/defaults/memory/v3/__tests__/live-integration.test.ts +7 -3
  589. package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +345 -8
  590. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +1 -4
  591. package/src/plugins/defaults/memory/v3/__tests__/section-needle.test.ts +64 -0
  592. package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +18 -13
  593. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +62 -1
  594. package/src/plugins/defaults/memory/v3/card.ts +1 -4
  595. package/src/plugins/defaults/memory/v3/dense.ts +60 -25
  596. package/src/plugins/defaults/memory/v3/edge.ts +1 -1
  597. package/src/plugins/defaults/memory/v3/gate-flag.ts +20 -0
  598. package/src/plugins/defaults/memory/v3/gate.ts +158 -0
  599. package/src/plugins/defaults/memory/v3/maintain-job.ts +2 -4
  600. package/src/plugins/defaults/memory/v3/orchestrate.ts +186 -25
  601. package/src/plugins/defaults/memory/v3/pool-select.test.ts +7 -9
  602. package/src/plugins/defaults/memory/v3/pool-select.ts +6 -6
  603. package/src/plugins/defaults/memory/v3/prune.ts +2 -2
  604. package/src/plugins/defaults/memory/v3/section-dense-store.ts +8 -11
  605. package/src/plugins/defaults/memory/v3/section-needle.ts +27 -7
  606. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +10 -0
  607. package/src/plugins/defaults/memory/v3-eval/eval-packets.ts +1 -4
  608. package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +5 -0
  609. package/src/plugins/defaults/turn-context/injectors.ts +1 -0
  610. package/src/plugins/defaults/turn-context/unified-turn-context.ts +12 -0
  611. package/src/plugins/injector-registry.ts +13 -2
  612. package/src/plugins/mtime-cache.ts +36 -47
  613. package/src/plugins/pipeline.ts +15 -1
  614. package/src/plugins/types.ts +14 -23
  615. package/src/prompts/__tests__/system-prompt.test.ts +170 -32
  616. package/src/prompts/persona-resolver.ts +13 -9
  617. package/src/prompts/system-prompt.ts +30 -9
  618. package/src/prompts/templates/BOOTSTRAP.md +16 -119
  619. package/src/prompts/templates/SOUL.md +43 -13
  620. package/src/prompts/templates/system-sections.ts +32 -27
  621. package/src/prompts/templates/users/default.md +19 -0
  622. package/src/providers/__tests__/placeholder-sentinels.test.ts +56 -0
  623. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +96 -1
  624. package/src/providers/model-intents.ts +2 -2
  625. package/src/providers/openai/chat-completions-provider.ts +14 -1
  626. package/src/providers/openai/responses-provider.ts +18 -0
  627. package/src/providers/placeholder-sentinels.ts +18 -8
  628. package/src/providers/retry.ts +1 -0
  629. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +0 -64
  630. package/src/providers/speech-to-text/__tests__/resolve.test.ts +92 -79
  631. package/src/providers/speech-to-text/deepgram-realtime.test.ts +98 -0
  632. package/src/providers/speech-to-text/deepgram-realtime.ts +62 -3
  633. package/src/providers/speech-to-text/provider-catalog.ts +0 -89
  634. package/src/providers/speech-to-text/resolve.ts +55 -0
  635. package/src/providers/tool-progress-events.test.ts +50 -0
  636. package/src/providers/tool-progress-events.ts +99 -0
  637. package/src/providers/types.ts +1 -1
  638. package/src/providers/vellum-model-routing.test.ts +51 -0
  639. package/src/providers/vellum-model-routing.ts +84 -0
  640. package/src/runtime/AGENTS.md +5 -4
  641. package/src/runtime/__tests__/agent-wake.test.ts +158 -1
  642. package/src/runtime/__tests__/channel-verification-service.test.ts +2 -2
  643. package/src/runtime/__tests__/trust-verdict-consumer.test.ts +37 -8
  644. package/src/runtime/access-request-helper.ts +154 -3
  645. package/src/runtime/actor-trust-resolver.ts +7 -21
  646. package/src/runtime/agent-wake.ts +41 -1
  647. package/src/runtime/assistant-event.ts +86 -12
  648. package/src/runtime/auth/__tests__/route-policy.test.ts +31 -0
  649. package/src/runtime/channel-approval-types.ts +52 -2
  650. package/src/runtime/channel-approvals.ts +23 -8
  651. package/src/runtime/channel-invite-transport.ts +2 -2
  652. package/src/runtime/channel-invite-transports/voice.ts +1 -1
  653. package/src/runtime/channel-readiness-service.ts +3 -5
  654. package/src/runtime/channel-retry-sweep.ts +18 -24
  655. package/src/runtime/channel-verification-service.ts +9 -11
  656. package/src/runtime/finalize-event-delivery.ts +30 -16
  657. package/src/runtime/guardian-action-service.ts +10 -11
  658. package/src/runtime/guardian-reply-router.ts +122 -21
  659. package/src/runtime/http-server.ts +44 -101
  660. package/src/runtime/introduction-policy.test.ts +214 -0
  661. package/src/runtime/introduction-policy.ts +284 -0
  662. package/src/runtime/invite-service.ts +104 -361
  663. package/src/runtime/local-actor-identity.test.ts +38 -0
  664. package/src/runtime/local-actor-identity.ts +14 -0
  665. package/src/runtime/middleware/__tests__/rate-limiter.test.ts +22 -0
  666. package/src/runtime/middleware/rate-limiter.ts +30 -0
  667. package/src/runtime/middleware/twilio-validation.ts +5 -7
  668. package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +28 -0
  669. package/src/runtime/migrations/origin-mode.ts +6 -6
  670. package/src/runtime/pre-first-message-gate.ts +1 -0
  671. package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +6 -35
  672. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +24 -3
  673. package/src/runtime/routes/__tests__/contact-routes.test.ts +80 -15
  674. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +7 -1
  675. package/src/runtime/routes/__tests__/global-search-routes.test.ts +0 -3
  676. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +142 -1
  677. package/src/runtime/routes/__tests__/invite-relay-routes.test.ts +119 -30
  678. package/src/runtime/routes/__tests__/migration-import-allowed-hosts-env.test.ts +70 -0
  679. package/src/runtime/routes/__tests__/monitoring-routes.test.ts +194 -0
  680. package/src/runtime/routes/__tests__/plugins-routes.test.ts +755 -29
  681. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +4 -0
  682. package/src/runtime/routes/__tests__/schedule-routes-create.test.ts +70 -0
  683. package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +161 -0
  684. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +1 -1
  685. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +1 -1
  686. package/src/runtime/routes/acp-routes.ts +15 -3
  687. package/src/runtime/routes/app-management-routes.ts +4 -7
  688. package/src/runtime/routes/approval-routes.ts +4 -0
  689. package/src/runtime/routes/attachment-routes.ts +54 -3
  690. package/src/runtime/routes/avatar-routes.ts +0 -4
  691. package/src/runtime/routes/background-tool-routes.ts +34 -5
  692. package/src/runtime/routes/btw-routes.ts +32 -4
  693. package/src/runtime/routes/channel-route-shared.ts +18 -14
  694. package/src/runtime/routes/channel-verification-routes.ts +0 -3
  695. package/src/runtime/routes/contact-routes.ts +298 -117
  696. package/src/runtime/routes/conversation-list-routes.ts +6 -0
  697. package/src/runtime/routes/conversation-management-routes.ts +74 -37
  698. package/src/runtime/routes/conversation-query-routes.ts +5 -2
  699. package/src/runtime/routes/conversation-routes.ts +222 -82
  700. package/src/runtime/routes/conversations-import-routes.ts +8 -1
  701. package/src/runtime/routes/credential-prompt-routes.ts +20 -5
  702. package/src/runtime/routes/debug-routes.ts +1 -0
  703. package/src/runtime/routes/documents-routes.ts +5 -2
  704. package/src/runtime/routes/empty-state-greeting-cache.ts +37 -10
  705. package/src/runtime/routes/global-search-routes.ts +2 -1
  706. package/src/runtime/routes/guardian-approval-interception.ts +51 -0
  707. package/src/runtime/routes/home-feed-routes.ts +60 -1
  708. package/src/runtime/routes/identity-routes.ts +4 -87
  709. package/src/runtime/routes/inbound-message-handler.ts +82 -41
  710. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +430 -57
  711. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +302 -572
  712. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +76 -270
  713. package/src/runtime/routes/inbound-stages/background-dispatch.ts +21 -116
  714. package/src/runtime/routes/inbound-stages/edit-intercept.ts +9 -1
  715. package/src/runtime/routes/inbound-stages/escalation-intercept.test.ts +158 -0
  716. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +22 -1
  717. package/src/runtime/routes/index.ts +6 -0
  718. package/src/runtime/routes/inference-provider-connection-routes.ts +22 -16
  719. package/src/runtime/routes/internal-twilio-routes.ts +0 -15
  720. package/src/runtime/routes/messages-lexical-routes.ts +75 -0
  721. package/src/runtime/routes/migration-routes.ts +69 -6
  722. package/src/runtime/routes/monitoring-routes.ts +237 -0
  723. package/src/runtime/routes/plugins-routes.ts +329 -8
  724. package/src/runtime/routes/schedule-routes.ts +49 -8
  725. package/src/runtime/routes/schedule-worker-routes.ts +210 -0
  726. package/src/runtime/routes/secret-routes.ts +1 -1
  727. package/src/runtime/routes/surface-conversation-resolver.ts +1 -0
  728. package/src/runtime/services/__tests__/analyze-conversation.test.ts +54 -9
  729. package/src/runtime/services/__tests__/auto-analysis-enqueue.test.ts +30 -6
  730. package/src/runtime/services/__tests__/conversation-analyze-job.test.ts +43 -8
  731. package/src/runtime/services/__tests__/conversation-serializer.test.ts +20 -0
  732. package/src/runtime/services/analyze-conversation.ts +14 -4
  733. package/src/runtime/services/conversation-serializer.ts +5 -0
  734. package/src/runtime/slack-no-response.ts +47 -0
  735. package/src/runtime/slack-reply-session.test.ts +633 -0
  736. package/src/runtime/slack-reply-session.ts +382 -0
  737. package/src/runtime/slack-task-progress.ts +95 -0
  738. package/src/runtime/sync/resource-sync-events.ts +25 -0
  739. package/src/runtime/tool-grant-request-helper.ts +17 -1
  740. package/src/runtime/trust-class.test.ts +141 -0
  741. package/src/runtime/trust-class.ts +121 -0
  742. package/src/runtime/trust-verdict-consumer.ts +7 -6
  743. package/src/schedule/__tests__/run-script.test.ts +21 -0
  744. package/src/schedule/__tests__/worker-control.test.ts +241 -0
  745. package/src/schedule/run-script.ts +13 -6
  746. package/src/schedule/schedule-usage-store.ts +1 -0
  747. package/src/schedule/scheduler.ts +178 -127
  748. package/src/schedule/worker-control.ts +118 -0
  749. package/src/schedule/worker.ts +131 -0
  750. package/src/skills/__tests__/categories-cache.test.ts +38 -0
  751. package/src/skills/categories-cache.ts +40 -5
  752. package/src/skills/managed-store.ts +41 -6
  753. package/src/skills/validate-input.ts +47 -0
  754. package/src/stt/types.ts +5 -9
  755. package/src/subagent/manager.ts +267 -39
  756. package/src/subagent/types.ts +6 -2
  757. package/src/subagent/validate-allowlists.ts +65 -0
  758. package/src/telemetry/watchdog-direct-emit.test.ts +109 -0
  759. package/src/telemetry/watchdog-direct-emit.ts +89 -0
  760. package/src/tools/acp/list-agents.test.ts +0 -6
  761. package/src/tools/background-tool-registry.ts +61 -0
  762. package/src/tools/document/document-comment-tool.test.ts +4 -0
  763. package/src/tools/host-terminal/host-shell.test.ts +397 -0
  764. package/src/tools/host-terminal/host-shell.ts +203 -14
  765. package/src/tools/skills/find-similar-skills.test.ts +152 -1
  766. package/src/tools/skills/find-similar-skills.ts +30 -4
  767. package/src/tools/skills/load.ts +36 -0
  768. package/src/tools/skills/scaffold-managed.ts +66 -34
  769. package/src/tools/skills/skill-script-runner.ts +17 -0
  770. package/src/tools/skills/skill-tool-factory.ts +5 -1
  771. package/src/tools/terminal/safe-env.ts +3 -0
  772. package/src/tools/terminal/shell.test.ts +177 -0
  773. package/src/tools/terminal/shell.ts +98 -4
  774. package/src/tools/tool-approval-handler.ts +106 -29
  775. package/{node_modules/@vellumai/skill-host-contracts/src → src/tools}/tool-types.ts +16 -17
  776. package/src/tools/types.ts +29 -17
  777. package/src/tools/ui-surface/definitions.ts +3 -1
  778. package/src/tts/__tests__/provider-adapters.test.ts +37 -0
  779. package/src/tts/__tests__/provider-catalog.test.ts +21 -0
  780. package/src/tts/provider-catalog.ts +40 -9
  781. package/src/tts/providers/fish-audio-provider.ts +19 -10
  782. package/src/tts/types.ts +3 -4
  783. package/src/util/__tests__/process-tree.test.ts +10 -2
  784. package/src/util/__tests__/text-spacing.test.ts +53 -0
  785. package/src/util/cgroup-memory.ts +142 -0
  786. package/src/util/image-conversion.ts +281 -0
  787. package/src/util/phone.ts +1 -8
  788. package/src/util/platform.ts +33 -0
  789. package/src/util/process-tree.ts +10 -2
  790. package/src/util/text-spacing.ts +53 -0
  791. package/src/util/worker-process.ts +271 -0
  792. package/src/workflows/journal-store.ts +16 -1
  793. package/src/workflows/library.test.ts +35 -20
  794. package/src/workflows/library.ts +105 -66
  795. package/src/workspace/git-service.ts +1 -0
  796. package/src/workspace/migrations/011-backfill-installation-id.ts +83 -31
  797. package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +41 -22
  798. package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +1 -1
  799. package/src/workspace/migrations/118-seed-now-md.ts +64 -0
  800. package/src/workspace/migrations/119-strip-persisted-memory-v3-tuning-defaults.ts +169 -0
  801. package/src/workspace/migrations/120-revise-onboarding-threads.ts +47 -0
  802. package/src/workspace/migrations/121-seed-default-user-guardrails.ts +82 -0
  803. package/src/workspace/migrations/122-relocate-default-user-boundary.ts +111 -0
  804. package/src/workspace/migrations/123-swap-quality-profile-to-fable.ts +85 -0
  805. package/src/workspace/migrations/__tests__/119-strip-persisted-memory-v3-tuning-defaults.test.ts +181 -0
  806. package/src/workspace/migrations/registry.ts +12 -0
  807. package/drizzle/0000_dizzy_maggott.sql +0 -301
  808. package/drizzle/meta/0000_snapshot.json +0 -1933
  809. package/drizzle/meta/_journal.json +0 -13
  810. package/node_modules/@vellumai/skill-host-contracts/__tests__/client.test.ts +0 -887
  811. package/node_modules/@vellumai/skill-host-contracts/bun.lock +0 -24
  812. package/node_modules/@vellumai/skill-host-contracts/package.json +0 -18
  813. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -97
  814. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +0 -1349
  815. package/node_modules/@vellumai/skill-host-contracts/src/index.ts +0 -6
  816. package/node_modules/@vellumai/skill-host-contracts/src/runtime-mode.ts +0 -11
  817. package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +0 -32
  818. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +0 -333
  819. package/node_modules/@vellumai/skill-host-contracts/tsconfig.json +0 -20
  820. package/node_modules/@vellumai/skill-host-contracts/tsconfig.test.json +0 -12
  821. package/src/__tests__/invite-redemption-service.test.ts +0 -1180
  822. package/src/__tests__/invite-service-ipc.test.ts +0 -350
  823. package/src/__tests__/job-handler-registry-guard.test.ts +0 -125
  824. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +0 -203
  825. package/src/__tests__/relay-server.test.ts +0 -6153
  826. package/src/__tests__/telephony-stt-routing.test.ts +0 -329
  827. package/src/__tests__/voice-invite-redemption.test.ts +0 -702
  828. package/src/calls/relay-server.ts +0 -2463
  829. package/src/calls/telephony-stt-routing.ts +0 -145
  830. package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +0 -128
  831. package/src/daemon/__tests__/daemon-skill-host.test.ts +0 -273
  832. package/src/daemon/daemon-skill-host.ts +0 -262
  833. package/src/daemon/message-types/meet.ts +0 -143
  834. package/src/daemon/shutdown-registry.ts +0 -40
  835. package/src/ipc/__tests__/skill-server-bidirectional.test.ts +0 -253
  836. package/src/ipc/__tests__/skill-server.test.ts +0 -182
  837. package/src/ipc/skill-ipc-types.ts +0 -54
  838. package/src/ipc/skill-routes/__tests__/config.test.ts +0 -146
  839. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +0 -448
  840. package/src/ipc/skill-routes/__tests__/identity.test.ts +0 -62
  841. package/src/ipc/skill-routes/__tests__/log.test.ts +0 -133
  842. package/src/ipc/skill-routes/__tests__/memory.test.ts +0 -203
  843. package/src/ipc/skill-routes/__tests__/platform.test.ts +0 -111
  844. package/src/ipc/skill-routes/__tests__/providers.test.ts +0 -265
  845. package/src/ipc/skill-routes/__tests__/registries.test.ts +0 -400
  846. package/src/ipc/skill-routes/config.ts +0 -47
  847. package/src/ipc/skill-routes/events.ts +0 -147
  848. package/src/ipc/skill-routes/identity.ts +0 -21
  849. package/src/ipc/skill-routes/index.ts +0 -37
  850. package/src/ipc/skill-routes/log.ts +0 -40
  851. package/src/ipc/skill-routes/memory.ts +0 -92
  852. package/src/ipc/skill-routes/platform.ts +0 -39
  853. package/src/ipc/skill-routes/providers.ts +0 -162
  854. package/src/ipc/skill-routes/registries.ts +0 -338
  855. package/src/ipc/skill-server.ts +0 -831
  856. package/src/ipc/skill-socket-path.ts +0 -20
  857. package/src/jobs/register-job-handlers.ts +0 -74
  858. package/src/persistence/invite-store.ts +0 -481
  859. package/src/plugins/job-handler-registry.ts +0 -94
  860. package/src/providers/cache-control.ts +0 -26
  861. package/src/runtime/__tests__/runtime-mode.test.ts +0 -62
  862. package/src/runtime/invite-redemption-service.ts +0 -828
  863. package/src/runtime/invite-redemption-templates.ts +0 -46
  864. package/src/runtime/runtime-mode.ts +0 -40
  865. package/src/runtime/slack-dm-text-delivery.ts +0 -177
  866. package/src/util/voice-code.ts +0 -31
@@ -129,6 +129,14 @@ class FakeAcpAgentProcess {
129
129
 
130
130
  async cancel(): Promise<void> {}
131
131
 
132
+ markStderr(): number {
133
+ return 0;
134
+ }
135
+
136
+ stderrSince(): string {
137
+ return "";
138
+ }
139
+
132
140
  kill(): void {
133
141
  this.killed = true;
134
142
  }
@@ -35,6 +35,12 @@ mock.module("../agent-process.js", () => ({
35
35
  return new Promise(() => {});
36
36
  }
37
37
  async cancel(): Promise<void> {}
38
+ markStderr(): number {
39
+ return 0;
40
+ }
41
+ stderrSince(): string {
42
+ return "";
43
+ }
38
44
  kill(): void {}
39
45
  },
40
46
  }));
@@ -37,6 +37,12 @@ mock.module("../agent-process.js", () => ({
37
37
  async cancel(sessionId: string): Promise<void> {
38
38
  cancelCalls.push(sessionId);
39
39
  }
40
+ markStderr(): number {
41
+ return 0;
42
+ }
43
+ stderrSince(): string {
44
+ return "";
45
+ }
40
46
  kill(): void {}
41
47
  },
42
48
  }));
@@ -0,0 +1,116 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { AcpAgentProcess } from "./agent-process.js";
4
+ import type { AcpAgentConfig } from "./types.js";
5
+
6
+ const config: AcpAgentConfig = { command: "noop", args: [] };
7
+
8
+ // The client factory is never invoked in these tests (no spawn), so a stub is
9
+ // sufficient.
10
+ const clientFactory = (() => ({})) as never;
11
+
12
+ function newProcess(): AcpAgentProcess {
13
+ return new AcpAgentProcess("test-agent", config, clientFactory);
14
+ }
15
+
16
+ // retainStderr is the private handler path the stderr "data" listener calls
17
+ // after logging; feed it directly to keep the test deterministic (no real
18
+ // child process or stream timing).
19
+ function feed(proc: AcpAgentProcess, line: string): void {
20
+ (proc as unknown as { retainStderr(text: string): void }).retainStderr(line);
21
+ }
22
+
23
+ describe("AcpAgentProcess.stderrSince(0)", () => {
24
+ test("returns empty string before any stderr is captured", () => {
25
+ expect(newProcess().stderrSince(0)).toBe("");
26
+ });
27
+
28
+ test("retains captured lines joined by newlines, in order", () => {
29
+ const proc = newProcess();
30
+ feed(proc, "line 1");
31
+ feed(proc, "line 2");
32
+ feed(proc, "line 3");
33
+
34
+ expect(proc.stderrSince(0)).toBe("line 1\nline 2\nline 3");
35
+ });
36
+
37
+ test("evicts oldest lines once the byte cap is exceeded", () => {
38
+ const proc = newProcess();
39
+ // Each line is ~1 KB; a handful exceeds the ~4 KB cap and forces eviction
40
+ // of the earliest lines while preserving newest-line ordering.
41
+ const kb = "x".repeat(1024);
42
+ for (let i = 0; i < 8; i++) {
43
+ feed(proc, `${i}:${kb}`);
44
+ }
45
+
46
+ const retained = proc.stderrSince(0).split("\n");
47
+ // Oldest ("0:") evicted, newest ("7:") retained, still in insertion order.
48
+ expect(retained.at(0)).not.toContain("0:");
49
+ expect(retained.at(-1)).toContain("7:");
50
+ expect(Buffer.byteLength(proc.stderrSince(0))).toBeLessThanOrEqual(
51
+ 4096 + kb.length,
52
+ );
53
+
54
+ const indices = retained.map((l) => Number(l.split(":")[0]));
55
+ const sorted = [...indices].sort((a, b) => a - b);
56
+ expect(indices).toEqual(sorted);
57
+ });
58
+
59
+ test("truncates an oversized line to the cap, keeping its tail (the diagnostic)", () => {
60
+ const proc = newProcess();
61
+ // The real adapter error sits at the END of the chunk; deriveFailureError
62
+ // reads from the tail, so truncation must drop the head, not the tail.
63
+ const huge = "H".repeat(6000) + "TAIL_DIAGNOSTIC";
64
+ feed(proc, huge);
65
+
66
+ const retained = proc.stderrSince(0);
67
+ expect(retained.length).toBe(4096);
68
+ expect(retained.endsWith("TAIL_DIAGNOSTIC")).toBe(true);
69
+ });
70
+
71
+ test("stderrSince(0) is pure: repeated reads do not clear the buffer", () => {
72
+ const proc = newProcess();
73
+ feed(proc, "only line");
74
+
75
+ expect(proc.stderrSince(0)).toBe("only line");
76
+ expect(proc.stderrSince(0)).toBe("only line");
77
+ });
78
+ });
79
+
80
+ describe("AcpAgentProcess.markStderr / stderrSince", () => {
81
+ test("stderrSince returns only lines produced after the mark", () => {
82
+ const proc = newProcess();
83
+ feed(proc, "before 1");
84
+ feed(proc, "before 2");
85
+
86
+ const mark = proc.markStderr();
87
+ feed(proc, "after 1");
88
+ feed(proc, "after 2");
89
+
90
+ expect(proc.stderrSince(mark)).toBe("after 1\nafter 2");
91
+ });
92
+
93
+ test("a mark taken after all lines excludes everything retained so far", () => {
94
+ const proc = newProcess();
95
+ feed(proc, "line 1");
96
+ feed(proc, "line 2");
97
+
98
+ const mark = proc.markStderr();
99
+ expect(proc.stderrSince(mark)).toBe("");
100
+ });
101
+
102
+ test("mark is monotonic across eviction: seq keeps counting evicted lines", () => {
103
+ const proc = newProcess();
104
+ // Overflow the byte cap so early lines are evicted, then mark and add one
105
+ // fresh line: only the post-mark line comes back, unaffected by eviction.
106
+ const kb = "z".repeat(1024);
107
+ for (let i = 0; i < 8; i++) {
108
+ feed(proc, `${i}:${kb}`);
109
+ }
110
+
111
+ const mark = proc.markStderr();
112
+ feed(proc, "fresh failure");
113
+
114
+ expect(proc.stderrSince(mark)).toBe("fresh failure");
115
+ });
116
+ });
@@ -30,6 +30,13 @@ const log = getLogger("acp");
30
30
  */
31
31
  const AUTH_REQUIRED_CODE = -32000;
32
32
 
33
+ /**
34
+ * Rough byte cap for retained stderr. Oldest lines are evicted once the sum of
35
+ * retained line lengths exceeds this, so the stderr ring (read via stderrSince)
36
+ * stays bounded.
37
+ */
38
+ const STDERR_RETENTION_BYTES = 4096;
39
+
33
40
  /**
34
41
  * Detects the ACP auth-required error. Checks the `code` property rather than
35
42
  * `instanceof acp.RequestError` so plain JSON-RPC error objects are also
@@ -69,6 +76,20 @@ export class AcpAgentProcess {
69
76
  */
70
77
  private spawnedEnv: NodeJS.ProcessEnv | null = null;
71
78
 
79
+ /**
80
+ * Ring of the most recent stderr lines, bounded to ~STDERR_RETENTION_BYTES.
81
+ * Read once on the failure path to surface the real adapter error. Each
82
+ * entry carries a monotonic `seq` so a caller can scope reads to lines
83
+ * produced after a checkpoint (see markStderr/stderrSince).
84
+ */
85
+ private stderrRing: { seq: number; text: string }[] = [];
86
+ private stderrRingBytes = 0;
87
+ /**
88
+ * Cumulative count of stderr lines ever retained, including ones later
89
+ * evicted. Assigned as each line's `seq`; markStderr() snapshots it.
90
+ */
91
+ private stderrSeq = 0;
92
+
72
93
  constructor(
73
94
  public readonly agentId: string,
74
95
  private readonly config: AcpAgentConfig,
@@ -108,6 +129,7 @@ export class AcpAgentProcess {
108
129
  const text = chunk.toString().trim();
109
130
  if (text) {
110
131
  log.error({ agentId: this.agentId, stderr: text }, "ACP agent stderr");
132
+ this.retainStderr(text);
111
133
  }
112
134
  });
113
135
 
@@ -124,6 +146,55 @@ export class AcpAgentProcess {
124
146
  });
125
147
  }
126
148
 
149
+ /**
150
+ * Appends a stderr line to the ring, evicting oldest lines once the retained
151
+ * total exceeds STDERR_RETENTION_BYTES. Always keeps at least the newest line
152
+ * so a single oversized line is not fully dropped.
153
+ */
154
+ private retainStderr(text: string): void {
155
+ // Cap a single oversized line: the "keep newest" rule below never evicts it,
156
+ // so an untruncated multi-MB chunk would blow the ring budget and make the
157
+ // failure-path stderr scan (deriveFailureError) super-linear on huge input.
158
+ // Keep the TAIL: the adapter's error JSON / last line sits at the end, and
159
+ // deriveFailureError reads from the end, so dropping the head is lossless.
160
+ const line =
161
+ text.length > STDERR_RETENTION_BYTES
162
+ ? text.slice(-STDERR_RETENTION_BYTES)
163
+ : text;
164
+ this.stderrSeq += 1;
165
+ this.stderrRing.push({ seq: this.stderrSeq, text: line });
166
+ this.stderrRingBytes += Buffer.byteLength(line);
167
+ while (
168
+ this.stderrRingBytes > STDERR_RETENTION_BYTES &&
169
+ this.stderrRing.length > 1
170
+ ) {
171
+ const evicted = this.stderrRing.shift()!;
172
+ this.stderrRingBytes -= Buffer.byteLength(evicted.text);
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Snapshots the current cumulative stderr line count. Pair with
178
+ * stderrSince() to read only stderr produced after this checkpoint, so a
179
+ * prompt's failure derives from its own stderr rather than lines retained
180
+ * from startup, resume, or an earlier (possibly cancelled) prompt.
181
+ */
182
+ markStderr(): number {
183
+ return this.stderrSeq;
184
+ }
185
+
186
+ /**
187
+ * Returns retained stderr lines produced after `mark` (a markStderr()
188
+ * checkpoint), joined by newlines. Best-effort: lines pushed after the mark
189
+ * but since evicted are simply absent; returns "" when nothing newer remains.
190
+ */
191
+ stderrSince(mark: number): string {
192
+ return this.stderrRing
193
+ .filter((e) => e.seq > mark)
194
+ .map((e) => e.text)
195
+ .join("\n");
196
+ }
197
+
127
198
  /**
128
199
  * Initializes the ACP connection by negotiating protocol version and capabilities.
129
200
  */
@@ -16,11 +16,12 @@
16
16
  * never the untrusted task project dir. A clean cwd has no project-local
17
17
  * `node_modules/.bin`, `bunfig.toml`, or `.npmrc`, so none of the
18
18
  * cwd-based package-resolution hijacks apply.
19
- * - The installer env is a SANITIZED copy of `process.env` with
20
- * `CLAUDE_CODE_OAUTH_TOKEN` and `GEMINI_API_KEY` stripped, so the secret
21
- * is never in scope during package resolution, and `BUN_CONFIG_REGISTRY`
22
- * forced to the public npm registry so a redirected registry cannot serve
23
- * a malicious package.
19
+ * - The installer env is a SANITIZED copy of `process.env` with known
20
+ * ambient secrets (`CLAUDE_CODE_OAUTH_TOKEN`, plus the `GEMINI_API_KEY`
21
+ * used by the Gemini LLM provider) stripped, so no secret is ever in scope
22
+ * during package resolution, and `BUN_CONFIG_REGISTRY` forced to the
23
+ * public npm registry so a redirected registry cannot serve a malicious
24
+ * package.
24
25
  * - The token is injected ONLY later, at spawn time, on the real installed
25
26
  * binary (see `prepare-agent-env.ts`). `prepareAgentEnv` is never called
26
27
  * here.
@@ -99,9 +100,12 @@ function execFileWithTimeout(
99
100
  }
100
101
 
101
102
  /**
102
- * A copy of `process.env` safe to hand to the package installer: the ACP
103
- * secrets are stripped so they can never leak into a resolved package's
104
- * lifecycle/runtime, and the registry is pinned to the trusted public one.
103
+ * A copy of `process.env` safe to hand to the package installer: known
104
+ * ambient secrets are stripped so they can never leak into a resolved
105
+ * package's lifecycle/runtime, and the registry is pinned to the trusted
106
+ * public one. `CLAUDE_CODE_OAUTH_TOKEN` is the ACP adapter token;
107
+ * `GEMINI_API_KEY` belongs to the Gemini LLM provider but may be present in
108
+ * the daemon env, so it is stripped here as defense-in-depth.
105
109
  */
106
110
  function sanitizedInstallEnv(): NodeJS.ProcessEnv {
107
111
  const env: NodeJS.ProcessEnv = { ...process.env };
@@ -0,0 +1,109 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { deriveFailureError } from "./failure-error.js";
4
+
5
+ const CODEX_MESSAGE =
6
+ "The 'gpt-5.3-codex' model is not supported when using Codex with a ChatGPT account.";
7
+
8
+ const ESC = String.fromCharCode(27); // ANSI escape (0x1B)
9
+
10
+ // A realistic codex-acp stderr line: a JSON error blob wrapped in a coloured
11
+ // log prefix and a trailing reset code.
12
+ const ansiCodexStderr =
13
+ `${ESC}[2m2026-06-30T12:00:00Z${ESC}[0m ${ESC}[31mERROR${ESC}[0m codex_core: ` +
14
+ `request failed {"type":"error","status":400,"error":` +
15
+ `{"type":"invalid_request_error","message":"${CODEX_MESSAGE}"}}${ESC}[0m`;
16
+
17
+ describe("deriveFailureError", () => {
18
+ test("extracts the inner error.message from codex-acp JSON stderr", () => {
19
+ const stderr =
20
+ `{"type":"error","status":400,"error":` +
21
+ `{"type":"invalid_request_error","message":"${CODEX_MESSAGE}"}}`;
22
+ expect(deriveFailureError("Internal error", stderr)).toBe(CODEX_MESSAGE);
23
+ });
24
+
25
+ test("strips ANSI codes and extracts error.message from a real log line", () => {
26
+ const result = deriveFailureError("Internal error", ansiCodexStderr);
27
+ expect(result).toBe(CODEX_MESSAGE);
28
+ expect(result).not.toContain(ESC);
29
+ });
30
+
31
+ test("parses JSON embedded in surrounding log text", () => {
32
+ const stderr = `2026-06-30 fatal: the adapter said {"error":{"message":"boom"}} and gave up`;
33
+ expect(deriveFailureError("Internal error", stderr)).toBe("boom");
34
+ });
35
+
36
+ test("returns the LAST JSON error.message when several are present", () => {
37
+ const stderr =
38
+ `{"error":{"message":"first failure"}}\n` +
39
+ `{"error":{"message":"final failure"}}`;
40
+ expect(deriveFailureError("Internal error", stderr)).toBe("final failure");
41
+ });
42
+
43
+ test("empty stderr yields the ack message", () => {
44
+ expect(deriveFailureError("Internal error", "")).toBe("Internal error");
45
+ });
46
+
47
+ test("generic ack with informative non-JSON stderr yields the last stderr line, ANSI-stripped", () => {
48
+ const stderr = `starting up...\n${ESC}[31mfatal: could not connect to backend${ESC}[0m`;
49
+ expect(deriveFailureError("Internal error", stderr)).toBe(
50
+ "fatal: could not connect to backend",
51
+ );
52
+ });
53
+
54
+ test("already-specific ack is preserved when stderr is empty", () => {
55
+ expect(deriveFailureError(CODEX_MESSAGE, "")).toBe(CODEX_MESSAGE);
56
+ });
57
+
58
+ test("already-specific ack is preserved (not double-appended) when stderr duplicates it", () => {
59
+ // codex often echoes the same message to stderr; we must not repeat it.
60
+ expect(deriveFailureError(CODEX_MESSAGE, CODEX_MESSAGE)).toBe(
61
+ CODEX_MESSAGE,
62
+ );
63
+ });
64
+
65
+ test("structured error duplicating the generic ack yields the clean ack, not the raw JSON", () => {
66
+ // The adapter acked "Internal error" and also emitted it as a JSON blob;
67
+ // return the clean ack rather than echoing the raw JSON line.
68
+ const stderr = `{"error":{"message":"Internal error"}}`;
69
+ expect(deriveFailureError("Internal error", stderr)).toBe("Internal error");
70
+ });
71
+
72
+ test("falls back to the ack message when stderr has no JSON and no lines", () => {
73
+ expect(deriveFailureError("Internal error", " \n \n")).toBe(
74
+ "Internal error",
75
+ );
76
+ });
77
+
78
+ test("ignores malformed JSON braces and uses the last stderr line", () => {
79
+ const stderr = "noise {not: valid json} more noise\nreal failure detail";
80
+ expect(deriveFailureError("Internal error", stderr)).toBe(
81
+ "real failure detail",
82
+ );
83
+ });
84
+
85
+ test("a stray unmatched brace before the JSON does not block extraction", () => {
86
+ const stderr =
87
+ `some log { unmatched brace here\n` +
88
+ `... {"type":"error","error":{"message":"The real error"}} ...`;
89
+ expect(deriveFailureError("Internal error", stderr)).toBe("The real error");
90
+ });
91
+
92
+ test("braces inside a JSON string literal don't throw off the scan", () => {
93
+ const stderr = `{"error":{"message":"unexpected } brace {in} the text"}}`;
94
+ expect(deriveFailureError("Internal error", stderr)).toBe(
95
+ "unexpected } brace {in} the text",
96
+ );
97
+ });
98
+
99
+ test("prefers the outer adapter error over a nested error.message", () => {
100
+ // JSON-RPC style: the real failure is the top-level error.message; a nested
101
+ // error.data.error.message must not shadow it.
102
+ const stderr =
103
+ `{"error":{"message":"outer adapter failure",` +
104
+ `"data":{"error":{"message":"inner debug detail"}}}}`;
105
+ expect(deriveFailureError("Internal error", stderr)).toBe(
106
+ "outer adapter failure",
107
+ );
108
+ });
109
+ });
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Derives a human-meaningful failure message for a failed ACP prompt.
3
+ *
4
+ * ACP adapters (e.g. codex-acp) frequently ack a failure with a generic
5
+ * "Internal error" while printing the real cause to stderr as a JSON blob like
6
+ * `{"error":{"message":"..."}}`. This picks the most specific signal available:
7
+ * a structured stderr error, else the last stderr line, else the ack message.
8
+ *
9
+ * Pure and dependency-free.
10
+ */
11
+
12
+ // CSI escape sequences (colour codes, cursor moves) that adapters interleave
13
+ // with their log lines: ESC `[`, parameter/intermediate bytes, then final byte.
14
+ const ANSI_ESCAPE = /\u001B\[[0-?]*[ -/]*[@-~]/g;
15
+
16
+ export function deriveFailureError(ackMessage: string, stderr: string): string {
17
+ const clean = stderr.replace(ANSI_ESCAPE, "").trim();
18
+
19
+ // Most precise: a structured adapter error — prefer it over the ack.
20
+ const jsonMessage = lastJsonErrorMessage(clean);
21
+ if (jsonMessage) return jsonMessage;
22
+
23
+ // An already-specific ack is preserved when stderr has no better detail.
24
+ if (ackMessage.length > 0 && ackMessage !== "Internal error")
25
+ return ackMessage;
26
+
27
+ // Generic/empty ack: fall back to the last meaningful stderr line.
28
+ return lastNonEmptyLine(clean) ?? ackMessage;
29
+ }
30
+
31
+ /**
32
+ * Returns the `error.message` of the LAST JSON object embedded in `text` whose
33
+ * shape is `{"error":{"message":"..."}}`, or null. Objects may be surrounded by
34
+ * arbitrary log text.
35
+ *
36
+ * A candidate object is attempted at EACH `{`, so a stray unmatched `{` in the
37
+ * surrounding log text never balances and is skipped rather than swallowing a
38
+ * later valid object.
39
+ */
40
+ function lastJsonErrorMessage(text: string): string | null {
41
+ let found: string | null = null;
42
+ for (let i = 0; i < text.length; i++) {
43
+ if (text[i] !== "{") {
44
+ continue;
45
+ }
46
+ const candidate = balancedObjectAt(text, i);
47
+ if (candidate === null) {
48
+ continue;
49
+ }
50
+ try {
51
+ const parsed = JSON.parse(candidate) as { error?: { message?: unknown } };
52
+ // A valid top-level object: skip past its end so we don't restart
53
+ // candidates at its nested braces. Otherwise a nested error.message
54
+ // (e.g. a JSON-RPC error.data payload) would shadow the outer adapter
55
+ // error. Only reached on a successful parse, so a stray unmatched brace
56
+ // that balances-but-fails-to-parse still lets inner objects be scanned.
57
+ i += candidate.length - 1;
58
+ const message = parsed.error?.message;
59
+ if (typeof message === "string" && message.length > 0) {
60
+ found = message;
61
+ }
62
+ } catch {
63
+ // Not valid JSON at this position; keep scanning later braces.
64
+ }
65
+ }
66
+ return found;
67
+ }
68
+
69
+ /**
70
+ * Returns the balanced `{...}` substring starting at `start`, tracking string
71
+ * literals so braces inside JSON strings don't throw off the depth count, or
72
+ * null if the braces never balance before the text ends.
73
+ */
74
+ function balancedObjectAt(text: string, start: number): string | null {
75
+ let depth = 0;
76
+ let inString = false;
77
+ let escaped = false;
78
+ for (let i = start; i < text.length; i++) {
79
+ const ch = text[i];
80
+ if (inString) {
81
+ if (escaped) {
82
+ escaped = false;
83
+ } else if (ch === "\\") {
84
+ escaped = true;
85
+ } else if (ch === '"') {
86
+ inString = false;
87
+ }
88
+ continue;
89
+ }
90
+ if (ch === '"') {
91
+ inString = true;
92
+ } else if (ch === "{") {
93
+ depth++;
94
+ } else if (ch === "}") {
95
+ depth--;
96
+ if (depth === 0) {
97
+ return text.slice(start, i + 1);
98
+ }
99
+ }
100
+ }
101
+ return null;
102
+ }
103
+
104
+ function lastNonEmptyLine(text: string): string | null {
105
+ const lines = text.split("\n");
106
+ for (let i = lines.length - 1; i >= 0; i--) {
107
+ const trimmed = lines[i].trim();
108
+ if (trimmed.length > 0) return trimmed;
109
+ }
110
+ return null;
111
+ }
@@ -145,11 +145,6 @@ async function injectOptionalCredential(
145
145
  * `binary_not_found` preflight in `resolveAcpAgent` and strictly better
146
146
  * than a `warn` + zombie subprocess 10 seconds later.
147
147
  *
148
- * For `gemini` the env var is `GEMINI_API_KEY`, provisioned the same two
149
- * ways (config.json override wins, vault field `gemini_api_key` second),
150
- * but it is OPTIONAL: the Gemini CLI supports its own OAuth login, so a
151
- * vault miss proceeds without the key instead of failing the spawn.
152
- *
153
148
  * For `codex-acp` the env vars are `OPENAI_API_KEY` (vault field
154
149
  * `acp/openai_api_key`) and `CODEX_API_KEY` (vault field
155
150
  * `acp/codex_api_key`), provisioned the same two ways (config.json
@@ -182,13 +177,6 @@ export async function prepareAgentEnv(
182
177
  "(or set it under acp.agents.<id>.env in config.json).",
183
178
  );
184
179
  }
185
- } else if (adapterCommand === "gemini") {
186
- await injectOptionalCredential(
187
- env,
188
- "gemini_api_key",
189
- "GEMINI_API_KEY",
190
- "Gemini API key for ACP agent authentication",
191
- );
192
180
  } else if (adapterCommand === "codex-acp") {
193
181
  // The two reads target independent vault fields and write disjoint env
194
182
  // keys, so running them concurrently is safe.
@@ -65,17 +65,6 @@ describe("resolveAcpAgent", () => {
65
65
  expect(result.agent.command).toBe("claude-agent-acp");
66
66
  });
67
67
 
68
- test("falls back to the gemini default profile when no user entry", () => {
69
- config.setConfig({ agents: {} });
70
-
71
- const result = resolveAcpAgent("gemini");
72
-
73
- expect(result.ok).toBe(true);
74
- if (!result.ok) return;
75
- expect(result.agent.command).toBe("gemini");
76
- expect(result.agent.args).toEqual(["--acp"]);
77
- });
78
-
79
68
  test.each([
80
69
  ["claude code", "claude-agent-acp"],
81
70
  ["Claude Code", "claude-agent-acp"],
@@ -83,9 +72,6 @@ describe("resolveAcpAgent", () => {
83
72
  ["claude_code", "claude-agent-acp"],
84
73
  ["codex cli", "codex-acp"],
85
74
  ["OpenAI Codex", "codex-acp"],
86
- ["gemini cli", "gemini"],
87
- ["Gemini CLI", "gemini"],
88
- ["google gemini", "gemini"],
89
75
  ])("alias %p resolves to the %p profile", (alias, command) => {
90
76
  config.setConfig({ agents: {} });
91
77
 
@@ -152,12 +138,7 @@ describe("resolveAcpAgent", () => {
152
138
  expect(result.reason).toBe("unknown_agent");
153
139
  if (result.reason !== "unknown_agent") return;
154
140
  // Defaults plus user-only ids, deduped, in stable order (defaults first).
155
- expect(result.available).toEqual([
156
- "claude",
157
- "codex",
158
- "gemini",
159
- "user-only",
160
- ]);
141
+ expect(result.available).toEqual(["claude", "codex", "user-only"]);
161
142
  });
162
143
 
163
144
  test("unknown_agent available list contains both defaults when user config is empty", () => {
@@ -171,7 +152,6 @@ describe("resolveAcpAgent", () => {
171
152
  if (result.reason !== "unknown_agent") return;
172
153
  expect(result.available).toContain("claude");
173
154
  expect(result.available).toContain("codex");
174
- expect(result.available).toContain("gemini");
175
155
  });
176
156
 
177
157
  test("returns binary_not_found with the registered install hint", () => {
@@ -333,7 +313,7 @@ describe("listAcpAgents", () => {
333
313
  const result = listAcpAgents();
334
314
 
335
315
  const ids = result.agents.map((a) => a.id);
336
- expect(ids).toEqual(["claude", "codex", "gemini"]);
316
+ expect(ids).toEqual(["claude", "codex"]);
337
317
  for (const entry of result.agents) {
338
318
  expect(entry.source).toBe("default");
339
319
  expect(entry.available).toBe(true);
@@ -355,7 +335,6 @@ describe("listAcpAgents", () => {
355
335
  which.setWhich({
356
336
  "my-claude": "/usr/bin/my-claude",
357
337
  "codex-acp": "/usr/bin/codex-acp",
358
- gemini: "/usr/bin/gemini",
359
338
  });
360
339
 
361
340
  const result = listAcpAgents();
@@ -385,7 +364,6 @@ describe("listAcpAgents", () => {
385
364
  expect(result.agents.map((a) => a.command)).toEqual([
386
365
  "claude-agent-acp",
387
366
  "codex-acp",
388
- "gemini",
389
367
  ]);
390
368
  });
391
369
 
@@ -401,30 +379,15 @@ describe("listAcpAgents", () => {
401
379
  expect(codex?.setupHint).toBe("bun add -g @zed-industries/codex-acp");
402
380
  });
403
381
 
404
- test("unavailable gemini surfaces the @google/gemini-cli install hint", () => {
405
- config.setConfig({ agents: {} });
406
- which.setWhich({
407
- "claude-agent-acp": "/usr/bin/claude-agent-acp",
408
- "codex-acp": "/usr/bin/codex-acp",
409
- });
410
-
411
- const result = listAcpAgents();
412
-
413
- const gemini = result.agents.find((a) => a.id === "gemini");
414
- expect(gemini?.available).toBe(false);
415
- expect(gemini?.unavailableReason).toBe("'gemini' is not on PATH");
416
- expect(gemini?.setupHint).toBe("bun add -g @google/gemini-cli");
417
- });
418
-
419
382
  test("aliases are resolution sugar, not catalog entries", () => {
420
383
  config.setConfig({ agents: {} });
421
384
 
422
- // "gemini cli" resolves via the alias...
423
- expect(resolveAcpAgent("gemini cli").ok).toBe(true);
385
+ // "codex cli" resolves via the alias...
386
+ expect(resolveAcpAgent("codex cli").ok).toBe(true);
424
387
 
425
388
  // ...but the catalog lists only canonical ids.
426
389
  const ids = listAcpAgents().agents.map((a) => a.id);
427
- expect(ids).toEqual(["claude", "codex", "gemini"]);
390
+ expect(ids).toEqual(["claude", "codex"]);
428
391
  });
429
392
 
430
393
  test("user-only agent appended after defaults in stable order", () => {
@@ -440,7 +403,6 @@ describe("listAcpAgents", () => {
440
403
  which.setWhich({
441
404
  "claude-agent-acp": "/x",
442
405
  "codex-acp": "/x",
443
- gemini: "/x",
444
406
  "my-binary": "/x",
445
407
  });
446
408
 
@@ -449,10 +411,9 @@ describe("listAcpAgents", () => {
449
411
  expect(result.agents.map((a) => a.id)).toEqual([
450
412
  "claude",
451
413
  "codex",
452
- "gemini",
453
414
  "my-agent",
454
415
  ]);
455
- const userOnly = result.agents[3];
416
+ const userOnly = result.agents[2];
456
417
  expect(userOnly.source).toBe("config");
457
418
  expect(userOnly.description).toBe("user-only");
458
419
  });