@vellumai/assistant 0.3.4 → 0.3.6

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 (506) hide show
  1. package/Dockerfile +2 -0
  2. package/README.md +88 -2
  3. package/eslint.config.mjs +31 -0
  4. package/package.json +1 -1
  5. package/scripts/ipc/check-swift-decoder-drift.ts +4 -1
  6. package/scripts/ipc/generate-swift.ts +31 -2
  7. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +438 -1
  8. package/src/__tests__/approval-conversation-turn.test.ts +214 -0
  9. package/src/__tests__/approval-hardcoded-copy-guard.test.ts +41 -0
  10. package/src/__tests__/approval-message-composer.test.ts +253 -0
  11. package/src/__tests__/browser-manager.test.ts +1 -0
  12. package/src/__tests__/call-conversation-messages.test.ts +130 -0
  13. package/src/__tests__/call-domain.test.ts +12 -2
  14. package/src/__tests__/call-orchestrator.test.ts +799 -249
  15. package/src/__tests__/call-pointer-messages.test.ts +148 -0
  16. package/src/__tests__/call-recovery.test.ts +3 -0
  17. package/src/__tests__/call-routes-http.test.ts +32 -2
  18. package/src/__tests__/call-store.test.ts +3 -0
  19. package/src/__tests__/channel-approval-routes.test.ts +1277 -98
  20. package/src/__tests__/channel-approval.test.ts +37 -0
  21. package/src/__tests__/channel-approvals.test.ts +36 -50
  22. package/src/__tests__/channel-guardian.test.ts +630 -22
  23. package/src/__tests__/channel-readiness-service.test.ts +324 -0
  24. package/src/__tests__/checker.test.ts +14 -7
  25. package/src/__tests__/clarification-resolver.test.ts +44 -24
  26. package/src/__tests__/commit-message-enrichment-service.test.ts +9 -4
  27. package/src/__tests__/computer-use-session-working-dir.test.ts +8 -0
  28. package/src/__tests__/config-schema.test.ts +14 -8
  29. package/src/__tests__/context-window-manager.test.ts +30 -2
  30. package/src/__tests__/contradiction-checker.test.ts +20 -5
  31. package/src/__tests__/credential-security-invariants.test.ts +7 -2
  32. package/src/__tests__/daemon-lifecycle.test.ts +13 -12
  33. package/src/__tests__/db-migration-rollback.test.ts +752 -0
  34. package/src/__tests__/dictation-mode-detection.test.ts +63 -0
  35. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -0
  36. package/src/__tests__/entity-search.test.ts +615 -0
  37. package/src/__tests__/fuzzy-match-property.test.ts +5 -5
  38. package/src/__tests__/guardian-action-store.test.ts +123 -0
  39. package/src/__tests__/guardian-action-sweep.test.ts +277 -0
  40. package/src/__tests__/guardian-dispatch.test.ts +389 -0
  41. package/src/__tests__/guardian-question-copy.test.ts +47 -0
  42. package/src/__tests__/handlers-telegram-config.test.ts +4 -2
  43. package/src/__tests__/handlers-twilio-config.test.ts +533 -0
  44. package/src/__tests__/intent-routing.test.ts +2 -0
  45. package/src/__tests__/ipc-snapshot.test.ts +291 -1
  46. package/src/__tests__/memory-upsert-concurrency.test.ts +828 -0
  47. package/src/__tests__/messaging-send-tool.test.ts +65 -0
  48. package/src/__tests__/model-intents.test.ts +96 -0
  49. package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +42 -0
  50. package/src/__tests__/oauth2-gateway-transport.test.ts +130 -0
  51. package/src/__tests__/onboarding-starter-tasks.test.ts +2 -0
  52. package/src/__tests__/provider-commit-message-generator.test.ts +89 -13
  53. package/src/__tests__/provider-error-scenarios.test.ts +621 -0
  54. package/src/__tests__/provider-fail-open-selection.test.ts +119 -0
  55. package/src/__tests__/qdrant-manager.test.ts +27 -20
  56. package/src/__tests__/relay-server.test.ts +779 -40
  57. package/src/__tests__/run-orchestrator-assistant-events.test.ts +6 -0
  58. package/src/__tests__/run-orchestrator.test.ts +42 -4
  59. package/src/__tests__/runtime-runs-http.test.ts +17 -1
  60. package/src/__tests__/runtime-runs.test.ts +16 -0
  61. package/src/__tests__/schedule-store.test.ts +18 -4
  62. package/src/__tests__/scheduler-recurrence.test.ts +13 -4
  63. package/src/__tests__/session-abort-tool-results.test.ts +6 -0
  64. package/src/__tests__/session-agent-loop.test.ts +857 -0
  65. package/src/__tests__/session-conflict-gate.test.ts +6 -0
  66. package/src/__tests__/session-pre-run-repair.test.ts +6 -0
  67. package/src/__tests__/session-profile-injection.test.ts +6 -0
  68. package/src/__tests__/session-provider-retry-repair.test.ts +6 -0
  69. package/src/__tests__/session-queue.test.ts +6 -0
  70. package/src/__tests__/session-runtime-assembly.test.ts +321 -13
  71. package/src/__tests__/session-slash-known.test.ts +6 -0
  72. package/src/__tests__/session-slash-queue.test.ts +6 -0
  73. package/src/__tests__/session-slash-unknown.test.ts +6 -0
  74. package/src/__tests__/session-surfaces-task-progress.test.ts +2 -0
  75. package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -0
  76. package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -0
  77. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -0
  78. package/src/__tests__/session-workspace-injection.test.ts +6 -0
  79. package/src/__tests__/session-workspace-tool-tracking.test.ts +6 -0
  80. package/src/__tests__/skills.test.ts +2 -0
  81. package/src/__tests__/sms-messaging-provider.test.ts +126 -0
  82. package/src/__tests__/starter-task-flow.test.ts +2 -0
  83. package/src/__tests__/swarm-dag-pathological.test.ts +535 -0
  84. package/src/__tests__/system-prompt.test.ts +2 -0
  85. package/src/__tests__/task-management-tools.test.ts +2 -2
  86. package/src/__tests__/task-runner.test.ts +14 -4
  87. package/src/__tests__/terminal-tools.test.ts +25 -19
  88. package/src/__tests__/tool-execution-abort-cleanup.test.ts +545 -0
  89. package/src/__tests__/tool-executor-shell-integration.test.ts +11 -11
  90. package/src/__tests__/tool-executor.test.ts +23 -24
  91. package/src/__tests__/trust-store.test.ts +3 -3
  92. package/src/__tests__/twilio-rest.test.ts +29 -0
  93. package/src/__tests__/twilio-routes-elevenlabs.test.ts +3 -0
  94. package/src/__tests__/twilio-routes-twiml.test.ts +11 -0
  95. package/src/__tests__/twilio-routes.test.ts +167 -11
  96. package/src/__tests__/twitter-cli-error-shaping.test.ts +2 -2
  97. package/src/__tests__/user-reference.test.ts +2 -0
  98. package/src/__tests__/voice-quality.test.ts +222 -0
  99. package/src/__tests__/web-search.test.ts +46 -30
  100. package/src/__tests__/work-item-output.test.ts +110 -0
  101. package/src/agent/loop.ts +1 -1
  102. package/src/agent-heartbeat/agent-heartbeat-service.ts +2 -10
  103. package/src/amazon/client.ts +1418 -0
  104. package/src/amazon/request-extractor.ts +135 -0
  105. package/src/amazon/session.ts +109 -0
  106. package/src/autonomy/autonomy-store.ts +5 -5
  107. package/src/browser-extension-relay/client.ts +124 -0
  108. package/src/browser-extension-relay/protocol.ts +63 -0
  109. package/src/browser-extension-relay/server.ts +177 -0
  110. package/src/bundler/app-bundler.ts +3 -3
  111. package/src/bundler/bundle-signer.ts +1 -1
  112. package/src/bundler/signature-verifier.ts +1 -1
  113. package/src/calls/call-conversation-messages.ts +33 -0
  114. package/src/calls/call-domain.ts +114 -10
  115. package/src/calls/call-orchestrator.ts +268 -59
  116. package/src/calls/call-pointer-messages.ts +53 -0
  117. package/src/calls/call-recovery.ts +3 -8
  118. package/src/calls/call-store.ts +69 -87
  119. package/src/calls/elevenlabs-config.ts +3 -2
  120. package/src/calls/guardian-action-sweep.ts +105 -0
  121. package/src/calls/guardian-dispatch.ts +203 -0
  122. package/src/calls/guardian-question-copy.ts +133 -0
  123. package/src/calls/relay-server.ts +466 -8
  124. package/src/calls/speaker-identification.ts +1 -1
  125. package/src/calls/twilio-config.ts +22 -14
  126. package/src/calls/twilio-provider.ts +6 -4
  127. package/src/calls/twilio-rest.ts +308 -7
  128. package/src/calls/twilio-routes.ts +65 -12
  129. package/src/calls/types.ts +3 -1
  130. package/src/channels/types.ts +25 -0
  131. package/src/cli/amazon.ts +815 -0
  132. package/src/cli/config-commands.ts +2 -2
  133. package/src/cli/core-commands.ts +4 -3
  134. package/src/cli/influencer.ts +244 -0
  135. package/src/cli/map.ts +89 -6
  136. package/src/cli.ts +1 -1
  137. package/src/config/agent-schema.ts +171 -0
  138. package/src/config/bundled-skills/amazon/SKILL.md +127 -0
  139. package/src/config/bundled-skills/amazon/icon.svg +13 -0
  140. package/src/config/bundled-skills/api-mapping/SKILL.md +78 -0
  141. package/src/config/bundled-skills/browser/SKILL.md +1 -0
  142. package/src/config/bundled-skills/browser/TOOLS.json +17 -0
  143. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +25 -0
  144. package/src/config/bundled-skills/doordash/SKILL.md +51 -51
  145. package/src/config/bundled-skills/email-setup/SKILL.md +14 -5
  146. package/src/config/bundled-skills/google-oauth-setup/SKILL.md +183 -0
  147. package/src/config/bundled-skills/influencer/SKILL.md +144 -0
  148. package/src/config/bundled-skills/knowledge-graph/SKILL.md +15 -0
  149. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +56 -0
  150. package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +185 -0
  151. package/src/config/bundled-skills/macos-automation/icon.svg +12 -0
  152. package/src/config/bundled-skills/media-processing/SKILL.md +176 -0
  153. package/src/config/bundled-skills/media-processing/TOOLS.json +230 -0
  154. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +77 -0
  155. package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +69 -0
  156. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +303 -0
  157. package/src/config/bundled-skills/media-processing/services/concurrency-pool.ts +55 -0
  158. package/src/config/bundled-skills/media-processing/services/cost-tracker.ts +86 -0
  159. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +339 -0
  160. package/src/config/bundled-skills/media-processing/services/preprocess.ts +551 -0
  161. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +259 -0
  162. package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
  163. package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +136 -0
  164. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +59 -0
  165. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +195 -0
  166. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +197 -0
  167. package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +143 -0
  168. package/src/config/bundled-skills/media-processing/tools/media-status.ts +75 -0
  169. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +65 -0
  170. package/src/config/bundled-skills/messaging/SKILL.md +33 -8
  171. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -7
  172. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +2 -1
  173. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -1
  174. package/src/config/bundled-skills/phone-calls/SKILL.md +88 -23
  175. package/src/config/bundled-skills/twitter/SKILL.md +19 -3
  176. package/src/config/bundled-skills/twitter/icon.svg +14 -0
  177. package/src/config/bundled-tool-registry.ts +310 -0
  178. package/src/config/calls-schema.ts +181 -0
  179. package/src/config/core-schema.ts +309 -0
  180. package/src/config/defaults.ts +28 -3
  181. package/src/config/env-registry.ts +162 -0
  182. package/src/config/env.ts +175 -0
  183. package/src/config/loader.ts +6 -6
  184. package/src/config/memory-schema.ts +528 -0
  185. package/src/config/sandbox-schema.ts +55 -0
  186. package/src/config/schema.ts +158 -1133
  187. package/src/config/skill-state.ts +1 -1
  188. package/src/config/skills-schema.ts +32 -0
  189. package/src/config/skills.ts +35 -24
  190. package/src/config/system-prompt.ts +131 -56
  191. package/src/config/templates/IDENTITY.md +2 -2
  192. package/src/config/templates/SOUL.md +1 -1
  193. package/src/config/types.ts +1 -0
  194. package/src/config/user-reference.ts +4 -9
  195. package/src/config/vellum-skills/catalog.json +6 -7
  196. package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
  197. package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +4 -3
  198. package/src/config/vellum-skills/sms-setup/SKILL.md +216 -0
  199. package/src/config/vellum-skills/twilio-setup/SKILL.md +40 -8
  200. package/src/context/window-manager.ts +27 -7
  201. package/src/daemon/approval-generators.ts +186 -0
  202. package/src/daemon/approved-devices-store.ts +140 -0
  203. package/src/daemon/assistant-attachments.ts +1 -1
  204. package/src/daemon/classifier.ts +35 -32
  205. package/src/daemon/config-watcher.ts +1 -1
  206. package/src/daemon/daemon-control.ts +217 -0
  207. package/src/daemon/handlers/apps.ts +2 -3
  208. package/src/daemon/handlers/config-channels.ts +158 -0
  209. package/src/daemon/handlers/config-inbox.ts +540 -0
  210. package/src/daemon/handlers/config-ingress.ts +231 -0
  211. package/src/daemon/handlers/config-integrations.ts +258 -0
  212. package/src/daemon/handlers/config-model.ts +143 -0
  213. package/src/daemon/handlers/config-parental.ts +163 -0
  214. package/src/daemon/handlers/config-scheduling.ts +172 -0
  215. package/src/daemon/handlers/config-slack.ts +92 -0
  216. package/src/daemon/handlers/config-telegram.ts +301 -0
  217. package/src/daemon/handlers/config-tools.ts +177 -0
  218. package/src/daemon/handlers/config-trust.ts +104 -0
  219. package/src/daemon/handlers/config-twilio.ts +1080 -0
  220. package/src/daemon/handlers/config.ts +53 -1689
  221. package/src/daemon/handlers/diagnostics.ts +1 -1
  222. package/src/daemon/handlers/dictation.ts +180 -0
  223. package/src/daemon/handlers/documents.ts +18 -32
  224. package/src/daemon/handlers/identity.ts +14 -23
  225. package/src/daemon/handlers/index.ts +11 -0
  226. package/src/daemon/handlers/misc.ts +3 -5
  227. package/src/daemon/handlers/pairing.ts +98 -0
  228. package/src/daemon/handlers/sessions.ts +56 -5
  229. package/src/daemon/handlers/shared.ts +6 -1
  230. package/src/daemon/handlers/skills.ts +1 -1
  231. package/src/daemon/handlers/twitter-auth.ts +2 -0
  232. package/src/daemon/handlers/work-items.ts +17 -9
  233. package/src/daemon/handlers/workspace-files.ts +4 -3
  234. package/src/daemon/install-cli-launchers.ts +113 -0
  235. package/src/daemon/ipc-contract/apps.ts +356 -0
  236. package/src/daemon/ipc-contract/browser.ts +74 -0
  237. package/src/daemon/ipc-contract/computer-use.ts +151 -0
  238. package/src/daemon/ipc-contract/diagnostics.ts +56 -0
  239. package/src/daemon/ipc-contract/documents.ts +74 -0
  240. package/src/daemon/ipc-contract/inbox.ts +209 -0
  241. package/src/daemon/ipc-contract/integrations.ts +284 -0
  242. package/src/daemon/ipc-contract/memory.ts +48 -0
  243. package/src/daemon/ipc-contract/messages.ts +211 -0
  244. package/src/daemon/ipc-contract/pairing.ts +45 -0
  245. package/src/daemon/ipc-contract/parental-control.ts +95 -0
  246. package/src/daemon/ipc-contract/schedules.ts +97 -0
  247. package/src/daemon/ipc-contract/sessions.ts +315 -0
  248. package/src/daemon/ipc-contract/shared.ts +42 -0
  249. package/src/daemon/ipc-contract/skills.ts +120 -0
  250. package/src/daemon/ipc-contract/subagents.ts +58 -0
  251. package/src/daemon/ipc-contract/surfaces.ts +250 -0
  252. package/src/daemon/ipc-contract/trust.ts +60 -0
  253. package/src/daemon/ipc-contract/work-items.ts +225 -0
  254. package/src/daemon/ipc-contract/workspace.ts +113 -0
  255. package/src/daemon/ipc-contract-inventory.json +70 -0
  256. package/src/daemon/ipc-contract-inventory.ts +55 -29
  257. package/src/daemon/ipc-contract.ts +229 -2426
  258. package/src/daemon/ipc-protocol.ts +1 -1
  259. package/src/daemon/ipc-validate.ts +7 -0
  260. package/src/daemon/lifecycle.ts +97 -377
  261. package/src/daemon/pairing-store.ts +177 -0
  262. package/src/daemon/providers-setup.ts +43 -0
  263. package/src/daemon/ride-shotgun-handler.ts +68 -3
  264. package/src/daemon/server.ts +66 -46
  265. package/src/daemon/session-agent-loop-handlers.ts +421 -0
  266. package/src/daemon/session-agent-loop.ts +117 -275
  267. package/src/daemon/session-dynamic-profile.ts +1 -1
  268. package/src/daemon/session-history.ts +1 -1
  269. package/src/daemon/session-media-retry.ts +1 -1
  270. package/src/daemon/session-messaging.ts +37 -2
  271. package/src/daemon/session-notifiers.ts +5 -25
  272. package/src/daemon/session-process.ts +99 -59
  273. package/src/daemon/session-queue-manager.ts +96 -4
  274. package/src/daemon/session-runtime-assembly.ts +199 -10
  275. package/src/daemon/session-surfaces.ts +19 -4
  276. package/src/daemon/session-tool-setup.ts +30 -30
  277. package/src/daemon/session-workspace.ts +1 -1
  278. package/src/daemon/session.ts +35 -2
  279. package/src/daemon/shutdown-handlers.ts +122 -0
  280. package/src/daemon/trace-emitter.ts +1 -1
  281. package/src/daemon/watch-handler.ts +36 -33
  282. package/src/doordash/cart-queries.ts +787 -0
  283. package/src/doordash/client.ts +144 -127
  284. package/src/doordash/order-queries.ts +85 -0
  285. package/src/doordash/queries.ts +10 -1308
  286. package/src/doordash/search-queries.ts +203 -0
  287. package/src/doordash/session.ts +3 -2
  288. package/src/doordash/store-queries.ts +246 -0
  289. package/src/doordash/types.ts +367 -0
  290. package/src/email/providers/agentmail.ts +2 -1
  291. package/src/email/providers/index.ts +3 -2
  292. package/src/email/service.ts +3 -2
  293. package/src/errors.ts +43 -0
  294. package/src/home-base/prebuilt/seed.ts +1 -1
  295. package/src/hooks/cli.ts +6 -5
  296. package/src/hooks/config.ts +6 -8
  297. package/src/hooks/discovery.ts +6 -5
  298. package/src/hooks/manager.ts +4 -3
  299. package/src/hooks/runner.ts +2 -2
  300. package/src/hooks/templates.ts +5 -5
  301. package/src/inbound/public-ingress-urls.ts +6 -4
  302. package/src/index.ts +4 -2
  303. package/src/influencer/client.ts +1104 -0
  304. package/src/instrument.ts +4 -3
  305. package/src/logfire.ts +4 -3
  306. package/src/memory/admin.ts +25 -35
  307. package/src/memory/attachments-store.ts +4 -7
  308. package/src/memory/channel-delivery-store.ts +30 -1
  309. package/src/memory/channel-guardian-store.ts +202 -2
  310. package/src/memory/clarification-resolver.ts +37 -33
  311. package/src/memory/conflict-store.ts +67 -61
  312. package/src/memory/contradiction-checker.ts +141 -117
  313. package/src/memory/conversation-store.ts +335 -51
  314. package/src/memory/db-connection.ts +27 -4
  315. package/src/memory/db-init.ts +265 -4
  316. package/src/memory/db.ts +14 -1
  317. package/src/memory/embedding-backend.ts +27 -5
  318. package/src/memory/embedding-ollama.ts +2 -1
  319. package/src/memory/entity-extractor.ts +38 -35
  320. package/src/memory/guardian-action-store.ts +430 -0
  321. package/src/memory/inbox-escalation-projection.ts +59 -0
  322. package/src/memory/inbox-thread-store.ts +218 -0
  323. package/src/memory/ingress-invite-store.ts +338 -0
  324. package/src/memory/ingress-member-store.ts +350 -0
  325. package/src/memory/items-extractor.ts +91 -97
  326. package/src/memory/job-handlers/index-maintenance.ts +3 -3
  327. package/src/memory/job-handlers/media-processing.ts +69 -0
  328. package/src/memory/job-handlers/summarization.ts +32 -26
  329. package/src/memory/job-utils.ts +3 -10
  330. package/src/memory/jobs-store.ts +8 -10
  331. package/src/memory/jobs-worker.ts +55 -36
  332. package/src/memory/media-store.ts +759 -0
  333. package/src/memory/migrations/001-job-deferrals.ts +45 -0
  334. package/src/memory/migrations/002-tool-invocations-fk.ts +43 -0
  335. package/src/memory/migrations/003-memory-fts-backfill.ts +24 -0
  336. package/src/memory/migrations/004-entity-relation-dedup.ts +87 -0
  337. package/src/memory/migrations/005-fingerprint-scope-unique.ts +80 -0
  338. package/src/memory/migrations/006-scope-salted-fingerprints.ts +62 -0
  339. package/src/memory/migrations/007-assistant-id-to-self.ts +254 -0
  340. package/src/memory/migrations/008-remove-assistant-id-columns.ts +208 -0
  341. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +83 -0
  342. package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +56 -0
  343. package/src/memory/migrations/011-call-sessions-provider-sid-dedup.ts +63 -0
  344. package/src/memory/migrations/012-call-sessions-add-initiated-from.ts +19 -0
  345. package/src/memory/migrations/013-guardian-action-tables.ts +68 -0
  346. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +76 -0
  347. package/src/memory/migrations/015-drop-active-search-index.ts +27 -0
  348. package/src/memory/migrations/016-memory-segments-indexes.ts +11 -0
  349. package/src/memory/migrations/017-memory-items-indexes.ts +10 -0
  350. package/src/memory/migrations/018-remaining-table-indexes.ts +13 -0
  351. package/src/memory/migrations/index.ts +24 -0
  352. package/src/memory/migrations/registry.ts +79 -0
  353. package/src/memory/migrations/validate-migration-state.ts +69 -0
  354. package/src/memory/qdrant-manager.ts +49 -8
  355. package/src/memory/query-builder.ts +1 -1
  356. package/src/memory/raw-query.ts +119 -0
  357. package/src/memory/recall-cache.ts +4 -1
  358. package/src/memory/retriever.ts +165 -47
  359. package/src/memory/schema-migration.ts +25 -984
  360. package/src/memory/schema.ts +228 -7
  361. package/src/memory/search/entity.ts +205 -31
  362. package/src/memory/search/lexical.ts +81 -52
  363. package/src/memory/search/ranking.ts +27 -23
  364. package/src/memory/search/semantic.ts +157 -19
  365. package/src/memory/search/types.ts +24 -0
  366. package/src/memory/shared-app-links-store.ts +4 -5
  367. package/src/memory/validation.ts +19 -0
  368. package/src/messaging/draft-store.ts +5 -6
  369. package/src/messaging/provider-types.ts +2 -0
  370. package/src/messaging/providers/sms/adapter.ts +201 -0
  371. package/src/messaging/providers/sms/client.ts +93 -0
  372. package/src/messaging/providers/sms/types.ts +7 -0
  373. package/src/messaging/providers/telegram-bot/adapter.ts +2 -5
  374. package/src/messaging/providers/whatsapp/adapter.ts +136 -0
  375. package/src/messaging/providers/whatsapp/client.ts +67 -0
  376. package/src/messaging/style-analyzer.ts +5 -4
  377. package/src/messaging/thread-summarizer.ts +61 -69
  378. package/src/messaging/triage-engine.ts +62 -71
  379. package/src/migrations/config-merge.ts +53 -0
  380. package/src/migrations/data-layout.ts +68 -0
  381. package/src/migrations/data-merge.ts +33 -0
  382. package/src/migrations/hooks-merge.ts +90 -0
  383. package/src/migrations/index.ts +6 -0
  384. package/src/migrations/log.ts +23 -0
  385. package/src/migrations/skills-merge.ts +33 -0
  386. package/src/migrations/workspace-layout.ts +79 -0
  387. package/src/permissions/checker.ts +133 -11
  388. package/src/permissions/prompter.ts +14 -0
  389. package/src/permissions/shell-identity.ts +31 -1
  390. package/src/permissions/trust-store.ts +21 -1
  391. package/src/providers/anthropic/client.ts +4 -4
  392. package/src/providers/failover.ts +2 -2
  393. package/src/providers/model-intents.ts +70 -0
  394. package/src/providers/ollama/client.ts +2 -1
  395. package/src/providers/provider-send-message.ts +176 -0
  396. package/src/providers/registry.ts +71 -30
  397. package/src/providers/retry.ts +35 -1
  398. package/src/providers/types.ts +12 -1
  399. package/src/runtime/approval-conversation-turn.ts +97 -0
  400. package/src/runtime/approval-message-composer.ts +253 -0
  401. package/src/runtime/channel-approval-parser.ts +36 -2
  402. package/src/runtime/channel-approvals.ts +11 -24
  403. package/src/runtime/channel-guardian-service.ts +88 -21
  404. package/src/runtime/channel-readiness-service.ts +418 -0
  405. package/src/runtime/channel-readiness-types.ts +35 -0
  406. package/src/runtime/channel-retry-sweep.ts +184 -0
  407. package/src/runtime/guardian-context-resolver.ts +108 -0
  408. package/src/runtime/http-server.ts +275 -717
  409. package/src/runtime/http-types.ts +59 -3
  410. package/src/runtime/middleware/auth.ts +116 -0
  411. package/src/runtime/middleware/error-handler.ts +33 -0
  412. package/src/runtime/middleware/twilio-validation.ts +127 -0
  413. package/src/runtime/routes/app-routes.ts +1 -1
  414. package/src/runtime/routes/call-routes.ts +51 -7
  415. package/src/runtime/routes/channel-delivery-routes.ts +170 -0
  416. package/src/runtime/routes/channel-guardian-routes.ts +1191 -0
  417. package/src/runtime/routes/channel-inbound-routes.ts +1152 -0
  418. package/src/runtime/routes/channel-route-shared.ts +144 -0
  419. package/src/runtime/routes/channel-routes.ts +32 -1588
  420. package/src/runtime/routes/conversation-routes.ts +50 -7
  421. package/src/runtime/routes/events-routes.ts +2 -2
  422. package/src/runtime/routes/identity-routes.ts +126 -0
  423. package/src/runtime/routes/pairing-routes.ts +143 -0
  424. package/src/runtime/routes/run-routes.ts +15 -1
  425. package/src/runtime/run-orchestrator.ts +86 -35
  426. package/src/schedule/schedule-store.ts +36 -32
  427. package/src/schedule/scheduler.ts +3 -3
  428. package/src/security/encrypted-store.ts +5 -7
  429. package/src/security/oauth2.ts +45 -15
  430. package/src/security/parental-control-store.ts +183 -0
  431. package/src/security/secret-allowlist.ts +4 -3
  432. package/src/security/secret-scanner.ts +5 -5
  433. package/src/security/secure-keys.ts +1 -1
  434. package/src/security/token-manager.ts +3 -2
  435. package/src/services/vercel-deploy.ts +6 -2
  436. package/src/skills/tool-manifest.ts +3 -3
  437. package/src/skills/vellum-catalog-remote.ts +75 -16
  438. package/src/slack/slack-webhook.ts +2 -1
  439. package/src/swarm/orchestrator.ts +92 -1
  440. package/src/swarm/router-planner.ts +6 -9
  441. package/src/swarm/worker-prompts.ts +9 -12
  442. package/src/tasks/task-compiler.ts +19 -28
  443. package/src/tasks/task-runner.ts +1 -1
  444. package/src/tools/assets/materialize.ts +2 -2
  445. package/src/tools/assets/search.ts +15 -14
  446. package/src/tools/browser/__tests__/auth-detector.test.ts +1 -0
  447. package/src/tools/browser/auto-navigate.ts +1 -0
  448. package/src/tools/browser/browser-execution.ts +10 -1
  449. package/src/tools/browser/browser-manager.ts +119 -4
  450. package/src/tools/browser/network-recorder.ts +5 -0
  451. package/src/tools/calls/call-start.ts +1 -0
  452. package/src/tools/credentials/broker.ts +11 -2
  453. package/src/tools/credentials/metadata-store.ts +18 -14
  454. package/src/tools/credentials/post-connect-hooks.ts +61 -0
  455. package/src/tools/credentials/vault.ts +49 -23
  456. package/src/tools/execution-target.ts +11 -1
  457. package/src/tools/executor.ts +68 -9
  458. package/src/tools/host-terminal/cli-discover.ts +1 -1
  459. package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
  460. package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
  461. package/src/tools/network/script-proxy/server.ts +1 -1
  462. package/src/tools/network/script-proxy/session-manager.ts +6 -5
  463. package/src/tools/network/web-fetch.ts +18 -2
  464. package/src/tools/network/web-search.ts +8 -4
  465. package/src/tools/reminder/reminder-store.ts +14 -15
  466. package/src/tools/schedule/create.ts +1 -0
  467. package/src/tools/schedule/list.ts +2 -1
  468. package/src/tools/shared/filesystem/file-ops-service.ts +5 -7
  469. package/src/tools/skills/skill-script-runner.ts +24 -9
  470. package/src/tools/skills/skill-tool-factory.ts +1 -0
  471. package/src/tools/tasks/work-item-enqueue.ts +2 -2
  472. package/src/tools/terminal/evaluate-typescript.ts +21 -12
  473. package/src/tools/terminal/parser.ts +50 -0
  474. package/src/tools/types.ts +2 -0
  475. package/src/tools/watcher/delete.ts +6 -0
  476. package/src/tools/weather/service.ts +1 -1
  477. package/src/twitter/client.ts +190 -24
  478. package/src/twitter/router.ts +1 -1
  479. package/src/twitter/session.ts +4 -3
  480. package/src/util/clipboard.ts +1 -1
  481. package/src/util/errors.ts +65 -8
  482. package/src/util/fs.ts +40 -0
  483. package/src/util/json.ts +10 -0
  484. package/src/util/log-redact.ts +189 -0
  485. package/src/util/logger.ts +19 -17
  486. package/src/util/object.ts +3 -0
  487. package/src/util/platform.ts +105 -363
  488. package/src/util/pricing.ts +1 -1
  489. package/src/util/promise-guard.ts +1 -1
  490. package/src/util/retry.ts +19 -0
  491. package/src/util/row-mapper.ts +79 -0
  492. package/src/util/silently.ts +21 -0
  493. package/src/watcher/engine.ts +5 -1
  494. package/src/watcher/provider-types.ts +20 -0
  495. package/src/watcher/providers/github.ts +156 -0
  496. package/src/watcher/providers/gmail.ts +1 -0
  497. package/src/watcher/providers/google-calendar.ts +1 -0
  498. package/src/watcher/providers/linear.ts +460 -0
  499. package/src/watcher/providers/slack.ts +1 -0
  500. package/src/work-items/work-item-runner.ts +1 -1
  501. package/src/workspace/git-service.ts +1 -1
  502. package/src/workspace/provider-commit-message-generator.ts +51 -22
  503. package/src/__tests__/call-bridge.test.ts +0 -517
  504. package/src/__tests__/session-process-bridge.test.ts +0 -244
  505. package/src/calls/call-bridge.ts +0 -168
  506. package/src/config/vellum-skills/google-oauth-setup/SKILL.md +0 -199
@@ -0,0 +1,324 @@
1
+ import { describe, test, expect, beforeEach } from 'bun:test';
2
+ import { ChannelReadinessService, REMOTE_TTL_MS } from '../runtime/channel-readiness-service.js';
3
+ import type { ChannelId } from '../channels/types.js';
4
+ import type { ChannelProbe, ReadinessCheckResult } from '../runtime/channel-readiness-types.js';
5
+
6
+ // ── Test helpers ────────────────────────────────────────────────────────────
7
+
8
+ function makeProbe(
9
+ channel: ChannelId,
10
+ localResults: ReadinessCheckResult[],
11
+ remoteResults?: ReadinessCheckResult[],
12
+ ): ChannelProbe & { localCallCount: number; remoteCallCount: number } {
13
+ const probe = {
14
+ channel,
15
+ localCallCount: 0,
16
+ remoteCallCount: 0,
17
+ runLocalChecks(): ReadinessCheckResult[] {
18
+ probe.localCallCount++;
19
+ return localResults;
20
+ },
21
+ ...(remoteResults !== undefined
22
+ ? {
23
+ async runRemoteChecks(): Promise<ReadinessCheckResult[]> {
24
+ probe.remoteCallCount++;
25
+ return remoteResults;
26
+ },
27
+ }
28
+ : {}),
29
+ };
30
+ return probe;
31
+ }
32
+
33
+ // ── Tests ───────────────────────────────────────────────────────────────────
34
+
35
+ describe('ChannelReadinessService', () => {
36
+ let service: ChannelReadinessService;
37
+
38
+ beforeEach(() => {
39
+ service = new ChannelReadinessService();
40
+ });
41
+
42
+ test('local checks run on every call (no caching of local results)', async () => {
43
+ const probe = makeProbe('sms', [
44
+ { name: 'creds', passed: true, message: 'ok' },
45
+ ]);
46
+ service.registerProbe(probe);
47
+
48
+ await service.getReadiness('sms');
49
+ await service.getReadiness('sms');
50
+
51
+ expect(probe.localCallCount).toBe(2);
52
+ });
53
+
54
+ test('cache miss runs local checks and returns snapshot', async () => {
55
+ const probe = makeProbe('sms', [
56
+ { name: 'creds', passed: true, message: 'ok' },
57
+ { name: 'phone', passed: false, message: 'missing' },
58
+ ]);
59
+ service.registerProbe(probe);
60
+
61
+ const [snapshot] = await service.getReadiness('sms');
62
+
63
+ expect(probe.localCallCount).toBe(1);
64
+ expect(snapshot.channel).toBe('sms');
65
+ expect(snapshot.ready).toBe(false);
66
+ expect(snapshot.localChecks).toHaveLength(2);
67
+ expect(snapshot.reasons).toEqual([
68
+ { code: 'phone', text: 'missing' },
69
+ ]);
70
+ });
71
+
72
+ test('includeRemote=true runs remote checks on cache miss', async () => {
73
+ const probe = makeProbe(
74
+ 'sms',
75
+ [{ name: 'creds', passed: true, message: 'ok' }],
76
+ [{ name: 'api_check', passed: true, message: 'remote ok' }],
77
+ );
78
+ service.registerProbe(probe);
79
+
80
+ const [snapshot] = await service.getReadiness('sms', true);
81
+
82
+ expect(probe.remoteCallCount).toBe(1);
83
+ expect(snapshot.remoteChecks).toHaveLength(1);
84
+ expect(snapshot.remoteChecks![0].passed).toBe(true);
85
+ });
86
+
87
+ test('cached remote checks reused within TTL', async () => {
88
+ const probe = makeProbe(
89
+ 'sms',
90
+ [{ name: 'creds', passed: true, message: 'ok' }],
91
+ [{ name: 'api_check', passed: true, message: 'remote ok' }],
92
+ );
93
+ service.registerProbe(probe);
94
+
95
+ // First call populates cache
96
+ await service.getReadiness('sms', true);
97
+ expect(probe.remoteCallCount).toBe(1);
98
+
99
+ // Second call within TTL should reuse cache
100
+ const [snapshot] = await service.getReadiness('sms', true);
101
+ expect(probe.remoteCallCount).toBe(1);
102
+ expect(snapshot.remoteChecks).toHaveLength(1);
103
+ });
104
+
105
+ test('stale cache triggers remote check re-run', async () => {
106
+ const probe = makeProbe(
107
+ 'sms',
108
+ [{ name: 'creds', passed: true, message: 'ok' }],
109
+ [{ name: 'api_check', passed: true, message: 'remote ok' }],
110
+ );
111
+ service.registerProbe(probe);
112
+
113
+ // First call
114
+ await service.getReadiness('sms', true);
115
+ expect(probe.remoteCallCount).toBe(1);
116
+
117
+ // Manually age the cached snapshot beyond TTL
118
+ const cached = (service as unknown as { snapshots: Map<string, { checkedAt: number }> }).snapshots.get('sms::__default__')!;
119
+ cached.checkedAt = Date.now() - REMOTE_TTL_MS - 1;
120
+
121
+ // Second call should re-run remote checks
122
+ await service.getReadiness('sms', true);
123
+ expect(probe.remoteCallCount).toBe(2);
124
+ });
125
+
126
+ test('invalidateChannel clears cache for specific channel', async () => {
127
+ const probe = makeProbe(
128
+ 'sms',
129
+ [{ name: 'creds', passed: true, message: 'ok' }],
130
+ [{ name: 'api_check', passed: true, message: 'remote ok' }],
131
+ );
132
+ service.registerProbe(probe);
133
+
134
+ await service.getReadiness('sms', true);
135
+ expect(probe.remoteCallCount).toBe(1);
136
+
137
+ service.invalidateChannel('sms');
138
+
139
+ // After invalidation, remote checks should run again
140
+ await service.getReadiness('sms', true);
141
+ expect(probe.remoteCallCount).toBe(2);
142
+ });
143
+
144
+ test('invalidateAll clears all cached snapshots', async () => {
145
+ const smsProbe = makeProbe(
146
+ 'sms',
147
+ [{ name: 'creds', passed: true, message: 'ok' }],
148
+ [{ name: 'api', passed: true, message: 'ok' }],
149
+ );
150
+ const telegramProbe = makeProbe(
151
+ 'telegram',
152
+ [{ name: 'token', passed: true, message: 'ok' }],
153
+ [{ name: 'webhook', passed: true, message: 'ok' }],
154
+ );
155
+ service.registerProbe(smsProbe);
156
+ service.registerProbe(telegramProbe);
157
+
158
+ await service.getReadiness(undefined, true);
159
+ expect(smsProbe.remoteCallCount).toBe(1);
160
+ expect(telegramProbe.remoteCallCount).toBe(1);
161
+
162
+ service.invalidateAll();
163
+
164
+ await service.getReadiness(undefined, true);
165
+ expect(smsProbe.remoteCallCount).toBe(2);
166
+ expect(telegramProbe.remoteCallCount).toBe(2);
167
+ });
168
+
169
+ test('unknown channel returns unsupported_channel reason', async () => {
170
+ // Cast to exercise runtime handling of an unrecognized channel value
171
+ const [snapshot] = await service.getReadiness('carrier_pigeon' as ChannelId);
172
+
173
+ expect(snapshot.channel).toBe('carrier_pigeon' as ChannelId);
174
+ expect(snapshot.ready).toBe(false);
175
+ expect(snapshot.reasons).toEqual([
176
+ { code: 'unsupported_channel', text: 'Channel carrier_pigeon is not supported' },
177
+ ]);
178
+ expect(snapshot.localChecks).toHaveLength(0);
179
+ });
180
+
181
+ test('all checks passing yields ready=true', async () => {
182
+ const probe = makeProbe('telegram', [
183
+ { name: 'a', passed: true, message: 'ok' },
184
+ { name: 'b', passed: true, message: 'ok' },
185
+ ]);
186
+ service.registerProbe(probe);
187
+
188
+ const [snapshot] = await service.getReadiness('telegram');
189
+
190
+ expect(snapshot.ready).toBe(true);
191
+ expect(snapshot.reasons).toHaveLength(0);
192
+ });
193
+
194
+ test('getReadiness with no channel returns all registered channels', async () => {
195
+ service.registerProbe(makeProbe('sms', [{ name: 'a', passed: true, message: 'ok' }]));
196
+ service.registerProbe(makeProbe('telegram', [{ name: 'b', passed: true, message: 'ok' }]));
197
+
198
+ const snapshots = await service.getReadiness();
199
+
200
+ expect(snapshots).toHaveLength(2);
201
+ const channels = snapshots.map((s) => s.channel).sort();
202
+ expect(channels).toEqual(['sms', 'telegram']);
203
+ });
204
+
205
+ test('cached remote checks preserve original checkedAt (TTL not reset on reuse)', async () => {
206
+ const probe = makeProbe(
207
+ 'sms',
208
+ [{ name: 'creds', passed: true, message: 'ok' }],
209
+ [{ name: 'api_check', passed: true, message: 'remote ok' }],
210
+ );
211
+ service.registerProbe(probe);
212
+
213
+ // First call populates cache with freshly fetched remote checks
214
+ const [first] = await service.getReadiness('sms', true);
215
+ const originalCheckedAt = first.checkedAt;
216
+ expect(probe.remoteCallCount).toBe(1);
217
+
218
+ // Second call within TTL reuses cache — checkedAt must stay at the original value
219
+ const [second] = await service.getReadiness('sms', true);
220
+ expect(probe.remoteCallCount).toBe(1);
221
+ expect(second.checkedAt).toBe(originalCheckedAt);
222
+ });
223
+
224
+ test('includeRemote runs remote checks when cache exists without remote data', async () => {
225
+ const probe = makeProbe(
226
+ 'sms',
227
+ [{ name: 'creds', passed: true, message: 'ok' }],
228
+ [{ name: 'api_check', passed: true, message: 'remote ok' }],
229
+ );
230
+ service.registerProbe(probe);
231
+
232
+ // First call without includeRemote — cache has no remote data
233
+ await service.getReadiness('sms', false);
234
+ expect(probe.remoteCallCount).toBe(0);
235
+
236
+ // Second call with includeRemote — should run remote checks even though
237
+ // the cached snapshot exists (because it has no remoteChecks)
238
+ const [snapshot] = await service.getReadiness('sms', true);
239
+ expect(probe.remoteCallCount).toBe(1);
240
+ expect(snapshot.remoteChecks).toHaveLength(1);
241
+ expect(snapshot.remoteChecks![0].passed).toBe(true);
242
+ });
243
+
244
+ test('failed remote check makes channel not ready', async () => {
245
+ const probe = makeProbe(
246
+ 'sms',
247
+ [{ name: 'creds', passed: true, message: 'ok' }],
248
+ [{ name: 'api_check', passed: false, message: 'API unreachable' }],
249
+ );
250
+ service.registerProbe(probe);
251
+
252
+ const [snapshot] = await service.getReadiness('sms', true);
253
+
254
+ expect(snapshot.ready).toBe(false);
255
+ expect(snapshot.reasons).toEqual([
256
+ { code: 'api_check', text: 'API unreachable' },
257
+ ]);
258
+ });
259
+
260
+ test('fresh cached remote failures do not affect local-only readiness', async () => {
261
+ const probe = makeProbe(
262
+ 'sms',
263
+ [{ name: 'creds', passed: true, message: 'ok' }],
264
+ [{ name: 'api_check', passed: false, message: 'API unreachable' }],
265
+ );
266
+ service.registerProbe(probe);
267
+
268
+ // Prime remote cache with a failing check
269
+ await service.getReadiness('sms', true);
270
+
271
+ // Immediately call with includeRemote=false (cache is still fresh within TTL).
272
+ // The cached remote failure should be surfaced for visibility but must NOT
273
+ // affect readiness when the caller explicitly opted out of remote checks.
274
+ const [snapshot] = await service.getReadiness('sms', false);
275
+ expect(snapshot.ready).toBe(true);
276
+ expect(snapshot.reasons).toEqual([]);
277
+ // Remote checks are still visible for informational purposes
278
+ expect(snapshot.remoteChecks).toHaveLength(1);
279
+ expect(snapshot.remoteChecks![0].passed).toBe(false);
280
+ });
281
+
282
+ test('stale cached remote failures do not affect local-only readiness', async () => {
283
+ const probe = makeProbe(
284
+ 'sms',
285
+ [{ name: 'creds', passed: true, message: 'ok' }],
286
+ [{ name: 'api_check', passed: false, message: 'API unreachable' }],
287
+ );
288
+ service.registerProbe(probe);
289
+
290
+ // Prime remote cache with a failing check
291
+ await service.getReadiness('sms', true);
292
+
293
+ // Age snapshot beyond TTL so remote checks are stale
294
+ const cached = (service as unknown as { snapshots: Map<string, { checkedAt: number }> }).snapshots.get('sms::__default__')!;
295
+ cached.checkedAt = Date.now() - REMOTE_TTL_MS - 1;
296
+
297
+ // Local-only call should not be blocked by stale remote failure
298
+ const [snapshot] = await service.getReadiness('sms', false);
299
+ expect(snapshot.stale).toBe(true);
300
+ expect(snapshot.ready).toBe(true);
301
+ expect(snapshot.reasons).toEqual([]);
302
+ });
303
+
304
+ test('remote cache is scoped per assistantId', async () => {
305
+ const remoteCalls: Record<string, number> = {};
306
+ const probe: ChannelProbe = {
307
+ channel: 'sms',
308
+ runLocalChecks: () => [{ name: 'local', passed: true, message: 'ok' }],
309
+ async runRemoteChecks(context) {
310
+ const key = context?.assistantId ?? '__default__';
311
+ remoteCalls[key] = (remoteCalls[key] ?? 0) + 1;
312
+ return [{ name: 'remote', passed: true, message: `ok-${key}` }];
313
+ },
314
+ };
315
+ service.registerProbe(probe);
316
+
317
+ await service.getReadiness('sms', true, 'ast-alpha');
318
+ await service.getReadiness('sms', true, 'ast-beta');
319
+ await service.getReadiness('sms', true, 'ast-alpha');
320
+
321
+ expect(remoteCalls['ast-alpha']).toBe(1);
322
+ expect(remoteCalls['ast-beta']).toBe(1);
323
+ });
324
+ });
@@ -1,7 +1,6 @@
1
1
  // Smoke command (run all security test files together):
2
2
  // bun test src/__tests__/checker.test.ts src/__tests__/trust-store.test.ts src/__tests__/session-skill-tools.test.ts src/__tests__/skill-script-runner-host.test.ts
3
3
 
4
- /* eslint-disable @typescript-eslint/no-explicit-any */
5
4
  import { describe, test, expect, beforeAll, beforeEach, afterEach, mock } from 'bun:test';
6
5
  import { mkdtempSync, mkdirSync, rmSync, writeFileSync, symlinkSync, realpathSync } from 'node:fs';
7
6
  import { tmpdir, homedir } from 'node:os';
@@ -39,9 +38,16 @@ mock.module('../util/logger.js', () => ({
39
38
 
40
39
  // Mutable config object so tests can switch permissions.mode between
41
40
  // 'legacy', 'strict', and 'workspace' without re-registering the mock.
42
- const testConfig: Record<string, any> = {
43
- permissions: { mode: 'legacy' as 'legacy' | 'strict' | 'workspace' },
44
- skills: { load: { extraDirs: [] as string[] } },
41
+ interface TestConfig {
42
+ permissions: { mode: 'legacy' | 'strict' | 'workspace' };
43
+ skills: { load: { extraDirs: string[] } };
44
+ sandbox: { enabled: boolean };
45
+ [key: string]: unknown;
46
+ }
47
+
48
+ const testConfig: TestConfig = {
49
+ permissions: { mode: 'legacy' },
50
+ skills: { load: { extraDirs: [] } },
45
51
  sandbox: { enabled: true },
46
52
  };
47
53
 
@@ -58,6 +64,7 @@ mock.module('../config/loader.js', () => ({
58
64
 
59
65
  import { classifyRisk, check, generateAllowlistOptions, generateScopeOptions, _resetLegacyDeprecationWarning } from '../permissions/checker.js';
60
66
  import { RiskLevel } from '../permissions/types.js';
67
+ import type { TrustRule } from '../permissions/types.js';
61
68
  import { addRule, clearCache, findHighestPriorityRule } from '../permissions/trust-store.js';
62
69
  import { getDefaultRuleTemplates } from '../permissions/defaults.js';
63
70
  import { registerTool, getTool } from '../tools/registry.js';
@@ -2353,13 +2360,13 @@ describe('Permission Checker', () => {
2353
2360
  const trustDir = dirnameFn(trustPath);
2354
2361
  if (!existsSync(trustDir)) mkdirSyncFs(trustDir, { recursive: true });
2355
2362
 
2356
- let currentRules: any[] = [];
2363
+ let currentRules: TrustRule[] = [];
2357
2364
  try {
2358
2365
  const raw = readFileSync(trustPath, 'utf-8');
2359
2366
  currentRules = JSON.parse(raw).rules ?? [];
2360
2367
  } catch { /* first run */ }
2361
2368
 
2362
- currentRules = currentRules.filter((r: any) => r.id !== opts.id);
2369
+ currentRules = currentRules.filter((r: TrustRule) => r.id !== opts.id);
2363
2370
  currentRules.push({
2364
2371
  ...opts,
2365
2372
  createdAt: Date.now(),
@@ -2486,7 +2493,7 @@ describe('Permission Checker', () => {
2486
2493
  // Write the executionTarget field directly (addVersionBoundRule doesn't support it)
2487
2494
  const trustPath = join(checkerTestDir, 'protected', 'trust.json');
2488
2495
  const raw = JSON.parse((await import('node:fs')).readFileSync(trustPath, 'utf-8'));
2489
- const rule = raw.rules.find((r: any) => r.id === 'inv4-target-scoped');
2496
+ const rule = raw.rules.find((r: TrustRule) => r.id === 'inv4-target-scoped');
2490
2497
  rule.executionTarget = '/usr/local/bin/node';
2491
2498
  (await import('node:fs')).writeFileSync(trustPath, JSON.stringify(raw, null, 2));
2492
2499
  clearCache();
@@ -6,33 +6,53 @@ let llmResolution: 'keep_existing' | 'keep_candidate' | 'merge' | 'still_unclear
6
6
  let llmResolvedStatement = '';
7
7
  let llmExplanation = 'Unclear response from user.';
8
8
 
9
- mock.module('@anthropic-ai/sdk', () => ({
10
- default: class MockAnthropic {
11
- messages = {
12
- create: async (_body: unknown, opts?: { signal?: AbortSignal }) => {
13
- llmCallCount += 1;
14
- if (llmDelayMs > 0) {
15
- await new Promise((resolve, reject) => {
16
- const timer = setTimeout(resolve, llmDelayMs);
17
- opts?.signal?.addEventListener('abort', () => {
18
- clearTimeout(timer);
19
- reject(new Error('Request was aborted.'));
20
- });
9
+ mock.module('../providers/provider-send-message.js', () => ({
10
+ getConfiguredProvider: () => ({
11
+ sendMessage: async (
12
+ _messages: unknown,
13
+ _tools: unknown,
14
+ _system: unknown,
15
+ opts?: { signal?: AbortSignal },
16
+ ) => {
17
+ llmCallCount += 1;
18
+ if (llmDelayMs > 0) {
19
+ await new Promise((resolve, reject) => {
20
+ const timer = setTimeout(resolve, llmDelayMs);
21
+ opts?.signal?.addEventListener('abort', () => {
22
+ clearTimeout(timer);
23
+ reject(new Error('Request was aborted.'));
21
24
  });
22
- }
23
- return {
24
- content: [{
25
- type: 'tool_use',
26
- input: {
27
- resolution: llmResolution,
28
- resolved_statement: llmResolvedStatement,
29
- explanation: llmExplanation,
30
- },
31
- }],
32
- };
33
- },
25
+ });
26
+ }
27
+ return {
28
+ content: [{
29
+ type: 'tool_use' as const,
30
+ id: 'test-tool-use-id',
31
+ name: 'resolve_conflict',
32
+ input: {
33
+ resolution: llmResolution,
34
+ resolved_statement: llmResolvedStatement,
35
+ explanation: llmExplanation,
36
+ },
37
+ }],
38
+ model: 'claude-haiku-4-5-20251001',
39
+ stopReason: 'tool_use',
40
+ usage: { inputTokens: 0, outputTokens: 0 },
41
+ };
42
+ },
43
+ }),
44
+ createTimeout: (ms: number) => {
45
+ const controller = new AbortController();
46
+ const timer = setTimeout(() => controller.abort(), ms);
47
+ return {
48
+ signal: controller.signal,
49
+ cleanup: () => clearTimeout(timer),
34
50
  };
35
51
  },
52
+ extractToolUse: (response: { content: Array<{ type: string }> }) => {
53
+ return response.content.find((b: { type: string }) => b.type === 'tool_use');
54
+ },
55
+ userMessage: (text: string) => ({ role: 'user', content: [{ type: 'text', text }] }),
36
56
  }));
37
57
 
38
58
  mock.module('../config/loader.js', () => ({
@@ -308,25 +308,30 @@ describe('CommitEnrichmentService', () => {
308
308
  });
309
309
 
310
310
  test('job timeout triggers retry with backoff then fails after max retries', async () => {
311
- // Use a very short timeout so the real git notes write times out
312
311
  const service = new CommitEnrichmentService({
313
312
  maxQueueSize: 10,
314
313
  maxConcurrency: 1,
315
- jobTimeoutMs: 1, // 1ms timeout — will always time out
314
+ jobTimeoutMs: 10, // short timeout
316
315
  maxRetries: 2,
317
316
  });
318
317
 
319
318
  const commitHash = await createCommit();
319
+
320
+ // Use a slow gitService so the timeout always wins the race.
321
+ // A 1ms timeout against a real git write is flaky on fast CI runners.
322
+ const slowGitService = new WorkspaceGitService(testDir);
323
+ await slowGitService.ensureInitialized();
324
+ slowGitService.writeNote = () => new Promise<void>(() => {});
325
+
320
326
  service.enqueue({
321
327
  workspaceDir: testDir,
322
328
  commitHash,
323
329
  context: makeContext(),
324
- gitService,
330
+ gitService: slowGitService,
325
331
  });
326
332
 
327
333
  // Wait for all retries to complete (initial + 2 retries, with backoff)
328
334
  // Backoff: 1s after attempt 1, 2s after attempt 2 = ~3s total
329
- // But since the job itself is very fast to time out, total time is dominated by backoff
330
335
  await waitForDrain(service, 10000);
331
336
  await service.shutdown();
332
337
 
@@ -8,6 +8,13 @@ mock.module('../util/logger.js', () => ({
8
8
  getLogger: () => new Proxy({} as Record<string, unknown>, {
9
9
  get: () => () => {},
10
10
  }),
11
+ getCliLogger: () => new Proxy({} as Record<string, unknown>, {
12
+ get: () => () => {},
13
+ }),
14
+ isDebug: () => false,
15
+ truncateForLog: (value: string, maxLen = 500) => value.length > maxLen ? value.slice(0, maxLen) + '...' : value,
16
+ initLogger: () => {},
17
+ pruneOldLogFiles: () => 0,
11
18
  }));
12
19
 
13
20
  mock.module('../util/platform.js', () => ({
@@ -38,6 +45,7 @@ mock.module('../util/platform.js', () => ({
38
45
  isMacOS: () => false,
39
46
  isLinux: () => true,
40
47
  isWindows: () => false,
48
+ readHttpToken: () => null,
41
49
  }));
42
50
 
43
51
  mock.module('../tools/executor.js', () => ({
@@ -74,9 +74,9 @@ describe('AssistantConfigSchema', () => {
74
74
  const result = AssistantConfigSchema.parse({});
75
75
  expect(result.provider).toBe('anthropic');
76
76
  expect(result.model).toBe('claude-opus-4-6');
77
- expect(result.maxTokens).toBe(64000);
77
+ expect(result.maxTokens).toBe(16000);
78
78
  expect(result.apiKeys).toEqual({});
79
- expect(result.thinking).toEqual({ enabled: false, budgetTokens: 10000 });
79
+ expect(result.thinking).toEqual({ enabled: false, budgetTokens: 10000, streamThinking: false });
80
80
  expect(result.contextWindow).toEqual({
81
81
  enabled: true,
82
82
  maxInputTokens: 180000,
@@ -206,6 +206,7 @@ describe('AssistantConfigSchema', () => {
206
206
  maxEdges: 40,
207
207
  neighborScoreMultiplier: 0.7,
208
208
  maxDepth: 3,
209
+ depthDecay: true,
209
210
  });
210
211
  });
211
212
 
@@ -681,7 +682,7 @@ describe('AssistantConfigSchema', () => {
681
682
  userConsultTimeoutSeconds: 120,
682
683
  disclosure: {
683
684
  enabled: true,
684
- text: 'At the very beginning of the call, introduce yourself as an assistant calling on behalf of the user.',
685
+ text: 'At the very beginning of the call, introduce yourself as an assistant calling on behalf of the person you represent. Do not say "AI assistant".',
685
686
  },
686
687
  safety: {
687
688
  denyCategories: [],
@@ -706,6 +707,11 @@ describe('AssistantConfigSchema', () => {
706
707
  callerIdentity: {
707
708
  allowPerCallOverride: true,
708
709
  },
710
+ verification: {
711
+ enabled: false,
712
+ maxAttempts: 3,
713
+ codeLength: 6,
714
+ },
709
715
  });
710
716
  });
711
717
 
@@ -1187,8 +1193,8 @@ describe('loadConfig with schema validation', () => {
1187
1193
  const config = loadConfig();
1188
1194
  expect(config.provider).toBe('anthropic');
1189
1195
  expect(config.model).toBe('claude-opus-4-6');
1190
- expect(config.maxTokens).toBe(64000);
1191
- expect(config.thinking).toEqual({ enabled: false, budgetTokens: 10000 });
1196
+ expect(config.maxTokens).toBe(16000);
1197
+ expect(config.thinking).toEqual({ enabled: false, budgetTokens: 10000, streamThinking: false });
1192
1198
  expect(config.contextWindow).toEqual({
1193
1199
  enabled: true,
1194
1200
  maxInputTokens: 180000,
@@ -1209,7 +1215,7 @@ describe('loadConfig with schema validation', () => {
1209
1215
  test('falls back to default for invalid maxTokens', () => {
1210
1216
  writeConfig({ maxTokens: -100 });
1211
1217
  const config = loadConfig();
1212
- expect(config.maxTokens).toBe(64000);
1218
+ expect(config.maxTokens).toBe(16000);
1213
1219
  });
1214
1220
 
1215
1221
  test('falls back to defaults for invalid nested values', () => {
@@ -1234,13 +1240,13 @@ describe('loadConfig with schema validation', () => {
1234
1240
  expect(config.model).toBe('gpt-4');
1235
1241
  expect(config.thinking.enabled).toBe(true);
1236
1242
  expect(config.thinking.budgetTokens).toBe(5000);
1237
- expect(config.maxTokens).toBe(64000);
1243
+ expect(config.maxTokens).toBe(16000);
1238
1244
  });
1239
1245
 
1240
1246
  test('handles no config file', () => {
1241
1247
  const config = loadConfig();
1242
1248
  expect(config.provider).toBe('anthropic');
1243
- expect(config.maxTokens).toBe(64000);
1249
+ expect(config.maxTokens).toBe(16000);
1244
1250
  });
1245
1251
 
1246
1252
  test('partial nested objects get defaults for missing fields', () => {
@@ -287,7 +287,7 @@ describe('ContextWindowManager', () => {
287
287
  expect(getSummaryFromContextMessage(userMessage)).toBeNull();
288
288
  });
289
289
 
290
- test('skips compaction during cooldown when projected gain is too low', async () => {
290
+ test('skips compaction during cooldown', async () => {
291
291
  const provider = createProvider(() => {
292
292
  throw new Error('summarizer should not be called while cooldown skip is active');
293
293
  });
@@ -307,7 +307,7 @@ describe('ContextWindowManager', () => {
307
307
  lastCompactedAt: Date.now() - 30_000,
308
308
  });
309
309
  expect(result.compacted).toBe(false);
310
- expect(result.reason).toBe('compaction cooldown active with low projected gain');
310
+ expect(result.reason).toBe('compaction cooldown active');
311
311
  });
312
312
 
313
313
  test('ignores cooldown and compacts under severe token pressure', async () => {
@@ -338,6 +338,34 @@ describe('ContextWindowManager', () => {
338
338
  expect(result.reason).toBeUndefined();
339
339
  });
340
340
 
341
+ test('force=true bypasses cooldown for context-too-large recovery', async () => {
342
+ const provider = createProvider(() => ({
343
+ content: [{ type: 'text', text: '## Goals\n- forced compaction' }],
344
+ model: 'mock-model',
345
+ usage: { inputTokens: 60, outputTokens: 12 },
346
+ stopReason: 'end_turn',
347
+ }));
348
+ const manager = new ContextWindowManager(
349
+ provider,
350
+ 'system prompt',
351
+ makeConfig({ maxInputTokens: 260, targetInputTokens: 180, preserveRecentUserTurns: 1 }),
352
+ );
353
+ const long = 'c'.repeat(220);
354
+ const history: Message[] = [
355
+ message('user', `u1 ${long}`),
356
+ message('assistant', `a1 ${long}`),
357
+ message('user', `u2 ${long}`),
358
+ ];
359
+
360
+ // Same setup as the cooldown test, but with force=true — should compact.
361
+ const result = await manager.maybeCompact(history, undefined, {
362
+ lastCompactedAt: Date.now() - 30_000,
363
+ force: true,
364
+ });
365
+ expect(result.compacted).toBe(true);
366
+ expect(result.reason).toBeUndefined();
367
+ });
368
+
341
369
  test('image-heavy payload is no longer underestimated as below-threshold', async () => {
342
370
  const provider = createProvider(() => ({
343
371
  content: [{ type: 'text', text: '## Goals\n- compacted image-heavy history' }],