@vellumai/assistant 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (535) hide show
  1. package/ARCHITECTURE.md +32 -49
  2. package/Dockerfile +1 -0
  3. package/README.md +1 -2
  4. package/__tests__/permissions/gateway-threshold-reader.test.ts +9 -3
  5. package/bun.lock +26 -26
  6. package/docs/architecture/security.md +20 -0
  7. package/docs/plugins.md +7 -9
  8. package/knip.json +1 -0
  9. package/node_modules/@vellumai/gateway-client/src/index.ts +1 -0
  10. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +39 -1
  11. package/node_modules/@vellumai/gateway-client/src/types.ts +11 -0
  12. package/node_modules/@vellumai/service-contracts/package.json +2 -0
  13. package/node_modules/@vellumai/service-contracts/src/__tests__/contracts.test.ts +4 -0
  14. package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +107 -0
  15. package/node_modules/@vellumai/service-contracts/src/index.ts +5 -1
  16. package/node_modules/@vellumai/service-contracts/src/ingress.ts +24 -0
  17. package/node_modules/@vellumai/service-contracts/src/twilio-ingress.ts +84 -0
  18. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +9 -0
  19. package/node_modules/@vellumai/twilio-client/bun.lock +24 -0
  20. package/node_modules/@vellumai/twilio-client/package.json +18 -0
  21. package/node_modules/@vellumai/twilio-client/src/__tests__/twilio-client.test.ts +128 -0
  22. package/node_modules/@vellumai/twilio-client/src/index.ts +179 -0
  23. package/node_modules/@vellumai/twilio-client/tsconfig.json +20 -0
  24. package/openapi.yaml +565 -12
  25. package/package.json +6 -3
  26. package/src/__tests__/app-builder-tool-scripts.test.ts +3 -3
  27. package/src/__tests__/app-bundler.test.ts +170 -1
  28. package/src/__tests__/app-control-flow.test.ts +374 -0
  29. package/src/__tests__/app-control-no-global-cgevent.test.ts +98 -0
  30. package/src/__tests__/app-control-tool-schemas.test.ts +621 -0
  31. package/src/__tests__/app-executors.test.ts +30 -43
  32. package/src/__tests__/approval-routes-http.test.ts +23 -6
  33. package/src/__tests__/assistant-event-hub-machine-name.test.ts +146 -0
  34. package/src/__tests__/assistant-event-hub-targeted.test.ts +257 -0
  35. package/src/__tests__/assistant-event-hub.test.ts +109 -2
  36. package/src/__tests__/assistant-event.test.ts +10 -0
  37. package/src/__tests__/assistant-events-sse-hardening.test.ts +7 -2
  38. package/src/__tests__/assistant-feature-flags-integration.test.ts +11 -7
  39. package/src/__tests__/background-shell-host-bash.test.ts +14 -15
  40. package/src/__tests__/bootstrap-turn-cleanup.test.ts +44 -0
  41. package/src/__tests__/btw-routes.test.ts +13 -4
  42. package/src/__tests__/call-controller.test.ts +49 -1
  43. package/src/__tests__/call-domain.test.ts +0 -2
  44. package/src/__tests__/call-routes-http.test.ts +0 -2
  45. package/src/__tests__/channel-readiness-service.test.ts +59 -1
  46. package/src/__tests__/checker.test.ts +3 -4
  47. package/src/__tests__/config-loader-backfill.test.ts +90 -155
  48. package/src/__tests__/config-loader-platform-defaults.test.ts +196 -0
  49. package/src/__tests__/config-schema-cmd.test.ts +0 -1
  50. package/src/__tests__/config-set-platform-guard.test.ts +48 -4
  51. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +2 -2
  52. package/src/__tests__/config-watcher.test.ts +2 -2
  53. package/src/__tests__/conversation-app-control-instantiation.test.ts +392 -0
  54. package/src/__tests__/conversation-app-control-lifecycle.test.ts +237 -0
  55. package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
  56. package/src/__tests__/conversation-lifecycle.test.ts +36 -0
  57. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +283 -0
  58. package/src/__tests__/conversation-routes-disk-view.test.ts +6 -0
  59. package/src/__tests__/conversation-routes-guardian-reply.test.ts +120 -72
  60. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  61. package/src/__tests__/conversation-slash-commands.test.ts +0 -4
  62. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +202 -0
  63. package/src/__tests__/conversation-surfaces-app-control.test.ts +317 -0
  64. package/src/__tests__/credential-execution-feature-gates.test.ts +5 -12
  65. package/src/__tests__/credential-execution-managed-contract.test.ts +3 -131
  66. package/src/__tests__/credentials-cli.test.ts +5 -12
  67. package/src/__tests__/cu-unified-flow.test.ts +185 -23
  68. package/src/__tests__/daemon-credential-client.test.ts +101 -19
  69. package/src/__tests__/db-schedule-syntax-migration.test.ts +2 -0
  70. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  71. package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
  72. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -2
  73. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -2
  74. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -1
  75. package/src/__tests__/heartbeat-service.test.ts +718 -1
  76. package/src/__tests__/helpers/call-route-handler.ts +7 -1
  77. package/src/__tests__/host-app-control-proxy.test.ts +602 -0
  78. package/src/__tests__/host-app-control-routes.test.ts +263 -0
  79. package/src/__tests__/host-bash-proxy.test.ts +246 -47
  80. package/src/__tests__/host-bash-routes.test.ts +294 -0
  81. package/src/__tests__/host-browser-proxy.test.ts +24 -22
  82. package/src/__tests__/host-browser-routes.test.ts +39 -13
  83. package/src/__tests__/host-cu-proxy.test.ts +41 -52
  84. package/src/__tests__/host-cu-routes-targeted.test.ts +300 -0
  85. package/src/__tests__/host-file-edit-tool.test.ts +47 -1
  86. package/src/__tests__/host-file-proxy-targeted.test.ts +339 -0
  87. package/src/__tests__/host-file-proxy.test.ts +37 -43
  88. package/src/__tests__/host-file-read-tool.test.ts +17 -0
  89. package/src/__tests__/host-file-routes-targeted.test.ts +262 -0
  90. package/src/__tests__/host-file-write-tool.test.ts +42 -1
  91. package/src/__tests__/host-proxy-base.test.ts +312 -0
  92. package/src/__tests__/host-shell-tool.test.ts +22 -4
  93. package/src/__tests__/host-transfer-proxy-targeted.test.ts +583 -0
  94. package/src/__tests__/host-transfer-proxy.test.ts +121 -22
  95. package/src/__tests__/host-transfer-routes-targeted.test.ts +447 -0
  96. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  97. package/src/__tests__/identity-intro-cache.test.ts +29 -0
  98. package/src/__tests__/identity-routes.test.ts +103 -1
  99. package/src/__tests__/init-feature-flag-overrides.test.ts +26 -3
  100. package/src/__tests__/inline-command-runner.test.ts +0 -1
  101. package/src/__tests__/inline-skill-load-permissions.test.ts +5 -11
  102. package/src/__tests__/integration-status.test.ts +85 -5
  103. package/src/__tests__/intent-routing.test.ts +0 -1
  104. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +95 -5
  105. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +17 -0
  106. package/src/__tests__/managed-skill-lifecycle.test.ts +0 -1
  107. package/src/__tests__/mcp-auth-routes.test.ts +197 -0
  108. package/src/__tests__/mcp-cli.test.ts +338 -2
  109. package/src/__tests__/memory-jobs-worker-lanes.test.ts +188 -0
  110. package/src/__tests__/migration-import-commit-http.test.ts +108 -2
  111. package/src/__tests__/mock-gateway-ipc.ts +1 -0
  112. package/src/__tests__/oauth-cli.test.ts +0 -2
  113. package/src/__tests__/oauth2-gateway-transport.test.ts +0 -1
  114. package/src/__tests__/persistence-secret-redaction.test.ts +299 -0
  115. package/src/__tests__/platform-bash-auto-approve.test.ts +5 -9
  116. package/src/__tests__/prechat-onboarding-contract.test.ts +3 -1
  117. package/src/__tests__/process-message-background-slack.test.ts +2 -0
  118. package/src/__tests__/provider-commit-message-generator.test.ts +0 -1
  119. package/src/__tests__/public-ingress-urls.test.ts +97 -0
  120. package/src/__tests__/require-fresh-approval.test.ts +0 -1
  121. package/src/__tests__/retry-backoff.test.ts +87 -0
  122. package/src/__tests__/runtime-events-sse.test.ts +10 -6
  123. package/src/__tests__/sanitize-config-for-transfer.test.ts +24 -2
  124. package/src/__tests__/schedule-retry.test.ts +715 -0
  125. package/src/__tests__/script-proxy-mitm-handler.test.ts +1 -1
  126. package/src/__tests__/secret-ingress-http.test.ts +1 -0
  127. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  128. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -1
  129. package/src/__tests__/skill-feature-flags.test.ts +43 -41
  130. package/src/__tests__/skill-load-feature-flag.test.ts +13 -14
  131. package/src/__tests__/skill-load-inline-command.test.ts +0 -51
  132. package/src/__tests__/skill-load-inline-includes.test.ts +0 -43
  133. package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
  134. package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -1
  135. package/src/__tests__/slack-channel-config.test.ts +9 -14
  136. package/src/__tests__/system-prompt-ask-mode.test.ts +0 -1
  137. package/src/__tests__/system-prompt.test.ts +0 -1
  138. package/src/__tests__/telegram-config.test.ts +0 -1
  139. package/src/__tests__/test-preload.ts +8 -0
  140. package/src/__tests__/tool-approval-handler.test.ts +3 -4
  141. package/src/__tests__/tool-audit-listener.test.ts +48 -0
  142. package/src/__tests__/tool-execute-pipeline.test.ts +0 -1
  143. package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -1
  144. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
  145. package/src/__tests__/tool-executor.test.ts +0 -1
  146. package/src/__tests__/twilio-config.test.ts +3 -16
  147. package/src/__tests__/twilio-routes.test.ts +3 -5
  148. package/src/__tests__/twilio-validation.test.ts +93 -0
  149. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -4
  150. package/src/__tests__/verification-control-plane-policy.test.ts +2 -4
  151. package/src/__tests__/voice-ingress-preflight.test.ts +19 -0
  152. package/src/__tests__/workspace-migration-006-services-config.test.ts +3 -2
  153. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +1 -5
  154. package/src/__tests__/workspace-migration-down-functions.test.ts +8 -8
  155. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +10 -6
  156. package/src/backup/__tests__/paths.test.ts +0 -22
  157. package/src/backup/__tests__/restore.test.ts +51 -151
  158. package/src/backup/paths.ts +2 -18
  159. package/src/backup/restore.ts +107 -231
  160. package/src/bundler/app-bundler.ts +51 -3
  161. package/src/calls/relay-server.ts +4 -44
  162. package/src/calls/twilio-config.ts +2 -17
  163. package/src/calls/twilio-rest.ts +33 -105
  164. package/src/calls/twilio-routes.ts +11 -12
  165. package/src/channels/types.ts +8 -7
  166. package/src/cli/commands/__tests__/backup.test.ts +6 -277
  167. package/src/cli/commands/__tests__/gateway.test.ts +288 -0
  168. package/src/cli/commands/__tests__/memory-v2.test.ts +4 -0
  169. package/src/cli/commands/__tests__/webhooks.test.ts +0 -1
  170. package/src/cli/commands/backup.ts +6 -331
  171. package/src/cli/commands/clients.ts +36 -37
  172. package/src/cli/commands/contacts.ts +73 -0
  173. package/src/cli/commands/conversations.ts +2 -5
  174. package/src/cli/commands/credentials.ts +15 -7
  175. package/src/cli/commands/domain.ts +66 -15
  176. package/src/cli/commands/gateway.ts +183 -0
  177. package/src/cli/commands/keys.ts +9 -6
  178. package/src/cli/commands/mcp.ts +116 -156
  179. package/src/cli/commands/memory-v2.ts +296 -1
  180. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
  181. package/src/cli/commands/platform/__tests__/connect.test.ts +0 -2
  182. package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -2
  183. package/src/cli/commands/platform/__tests__/status.test.ts +13 -15
  184. package/src/cli/commands/platform/disconnect.ts +5 -4
  185. package/src/cli/commands/platform/index.ts +0 -18
  186. package/src/cli/lib/daemon-credential-client.ts +110 -28
  187. package/src/cli/program.ts +2 -0
  188. package/src/config/assistant-feature-flags.ts +67 -10
  189. package/src/config/bundled-skills/acp/SKILL.md +6 -0
  190. package/src/config/bundled-skills/acp/TOOLS.json +1 -22
  191. package/src/config/bundled-skills/app-builder/SKILL.md +14 -109
  192. package/src/config/bundled-skills/app-builder/TOOLS.json +1 -28
  193. package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -10
  194. package/src/config/bundled-skills/app-control/SKILL.md +75 -0
  195. package/src/config/bundled-skills/app-control/TOOLS.json +299 -0
  196. package/src/config/bundled-skills/app-control/tools/app-control-click.ts +12 -0
  197. package/src/config/bundled-skills/app-control/tools/app-control-combo.ts +12 -0
  198. package/src/config/bundled-skills/app-control/tools/app-control-drag.ts +12 -0
  199. package/src/config/bundled-skills/app-control/tools/app-control-observe.ts +12 -0
  200. package/src/config/bundled-skills/app-control/tools/app-control-press.ts +12 -0
  201. package/src/config/bundled-skills/app-control/tools/app-control-sequence.ts +12 -0
  202. package/src/config/bundled-skills/app-control/tools/app-control-start.ts +12 -0
  203. package/src/config/bundled-skills/app-control/tools/app-control-stop.ts +12 -0
  204. package/src/config/bundled-skills/app-control/tools/app-control-type.ts +12 -0
  205. package/src/config/bundled-skills/computer-use/SKILL.md +6 -0
  206. package/src/config/bundled-skills/computer-use/TOOLS.json +67 -43
  207. package/src/config/bundled-skills/contacts/TOOLS.json +0 -16
  208. package/src/config/bundled-skills/document/TOOLS.json +0 -8
  209. package/src/config/bundled-skills/followups/TOOLS.json +0 -12
  210. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  211. package/src/config/bundled-skills/image-studio/TOOLS.json +0 -4
  212. package/src/config/bundled-skills/media-processing/TOOLS.json +0 -24
  213. package/src/config/bundled-skills/messaging/TOOLS.json +0 -40
  214. package/src/config/bundled-skills/phone-calls/TOOLS.json +0 -12
  215. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +19 -4
  216. package/src/config/bundled-skills/playbooks/TOOLS.json +0 -16
  217. package/src/config/bundled-skills/schedule/TOOLS.json +14 -14
  218. package/src/config/bundled-skills/sequences/TOOLS.json +0 -36
  219. package/src/config/bundled-skills/settings/SKILL.md +4 -0
  220. package/src/config/bundled-skills/settings/TOOLS.json +0 -12
  221. package/src/config/bundled-skills/skill-management/SKILL.md +6 -0
  222. package/src/config/bundled-skills/skill-management/TOOLS.json +0 -8
  223. package/src/config/bundled-skills/subagent/SKILL.md +6 -2
  224. package/src/config/bundled-skills/subagent/TOOLS.json +0 -20
  225. package/src/config/bundled-skills/transcribe/SKILL.md +4 -0
  226. package/src/config/bundled-skills/transcribe/TOOLS.json +0 -4
  227. package/src/config/bundled-tool-registry.ts +21 -0
  228. package/src/config/env-registry.ts +0 -2
  229. package/src/config/env.ts +19 -12
  230. package/src/config/feature-flag-registry.json +21 -133
  231. package/src/config/loader.ts +73 -99
  232. package/src/config/sanitize-for-transfer.ts +2 -0
  233. package/src/config/schemas/__tests__/memory-lifecycle.test.ts +80 -0
  234. package/src/config/schemas/__tests__/memory-v2.test.ts +7 -4
  235. package/src/config/schemas/calls.ts +0 -9
  236. package/src/config/schemas/heartbeat.ts +63 -0
  237. package/src/config/schemas/ingress.ts +10 -6
  238. package/src/config/schemas/llm.ts +5 -10
  239. package/src/config/schemas/memory-lifecycle.ts +77 -24
  240. package/src/config/schemas/memory-v2.ts +48 -4
  241. package/src/config/schemas/platform.ts +6 -0
  242. package/src/config/schemas/services.ts +1 -15
  243. package/src/config/schemas/skills.ts +0 -6
  244. package/src/config/seed-inference-profiles.ts +1 -1
  245. package/src/contacts/contact-store.ts +0 -30
  246. package/src/contacts/contacts-write.ts +0 -27
  247. package/src/context/window-manager.ts +1 -2
  248. package/src/credential-execution/feature-gates.ts +10 -10
  249. package/src/credential-execution/process-manager.ts +12 -41
  250. package/src/daemon/__tests__/conversation-tool-setup.test.ts +126 -5
  251. package/src/daemon/bootstrap-turn-cleanup.ts +45 -0
  252. package/src/daemon/config-watcher.ts +4 -3
  253. package/src/daemon/conversation-agent-loop-handlers.ts +21 -3
  254. package/src/daemon/conversation-agent-loop.ts +32 -28
  255. package/src/daemon/conversation-lifecycle.ts +8 -1
  256. package/src/daemon/conversation-process.ts +16 -11
  257. package/src/daemon/conversation-runtime-assembly.ts +2 -2
  258. package/src/daemon/conversation-surfaces.ts +125 -4
  259. package/src/daemon/conversation-tool-setup.ts +16 -55
  260. package/src/daemon/conversation.ts +21 -2
  261. package/src/daemon/doordash-steps.ts +1 -1
  262. package/src/daemon/handlers/shared.ts +4 -1
  263. package/src/daemon/host-app-control-proxy.ts +293 -0
  264. package/src/daemon/host-bash-proxy.ts +84 -74
  265. package/src/daemon/host-browser-proxy.ts +67 -82
  266. package/src/daemon/host-cu-proxy.ts +81 -86
  267. package/src/daemon/host-file-proxy.ts +93 -69
  268. package/src/daemon/host-proxy-base.ts +294 -0
  269. package/src/daemon/host-proxy-preactivation.ts +82 -0
  270. package/src/daemon/host-transfer-proxy.ts +247 -129
  271. package/src/daemon/lifecycle.ts +115 -117
  272. package/src/daemon/message-protocol.ts +3 -8
  273. package/src/daemon/message-types/contacts.ts +23 -1
  274. package/src/daemon/message-types/conversations.ts +11 -8
  275. package/src/daemon/message-types/host-app-control.ts +150 -0
  276. package/src/daemon/message-types/host-bash.ts +4 -0
  277. package/src/daemon/message-types/host-cu.ts +2 -0
  278. package/src/daemon/message-types/host-file.ts +4 -0
  279. package/src/daemon/message-types/host-transfer.ts +3 -0
  280. package/src/daemon/message-types/schedules.ts +8 -3
  281. package/src/daemon/message-types/skills.ts +2 -2
  282. package/src/daemon/process-message.ts +18 -1
  283. package/src/daemon/shutdown-handlers.ts +0 -3
  284. package/src/daemon/tool-setup-types.ts +51 -0
  285. package/src/daemon/tool-side-effects.ts +1 -1
  286. package/src/events/tool-audit-listener.ts +2 -1
  287. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +15 -7
  288. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +216 -0
  289. package/src/heartbeat/heartbeat-run-store.ts +236 -0
  290. package/src/heartbeat/heartbeat-service.ts +280 -49
  291. package/src/home/__tests__/post-connect-feed.test.ts +99 -0
  292. package/src/home/__tests__/relationship-state-writer.test.ts +11 -9
  293. package/src/home/__tests__/suggested-prompts.test.ts +89 -0
  294. package/src/home/post-connect-feed.ts +68 -0
  295. package/src/home/relationship-state-writer.ts +17 -92
  296. package/src/home/suggested-prompts.ts +46 -10
  297. package/src/inbound/public-ingress-urls.ts +32 -34
  298. package/src/ipc/__tests__/route-error-envelope.test.ts +80 -0
  299. package/src/ipc/assistant-server.ts +14 -1
  300. package/src/ipc/cli-client.ts +32 -1
  301. package/src/live-voice/live-voice-metrics.ts +10 -10
  302. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +304 -0
  303. package/src/mcp/mcp-auth-orchestrator.ts +213 -0
  304. package/src/mcp/mcp-auth-state.ts +133 -0
  305. package/src/mcp/mcp-oauth-provider.ts +19 -0
  306. package/src/memory/__tests__/jobs-store-job-classes.test.ts +24 -0
  307. package/src/memory/__tests__/qdrant-client-sentinel.test.ts +49 -0
  308. package/src/memory/__tests__/sparse-tokenize.test.ts +66 -0
  309. package/src/memory/anisotropy.test.ts +247 -0
  310. package/src/memory/anisotropy.ts +443 -0
  311. package/src/memory/auto-analysis-constants.ts +17 -0
  312. package/src/memory/auto-analysis-guard.ts +5 -15
  313. package/src/memory/canonical-guardian-store.ts +7 -7
  314. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +122 -0
  315. package/src/memory/context-search/agent-protocol.ts +6 -6
  316. package/src/memory/context-search/agent-runner.ts +32 -7
  317. package/src/memory/context-search/sources/memory-v2.ts +17 -5
  318. package/src/memory/conversation-crud.ts +1 -1
  319. package/src/memory/conversation-key-store.ts +2 -15
  320. package/src/memory/db-init.ts +4 -0
  321. package/src/memory/embedding-backend.ts +9 -21
  322. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +49 -4
  323. package/src/memory/graph/conversation-graph-memory.ts +1 -24
  324. package/src/memory/graph/graph-search.ts +8 -0
  325. package/src/memory/graph/retriever.ts +28 -0
  326. package/src/memory/graph/tools.ts +1 -1
  327. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +8 -2
  328. package/src/memory/jobs/embed-concept-page.ts +28 -2
  329. package/src/memory/jobs/embed-pkb-file.test.ts +2 -2
  330. package/src/memory/jobs-store.ts +66 -22
  331. package/src/memory/jobs-worker.ts +112 -63
  332. package/src/memory/memory-v2-activation-log-store.ts +1 -1
  333. package/src/memory/migrations/237-heartbeat-runs.ts +45 -0
  334. package/src/memory/migrations/238-schedule-retry-policy.ts +20 -0
  335. package/src/memory/migrations/index.ts +5 -0
  336. package/src/memory/migrations/registry.ts +8 -0
  337. package/src/memory/pkb/pkb-search.ts +7 -0
  338. package/src/memory/qdrant-client.ts +50 -20
  339. package/src/memory/schema/infrastructure.ts +15 -0
  340. package/src/memory/search/semantic.ts +7 -0
  341. package/src/memory/sparse-tokenize.ts +49 -0
  342. package/src/memory/v2/__tests__/activation.test.ts +77 -95
  343. package/src/memory/v2/__tests__/injection.test.ts +43 -21
  344. package/src/memory/v2/__tests__/sim.test.ts +166 -6
  345. package/src/memory/v2/__tests__/sparse-bm25.test.ts +292 -0
  346. package/src/memory/v2/__tests__/static-context.test.ts +0 -1
  347. package/src/memory/v2/activation.ts +69 -88
  348. package/src/memory/v2/consolidation-job.ts +3 -5
  349. package/src/memory/v2/constants.ts +7 -0
  350. package/src/memory/v2/injection.ts +86 -53
  351. package/src/memory/v2/prompts/consolidation.ts +312 -91
  352. package/src/memory/v2/qdrant.ts +99 -1
  353. package/src/memory/v2/sim.ts +126 -16
  354. package/src/memory/v2/skill-qdrant.ts +12 -3
  355. package/src/memory/v2/skill-store.ts +16 -1
  356. package/src/memory/v2/sparse-bm25.ts +245 -0
  357. package/src/memory/v2/static-context.ts +6 -5
  358. package/src/messaging/providers/gmail/types.ts +0 -49
  359. package/src/messaging/providers/slack/adapter.ts +1 -31
  360. package/src/messaging/providers/slack/types.ts +0 -32
  361. package/src/notifications/README.md +10 -10
  362. package/src/notifications/broadcaster.ts +1 -1
  363. package/src/notifications/guardian-question-mode.ts +5 -5
  364. package/src/oauth/connect-orchestrator.ts +4 -0
  365. package/src/oauth/credential-token-resolver.ts +1 -3
  366. package/src/oauth/manual-token-connection.ts +0 -4
  367. package/src/outbound-proxy/index.ts +1 -37
  368. package/src/outbound-proxy/logging.ts +1 -1
  369. package/src/outbound-proxy/policy.ts +6 -5
  370. package/src/outbound-proxy/router.ts +2 -1
  371. package/src/permissions/approval-policy.test.ts +6 -275
  372. package/src/permissions/approval-policy.ts +0 -51
  373. package/src/permissions/checker.test.ts +0 -1
  374. package/src/permissions/checker.ts +3 -17
  375. package/src/permissions/gateway-threshold-reader.ts +2 -0
  376. package/src/permissions/prompter.ts +34 -1
  377. package/src/permissions/secret-prompter.ts +6 -2
  378. package/src/prompts/bootstrap-cleanup.ts +27 -0
  379. package/src/prompts/system-prompt.ts +3 -18
  380. package/src/prompts/templates/SOUL.md +13 -1
  381. package/src/providers/speech-to-text/provider-catalog.ts +7 -8
  382. package/src/runtime/assistant-event-hub.ts +118 -96
  383. package/src/runtime/assistant-event.ts +1 -0
  384. package/src/runtime/auth/__tests__/middleware.test.ts +11 -56
  385. package/src/runtime/auth/middleware.ts +0 -96
  386. package/src/runtime/auth/route-policy.ts +19 -0
  387. package/src/runtime/btw-sidechain.ts +2 -3
  388. package/src/runtime/channel-invite-transport.ts +2 -48
  389. package/src/runtime/channel-invite-transports/email.ts +1 -1
  390. package/src/runtime/channel-invite-transports/slack.ts +1 -1
  391. package/src/runtime/channel-invite-transports/telegram.ts +1 -1
  392. package/src/runtime/channel-invite-transports/voice.ts +1 -1
  393. package/src/runtime/channel-invite-transports/whatsapp.ts +1 -1
  394. package/src/runtime/channel-invite-types.ts +54 -0
  395. package/src/runtime/channel-readiness-service.ts +32 -13
  396. package/src/runtime/http-server.ts +3 -329
  397. package/src/runtime/http-types.ts +0 -5
  398. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +413 -0
  399. package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +260 -0
  400. package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +189 -0
  401. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +153 -1
  402. package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +451 -0
  403. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  404. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +515 -0
  405. package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +437 -0
  406. package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +319 -0
  407. package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +51 -1
  408. package/src/runtime/migrations/migration-transport.ts +7 -7
  409. package/src/runtime/migrations/vbundle-builder.ts +327 -60
  410. package/src/runtime/migrations/vbundle-import-analyzer.ts +4 -4
  411. package/src/runtime/migrations/vbundle-import-policy.ts +172 -0
  412. package/src/runtime/migrations/vbundle-importer.ts +245 -68
  413. package/src/runtime/migrations/vbundle-streaming-importer.ts +326 -35
  414. package/src/runtime/migrations/vbundle-streaming-validator.ts +157 -4
  415. package/src/runtime/migrations/vbundle-tar-stream.ts +15 -6
  416. package/src/runtime/migrations/vbundle-validator.ts +114 -0
  417. package/src/runtime/pending-interactions.ts +35 -9
  418. package/src/runtime/routes/__tests__/backup-routes.test.ts +22 -150
  419. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
  420. package/src/runtime/routes/__tests__/gateway-log-routes.test.ts +242 -0
  421. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +112 -0
  422. package/src/runtime/routes/approval-interception-types.ts +13 -0
  423. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +1 -1
  424. package/src/runtime/routes/backup-routes.ts +15 -38
  425. package/src/runtime/routes/btw-routes.ts +14 -37
  426. package/src/runtime/routes/client-routes.ts +1 -0
  427. package/src/runtime/routes/contact-prompt-routes.ts +183 -0
  428. package/src/runtime/routes/conversation-query-routes.ts +36 -1
  429. package/src/runtime/routes/conversation-routes.ts +30 -13
  430. package/src/runtime/routes/document-pdf-renderer.ts +165 -0
  431. package/src/runtime/routes/documents-routes.ts +30 -0
  432. package/src/runtime/routes/errors.ts +19 -4
  433. package/src/runtime/routes/events-routes.ts +12 -6
  434. package/src/runtime/routes/gateway-log-routes.ts +79 -0
  435. package/src/runtime/routes/guardian-approval-interception.ts +2 -8
  436. package/src/runtime/routes/heartbeat-routes.ts +103 -38
  437. package/src/runtime/routes/host-app-control-routes.ts +134 -0
  438. package/src/runtime/routes/host-bash-routes.ts +36 -6
  439. package/src/runtime/routes/host-browser-routes.ts +108 -13
  440. package/src/runtime/routes/host-cu-routes.ts +44 -14
  441. package/src/runtime/routes/host-file-routes.ts +33 -10
  442. package/src/runtime/routes/host-transfer-routes.ts +64 -24
  443. package/src/runtime/routes/http-adapter.ts +1 -0
  444. package/src/runtime/routes/identity-intro-cache.ts +30 -0
  445. package/src/runtime/routes/identity-routes.ts +15 -43
  446. package/src/runtime/routes/inbound-message-handler.ts +1 -9
  447. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -7
  448. package/src/runtime/routes/inbound-stages/edit-intercept.ts +0 -8
  449. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +0 -20
  450. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +5 -13
  451. package/src/runtime/routes/index.ts +8 -0
  452. package/src/runtime/routes/mcp-auth-routes.ts +132 -0
  453. package/src/runtime/routes/memory-item-routes.ts +10 -12
  454. package/src/runtime/routes/memory-v2-routes.ts +441 -1
  455. package/src/runtime/routes/migration-routes.ts +96 -0
  456. package/src/runtime/routes/schedule-routes.ts +7 -0
  457. package/src/runtime/verification-templates.ts +4 -7
  458. package/src/schedule/integration-status.ts +66 -2
  459. package/src/schedule/recurrence-engine.ts +4 -1
  460. package/src/schedule/retry-backoff.ts +18 -0
  461. package/src/schedule/retry-policy.ts +82 -0
  462. package/src/schedule/schedule-recovery.ts +64 -0
  463. package/src/schedule/schedule-store.ts +106 -2
  464. package/src/schedule/scheduler-types.ts +25 -0
  465. package/src/schedule/scheduler.ts +63 -38
  466. package/src/security/oauth-callback-registry.ts +8 -0
  467. package/src/sequence/analytics.ts +5 -5
  468. package/src/sequence/engine.ts +1 -1
  469. package/src/skills/catalog-files.ts +2 -8
  470. package/src/skills/include-graph.ts +5 -5
  471. package/src/skills/remote-skill-policy.ts +5 -5
  472. package/src/skills/skill-file-provider.ts +1 -1
  473. package/src/skills/skill-file-types.ts +13 -0
  474. package/src/skills/skillssh-audit-types.ts +28 -0
  475. package/src/skills/skillssh-registry.ts +8 -21
  476. package/src/telemetry/types.ts +2 -0
  477. package/src/telemetry/usage-telemetry-reporter.test.ts +21 -0
  478. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  479. package/src/tools/app-control/skill-proxy-bridge.ts +28 -0
  480. package/src/tools/apps/executors.ts +56 -69
  481. package/src/tools/browser/__tests__/browser-status.test.ts +21 -18
  482. package/src/tools/browser/browser-execution.ts +2 -2
  483. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +55 -4
  484. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +12 -6
  485. package/src/tools/browser/cdp-client/factory.ts +23 -24
  486. package/src/tools/browser/cdp-client/index.ts +1 -14
  487. package/src/tools/computer-use/definitions.ts +42 -20
  488. package/src/tools/executor.ts +2 -0
  489. package/src/tools/host-filesystem/edit.ts +26 -0
  490. package/src/tools/host-filesystem/read.ts +26 -0
  491. package/src/tools/host-filesystem/transfer.ts +31 -1
  492. package/src/tools/host-filesystem/write.ts +26 -0
  493. package/src/tools/host-terminal/host-shell.ts +58 -0
  494. package/src/tools/schedule/create.ts +6 -0
  495. package/src/tools/schedule/list.ts +2 -0
  496. package/src/tools/schedule/update.ts +10 -0
  497. package/src/tools/shared/filesystem/file-ops-service.ts +2 -0
  498. package/src/tools/shared/filesystem/path-policy.ts +25 -1
  499. package/src/tools/skills/load.ts +0 -32
  500. package/src/tools/tool-approval-handler.ts +1 -5
  501. package/src/tools/types.ts +4 -0
  502. package/src/usage/pricing.ts +1 -1
  503. package/src/workspace/hatched-date.ts +86 -0
  504. package/src/workspace/migrations/003-seed-device-id.ts +1 -1
  505. package/src/workspace/migrations/006-services-config.ts +8 -5
  506. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +3 -9
  507. package/src/workspace/migrations/021-move-signals-to-workspace.ts +4 -10
  508. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +4 -10
  509. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +4 -11
  510. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +3 -10
  511. package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +3 -2
  512. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +2 -1
  513. package/src/workspace/migrations/059-move-pid-to-workspace.ts +3 -8
  514. package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +3 -8
  515. package/src/workspace/migrations/AGENTS.md +1 -1
  516. package/src/workspace/migrations/migrate-to-workspace-volume.ts +4 -10
  517. package/src/workspace/migrations/utils.ts +21 -0
  518. package/src/__tests__/host-browser-e2e-cloud.test.ts +0 -443
  519. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +0 -226
  520. package/src/__tests__/host-browser-ws-events-e2e.test.ts +0 -427
  521. package/src/__tests__/twilio-rest.test.ts +0 -34
  522. package/src/backup/__tests__/backup-key.test.ts +0 -152
  523. package/src/backup/__tests__/backup-worker.test.ts +0 -782
  524. package/src/backup/__tests__/offsite-writer.test.ts +0 -641
  525. package/src/backup/__tests__/stream-crypt.test.ts +0 -228
  526. package/src/backup/backup-key.ts +0 -137
  527. package/src/backup/backup-worker.ts +0 -472
  528. package/src/backup/offsite-writer.ts +0 -222
  529. package/src/backup/stream-crypt.ts +0 -263
  530. package/src/daemon/message-types/pairing.ts +0 -58
  531. package/src/outbound-proxy/config.ts +0 -20
  532. package/src/outbound-proxy/health.ts +0 -18
  533. package/src/outbound-proxy/types.ts +0 -150
  534. package/src/runtime/capability-tokens.ts +0 -190
  535. package/src/signals/mcp-reload.ts +0 -18
@@ -42,9 +42,6 @@ export const GUARDIAN_VERIFY_TEMPLATE_KEYS = {
42
42
  CHANNEL_BOOTSTRAP_BOUND: "guardian_verify.channel.bootstrap_bound",
43
43
  } as const;
44
44
 
45
- export type GuardianVerifyTemplateKey =
46
- (typeof GUARDIAN_VERIFY_TEMPLATE_KEYS)[keyof typeof GUARDIAN_VERIFY_TEMPLATE_KEYS];
47
-
48
45
  /** Template keys for Telegram/Slack text-based verification messages. */
49
46
  type TextVerifyTemplateKey =
50
47
  | typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.ALREADY_VERIFIED
@@ -56,7 +53,7 @@ type TextVerifyTemplateKey =
56
53
  | typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.SLACK_TRUSTED_CONTACT_RESEND;
57
54
 
58
55
  /** Template keys for deterministic channel verification reply messages. */
59
- export type ChannelVerifyReplyTemplateKey =
56
+ type ChannelVerifyReplyTemplateKey =
60
57
  | typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.CHANNEL_VERIFY_SUCCESS
61
58
  | typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.CHANNEL_VERIFY_FAILED
62
59
  | typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.CHANNEL_BOOTSTRAP_BOUND;
@@ -65,18 +62,18 @@ export type ChannelVerifyReplyTemplateKey =
65
62
  // Template Variables
66
63
  // ---------------------------------------------------------------------------
67
64
 
68
- export interface GuardianVerifyTemplateVars {
65
+ interface GuardianVerifyTemplateVars {
69
66
  code: string;
70
67
  expiresInMinutes: number;
71
68
  assistantName?: string;
72
69
  }
73
70
 
74
- export interface GuardianVerifyVoiceTemplateVars {
71
+ interface GuardianVerifyVoiceTemplateVars {
75
72
  /** Number of digits in the verification code. */
76
73
  codeDigits: number;
77
74
  }
78
75
 
79
- export interface ChannelVerifyReplyVars {
76
+ interface ChannelVerifyReplyVars {
80
77
  /** Failure reason (anti-oracle: generic message). Only used for failed template. */
81
78
  failureReason?: string;
82
79
  /** Drives different success copy for guardian vs trusted contact verification. */
@@ -1,9 +1,73 @@
1
1
  import { hasTwilioCredentials } from "../calls/twilio-rest.js";
2
+ import type { Services } from "../config/schemas/services.js";
2
3
  import {
3
4
  getConnectionByProvider,
5
+ getProvider,
4
6
  isProviderConnected,
5
7
  } from "../oauth/oauth-store.js";
6
8
 
9
+ /**
10
+ * Check whether a provider has an active connection, handling both BYO
11
+ * (local SQLite) and managed (platform) modes.
12
+ */
13
+ async function isOAuthProviderConnected(provider: string): Promise<boolean> {
14
+ const providerRow = getProvider(provider);
15
+ const managedKey = providerRow?.managedServiceConfigKey;
16
+
17
+ if (managedKey) {
18
+ try {
19
+ const { ServicesSchema, getServiceMode } =
20
+ await import("../config/schemas/services.js");
21
+
22
+ if (managedKey in ServicesSchema.shape) {
23
+ const { getConfig } = await import("../config/loader.js");
24
+ const services: Services = getConfig().services;
25
+ if (
26
+ getServiceMode(services, managedKey as keyof Services) === "managed"
27
+ ) {
28
+ return isProviderConnectedOnPlatform(provider);
29
+ }
30
+ }
31
+ } catch {
32
+ // Config unavailable — fall through to BYO check
33
+ }
34
+ }
35
+
36
+ return isProviderConnected(provider);
37
+ }
38
+
39
+ /**
40
+ * Check the platform for active connections for a managed provider.
41
+ * Returns false on any error (network, auth, etc.) rather than throwing.
42
+ */
43
+ async function isProviderConnectedOnPlatform(
44
+ provider: string,
45
+ ): Promise<boolean> {
46
+ try {
47
+ const { VellumPlatformClient } = await import("../platform/client.js");
48
+ const client = await VellumPlatformClient.create();
49
+ if (!client?.platformAssistantId) return false;
50
+
51
+ const params = new URLSearchParams();
52
+ params.set("provider", provider);
53
+ params.set("status", "ACTIVE");
54
+
55
+ const path = `/v1/assistants/${encodeURIComponent(client.platformAssistantId)}/oauth/connections/?${params.toString()}`;
56
+ const response = await client.fetch(path);
57
+
58
+ if (!response.ok) return false;
59
+
60
+ const body = (await response.json()) as unknown;
61
+ const connections = Array.isArray(body)
62
+ ? body
63
+ : ((body as Record<string, unknown>).results ?? []);
64
+
65
+ return (connections as unknown[]).length > 0;
66
+ } catch {
67
+ return false;
68
+ }
69
+ }
70
+
7
71
  interface IntegrationProbe {
8
72
  name: string;
9
73
  category: string;
@@ -15,12 +79,12 @@ const INTEGRATION_PROBES: IntegrationProbe[] = [
15
79
  {
16
80
  name: "Gmail",
17
81
  category: "email",
18
- isConnected: () => isProviderConnected("google"),
82
+ isConnected: () => isOAuthProviderConnected("google"),
19
83
  },
20
84
  {
21
85
  name: "Slack",
22
86
  category: "messaging",
23
- isConnected: () => isProviderConnected("slack"),
87
+ isConnected: () => isOAuthProviderConnected("slack"),
24
88
  },
25
89
  {
26
90
  name: "Twilio",
@@ -107,7 +107,10 @@ export function validateRruleSetLines(expression: string): string | null {
107
107
  export function isValidScheduleExpression(spec: ScheduleSpec): boolean {
108
108
  try {
109
109
  if (spec.syntax === "cron") {
110
- new Cron(spec.expression, { maxRuns: 0 });
110
+ new Cron(spec.expression, {
111
+ maxRuns: 0,
112
+ timezone: spec.timezone ?? undefined,
113
+ });
111
114
  return true;
112
115
  }
113
116
 
@@ -0,0 +1,18 @@
1
+ export const DEFAULT_MAX_BACKOFF_MS = 30 * 60 * 1000; // 30 minutes
2
+
3
+ /**
4
+ * Compute retry delay with exponential backoff and jitter.
5
+ * The exponential term is clamped to maxMs BEFORE jitter to prevent
6
+ * Infinity/NaN for large attempt values, and capped again after jitter
7
+ * so the result never exceeds maxMs.
8
+ */
9
+ export function computeRetryDelay(
10
+ attempt: number,
11
+ baseMs: number,
12
+ maxMs: number = DEFAULT_MAX_BACKOFF_MS,
13
+ random: () => number = Math.random,
14
+ ): number {
15
+ const exponential = Math.min(baseMs * Math.pow(2, attempt), maxMs);
16
+ const jitter = exponential * 0.2 * (2 * random() - 1);
17
+ return Math.max(0, Math.min(Math.round(exponential + jitter), maxMs));
18
+ }
@@ -0,0 +1,82 @@
1
+ import type { Logger } from "pino";
2
+
3
+ import { computeRetryDelay } from "./retry-backoff.js";
4
+
5
+ export interface RetryPolicyJob {
6
+ id: string;
7
+ name: string;
8
+ retryCount: number;
9
+ maxRetries: number;
10
+ retryBackoffMs: number;
11
+ }
12
+
13
+ export type RetryDecision =
14
+ | { action: "retry"; delayMs: number; nextRetryAt: number }
15
+ | { action: "exhaust" };
16
+
17
+ /**
18
+ * Pure decision function: given a job's retry state, decide whether
19
+ * to retry with backoff or give up.
20
+ *
21
+ * `retryCount` is the PRE-increment value (before completeScheduleRun
22
+ * bumped it). So `retryCount < maxRetries` allows exactly maxRetries
23
+ * retries: retryCount 0, 1, …, maxRetries−1 all pass the check.
24
+ */
25
+ export function decideRetry(
26
+ job: RetryPolicyJob,
27
+ now: number = Date.now(),
28
+ ): RetryDecision {
29
+ if (job.retryCount < job.maxRetries) {
30
+ const delayMs = computeRetryDelay(job.retryCount, job.retryBackoffMs);
31
+ return { action: "retry", delayMs, nextRetryAt: now + delayMs };
32
+ }
33
+ return { action: "exhaust" };
34
+ }
35
+
36
+ /**
37
+ * Apply the retry decision to a schedule: schedule a retry or exhaust.
38
+ * Calls the provided store operations so this module stays decoupled
39
+ * from direct DB imports.
40
+ */
41
+ export function applyRetryDecision(params: {
42
+ job: RetryPolicyJob;
43
+ isOneShot: boolean;
44
+ errorMsg: string;
45
+ decision: RetryDecision;
46
+ scheduleRetry: (id: string, nextRetryAt: number) => void;
47
+ failOneShotPermanently: (id: string) => void;
48
+ resetRetryCount: (id: string) => void;
49
+ emitAlert: (title: string, summary: string, dedupKey: string) => void;
50
+ log: Logger;
51
+ }): void {
52
+ const { job, isOneShot, errorMsg, decision } = params;
53
+
54
+ if (decision.action === "retry") {
55
+ params.scheduleRetry(job.id, decision.nextRetryAt);
56
+ params.log.info(
57
+ {
58
+ jobId: job.id,
59
+ name: job.name,
60
+ attempt: job.retryCount + 1,
61
+ maxRetries: job.maxRetries,
62
+ delayMs: decision.delayMs,
63
+ },
64
+ "Scheduling retry with backoff",
65
+ );
66
+ } else {
67
+ if (isOneShot) {
68
+ params.failOneShotPermanently(job.id);
69
+ } else {
70
+ params.resetRetryCount(job.id);
71
+ }
72
+ params.emitAlert(
73
+ `${job.name}: Retries exhausted`,
74
+ `Failed after ${job.retryCount + 1} attempt(s): ${errorMsg}`,
75
+ `schedule-retries-exhausted:${job.id}:${Date.now()}`,
76
+ );
77
+ params.log.warn(
78
+ { jobId: job.id, name: job.name, attempts: job.retryCount + 1 },
79
+ "Schedule retries exhausted",
80
+ );
81
+ }
82
+ }
@@ -0,0 +1,64 @@
1
+ import { getLogger } from "../util/logger.js";
2
+ import { applyRetryDecision, decideRetry } from "./retry-policy.js";
3
+ import {
4
+ completeScheduleRun,
5
+ createScheduleRun,
6
+ failOneShotPermanently,
7
+ findStaleInFlightJobs,
8
+ getSchedule,
9
+ resetRetryCount,
10
+ scheduleRetry,
11
+ } from "./schedule-store.js";
12
+
13
+ const log = getLogger("schedule-recovery");
14
+
15
+ /**
16
+ * Recover schedules left in an inconsistent state by a prior process crash.
17
+ * Called once at daemon startup, before the scheduler tick loop starts,
18
+ * so all "firing" / "running" rows are definitively stale.
19
+ */
20
+ export function recoverStaleSchedules(): number {
21
+ const stale = findStaleInFlightJobs(0);
22
+ if (stale.length === 0) return 0;
23
+
24
+ log.info({ count: stale.length }, "Recovering stale in-flight schedules");
25
+
26
+ let recovered = 0;
27
+ for (const { jobId, staleRunId } of stale) {
28
+ try {
29
+ const job = getSchedule(jobId);
30
+ if (!job) continue;
31
+
32
+ const errorMsg =
33
+ "Process terminated during execution (recovered on restart)";
34
+
35
+ if (staleRunId) {
36
+ completeScheduleRun(staleRunId, { status: "error", error: errorMsg });
37
+ } else {
38
+ const runId = createScheduleRun(jobId, `recovery:${jobId}`);
39
+ completeScheduleRun(runId, { status: "error", error: errorMsg });
40
+ }
41
+
42
+ // Use the same retry-or-exhaust path as the scheduler
43
+ const isOneShot = job.expression == null;
44
+ const decision = decideRetry(job);
45
+ applyRetryDecision({
46
+ job,
47
+ isOneShot,
48
+ errorMsg,
49
+ decision,
50
+ scheduleRetry,
51
+ failOneShotPermanently,
52
+ resetRetryCount,
53
+ emitAlert: () => {}, // no feed event on startup recovery
54
+ log,
55
+ });
56
+ recovered++;
57
+ } catch (err) {
58
+ log.error({ err, jobId }, "Failed to recover stale schedule");
59
+ }
60
+ }
61
+
62
+ log.info({ recovered }, "Stale schedule recovery complete");
63
+ return recovered;
64
+ }
@@ -33,6 +33,8 @@ export interface ScheduleJob {
33
33
  lastRunAt: number | null;
34
34
  lastStatus: string | null;
35
35
  retryCount: number;
36
+ maxRetries: number;
37
+ retryBackoffMs: number;
36
38
  createdBy: string;
37
39
  mode: ScheduleMode;
38
40
  routingIntent: RoutingIntent;
@@ -83,6 +85,8 @@ export function createSchedule(params: {
83
85
  routingHints?: Record<string, unknown>;
84
86
  quiet?: boolean;
85
87
  reuseConversation?: boolean;
88
+ maxRetries?: number;
89
+ retryBackoffMs?: number;
86
90
  }): ScheduleJob {
87
91
  const expression = params.expression ?? params.cronExpression ?? null;
88
92
  const isOneShot = expression == null;
@@ -116,6 +120,8 @@ export function createSchedule(params: {
116
120
  const routingHints = params.routingHints ?? {};
117
121
  const quiet = params.quiet ?? false;
118
122
  const reuseConversation = params.reuseConversation ?? false;
123
+ const maxRetries = params.maxRetries ?? 3;
124
+ const retryBackoffMs = params.retryBackoffMs ?? 60000;
119
125
 
120
126
  let nextRunAt: number;
121
127
  if (isOneShot) {
@@ -140,6 +146,8 @@ export function createSchedule(params: {
140
146
  lastRunAt: null as number | null,
141
147
  lastStatus: null as string | null,
142
148
  retryCount: 0,
149
+ maxRetries,
150
+ retryBackoffMs,
143
151
  createdBy: params.createdBy ?? "agent",
144
152
  mode,
145
153
  routingIntent,
@@ -235,6 +243,8 @@ export function updateSchedule(
235
243
  quiet?: boolean;
236
244
  reuseConversation?: boolean;
237
245
  wakeConversationId?: string | null;
246
+ maxRetries?: number;
247
+ retryBackoffMs?: number;
238
248
  },
239
249
  ): ScheduleJob | null {
240
250
  const db = getDb();
@@ -257,12 +267,13 @@ export function updateSchedule(
257
267
 
258
268
  const isOneShot = newExpr == null;
259
269
 
260
- // Validate if expression or syntax changed (only for recurring schedules)
270
+ // Validate if expression, syntax, or timezone changed (only for recurring schedules)
261
271
  if (
262
272
  !isOneShot &&
263
273
  (updates.expression !== undefined ||
264
274
  updates.cronExpression !== undefined ||
265
- updates.syntax !== undefined)
275
+ updates.syntax !== undefined ||
276
+ updates.timezone !== undefined)
266
277
  ) {
267
278
  const spec = {
268
279
  syntax: newSyntax,
@@ -295,6 +306,9 @@ export function updateSchedule(
295
306
  set.reuseConversation = updates.reuseConversation;
296
307
  if (updates.wakeConversationId !== undefined)
297
308
  set.wakeConversationId = updates.wakeConversationId;
309
+ if (updates.maxRetries !== undefined) set.maxRetries = updates.maxRetries;
310
+ if (updates.retryBackoffMs !== undefined)
311
+ set.retryBackoffMs = updates.retryBackoffMs;
298
312
 
299
313
  // Recompute nextRunAt if schedule timing may have changed (only for recurring)
300
314
  if (
@@ -817,6 +831,94 @@ export function describeCronExpression(expr: string | null): string {
817
831
  }
818
832
  }
819
833
 
834
+ /**
835
+ * Set the next retry time for a schedule and revert one-shot status from
836
+ * "firing" to "active" so the scheduler will claim it again when nextRetryAt
837
+ * arrives. No-op for recurring schedules (they stay in their current status).
838
+ */
839
+ export function scheduleRetry(id: string, nextRetryAt: number): void {
840
+ const db = getDb();
841
+ const now = Date.now();
842
+ db.update(scheduleJobs)
843
+ .set({ nextRunAt: nextRetryAt, updatedAt: now })
844
+ .where(eq(scheduleJobs.id, id))
845
+ .run();
846
+ // Revert one-shot status from "firing" to "active" so the scheduler
847
+ // will claim it again when nextRetryAt arrives. No-op for recurring.
848
+ db.update(scheduleJobs)
849
+ .set({ status: "active", updatedAt: now })
850
+ .where(and(eq(scheduleJobs.id, id), eq(scheduleJobs.status, "firing")))
851
+ .run();
852
+ }
853
+
854
+ /**
855
+ * Reset the retry count for a schedule back to zero (e.g. after a successful run).
856
+ */
857
+ export function resetRetryCount(id: string): void {
858
+ const db = getDb();
859
+ db.update(scheduleJobs)
860
+ .set({ retryCount: 0, updatedAt: Date.now() })
861
+ .where(eq(scheduleJobs.id, id))
862
+ .run();
863
+ }
864
+
865
+ /**
866
+ * Find schedules stuck in an in-flight state (one-shots in "firing",
867
+ * cron runs in "running"). Used at daemon startup to recover from
868
+ * a prior process crash.
869
+ *
870
+ * @param staleThresholdMs If >0, only consider rows whose lastRunAt
871
+ * (for one-shots) or startedAt (for runs) is older than `now - staleThresholdMs`.
872
+ * Pass 0 at startup (the previous process is definitely dead).
873
+ */
874
+ export function findStaleInFlightJobs(staleThresholdMs: number = 0): Array<{
875
+ jobId: string;
876
+ staleRunId: string | null;
877
+ }> {
878
+ const db = getDb();
879
+ const cutoff = Date.now() - staleThresholdMs;
880
+
881
+ // One-shots stuck in "firing" where lastRunAt is older than cutoff
882
+ const staleOneShots = db
883
+ .select({ id: scheduleJobs.id })
884
+ .from(scheduleJobs)
885
+ .where(
886
+ and(
887
+ isNull(scheduleJobs.cronExpression),
888
+ eq(scheduleJobs.status, "firing"),
889
+ eq(scheduleJobs.enabled, true),
890
+ staleThresholdMs > 0 ? lte(scheduleJobs.lastRunAt, cutoff) : undefined,
891
+ ),
892
+ )
893
+ .all();
894
+
895
+ // Cron runs stuck in "running" where startedAt is older than cutoff
896
+ const staleRuns = db
897
+ .select({ id: scheduleRuns.id, jobId: scheduleRuns.jobId })
898
+ .from(scheduleRuns)
899
+ .where(
900
+ and(
901
+ eq(scheduleRuns.status, "running"),
902
+ staleThresholdMs > 0 ? lte(scheduleRuns.startedAt, cutoff) : undefined,
903
+ ),
904
+ )
905
+ .all();
906
+
907
+ const result: Array<{ jobId: string; staleRunId: string | null }> = [];
908
+ const seenJobIds = new Set<string>();
909
+
910
+ for (const run of staleRuns) {
911
+ result.push({ jobId: run.jobId, staleRunId: run.id });
912
+ seenJobIds.add(run.jobId);
913
+ }
914
+ for (const job of staleOneShots) {
915
+ if (!seenJobIds.has(job.id)) {
916
+ result.push({ jobId: job.id, staleRunId: null });
917
+ }
918
+ }
919
+ return result;
920
+ }
921
+
820
922
  function parseJobRow(row: typeof scheduleJobs.$inferSelect): ScheduleJob {
821
923
  return {
822
924
  id: row.id,
@@ -833,6 +935,8 @@ function parseJobRow(row: typeof scheduleJobs.$inferSelect): ScheduleJob {
833
935
  lastRunAt: row.lastRunAt,
834
936
  lastStatus: row.lastStatus,
835
937
  retryCount: row.retryCount,
938
+ maxRetries: row.maxRetries ?? 3,
939
+ retryBackoffMs: row.retryBackoffMs ?? 60000,
836
940
  createdBy: row.createdBy,
837
941
  mode: (row.mode ?? "execute") as ScheduleMode,
838
942
  routingIntent: (row.routingIntent ?? "all_channels") as RoutingIntent,
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Types extracted from scheduler.ts to break the scheduler ↔ engine cycle.
3
+ * `sequence/engine.ts` needs `ScheduleMessageProcessor` but scheduler.ts
4
+ * imports from engine — extracting the type here breaks the back-edge.
5
+ */
6
+
7
+ import type { LLMCallSite } from "../config/schemas/llm.js";
8
+
9
+ export interface ScheduleMessageOptions {
10
+ trustClass?: "guardian" | "trusted_contact" | "unknown";
11
+ taskRunId?: string;
12
+ /**
13
+ * Optional LLM call-site identifier propagated to the per-call provider
14
+ * config. Schedule and sequence callers will start passing their own call-site
15
+ * (e.g. for a future scheduled-agent profile) once PRs 7-11 migrate them off
16
+ * the default `mainAgent` route.
17
+ */
18
+ callSite?: LLMCallSite;
19
+ }
20
+
21
+ export type ScheduleMessageProcessor = (
22
+ conversationId: string,
23
+ message: string,
24
+ options?: ScheduleMessageOptions,
25
+ ) => Promise<unknown>;