vellum 0.2.1 → 0.2.7

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 (361) hide show
  1. package/README.md +15 -2
  2. package/bun.lock +71 -100
  3. package/package.json +5 -3
  4. package/scripts/capture-x-graphql.ts +562 -0
  5. package/scripts/ipc/check-swift-decoder-drift.ts +2 -1
  6. package/scripts/test.sh +5 -0
  7. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +133 -34
  8. package/src/__tests__/account-registry.test.ts +2 -1
  9. package/src/__tests__/agent-heartbeat-service.test.ts +250 -0
  10. package/src/__tests__/asset-materialize-tool.test.ts +16 -15
  11. package/src/__tests__/asset-search-tool.test.ts +23 -22
  12. package/src/__tests__/attachments-store.test.ts +56 -127
  13. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +5 -4
  14. package/src/__tests__/browser-skill-endstate.test.ts +4 -3
  15. package/src/__tests__/call-bridge.test.ts +385 -0
  16. package/src/__tests__/call-constants.test.ts +40 -0
  17. package/src/__tests__/call-orchestrator.test.ts +130 -4
  18. package/src/__tests__/call-recovery.test.ts +518 -0
  19. package/src/__tests__/call-routes-http.test.ts +459 -0
  20. package/src/__tests__/call-state-machine.test.ts +143 -0
  21. package/src/__tests__/call-store.test.ts +216 -1
  22. package/src/__tests__/cli-discover.test.ts +1 -1
  23. package/src/__tests__/commit-message-enrichment-service.test.ts +148 -7
  24. package/src/__tests__/compaction.benchmark.test.ts +176 -0
  25. package/src/__tests__/computer-use-tools.test.ts +250 -0
  26. package/src/__tests__/config-schema.test.ts +305 -3
  27. package/src/__tests__/conflict-store.test.ts +2 -1
  28. package/src/__tests__/contacts-tools.test.ts +331 -0
  29. package/src/__tests__/conversation-store.test.ts +30 -32
  30. package/src/__tests__/credential-security-invariants.test.ts +4 -0
  31. package/src/__tests__/date-context.test.ts +373 -0
  32. package/src/__tests__/db-schedule-syntax-migration.test.ts +129 -0
  33. package/src/__tests__/fixtures/media-reuse-fixtures.ts +3 -3
  34. package/src/__tests__/followup-tools.test.ts +303 -0
  35. package/src/__tests__/handlers-twilio-config.test.ts +221 -0
  36. package/src/__tests__/handlers-twitter-config.test.ts +718 -0
  37. package/src/__tests__/intent-routing.test.ts +64 -57
  38. package/src/__tests__/ipc-roundtrip.benchmark.test.ts +237 -0
  39. package/src/__tests__/ipc-snapshot.test.ts +71 -28
  40. package/src/__tests__/llm-usage-store.test.ts +3 -8
  41. package/src/__tests__/media-generate-image.test.ts +1 -1
  42. package/src/__tests__/media-reuse-story.e2e.test.ts +7 -7
  43. package/src/__tests__/memory-regressions.test.ts +100 -2
  44. package/src/__tests__/memory-retrieval.benchmark.test.ts +430 -0
  45. package/src/__tests__/parallel-tool.benchmark.test.ts +294 -0
  46. package/src/__tests__/playbook-tools.test.ts +342 -0
  47. package/src/__tests__/profile-compiler.test.ts +2 -1
  48. package/src/__tests__/provider-commit-message-generator.test.ts +303 -0
  49. package/src/__tests__/provider-streaming.benchmark.test.ts +773 -0
  50. package/src/__tests__/recurrence-engine-rruleset.test.ts +78 -0
  51. package/src/__tests__/recurrence-engine.test.ts +69 -0
  52. package/src/__tests__/recurrence-types.test.ts +71 -0
  53. package/src/__tests__/registry.test.ts +5 -3
  54. package/src/__tests__/relay-server.test.ts +633 -0
  55. package/src/__tests__/reminder-store.test.ts +6 -3
  56. package/src/__tests__/reminder.test.ts +43 -77
  57. package/src/__tests__/run-orchestrator-assistant-events.test.ts +8 -4
  58. package/src/__tests__/run-orchestrator.test.ts +4 -4
  59. package/src/__tests__/runtime-attachment-metadata.test.ts +7 -6
  60. package/src/__tests__/runtime-runs-http.test.ts +4 -4
  61. package/src/__tests__/runtime-runs.test.ts +4 -4
  62. package/src/__tests__/schedule-store.test.ts +482 -0
  63. package/src/__tests__/schedule-tools.test.ts +700 -0
  64. package/src/__tests__/scheduler-recurrence.test.ts +329 -0
  65. package/src/__tests__/server-history-render.test.ts +14 -13
  66. package/src/__tests__/session-conflict-gate.test.ts +28 -25
  67. package/src/__tests__/session-error.test.ts +28 -0
  68. package/src/__tests__/session-init.benchmark.test.ts +462 -0
  69. package/src/__tests__/session-queue.test.ts +71 -48
  70. package/src/__tests__/session-runtime-assembly.test.ts +161 -0
  71. package/src/__tests__/session-surfaces-task-progress.test.ts +104 -0
  72. package/src/__tests__/signup-e2e.test.ts +2 -1
  73. package/src/__tests__/skill-projection.benchmark.test.ts +328 -0
  74. package/src/__tests__/skill-script-runner.test.ts +159 -0
  75. package/src/__tests__/speaker-identification.test.ts +52 -0
  76. package/src/__tests__/subagent-manager-notify.test.ts +42 -10
  77. package/src/__tests__/subagent-tools.test.ts +141 -41
  78. package/src/__tests__/task-compiler.test.ts +2 -1
  79. package/src/__tests__/task-runner.test.ts +2 -1
  80. package/src/__tests__/task-scheduler.test.ts +2 -1
  81. package/src/__tests__/task-tools.test.ts +49 -56
  82. package/src/__tests__/tool-audit-listener.test.ts +1 -0
  83. package/src/__tests__/tool-domain-event-publisher.test.ts +2 -0
  84. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +500 -0
  85. package/src/__tests__/tool-executor.test.ts +13 -17
  86. package/src/__tests__/turn-commit.test.ts +218 -3
  87. package/src/__tests__/twilio-provider.test.ts +143 -0
  88. package/src/__tests__/twilio-routes.test.ts +789 -0
  89. package/src/__tests__/twitter-auth-handler.test.ts +581 -0
  90. package/src/__tests__/view-image-tool.test.ts +217 -0
  91. package/src/__tests__/workspace-git-service.test.ts +186 -0
  92. package/src/__tests__/workspace-heartbeat-service.test.ts +13 -3
  93. package/src/agent-heartbeat/agent-heartbeat-service.ts +155 -0
  94. package/src/bundler/app-bundler.ts +12 -8
  95. package/src/calls/__tests__/twilio-webhook-urls.test.ts +162 -0
  96. package/src/calls/call-bridge.ts +95 -0
  97. package/src/calls/call-constants.ts +43 -5
  98. package/src/calls/call-domain.ts +276 -0
  99. package/src/calls/call-orchestrator.ts +43 -17
  100. package/src/calls/call-recovery.ts +207 -0
  101. package/src/calls/call-state-machine.ts +68 -0
  102. package/src/calls/call-store.ts +192 -5
  103. package/src/calls/relay-server.ts +41 -4
  104. package/src/calls/speaker-identification.ts +213 -0
  105. package/src/calls/twilio-config.ts +8 -8
  106. package/src/calls/twilio-provider.ts +13 -9
  107. package/src/calls/twilio-routes.ts +90 -76
  108. package/src/calls/twilio-webhook-urls.ts +50 -0
  109. package/src/calls/types.ts +1 -1
  110. package/src/cli/config-commands.ts +334 -0
  111. package/src/cli/core-commands.ts +776 -0
  112. package/src/cli/doordash.ts +251 -1
  113. package/src/cli/ipc-client.ts +82 -0
  114. package/src/cli/map.ts +270 -0
  115. package/src/cli/twitter.ts +575 -0
  116. package/src/cli.ts +7 -5
  117. package/src/commands/__tests__/cc-command-registry.test.ts +319 -0
  118. package/src/commands/cc-command-registry.ts +209 -0
  119. package/src/config/bundled-skills/contacts/SKILL.md +39 -0
  120. package/src/config/bundled-skills/contacts/TOOLS.json +122 -0
  121. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +9 -0
  122. package/src/config/bundled-skills/contacts/tools/contact-search.ts +9 -0
  123. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +9 -0
  124. package/src/config/bundled-skills/document/SKILL.md +18 -0
  125. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  126. package/src/config/bundled-skills/document/tools/document-create.ts +9 -0
  127. package/src/config/bundled-skills/document/tools/document-update.ts +9 -0
  128. package/src/config/bundled-skills/doordash/SKILL.md +82 -23
  129. package/src/config/bundled-skills/followups/SKILL.md +32 -0
  130. package/src/config/bundled-skills/followups/TOOLS.json +100 -0
  131. package/src/config/bundled-skills/followups/tools/followup-create.ts +9 -0
  132. package/src/config/bundled-skills/followups/tools/followup-list.ts +9 -0
  133. package/src/config/bundled-skills/followups/tools/followup-resolve.ts +9 -0
  134. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +1 -23
  135. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -1
  136. package/src/config/bundled-skills/playbooks/SKILL.md +31 -0
  137. package/src/config/bundled-skills/playbooks/TOOLS.json +126 -0
  138. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +9 -0
  139. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +9 -0
  140. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +9 -0
  141. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +9 -0
  142. package/src/config/bundled-skills/reminder/SKILL.md +20 -0
  143. package/src/config/bundled-skills/reminder/TOOLS.json +67 -0
  144. package/src/config/bundled-skills/reminder/tools/reminder-cancel.ts +9 -0
  145. package/src/config/bundled-skills/reminder/tools/reminder-create.ts +9 -0
  146. package/src/config/bundled-skills/reminder/tools/reminder-list.ts +9 -0
  147. package/src/config/bundled-skills/schedule/SKILL.md +74 -0
  148. package/src/config/bundled-skills/schedule/TOOLS.json +135 -0
  149. package/src/config/bundled-skills/schedule/tools/schedule-create.ts +9 -0
  150. package/src/config/bundled-skills/schedule/tools/schedule-delete.ts +9 -0
  151. package/src/config/bundled-skills/schedule/tools/schedule-list.ts +9 -0
  152. package/src/config/bundled-skills/schedule/tools/schedule-update.ts +9 -0
  153. package/src/config/bundled-skills/subagent/SKILL.md +25 -0
  154. package/src/config/bundled-skills/subagent/TOOLS.json +107 -0
  155. package/src/config/bundled-skills/subagent/tools/subagent-abort.ts +9 -0
  156. package/src/config/bundled-skills/subagent/tools/subagent-message.ts +9 -0
  157. package/src/config/bundled-skills/subagent/tools/subagent-read.ts +9 -0
  158. package/src/config/bundled-skills/subagent/tools/subagent-spawn.ts +9 -0
  159. package/src/config/bundled-skills/subagent/tools/subagent-status.ts +9 -0
  160. package/src/config/bundled-skills/tasks/SKILL.md +28 -0
  161. package/src/config/bundled-skills/tasks/TOOLS.json +256 -0
  162. package/src/config/bundled-skills/tasks/tools/task-delete.ts +9 -0
  163. package/src/config/bundled-skills/tasks/tools/task-list-add.ts +9 -0
  164. package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +9 -0
  165. package/src/config/bundled-skills/tasks/tools/task-list-show.ts +9 -0
  166. package/src/config/bundled-skills/tasks/tools/task-list-update.ts +9 -0
  167. package/src/config/bundled-skills/tasks/tools/task-list.ts +9 -0
  168. package/src/config/bundled-skills/tasks/tools/task-run.ts +9 -0
  169. package/src/config/bundled-skills/tasks/tools/task-save.ts +9 -0
  170. package/src/config/bundled-skills/twitter/SKILL.md +134 -0
  171. package/src/config/bundled-skills/watcher/SKILL.md +27 -0
  172. package/src/config/bundled-skills/watcher/TOOLS.json +147 -0
  173. package/src/config/bundled-skills/watcher/tools/watcher-create.ts +9 -0
  174. package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +9 -0
  175. package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +9 -0
  176. package/src/config/bundled-skills/watcher/tools/watcher-list.ts +9 -0
  177. package/src/config/bundled-skills/watcher/tools/watcher-update.ts +9 -0
  178. package/src/config/defaults.ts +34 -0
  179. package/src/config/loader.ts +4 -1
  180. package/src/config/schema.ts +165 -1
  181. package/src/config/system-prompt.ts +61 -16
  182. package/src/config/templates/IDENTITY.md +7 -0
  183. package/src/config/types.ts +4 -0
  184. package/src/config/vellum-skills/telegram-setup/SKILL.md +1 -5
  185. package/src/contacts/contact-store.ts +4 -4
  186. package/src/daemon/assistant-attachments.ts +10 -0
  187. package/src/daemon/classifier.ts +3 -1
  188. package/src/daemon/computer-use-session.ts +3 -1
  189. package/src/daemon/date-context.ts +136 -0
  190. package/src/daemon/handlers/apps.ts +16 -1
  191. package/src/daemon/handlers/browser.ts +54 -0
  192. package/src/daemon/handlers/computer-use.ts +7 -1
  193. package/src/daemon/handlers/config.ts +205 -5
  194. package/src/daemon/handlers/diagnostics.ts +5 -1
  195. package/src/daemon/handlers/documents.ts +18 -29
  196. package/src/daemon/handlers/home-base.ts +5 -1
  197. package/src/daemon/handlers/index.ts +40 -277
  198. package/src/daemon/handlers/misc.ts +9 -1
  199. package/src/daemon/handlers/publish.ts +6 -1
  200. package/src/daemon/handlers/sessions.ts +65 -12
  201. package/src/daemon/handlers/shared.ts +36 -1
  202. package/src/daemon/handlers/signing.ts +37 -0
  203. package/src/daemon/handlers/skills.ts +20 -6
  204. package/src/daemon/handlers/subagents.ts +8 -3
  205. package/src/daemon/handlers/twitter-auth.ts +169 -0
  206. package/src/daemon/handlers/work-items.ts +384 -68
  207. package/src/daemon/ipc-contract-inventory.json +32 -4
  208. package/src/daemon/ipc-contract.ts +156 -37
  209. package/src/daemon/ipc-protocol.ts +7 -2
  210. package/src/daemon/lifecycle.ts +21 -0
  211. package/src/daemon/main.ts +10 -4
  212. package/src/daemon/ride-shotgun-handler.ts +75 -10
  213. package/src/daemon/server.ts +143 -26
  214. package/src/daemon/session-agent-loop.ts +922 -0
  215. package/src/daemon/session-attachments.ts +28 -5
  216. package/src/daemon/session-conflict-gate.ts +18 -109
  217. package/src/daemon/session-error.ts +24 -3
  218. package/src/daemon/session-lifecycle.ts +147 -0
  219. package/src/daemon/session-media-retry.ts +147 -0
  220. package/src/daemon/session-messaging.ts +145 -0
  221. package/src/daemon/session-notifiers.ts +164 -0
  222. package/src/daemon/session-process.ts +2 -2
  223. package/src/daemon/session-queue-manager.ts +1 -0
  224. package/src/daemon/session-runtime-assembly.ts +52 -0
  225. package/src/daemon/session-skill-tools.ts +124 -5
  226. package/src/daemon/session-slash.ts +3 -0
  227. package/src/daemon/session-surfaces.ts +77 -2
  228. package/src/daemon/session-tool-setup.ts +216 -2
  229. package/src/daemon/session-usage.ts +0 -2
  230. package/src/daemon/session.ts +114 -1404
  231. package/src/daemon/video-thumbnail.ts +60 -0
  232. package/src/doordash/client.ts +121 -27
  233. package/src/doordash/queries.ts +1 -2
  234. package/src/export/formatter.ts +3 -1
  235. package/src/followups/followup-store.ts +4 -2
  236. package/src/followups/types.ts +6 -0
  237. package/src/hooks/templates.ts +1 -1
  238. package/src/index.ts +32 -1153
  239. package/src/memory/attachments-store.ts +28 -83
  240. package/src/memory/channel-delivery-store.ts +7 -21
  241. package/src/memory/clarification-resolver.ts +6 -5
  242. package/src/memory/conflict-intent.ts +114 -0
  243. package/src/memory/contradiction-checker.ts +3 -2
  244. package/src/memory/conversation-key-store.ts +10 -29
  245. package/src/memory/conversation-store.ts +2 -1
  246. package/src/memory/db.ts +96 -2
  247. package/src/memory/entity-extractor.ts +6 -3
  248. package/src/memory/items-extractor.ts +5 -4
  249. package/src/memory/job-handlers/conflict.ts +23 -1
  250. package/src/memory/jobs-store.ts +3 -2
  251. package/src/memory/llm-usage-store.ts +1 -2
  252. package/src/memory/runs-store.ts +1 -2
  253. package/src/memory/schema.ts +23 -2
  254. package/src/messaging/style-analyzer.ts +3 -2
  255. package/src/messaging/thread-summarizer.ts +8 -12
  256. package/src/messaging/triage-engine.ts +4 -2
  257. package/src/providers/openrouter/client.ts +20 -0
  258. package/src/providers/registry.ts +8 -0
  259. package/src/runtime/gateway-client.ts +36 -0
  260. package/src/runtime/http-server.ts +166 -22
  261. package/src/runtime/routes/attachment-routes.ts +2 -3
  262. package/src/runtime/routes/call-routes.ts +140 -0
  263. package/src/runtime/routes/channel-routes.ts +125 -88
  264. package/src/runtime/routes/conversation-routes.ts +5 -5
  265. package/src/runtime/routes/run-routes.ts +2 -2
  266. package/src/runtime/run-orchestrator.ts +9 -3
  267. package/src/schedule/recurrence-engine.ts +138 -0
  268. package/src/schedule/recurrence-types.ts +67 -0
  269. package/src/schedule/schedule-store.ts +102 -57
  270. package/src/schedule/scheduler.ts +9 -6
  271. package/src/security/oauth2.ts +29 -4
  272. package/src/security/secret-allowlist.ts +46 -0
  273. package/src/skills/clawhub.ts +1 -1
  274. package/src/subagent/manager.ts +40 -8
  275. package/src/swarm/backend-claude-code.ts +64 -9
  276. package/src/swarm/worker-prompts.ts +2 -1
  277. package/src/tasks/SPEC.md +34 -28
  278. package/src/tasks/ephemeral-permissions.ts +16 -7
  279. package/src/tasks/task-compiler.ts +5 -4
  280. package/src/tasks/task-runner.ts +10 -5
  281. package/src/tasks/task-scheduler.ts +1 -1
  282. package/src/tasks/tool-sanitizer.ts +36 -0
  283. package/src/tools/assets/search.ts +4 -4
  284. package/src/tools/browser/api-map.ts +293 -0
  285. package/src/tools/browser/auto-navigate.ts +270 -0
  286. package/src/tools/browser/browser-execution.ts +2 -1
  287. package/src/tools/browser/browser-manager.ts +2 -2
  288. package/src/tools/browser/network-recorder.ts +5 -4
  289. package/src/tools/browser/x-auto-navigate.ts +207 -0
  290. package/src/tools/calls/call-end.ts +17 -67
  291. package/src/tools/calls/call-start.ts +24 -85
  292. package/src/tools/calls/call-status.ts +35 -51
  293. package/src/tools/claude-code/claude-code.ts +207 -11
  294. package/src/tools/contacts/contact-merge.ts +46 -78
  295. package/src/tools/contacts/contact-search.ts +35 -79
  296. package/src/tools/contacts/contact-upsert.ts +35 -108
  297. package/src/tools/credentials/vault.ts +20 -4
  298. package/src/tools/document/document-tool.ts +71 -144
  299. package/src/tools/executor.ts +129 -10
  300. package/src/tools/followups/followup_create.ts +46 -88
  301. package/src/tools/followups/followup_list.ts +34 -74
  302. package/src/tools/followups/followup_resolve.ts +31 -66
  303. package/src/tools/host-terminal/cli-discover.ts +2 -1
  304. package/src/tools/host-terminal/host-shell.ts +10 -0
  305. package/src/tools/memory/handlers.ts +5 -4
  306. package/src/tools/network/__tests__/web-search.test.ts +427 -0
  307. package/src/tools/network/script-proxy/__tests__/logging.test.ts +248 -0
  308. package/src/tools/network/script-proxy/__tests__/policy.test.ts +234 -0
  309. package/src/tools/network/script-proxy/__tests__/router.test.ts +76 -0
  310. package/src/tools/network/web-fetch.ts +18 -6
  311. package/src/tools/playbooks/index.ts +4 -5
  312. package/src/tools/playbooks/playbook-create.ts +3 -47
  313. package/src/tools/playbooks/playbook-delete.ts +1 -25
  314. package/src/tools/playbooks/playbook-list.ts +1 -28
  315. package/src/tools/playbooks/playbook-update.ts +3 -51
  316. package/src/tools/reminder/reminder.ts +5 -78
  317. package/src/tools/schedule/create.ts +69 -74
  318. package/src/tools/schedule/delete.ts +21 -47
  319. package/src/tools/schedule/list.ts +55 -74
  320. package/src/tools/schedule/update.ts +77 -84
  321. package/src/tools/subagent/abort.ts +29 -58
  322. package/src/tools/subagent/message.ts +30 -63
  323. package/src/tools/subagent/read.ts +53 -84
  324. package/src/tools/subagent/spawn.ts +43 -82
  325. package/src/tools/subagent/status.ts +42 -71
  326. package/src/tools/swarm/delegate.ts +2 -1
  327. package/src/tools/tasks/index.ts +8 -8
  328. package/src/tools/tasks/task-delete.ts +60 -88
  329. package/src/tools/tasks/task-list.ts +31 -52
  330. package/src/tools/tasks/task-run.ts +72 -108
  331. package/src/tools/tasks/task-save.ts +33 -65
  332. package/src/tools/tasks/work-item-enqueue.ts +183 -215
  333. package/src/tools/tasks/work-item-list.ts +33 -63
  334. package/src/tools/tasks/work-item-remove.ts +45 -97
  335. package/src/tools/tasks/work-item-update.ts +91 -163
  336. package/src/tools/terminal/backends/native.ts +3 -1
  337. package/src/tools/tool-manifest.ts +0 -62
  338. package/src/tools/types.ts +6 -0
  339. package/src/tools/ui-surface/definitions.ts +3 -1
  340. package/src/tools/watch/screen-watch.ts +3 -1
  341. package/src/tools/watcher/create.ts +52 -98
  342. package/src/tools/watcher/delete.ts +20 -46
  343. package/src/tools/watcher/digest.ts +36 -70
  344. package/src/tools/watcher/list.ts +49 -79
  345. package/src/tools/watcher/update.ts +45 -91
  346. package/src/twitter/client.ts +690 -0
  347. package/src/twitter/session.ts +91 -0
  348. package/src/usage/types.ts +0 -1
  349. package/src/util/truncate.ts +6 -0
  350. package/src/watcher/providers/slack.ts +2 -1
  351. package/src/watcher/watcher-store.ts +3 -2
  352. package/src/work-items/work-item-store.ts +27 -2
  353. package/src/workspace/commit-message-enrichment-service.ts +31 -7
  354. package/src/workspace/git-service.ts +87 -22
  355. package/src/workspace/provider-commit-message-generator.ts +269 -0
  356. package/src/workspace/turn-commit.ts +62 -3
  357. package/src/tools/contacts/index.ts +0 -4
  358. package/src/tools/document/index.ts +0 -5
  359. package/src/tools/followups/index.ts +0 -3
  360. package/src/tools/subagent/index.ts +0 -5
  361. /package/src/__tests__/{memory-context-benchmark.test.ts → memory-context-benchmark.benchmark.test.ts} +0 -0
@@ -0,0 +1,269 @@
1
+ import { getLogger } from '../util/logger.js';
2
+ import { getConfig } from '../config/loader.js';
3
+ import type { CommitContext } from './commit-message-provider.js';
4
+ import { DefaultCommitMessageProvider } from './commit-message-provider.js';
5
+ import type { Message } from '../providers/types.js';
6
+
7
+ const log = getLogger('commit-message-llm');
8
+
9
+ export type CommitMessageSource = 'llm' | 'deterministic';
10
+ export type LLMFallbackReason =
11
+ | 'disabled'
12
+ | 'missing_provider_api_key'
13
+ | 'provider_not_initialized'
14
+ | 'breaker_open'
15
+ | 'insufficient_budget'
16
+ | 'timeout'
17
+ | 'provider_error'
18
+ | 'invalid_output';
19
+
20
+ export interface GenerateCommitMessageResult {
21
+ message: string;
22
+ source: CommitMessageSource;
23
+ reason?: LLMFallbackReason;
24
+ }
25
+
26
+ interface GenerateOptions {
27
+ deadlineMs?: number;
28
+ changedFiles: string[];
29
+ diffSummary?: string;
30
+ }
31
+
32
+ const SYSTEM_PROMPT = `You generate concise git commit messages for workspace file changes.
33
+ Rules:
34
+ - Write a single short subject line (max 72 chars), optionally followed by a blank line and 2-4 concise bullet points
35
+ - No markdown headings or formatting
36
+ - Only mention files and changes actually provided
37
+ - Total output must be under 300 characters
38
+ - If you cannot determine a meaningful message, respond with exactly: FALLBACK`;
39
+
40
+ const PROVIDER_DEFAULT_FAST_MODELS: Record<string, string> = {
41
+ anthropic: 'claude-haiku-4-5-20251001',
42
+ openai: 'gpt-4o-mini',
43
+ gemini: 'gemini-2.0-flash',
44
+ };
45
+
46
+ // Providers that can be initialized without an API key (e.g., Ollama runs locally)
47
+ const KEYLESS_PROVIDERS = new Set(['ollama']);
48
+
49
+ const deterministicProvider = new DefaultCommitMessageProvider();
50
+
51
+ function buildDeterministicResult(
52
+ context: CommitContext,
53
+ reason: LLMFallbackReason,
54
+ ): GenerateCommitMessageResult {
55
+ return {
56
+ message: deterministicProvider.buildImmediateMessage(context).message,
57
+ source: 'deterministic',
58
+ reason,
59
+ };
60
+ }
61
+
62
+ export class ProviderCommitMessageGenerator {
63
+ private consecutiveFailures = 0;
64
+ private nextAllowedAttemptMs = 0;
65
+
66
+ private isBreakerOpen(): boolean {
67
+ const config = getConfig();
68
+ const { openAfterFailures } = config.workspaceGit.commitMessageLLM.breaker;
69
+ if (this.consecutiveFailures < openAfterFailures) return false;
70
+ return Date.now() < this.nextAllowedAttemptMs;
71
+ }
72
+
73
+ private recordSuccess(): void {
74
+ if (this.consecutiveFailures > 0) {
75
+ log.info(
76
+ { previousFailures: this.consecutiveFailures },
77
+ 'Commit message LLM breaker closed: succeeded after failures',
78
+ );
79
+ }
80
+ this.consecutiveFailures = 0;
81
+ this.nextAllowedAttemptMs = 0;
82
+ }
83
+
84
+ private recordFailure(): void {
85
+ const config = getConfig();
86
+ const { backoffBaseMs, backoffMaxMs } = config.workspaceGit.commitMessageLLM.breaker;
87
+ this.consecutiveFailures++;
88
+ const delay = Math.min(
89
+ backoffBaseMs * Math.pow(2, this.consecutiveFailures - 1),
90
+ backoffMaxMs,
91
+ );
92
+ this.nextAllowedAttemptMs = Date.now() + delay;
93
+ log.warn(
94
+ { consecutiveFailures: this.consecutiveFailures, backoffMs: delay },
95
+ 'Commit message LLM breaker opened: backing off',
96
+ );
97
+ }
98
+
99
+ async generateCommitMessage(
100
+ context: CommitContext,
101
+ options: GenerateOptions,
102
+ ): Promise<GenerateCommitMessageResult> {
103
+ const config = getConfig();
104
+ const llmConfig = config.workspaceGit.commitMessageLLM;
105
+
106
+ // Step 1: Feature gate
107
+ if (!llmConfig.enabled) {
108
+ return buildDeterministicResult(context, 'disabled');
109
+ }
110
+
111
+ // Step 2: Provider gate
112
+ if (!llmConfig.useConfiguredProvider) {
113
+ return buildDeterministicResult(context, 'disabled');
114
+ }
115
+
116
+ // Step 2.5: API key preflight (skip for providers that run without a key)
117
+ if (!KEYLESS_PROVIDERS.has(config.provider)) {
118
+ const providerApiKey = config.apiKeys[config.provider];
119
+ if (!providerApiKey || providerApiKey === '') {
120
+ log.debug('Provider API key missing; falling back to deterministic');
121
+ return buildDeterministicResult(context, 'missing_provider_api_key');
122
+ }
123
+ }
124
+
125
+ // Step 3: Circuit breaker
126
+ if (this.isBreakerOpen()) {
127
+ log.debug(
128
+ { consecutiveFailures: this.consecutiveFailures },
129
+ 'Commit message LLM breaker open; falling back to deterministic',
130
+ );
131
+ return buildDeterministicResult(context, 'breaker_open');
132
+ }
133
+
134
+ // Step 4: Budget check
135
+ if (options.deadlineMs !== undefined) {
136
+ const remaining = options.deadlineMs - Date.now();
137
+ if (remaining < llmConfig.minRemainingTurnBudgetMs) {
138
+ log.debug(
139
+ { remainingMs: remaining, minBudgetMs: llmConfig.minRemainingTurnBudgetMs },
140
+ 'Insufficient budget for LLM commit message',
141
+ );
142
+ return buildDeterministicResult(context, 'insufficient_budget');
143
+ }
144
+ }
145
+
146
+ // Step 5: Call the provider
147
+ try {
148
+ const { getProvider } = await import('../providers/registry.js');
149
+
150
+ let provider;
151
+ try {
152
+ provider = getProvider(config.provider);
153
+ } catch {
154
+ log.debug({ provider: config.provider }, 'Provider not initialized; falling back to deterministic');
155
+ return buildDeterministicResult(context, 'provider_not_initialized');
156
+ }
157
+
158
+ // Build prompt
159
+ const fileList = options.changedFiles
160
+ .slice(0, llmConfig.maxFilesInPrompt)
161
+ .join('\n');
162
+ const truncatedSuffix = options.changedFiles.length > llmConfig.maxFilesInPrompt
163
+ ? `\n... and ${options.changedFiles.length - llmConfig.maxFilesInPrompt} more files`
164
+ : '';
165
+
166
+ let userText = `Changed files:\n${fileList}${truncatedSuffix}`;
167
+ if (options.diffSummary) {
168
+ const diffBytes = new TextEncoder().encode(options.diffSummary).length;
169
+ const diff = diffBytes > llmConfig.maxDiffBytes
170
+ ? new TextDecoder().decode(new TextEncoder().encode(options.diffSummary).slice(0, llmConfig.maxDiffBytes)) + '\n... (truncated)'
171
+ : options.diffSummary;
172
+ userText += `\n\nDiff summary:\n${diff}`;
173
+ }
174
+
175
+ const messages: Message[] = [
176
+ {
177
+ role: 'user',
178
+ content: [{ type: 'text', text: userText }],
179
+ },
180
+ ];
181
+
182
+ // Resolve fast model
183
+ const fastModel = llmConfig.providerFastModelOverrides[config.provider]
184
+ ?? PROVIDER_DEFAULT_FAST_MODELS[config.provider];
185
+ if (!fastModel) {
186
+ log.debug({ provider: config.provider }, 'No default fast model for provider; falling back to deterministic');
187
+ return buildDeterministicResult(context, 'provider_error');
188
+ }
189
+
190
+ // AbortController with timeout
191
+ const ac = new AbortController();
192
+ const timer = setTimeout(() => ac.abort(), llmConfig.timeoutMs);
193
+
194
+ let response;
195
+ try {
196
+ response = await provider.sendMessage(
197
+ messages,
198
+ undefined,
199
+ SYSTEM_PROMPT,
200
+ {
201
+ signal: ac.signal,
202
+ config: { model: fastModel, max_tokens: llmConfig.maxTokens, temperature: llmConfig.temperature },
203
+ },
204
+ );
205
+ } catch (err: unknown) {
206
+ clearTimeout(timer);
207
+ if (ac.signal.aborted) {
208
+ log.warn('Commit message LLM timed out; falling back to deterministic');
209
+ this.recordFailure();
210
+ return buildDeterministicResult(context, 'timeout');
211
+ }
212
+ throw err;
213
+ }
214
+ clearTimeout(timer);
215
+
216
+ // Extract text from response
217
+ const textBlocks = response.content.filter((b) => b.type === 'text');
218
+ const text = textBlocks
219
+ .map((b) => (b as { type: 'text'; text: string }).text)
220
+ .join('')
221
+ .trim();
222
+
223
+ // Validate output
224
+ if (!text || text === 'FALLBACK' || text.length > 500) {
225
+ log.debug(
226
+ { outputLength: text?.length ?? 0, isFallback: text === 'FALLBACK' },
227
+ 'LLM output invalid; falling back to deterministic',
228
+ );
229
+ this.recordFailure();
230
+ return buildDeterministicResult(context, 'invalid_output');
231
+ }
232
+
233
+ // Validate single-line subject: first line must be <= 72 chars
234
+ const firstLine = text.split('\n')[0];
235
+ if (firstLine.length > 72) {
236
+ log.debug(
237
+ { subjectLength: firstLine.length },
238
+ 'LLM subject line too long; falling back to deterministic',
239
+ );
240
+ this.recordFailure();
241
+ return buildDeterministicResult(context, 'invalid_output');
242
+ }
243
+
244
+ this.recordSuccess();
245
+ return { message: text, source: 'llm' };
246
+ } catch (err: unknown) {
247
+ // Step 6: Any error -> deterministic fallback
248
+ log.warn(
249
+ { err: err instanceof Error ? err.message : String(err) },
250
+ 'Commit message LLM provider error; falling back to deterministic',
251
+ );
252
+ this.recordFailure();
253
+ return buildDeterministicResult(context, 'provider_error');
254
+ }
255
+ }
256
+ }
257
+
258
+ let instance: ProviderCommitMessageGenerator | null = null;
259
+
260
+ export function getCommitMessageGenerator(): ProviderCommitMessageGenerator {
261
+ if (!instance) {
262
+ instance = new ProviderCommitMessageGenerator();
263
+ }
264
+ return instance;
265
+ }
266
+
267
+ export function _resetCommitMessageGenerator(): void {
268
+ instance = null;
269
+ }
@@ -17,6 +17,8 @@ import {
17
17
  type CommitMessageProvider,
18
18
  } from './commit-message-provider.js';
19
19
  import { getEnrichmentService } from './commit-message-enrichment-service.js';
20
+ import { getCommitMessageGenerator } from './provider-commit-message-generator.js';
21
+ import type { CommitMessageSource, LLMFallbackReason } from './provider-commit-message-generator.js';
20
22
 
21
23
  const log = getLogger('turn-commit');
22
24
 
@@ -58,8 +60,54 @@ export async function commitTurnChanges(
58
60
  const gitService = getWorkspaceGitService(workspaceDir);
59
61
  const commitStartMs = Date.now();
60
62
 
61
- // Atomic status check + commit within a single mutex lock to prevent
62
- // TOCTOU races with heartbeat commits.
63
+ // Attempt LLM message generation BEFORE entering commitIfDirty so
64
+ // the LLM call never runs while holding the git mutex.
65
+ // Only attempt LLM when:
66
+ // 1. No custom provider was injected (respect caller contract)
67
+ // 2. The workspace actually has pending changes (avoid wasting budget)
68
+ let llmMessage: string | undefined;
69
+ let commitMessageSource: CommitMessageSource = 'deterministic';
70
+ let llmFallbackReason: LLMFallbackReason | undefined;
71
+
72
+ if (!provider) {
73
+ // Guard: skip pre-check if deadline already elapsed to avoid unnecessary mutex contention
74
+ let preClean = false;
75
+ if (!deadlineMs || Date.now() < deadlineMs) {
76
+ try {
77
+ const preStatus = await gitService.getStatus();
78
+ preClean = preStatus.clean;
79
+ } catch {
80
+ // If we can't determine status, assume dirty so we don't skip the commit
81
+ }
82
+ }
83
+
84
+ if (!preClean) {
85
+ try {
86
+ const generator = getCommitMessageGenerator();
87
+ const result = await generator.generateCommitMessage(
88
+ {
89
+ workspaceDir,
90
+ trigger: 'turn',
91
+ sessionId,
92
+ turnNumber,
93
+ changedFiles: [], // File list unavailable outside the git mutex; generator handles empty arrays
94
+ timestampMs: Date.now(),
95
+ },
96
+ { deadlineMs, changedFiles: [] },
97
+ );
98
+ commitMessageSource = result.source;
99
+ llmFallbackReason = result.reason;
100
+ if (result.source === 'llm') {
101
+ llmMessage = result.message;
102
+ }
103
+ } catch (llmErr) {
104
+ // Never let LLM errors affect the commit path
105
+ log.debug({ err: llmErr }, 'LLM commit message generation failed (non-fatal)');
106
+ llmFallbackReason = 'provider_error';
107
+ }
108
+ }
109
+ }
110
+
63
111
  const { committed, status } = await gitService.commitIfDirty((st) => {
64
112
  const uniqueFiles = [...new Set([...st.staged, ...st.modified, ...st.untracked])];
65
113
 
@@ -72,6 +120,10 @@ export async function commitTurnChanges(
72
120
  timestampMs: Date.now(),
73
121
  };
74
122
 
123
+ // Use LLM message if available, otherwise deterministic
124
+ if (llmMessage) {
125
+ return { message: llmMessage };
126
+ }
75
127
  return messageProvider.buildImmediateMessage(ctx);
76
128
  }, deadlineMs !== undefined ? { deadlineMs } : undefined);
77
129
 
@@ -80,7 +132,14 @@ export async function commitTurnChanges(
80
132
  if (committed) {
81
133
  const uniqueFiles = [...new Set([...status.staged, ...status.modified, ...status.untracked])];
82
134
  log.info(
83
- { sessionId, turnNumber, filesChanged: uniqueFiles.length, durationMs: commitDurationMs },
135
+ {
136
+ sessionId,
137
+ turnNumber,
138
+ filesChanged: uniqueFiles.length,
139
+ durationMs: commitDurationMs,
140
+ commitMessageSource,
141
+ ...(llmFallbackReason ? { llmFallbackReason } : {}),
142
+ },
84
143
  'Turn-boundary commit created',
85
144
  );
86
145
 
@@ -1,4 +0,0 @@
1
- // Side-effect imports — each module calls registerTool() at the top level.
2
- import './contact-upsert.js';
3
- import './contact-search.js';
4
- import './contact-merge.js';
@@ -1,5 +0,0 @@
1
- export * from './document-tool.js';
2
-
3
- import { documentCreateTool, documentUpdateTool } from './document-tool.js';
4
-
5
- export const allDocumentTools = [documentCreateTool, documentUpdateTool];
@@ -1,3 +0,0 @@
1
- export { followupCreateTool } from './followup_create.js';
2
- export { followupListTool } from './followup_list.js';
3
- export { followupResolveTool } from './followup_resolve.js';
@@ -1,5 +0,0 @@
1
- export { subagentSpawnTool } from './spawn.js';
2
- export { subagentStatusTool } from './status.js';
3
- export { subagentAbortTool } from './abort.js';
4
- export { subagentMessageTool } from './message.js';
5
- export { subagentReadTool } from './read.js';