@vellumai/assistant 0.5.13 → 0.5.14

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 (425) hide show
  1. package/.env.example +1 -6
  2. package/AGENTS.md +4 -0
  3. package/ARCHITECTURE.md +0 -1
  4. package/bunfig.toml +1 -0
  5. package/docs/architecture/memory.md +3 -3
  6. package/openapi.yaml +127 -22
  7. package/package.json +1 -1
  8. package/src/__tests__/access-request-decision.test.ts +2 -32
  9. package/src/__tests__/actor-token-service.test.ts +1 -31
  10. package/src/__tests__/anthropic-provider.test.ts +53 -40
  11. package/src/__tests__/app-git-history.test.ts +9 -17
  12. package/src/__tests__/app-git-service.test.ts +14 -20
  13. package/src/__tests__/app-store-dir-names.test.ts +10 -20
  14. package/src/__tests__/approval-cascade.test.ts +2 -19
  15. package/src/__tests__/approval-primitive.test.ts +2 -27
  16. package/src/__tests__/approval-routes-http.test.ts +2 -30
  17. package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -28
  18. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -45
  19. package/src/__tests__/attachments-store.test.ts +5 -32
  20. package/src/__tests__/audit-log-rotation.test.ts +5 -36
  21. package/src/__tests__/avatar-e2e.test.ts +1 -9
  22. package/src/__tests__/avatar-generator.test.ts +1 -7
  23. package/src/__tests__/browser-fill-credential.test.ts +0 -4
  24. package/src/__tests__/browser-manager.test.ts +0 -6
  25. package/src/__tests__/call-controller.test.ts +1 -22
  26. package/src/__tests__/call-conversation-messages.test.ts +0 -21
  27. package/src/__tests__/call-domain.test.ts +0 -25
  28. package/src/__tests__/call-pointer-messages.test.ts +0 -21
  29. package/src/__tests__/call-recovery.test.ts +0 -22
  30. package/src/__tests__/call-routes-http.test.ts +0 -24
  31. package/src/__tests__/call-store.test.ts +0 -21
  32. package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -24
  33. package/src/__tests__/canonical-guardian-store.test.ts +48 -21
  34. package/src/__tests__/channel-approval-routes.test.ts +6 -26
  35. package/src/__tests__/channel-approvals.test.ts +1 -38
  36. package/src/__tests__/channel-delivery-store.test.ts +0 -21
  37. package/src/__tests__/channel-guardian.test.ts +0 -26
  38. package/src/__tests__/channel-reply-delivery.test.ts +5 -0
  39. package/src/__tests__/channel-retry-sweep.test.ts +0 -21
  40. package/src/__tests__/checker.test.ts +26 -61
  41. package/src/__tests__/clawhub.test.ts +9 -25
  42. package/src/__tests__/cli-command-risk-guard.test.ts +0 -18
  43. package/src/__tests__/config-loader-backfill.test.ts +9 -28
  44. package/src/__tests__/config-schema-cmd.test.ts +5 -25
  45. package/src/__tests__/config-schema.test.ts +21 -40
  46. package/src/__tests__/config-watcher.test.ts +4 -91
  47. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -21
  48. package/src/__tests__/contacts-tools.test.ts +0 -21
  49. package/src/__tests__/context-memory-e2e.test.ts +0 -21
  50. package/src/__tests__/context-window-manager.test.ts +130 -3
  51. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -4
  52. package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -4
  53. package/src/__tests__/conversation-agent-loop.test.ts +0 -4
  54. package/src/__tests__/conversation-attachments.test.ts +1 -24
  55. package/src/__tests__/conversation-attention-store.test.ts +0 -21
  56. package/src/__tests__/conversation-attention-telegram.test.ts +0 -22
  57. package/src/__tests__/conversation-clear-safety.test.ts +0 -22
  58. package/src/__tests__/conversation-confirmation-signals.test.ts +2 -21
  59. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -24
  60. package/src/__tests__/conversation-disk-view-integration.test.ts +1 -23
  61. package/src/__tests__/conversation-disk-view.test.ts +5 -27
  62. package/src/__tests__/conversation-error.test.ts +1 -1
  63. package/src/__tests__/conversation-fork-crud.test.ts +1 -33
  64. package/src/__tests__/conversation-fork-route.test.ts +0 -27
  65. package/src/__tests__/conversation-history-web-search.test.ts +23 -16
  66. package/src/__tests__/conversation-init.benchmark.test.ts +22 -43
  67. package/src/__tests__/conversation-key-store-disk-view.test.ts +8 -34
  68. package/src/__tests__/conversation-load-history-repair.test.ts +0 -4
  69. package/src/__tests__/conversation-pre-run-repair.test.ts +0 -4
  70. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -4
  71. package/src/__tests__/conversation-queue.test.ts +8 -8
  72. package/src/__tests__/conversation-routes-disk-view.test.ts +13 -51
  73. package/src/__tests__/conversation-runtime-assembly.test.ts +64 -38
  74. package/src/__tests__/conversation-slash-commands.test.ts +5 -0
  75. package/src/__tests__/conversation-slash-queue.test.ts +0 -4
  76. package/src/__tests__/conversation-slash-unknown.test.ts +0 -4
  77. package/src/__tests__/conversation-speed-override.test.ts +326 -0
  78. package/src/__tests__/conversation-starter-routes.test.ts +0 -23
  79. package/src/__tests__/conversation-store.test.ts +0 -21
  80. package/src/__tests__/conversation-unread-route.test.ts +0 -24
  81. package/src/__tests__/conversation-usage.test.ts +56 -21
  82. package/src/__tests__/conversation-wipe.test.ts +0 -21
  83. package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -4
  84. package/src/__tests__/conversation-workspace-injection.test.ts +0 -4
  85. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -4
  86. package/src/__tests__/credential-execution-shell-lockdown.test.ts +8 -5
  87. package/src/__tests__/credential-vault-unit.test.ts +9 -428
  88. package/src/__tests__/credentials-cli.test.ts +10 -10
  89. package/src/__tests__/daemon-assistant-events.test.ts +0 -19
  90. package/src/__tests__/date-context.test.ts +77 -97
  91. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +7 -24
  92. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +29 -42
  93. package/src/__tests__/delete-managed-skill-tool.test.ts +2 -10
  94. package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -26
  95. package/src/__tests__/docker-signing-key-bootstrap.test.ts +61 -15
  96. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -36
  97. package/src/__tests__/email-cli.test.ts +6 -6
  98. package/src/__tests__/ephemeral-permissions.test.ts +5 -17
  99. package/src/__tests__/first-greeting.test.ts +4 -32
  100. package/src/__tests__/followup-tools.test.ts +0 -21
  101. package/src/__tests__/gateway-only-enforcement.test.ts +0 -20
  102. package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -23
  103. package/src/__tests__/guardian-action-followup-executor.test.ts +0 -23
  104. package/src/__tests__/guardian-action-followup-store.test.ts +0 -21
  105. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -21
  106. package/src/__tests__/guardian-action-late-reply.test.ts +0 -21
  107. package/src/__tests__/guardian-action-store.test.ts +0 -21
  108. package/src/__tests__/guardian-action-sweep.test.ts +0 -21
  109. package/src/__tests__/guardian-binding-drift-heal.test.ts +0 -23
  110. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +172 -22
  111. package/src/__tests__/guardian-dispatch.test.ts +0 -21
  112. package/src/__tests__/guardian-grant-minting.test.ts +0 -22
  113. package/src/__tests__/guardian-outbound-http.test.ts +0 -22
  114. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +0 -23
  115. package/src/__tests__/guardian-routing-invariants.test.ts +0 -22
  116. package/src/__tests__/guardian-routing-state.test.ts +0 -22
  117. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -24
  118. package/src/__tests__/headless-browser-interactions.test.ts +0 -4
  119. package/src/__tests__/headless-browser-navigate.test.ts +0 -4
  120. package/src/__tests__/headless-browser-read-tools.test.ts +0 -4
  121. package/src/__tests__/headless-browser-snapshot.test.ts +0 -4
  122. package/src/__tests__/heartbeat-service.test.ts +99 -26
  123. package/src/__tests__/hooks-blocking.test.ts +3 -3
  124. package/src/__tests__/hooks-config.test.ts +7 -7
  125. package/src/__tests__/hooks-discovery.test.ts +3 -3
  126. package/src/__tests__/hooks-integration.test.ts +5 -5
  127. package/src/__tests__/hooks-manager.test.ts +3 -3
  128. package/src/__tests__/hooks-runner.test.ts +5 -23
  129. package/src/__tests__/hooks-settings.test.ts +3 -3
  130. package/src/__tests__/hooks-templates.test.ts +3 -3
  131. package/src/__tests__/http-conversation-lineage.test.ts +0 -27
  132. package/src/__tests__/identity-intro-cache.test.ts +0 -4
  133. package/src/__tests__/inbound-invite-redemption.test.ts +0 -22
  134. package/src/__tests__/inline-skill-load-permissions.test.ts +5 -16
  135. package/src/__tests__/intent-routing.test.ts +2 -55
  136. package/src/__tests__/invite-redemption-service.test.ts +0 -21
  137. package/src/__tests__/invite-routes-http.test.ts +0 -21
  138. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +0 -17
  139. package/src/__tests__/journal-context.test.ts +8 -75
  140. package/src/__tests__/list-messages-attachments.test.ts +0 -22
  141. package/src/__tests__/llm-context-route-provider.test.ts +0 -21
  142. package/src/__tests__/llm-request-log-turn-query.test.ts +46 -28
  143. package/src/__tests__/llm-usage-store.test.ts +0 -21
  144. package/src/__tests__/log-export-workspace.test.ts +1 -1
  145. package/src/__tests__/managed-skill-lifecycle.test.ts +1 -1
  146. package/src/__tests__/managed-store.test.ts +1 -1
  147. package/src/__tests__/mcp-cli.test.ts +7 -10
  148. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -21
  149. package/src/__tests__/memory-jobs-worker-backoff.test.ts +0 -11
  150. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -21
  151. package/src/__tests__/memory-recall-log-store.test.ts +0 -27
  152. package/src/__tests__/memory-recall-quality.test.ts +0 -21
  153. package/src/__tests__/memory-regressions.experimental.test.ts +31 -30
  154. package/src/__tests__/memory-regressions.test.ts +282 -70
  155. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -21
  156. package/src/__tests__/memory-upsert-concurrency.test.ts +0 -21
  157. package/src/__tests__/messaging-send-tool.test.ts +201 -0
  158. package/src/__tests__/migration-cross-version-compatibility.test.ts +18 -13
  159. package/src/__tests__/migration-export-http.test.ts +7 -1
  160. package/src/__tests__/migration-import-commit-http.test.ts +16 -14
  161. package/src/__tests__/migration-import-preflight-http.test.ts +27 -44
  162. package/src/__tests__/migration-validate-http.test.ts +1 -28
  163. package/src/__tests__/native-web-search.test.ts +25 -22
  164. package/src/__tests__/non-member-access-request.test.ts +0 -22
  165. package/src/__tests__/notification-guardian-path.test.ts +0 -21
  166. package/src/__tests__/notification-schedule-dedup.test.ts +1 -25
  167. package/src/__tests__/oauth-apps-routes.test.ts +103 -2
  168. package/src/__tests__/oauth-cli.test.ts +52 -0
  169. package/src/__tests__/oauth-provider-profiles.test.ts +0 -16
  170. package/src/__tests__/oauth-provider-serializer.test.ts +232 -0
  171. package/src/__tests__/oauth-providers-routes.test.ts +257 -0
  172. package/src/__tests__/oauth-store.test.ts +0 -21
  173. package/src/__tests__/onboarding-template-contract.test.ts +2 -2
  174. package/src/__tests__/openai-provider.test.ts +261 -0
  175. package/src/__tests__/pairing-concurrent.test.ts +6 -6
  176. package/src/__tests__/pairing-routes.test.ts +7 -1
  177. package/src/__tests__/path-policy.test.ts +1 -1
  178. package/src/__tests__/platform.test.ts +64 -88
  179. package/src/__tests__/playbook-execution.test.ts +0 -21
  180. package/src/__tests__/playbook-tools.test.ts +0 -21
  181. package/src/__tests__/pricing.test.ts +100 -0
  182. package/src/__tests__/relay-server.test.ts +1 -25
  183. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -24
  184. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -24
  185. package/src/__tests__/runtime-events-sse.test.ts +0 -24
  186. package/src/__tests__/sandbox-diagnostics.test.ts +2 -1
  187. package/src/__tests__/scaffold-managed-skill-tool.test.ts +1 -1
  188. package/src/__tests__/schedule-store.test.ts +0 -21
  189. package/src/__tests__/schedule-tools.test.ts +0 -21
  190. package/src/__tests__/scheduler-recurrence.test.ts +0 -21
  191. package/src/__tests__/scoped-approval-grants.test.ts +0 -21
  192. package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -21
  193. package/src/__tests__/secret-allowlist.test.ts +1 -1
  194. package/src/__tests__/secret-ingress-channel.test.ts +0 -5
  195. package/src/__tests__/secret-ingress-cli.test.ts +0 -6
  196. package/src/__tests__/secret-ingress-http.test.ts +0 -5
  197. package/src/__tests__/secret-ingress.test.ts +0 -5
  198. package/src/__tests__/send-endpoint-busy.test.ts +0 -24
  199. package/src/__tests__/sequence-store.test.ts +0 -21
  200. package/src/__tests__/server-history-render.test.ts +0 -24
  201. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -4
  202. package/src/__tests__/skill-load-inline-command.test.ts +9 -0
  203. package/src/__tests__/skill-load-inline-includes.test.ts +9 -0
  204. package/src/__tests__/skill-load-tool.test.ts +11 -0
  205. package/src/__tests__/skills-uninstall.test.ts +10 -8
  206. package/src/__tests__/skills.test.ts +1 -1
  207. package/src/__tests__/slack-channel-config.test.ts +1 -1
  208. package/src/__tests__/slack-inbound-verification.test.ts +0 -22
  209. package/src/__tests__/starter-bundle.test.ts +4 -1
  210. package/src/__tests__/suggestion-routes.test.ts +2 -0
  211. package/src/__tests__/system-prompt.test.ts +1 -1
  212. package/src/__tests__/terminal-tools.test.ts +1 -1
  213. package/src/__tests__/test-preload.ts +31 -0
  214. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -1
  215. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
  216. package/src/__tests__/tool-executor.test.ts +0 -20
  217. package/src/__tests__/tool-input-summary.test.ts +124 -0
  218. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -1
  219. package/src/__tests__/trust-store.test.ts +7 -1
  220. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -1
  221. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +1 -1
  222. package/src/__tests__/trusted-contact-multichannel.test.ts +1 -1
  223. package/src/__tests__/trusted-contact-verification.test.ts +1 -1
  224. package/src/__tests__/turn-boundary-resolution.test.ts +1 -1
  225. package/src/__tests__/twilio-routes.test.ts +1 -1
  226. package/src/__tests__/update-bulletin.test.ts +1 -1
  227. package/src/__tests__/vbundle-pax-and-symlink.test.ts +1 -1
  228. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -0
  229. package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -1
  230. package/src/__tests__/voice-session-bridge.test.ts +1 -1
  231. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +4 -4
  232. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -1
  233. package/src/__tests__/workspace-migration-down-functions.test.ts +15 -3
  234. package/src/__tests__/workspace-migration-seed-device-id.test.ts +40 -4
  235. package/src/agent/loop.ts +6 -9
  236. package/src/approvals/guardian-decision-primitive.ts +46 -18
  237. package/src/approvals/guardian-request-resolvers.ts +19 -2
  238. package/src/calls/active-call-lease.ts +2 -2
  239. package/src/cli/AGENTS.md +1 -1
  240. package/src/cli/commands/doctor.ts +9 -9
  241. package/src/cli/commands/memory.ts +142 -0
  242. package/src/cli/commands/oauth/__tests__/connect.test.ts +13 -11
  243. package/src/cli/commands/oauth/__tests__/ping.test.ts +1 -1
  244. package/src/cli/commands/oauth/connect.ts +13 -12
  245. package/src/cli/commands/oauth/index.ts +1 -1
  246. package/src/cli/commands/oauth/providers.ts +47 -62
  247. package/src/cli/commands/platform/__tests__/connect.test.ts +72 -46
  248. package/src/cli/commands/platform/__tests__/disconnect.test.ts +54 -1
  249. package/src/cli/commands/platform/__tests__/status.test.ts +36 -0
  250. package/src/cli/commands/platform/connect.ts +17 -7
  251. package/src/cli/commands/platform/disconnect.ts +28 -3
  252. package/src/cli/commands/platform/index.ts +3 -3
  253. package/src/cli.ts +1 -299
  254. package/src/config/assistant-feature-flags.ts +23 -15
  255. package/src/config/bundled-skills/app-builder/TOOLS.json +16 -0
  256. package/src/config/bundled-skills/app-builder/tools/app-create.ts +4 -0
  257. package/src/config/bundled-skills/app-builder/tools/app-delete.ts +5 -1
  258. package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +9 -1
  259. package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +5 -1
  260. package/src/config/bundled-skills/contacts/TOOLS.json +8 -0
  261. package/src/config/bundled-skills/contacts/tools/contact-search.ts +10 -1
  262. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +16 -2
  263. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +1 -0
  264. package/src/config/bundled-skills/messaging/SKILL.md +7 -7
  265. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +37 -0
  266. package/src/config/bundled-skills/slack/SKILL.md +18 -0
  267. package/src/config/env-registry.ts +15 -11
  268. package/src/config/env.ts +1 -11
  269. package/src/config/feature-flag-registry.json +16 -0
  270. package/src/config/schema.ts +4 -0
  271. package/src/config/schemas/heartbeat.ts +6 -1
  272. package/src/config/schemas/inference.ts +14 -3
  273. package/src/config/schemas/memory-processing.ts +16 -8
  274. package/src/config/schemas/memory-retrieval.ts +3 -3
  275. package/src/config/skills.ts +1 -1
  276. package/src/context/window-manager.ts +174 -51
  277. package/src/credential-execution/executable-discovery.ts +2 -2
  278. package/src/daemon/approved-devices-store.ts +2 -2
  279. package/src/daemon/assistant-attachments.ts +2 -0
  280. package/src/daemon/config-watcher.ts +4 -50
  281. package/src/daemon/conversation-agent-loop-handlers.ts +9 -1
  282. package/src/daemon/conversation-agent-loop.ts +12 -0
  283. package/src/daemon/conversation-error.ts +3 -5
  284. package/src/daemon/conversation-history.ts +7 -3
  285. package/src/daemon/conversation-lifecycle.ts +16 -0
  286. package/src/daemon/conversation-messaging.ts +1 -0
  287. package/src/daemon/conversation-notifiers.ts +67 -30
  288. package/src/daemon/conversation-process.ts +161 -2
  289. package/src/daemon/conversation-queue-manager.ts +2 -0
  290. package/src/daemon/conversation-runtime-assembly.ts +33 -11
  291. package/src/daemon/conversation-slash.ts +14 -3
  292. package/src/daemon/conversation-tool-setup.ts +2 -0
  293. package/src/daemon/conversation-usage.ts +32 -4
  294. package/src/daemon/conversation.ts +33 -1
  295. package/src/daemon/daemon-control.ts +32 -16
  296. package/src/daemon/date-context.ts +47 -45
  297. package/src/daemon/dictation-profile-store.ts +2 -2
  298. package/src/daemon/handlers/conversations.ts +19 -0
  299. package/src/daemon/handlers/shared.ts +14 -21
  300. package/src/daemon/lifecycle.ts +5 -7
  301. package/src/daemon/message-types/conversations.ts +2 -0
  302. package/src/daemon/message-types/guardian-actions.ts +3 -17
  303. package/src/daemon/message-types/integrations.ts +11 -1
  304. package/src/daemon/message-types/messages.ts +1 -0
  305. package/src/daemon/pairing-store.ts +2 -79
  306. package/src/daemon/server.ts +154 -8
  307. package/src/daemon/watch-handler.ts +65 -21
  308. package/src/email/guardrails.ts +3 -3
  309. package/src/heartbeat/heartbeat-service.ts +14 -7
  310. package/src/hooks/cli.ts +2 -2
  311. package/src/hooks/config.ts +2 -2
  312. package/src/hooks/discovery.ts +2 -2
  313. package/src/hooks/manager.ts +2 -2
  314. package/src/hooks/runner.ts +5 -2
  315. package/src/hooks/templates.ts +2 -2
  316. package/src/memory/admin.ts +181 -2
  317. package/src/memory/app-git-service.ts +61 -4
  318. package/src/memory/attachments-store.ts +2 -0
  319. package/src/memory/canonical-guardian-store.ts +16 -0
  320. package/src/memory/db-init.ts +8 -0
  321. package/src/memory/embedding-local.ts +5 -2
  322. package/src/memory/indexer.ts +44 -26
  323. package/src/memory/items-extractor.ts +34 -82
  324. package/src/memory/job-handlers/batch-extraction.ts +741 -0
  325. package/src/memory/job-handlers/journal-carry-forward.test.ts +383 -0
  326. package/src/memory/job-handlers/journal-carry-forward.ts +255 -0
  327. package/src/memory/jobs-store.ts +28 -0
  328. package/src/memory/jobs-worker.ts +56 -9
  329. package/src/memory/lifecycle-events-store.ts +4 -2
  330. package/src/memory/llm-request-log-store.ts +40 -2
  331. package/src/memory/llm-usage-store.ts +4 -3
  332. package/src/memory/migrations/199-guardian-request-enrichment-columns.ts +71 -0
  333. package/src/memory/migrations/200-usage-llm-call-count.ts +20 -0
  334. package/src/memory/migrations/index.ts +2 -0
  335. package/src/memory/query-expansion.ts +83 -0
  336. package/src/memory/retriever.test.ts +119 -0
  337. package/src/memory/retriever.ts +513 -105
  338. package/src/memory/schema/guardian.ts +4 -0
  339. package/src/memory/schema/infrastructure.ts +1 -0
  340. package/src/memory/search/formatting.test.ts +140 -0
  341. package/src/memory/search/formatting.ts +143 -198
  342. package/src/memory/search/mmr.ts +136 -0
  343. package/src/memory/search/staleness.ts +0 -15
  344. package/src/memory/search/tier-classifier.ts +10 -21
  345. package/src/memory/search/types.ts +17 -0
  346. package/src/messaging/providers/slack/adapter.ts +51 -5
  347. package/src/notifications/broadcaster.ts +13 -0
  348. package/src/notifications/copy-composer.ts +8 -0
  349. package/src/oauth/connect-orchestrator.ts +1 -1
  350. package/src/oauth/connection-resolver.ts +2 -2
  351. package/src/oauth/provider-serializer.ts +116 -0
  352. package/src/permissions/trust-store.ts +24 -7
  353. package/src/prompts/__tests__/build-cli-reference-section.test.ts +5 -0
  354. package/src/prompts/journal-context.ts +50 -35
  355. package/src/prompts/persona-resolver.ts +1 -1
  356. package/src/prompts/system-prompt.ts +27 -28
  357. package/src/prompts/templates/BOOTSTRAP.md +14 -1
  358. package/src/prompts/templates/HEARTBEAT.md +10 -0
  359. package/src/prompts/templates/NOW.md +19 -25
  360. package/src/prompts/templates/SOUL.md +13 -1
  361. package/src/prompts/templates/UPDATES.md +12 -0
  362. package/src/prompts/update-bulletin.ts +1 -1
  363. package/src/providers/anthropic/client.ts +89 -18
  364. package/src/providers/model-catalog.ts +22 -2
  365. package/src/providers/model-intents.ts +2 -2
  366. package/src/providers/openai/client.ts +40 -1
  367. package/src/providers/retry.ts +23 -4
  368. package/src/providers/types.ts +2 -0
  369. package/src/runtime/assistant-scope.ts +1 -1
  370. package/src/runtime/auth/__tests__/credential-service.test.ts +1 -0
  371. package/src/runtime/auth/route-policy.ts +1 -0
  372. package/src/runtime/auth/token-service.ts +51 -29
  373. package/src/runtime/confirmation-request-guardian-bridge.ts +3 -1
  374. package/src/runtime/guardian-decision-types.ts +16 -10
  375. package/src/runtime/http-server.ts +3 -14
  376. package/src/runtime/http-types.ts +1 -0
  377. package/src/runtime/migrations/vbundle-builder.ts +7 -4
  378. package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -4
  379. package/src/runtime/migrations/vbundle-importer.ts +1 -1
  380. package/src/runtime/routes/conversation-query-routes.ts +40 -8
  381. package/src/runtime/routes/conversation-routes.ts +125 -3
  382. package/src/runtime/routes/guardian-action-routes.ts +9 -3
  383. package/src/runtime/routes/identity-routes.ts +25 -4
  384. package/src/runtime/routes/llm-context-normalization.ts +1 -0
  385. package/src/runtime/routes/log-export-routes.ts +34 -12
  386. package/src/runtime/routes/migration-routes.ts +6 -10
  387. package/src/runtime/routes/oauth-apps.ts +2 -9
  388. package/src/runtime/routes/oauth-providers.ts +60 -0
  389. package/src/runtime/routes/pairing-routes.ts +0 -8
  390. package/src/runtime/routes/settings-routes.ts +0 -1
  391. package/src/runtime/routes/telemetry-routes.ts +16 -4
  392. package/src/security/encrypted-store.ts +2 -2
  393. package/src/security/secret-allowlist.ts +3 -3
  394. package/src/signals/emit-event.ts +42 -0
  395. package/src/signals/user-message.ts +37 -0
  396. package/src/telemetry/usage-telemetry-reporter.test.ts +83 -19
  397. package/src/telemetry/usage-telemetry-reporter.ts +23 -17
  398. package/src/tools/browser/runtime-check.ts +2 -2
  399. package/src/tools/credentials/vault.ts +2 -249
  400. package/src/tools/memory/definitions.ts +1 -1
  401. package/src/tools/memory/handlers.test.ts +50 -8
  402. package/src/tools/memory/handlers.ts +3 -1
  403. package/src/tools/side-effects.ts +1 -6
  404. package/src/tools/terminal/safe-env.ts +3 -2
  405. package/src/tools/terminal/shell.ts +11 -14
  406. package/src/tools/tool-approval-handler.ts +20 -1
  407. package/src/tools/tool-input-summary.ts +66 -0
  408. package/src/tools/types.ts +4 -0
  409. package/src/usage/types.ts +4 -0
  410. package/src/util/device-id.ts +10 -10
  411. package/src/util/platform.ts +71 -33
  412. package/src/util/pricing.ts +19 -6
  413. package/src/util/strip-comment-lines.ts +28 -0
  414. package/src/workspace/git-service.ts +8 -18
  415. package/src/workspace/migrations/003-seed-device-id.ts +6 -4
  416. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +7 -1
  417. package/src/workspace/migrations/017-seed-persona-dirs.ts +2 -4
  418. package/src/workspace/migrations/021-move-signals-to-workspace.ts +84 -0
  419. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +94 -0
  420. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +86 -0
  421. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +126 -0
  422. package/src/workspace/migrations/migrate-to-workspace-volume.ts +3 -6
  423. package/src/workspace/migrations/registry.ts +8 -0
  424. package/src/signals/confirm.ts +0 -82
  425. package/src/signals/trust-rule.ts +0 -174
@@ -1,13 +1,21 @@
1
+ import { and, count, desc, eq, sql } from "drizzle-orm";
2
+
1
3
  import { getConfig } from "../config/loader.js";
2
4
  import { getLogger } from "../util/logger.js";
3
- import { rawGet } from "./db.js";
5
+ import { deleteMemoryCheckpoint } from "./checkpoints.js";
6
+ import { getConversationMemoryScopeId } from "./conversation-crud.js";
7
+ import { getDb, rawGet } from "./db.js";
4
8
  import { getMemoryBackendStatus } from "./embedding-backend.js";
5
- import { enqueueBackfillJob, enqueueRebuildIndexJob } from "./indexer.js";
9
+ import { enqueueBackfillJob, enqueueRebuildIndexJob, MIN_SEGMENT_CHARS } from "./indexer.js";
6
10
  import {
7
11
  enqueueCleanupStaleSupersededItemsJob,
12
+ enqueueMemoryJob,
8
13
  getMemoryJobCounts,
9
14
  } from "./jobs-store.js";
15
+ import { withQdrantBreaker } from "./qdrant-circuit-breaker.js";
16
+ import { getQdrantClient } from "./qdrant-client.js";
10
17
  import { queryMemoryForCli } from "./retriever.js";
18
+ import { conversations, memorySegments, memorySummaries, messages } from "./schema.js";
11
19
 
12
20
  const log = getLogger("memory-admin");
13
21
 
@@ -106,3 +114,174 @@ export function requestMemoryCleanup(retentionMs?: number): {
106
114
  export async function queryMemory(query: string, conversationId: string) {
107
115
  return queryMemoryForCli(query, conversationId, getConfig());
108
116
  }
117
+
118
+ // ── Short segment cleanup ─────────────────────────────────────────────
119
+
120
+ export interface CleanupShortSegmentsResult {
121
+ removed: number;
122
+ failed: number;
123
+ dryRunCount?: number;
124
+ }
125
+
126
+ /**
127
+ * Remove segments shorter than MIN_SEGMENT_CHARS from SQLite and Qdrant.
128
+ * These short fragments waste embedding budget, retrieval slots, and
129
+ * injection tokens.
130
+ */
131
+ export async function cleanupShortSegments(
132
+ opts?: { dryRun?: boolean },
133
+ ): Promise<CleanupShortSegmentsResult> {
134
+ const db = getDb();
135
+
136
+ const shortSegments = db
137
+ .select({ id: memorySegments.id })
138
+ .from(memorySegments)
139
+ .where(sql`length(${memorySegments.text}) < ${MIN_SEGMENT_CHARS}`)
140
+ .all();
141
+
142
+ if (opts?.dryRun) {
143
+ return { removed: 0, failed: 0, dryRunCount: shortSegments.length };
144
+ }
145
+
146
+ let removed = 0;
147
+ let failed = 0;
148
+ for (const row of shortSegments) {
149
+ try {
150
+ const qdrant = getQdrantClient();
151
+ await withQdrantBreaker(() => qdrant.deleteByTarget("segment", row.id));
152
+ } catch (err) {
153
+ // Keep the SQLite row so the target ID is preserved for retry
154
+ log.warn({ segmentId: row.id, err }, "Qdrant deletion failed — skipping SQLite deletion to preserve target ID");
155
+ failed++;
156
+ continue;
157
+ }
158
+
159
+ db.delete(memorySegments)
160
+ .where(eq(memorySegments.id, row.id))
161
+ .run();
162
+ removed++;
163
+ }
164
+
165
+ log.info({ removed, failed, threshold: MIN_SEGMENT_CHARS }, "Cleaned up short segments");
166
+ return { removed, failed };
167
+ }
168
+
169
+ // ── Re-extraction ──────────────────────────────────────────────────────
170
+
171
+ export interface ReextractTarget {
172
+ conversationId: string;
173
+ title: string | null;
174
+ messageCount: number;
175
+ }
176
+
177
+ /**
178
+ * Find the top N conversations by message count for re-extraction.
179
+ * Excludes background and private conversations.
180
+ */
181
+ export function findReextractTargets(limit: number): ReextractTarget[] {
182
+ const db = getDb();
183
+ interface Row {
184
+ id: string;
185
+ title: string | null;
186
+ msg_count: number;
187
+ }
188
+ const rows = db
189
+ .select({
190
+ id: conversations.id,
191
+ title: conversations.title,
192
+ msg_count: count(messages.id),
193
+ })
194
+ .from(conversations)
195
+ .leftJoin(messages, eq(messages.conversationId, conversations.id))
196
+ .where(
197
+ sql`${conversations.conversationType} NOT IN ('background', 'private')`,
198
+ )
199
+ .groupBy(conversations.id)
200
+ .orderBy(desc(sql`count(${messages.id})`))
201
+ .limit(limit)
202
+ .all() as Row[];
203
+
204
+ return rows.map((r) => ({
205
+ conversationId: r.id,
206
+ title: r.title,
207
+ messageCount: r.msg_count,
208
+ }));
209
+ }
210
+
211
+ /**
212
+ * Look up a conversation for re-extraction targeting.
213
+ */
214
+ export function findReextractTarget(
215
+ conversationId: string,
216
+ ): ReextractTarget | null {
217
+ const db = getDb();
218
+ const conv = db
219
+ .select({ id: conversations.id, title: conversations.title })
220
+ .from(conversations)
221
+ .where(eq(conversations.id, conversationId))
222
+ .get();
223
+ if (!conv) return null;
224
+
225
+ const [{ total }] = db
226
+ .select({ total: count() })
227
+ .from(messages)
228
+ .where(eq(messages.conversationId, conversationId))
229
+ .all();
230
+
231
+ return {
232
+ conversationId: conv.id,
233
+ title: conv.title,
234
+ messageCount: total,
235
+ };
236
+ }
237
+
238
+ /**
239
+ * Queue re-extraction for a set of conversations.
240
+ * Resets extraction checkpoints and clears extraction summaries so the
241
+ * batch extraction handler processes all messages from scratch with
242
+ * expanded supersession context.
243
+ */
244
+ export function requestReextract(
245
+ targets: ReextractTarget[],
246
+ ): { jobIds: string[] } {
247
+ const db = getDb();
248
+ const jobIds: string[] = [];
249
+
250
+ for (const target of targets) {
251
+ const { conversationId } = target;
252
+
253
+ // Reset batch extraction checkpoints
254
+ deleteMemoryCheckpoint(
255
+ `batch_extract:${conversationId}:last_message_id`,
256
+ );
257
+ deleteMemoryCheckpoint(
258
+ `batch_extract:${conversationId}:pending_count`,
259
+ );
260
+
261
+ // Clear the extraction summary so it starts fresh
262
+ db.delete(memorySummaries)
263
+ .where(
264
+ and(
265
+ eq(memorySummaries.scope, "extraction_context"),
266
+ eq(memorySummaries.scopeKey, conversationId),
267
+ ),
268
+ )
269
+ .run();
270
+
271
+ // Resolve scope and enqueue with fullReextract flag
272
+ const scopeId = getConversationMemoryScopeId(conversationId);
273
+ const jobId = enqueueMemoryJob("batch_extract", {
274
+ conversationId,
275
+ scopeId,
276
+ fullReextract: true,
277
+ });
278
+ jobIds.push(jobId);
279
+
280
+ log.info(
281
+ { conversationId, title: target.title, messages: target.messageCount },
282
+ "Queued re-extraction job",
283
+ );
284
+ }
285
+
286
+ return { jobIds };
287
+ }
@@ -26,6 +26,27 @@ import { getAppsDir, resolveAppDir } from "./app-store.js";
26
26
 
27
27
  const log = getLogger("app-git");
28
28
 
29
+ // ---------------------------------------------------------------------------
30
+ // Pending commit message — set by app tool executors, consumed at turn boundary
31
+ // ---------------------------------------------------------------------------
32
+
33
+ const pendingAppCommitMessages = new Map<string, string>();
34
+
35
+ /** Set the commit message for the next app turn-boundary commit. */
36
+ export function setAppCommitMessage(
37
+ conversationId: string,
38
+ message: string,
39
+ ): void {
40
+ pendingAppCommitMessages.set(conversationId, message);
41
+ }
42
+
43
+ /** Consume and clear the pending commit message for a conversation. */
44
+ function consumeAppCommitMessage(conversationId: string): string | undefined {
45
+ const msg = pendingAppCommitMessages.get(conversationId);
46
+ pendingAppCommitMessages.delete(conversationId);
47
+ return msg;
48
+ }
49
+
29
50
  // ---------------------------------------------------------------------------
30
51
  // Types
31
52
  // ---------------------------------------------------------------------------
@@ -170,15 +191,51 @@ export async function commitAppTurnChanges(
170
191
  conversationId: string,
171
192
  turnNumber: number,
172
193
  ): Promise<void> {
194
+ // Consume before any work that could throw, so the message doesn't leak
195
+ const changeSummary = consumeAppCommitMessage(conversationId);
173
196
  try {
174
197
  const appsDir = getAppsDir();
175
198
  ensureAppGitignoreRules(appsDir);
176
199
 
177
200
  const gitService = getWorkspaceGitService(appsDir);
178
- await gitService.commitIfDirty(() => ({
179
- message: `Turn ${turnNumber}: app changes`,
180
- metadata: { conversationId, turnNumber },
181
- }));
201
+
202
+ await gitService.commitIfDirty((status) => {
203
+ if (changeSummary) {
204
+ return {
205
+ message: changeSummary,
206
+ metadata: { conversationId, turnNumber },
207
+ };
208
+ }
209
+
210
+ // Fallback: derive app names from changed file paths
211
+ const allFiles = [
212
+ ...new Set([...status.staged, ...status.modified, ...status.untracked]),
213
+ ];
214
+ const dirNames = [
215
+ ...new Set(allFiles.map((f) => f.split("/")[0].replace(/\.json$/, ""))),
216
+ ];
217
+ const appNames = dirNames.map((dirName) => {
218
+ try {
219
+ const jsonPath = join(appsDir, `${dirName}.json`);
220
+ const raw = readFileSync(jsonPath, "utf-8");
221
+ const app = JSON.parse(raw) as { name?: string };
222
+ return app.name || dirName;
223
+ } catch {
224
+ return dirName;
225
+ }
226
+ });
227
+ const subject =
228
+ appNames.length === 1
229
+ ? `update ${appNames[0]}`
230
+ : appNames.length <= 3
231
+ ? `update ${appNames.join(", ")}`
232
+ : `update ${appNames.length} apps`;
233
+
234
+ return {
235
+ message: subject,
236
+ metadata: { conversationId, turnNumber },
237
+ };
238
+ });
182
239
  } catch (err) {
183
240
  log.error(
184
241
  { err, conversationId, turnNumber },
@@ -345,6 +345,8 @@ const ALLOWED_MIME_TYPES = new Set([
345
345
  "image/bmp",
346
346
  "image/tiff",
347
347
  "image/x-icon",
348
+ "image/heic",
349
+ "image/heif",
348
350
  // Audio
349
351
  "audio/mpeg",
350
352
  "audio/ogg",
@@ -59,6 +59,10 @@ export interface CanonicalGuardianRequest {
59
59
  requestCode: string | null;
60
60
  toolName: string | null;
61
61
  inputDigest: string | null;
62
+ commandPreview: string | null;
63
+ riskLevel: string | null;
64
+ activityText: string | null;
65
+ executionTarget: string | null;
62
66
  status: CanonicalRequestStatus;
63
67
  answerText: string | null;
64
68
  decidedByExternalUserId: string | null;
@@ -149,6 +153,10 @@ function rowToRequest(
149
153
  requestCode: row.requestCode,
150
154
  toolName: row.toolName,
151
155
  inputDigest: row.inputDigest,
156
+ commandPreview: row.commandPreview,
157
+ riskLevel: row.riskLevel,
158
+ activityText: row.activityText,
159
+ executionTarget: row.executionTarget,
152
160
  status: row.status as CanonicalRequestStatus,
153
161
  answerText: row.answerText,
154
162
  decidedByExternalUserId: row.decidedByExternalUserId,
@@ -196,6 +204,10 @@ export interface CreateCanonicalGuardianRequestParams {
196
204
  requestCode?: string;
197
205
  toolName?: string;
198
206
  inputDigest?: string;
207
+ commandPreview?: string;
208
+ riskLevel?: string;
209
+ activityText?: string;
210
+ executionTarget?: string;
199
211
  status?: CanonicalRequestStatus;
200
212
  answerText?: string;
201
213
  decidedByExternalUserId?: string;
@@ -250,6 +262,10 @@ export function createCanonicalGuardianRequest(
250
262
  requestCode: params.requestCode ?? generateCanonicalRequestCode(),
251
263
  toolName: params.toolName ?? null,
252
264
  inputDigest: params.inputDigest ?? null,
265
+ commandPreview: params.commandPreview ?? null,
266
+ riskLevel: params.riskLevel ?? null,
267
+ activityText: params.activityText ?? null,
268
+ executionTarget: params.executionTarget ?? null,
253
269
  status: params.status ?? ("pending" as const),
254
270
  answerText: params.answerText ?? null,
255
271
  decidedByExternalUserId: params.decidedByExternalUserId ?? null,
@@ -82,6 +82,7 @@ import {
82
82
  migrateGuardianDeliveryConversationIndex,
83
83
  migrateGuardianPrincipalIdColumns,
84
84
  migrateGuardianPrincipalIdNotNull,
85
+ migrateGuardianRequestEnrichmentColumns,
85
86
  migrateGuardianTimestampsEpochMs,
86
87
  migrateGuardianVerificationPurpose,
87
88
  migrateGuardianVerificationSessions,
@@ -122,6 +123,7 @@ import {
122
123
  migrateSchemaIndexesAndColumns,
123
124
  migrateStripIntegrationPrefixFromProviderKeys,
124
125
  migrateUsageDashboardIndexes,
126
+ migrateUsageLlmCallCount,
125
127
  migrateVoiceInviteColumns,
126
128
  migrateVoiceInviteDisplayMetadata,
127
129
  recoverCrashedMigrations,
@@ -532,6 +534,12 @@ export function initializeDb(): void {
532
534
  // 96. Drop the setup_skill_id column from oauth_providers (concept removed)
533
535
  migrateDropSetupSkillIdColumn(database);
534
536
 
537
+ // 97. Add enrichment columns to canonical_guardian_requests for guardian approval UX
538
+ migrateGuardianRequestEnrichmentColumns(database);
539
+
540
+ // 98. Add llm_call_count column to llm_usage_events for accurate LLM call counting
541
+ migrateUsageLlmCallCount(database);
542
+
535
543
  validateMigrationState(database);
536
544
 
537
545
  if (process.env.BUN_TEST === "1") {
@@ -3,7 +3,10 @@ import { join } from "node:path";
3
3
 
4
4
  import { getIsContainerized } from "../config/env-registry.js";
5
5
  import { getLogger } from "../util/logger.js";
6
- import { getEmbeddingModelsDir, getRootDir } from "../util/platform.js";
6
+ import {
7
+ getEmbeddingModelsDir,
8
+ getEmbedWorkerPidPath,
9
+ } from "../util/platform.js";
7
10
  import { PromiseGuard } from "../util/promise-guard.js";
8
11
  import type {
9
12
  EmbeddingBackend,
@@ -404,7 +407,7 @@ export class LocalEmbeddingBackend implements EmbeddingBackend {
404
407
  if (getIsContainerized()) {
405
408
  return join("/tmp", LocalEmbeddingBackend.PID_FILENAME);
406
409
  }
407
- return join(getRootDir(), LocalEmbeddingBackend.PID_FILENAME);
410
+ return getEmbedWorkerPidPath();
408
411
  }
409
412
 
410
413
  private writePidFile(pid: number): void {
@@ -5,6 +5,7 @@ import { getConfig } from "../config/loader.js";
5
5
  import type { MemoryConfig } from "../config/types.js";
6
6
  import type { TrustClass } from "../runtime/actor-trust-resolver.js";
7
7
  import { getLogger } from "../util/logger.js";
8
+ import { getMemoryCheckpoint, setMemoryCheckpoint } from "./checkpoints.js";
8
9
  import { getDb } from "./db.js";
9
10
  import { selectedBackendSupportsMultimodal } from "./embedding-backend.js";
10
11
  import { enqueueMemoryJob, upsertDebouncedJob } from "./jobs-store.js";
@@ -17,10 +18,8 @@ import { segmentText } from "./segmenter.js";
17
18
 
18
19
  const log = getLogger("memory-indexer");
19
20
 
20
- /** Delay before a conversation summary job becomes eligible to run.
21
- * Each new message in the same conversation resets the timer, so the
22
- * summary is only built once the conversation has been idle for this long. */
23
- const SUMMARY_DEBOUNCE_MS = 3 * 60 * 1000; // 3 minutes
21
+ /** Minimum character length for a segment to be worth storing and embedding (~12-15 tokens). */
22
+ export const MIN_SEGMENT_CHARS = 50;
24
23
 
25
24
  export interface IndexMessageInput {
26
25
  messageId: string;
@@ -59,12 +58,7 @@ export async function indexMessageNow(
59
58
 
60
59
  const text = extractTextFromStoredMessageContent(input.content);
61
60
  if (text.length === 0) {
62
- upsertDebouncedJob(
63
- "build_conversation_summary",
64
- { conversationId: input.conversationId },
65
- Date.now() + SUMMARY_DEBOUNCE_MS,
66
- );
67
- return { indexedSegments: 0, enqueuedJobs: 1 };
61
+ return { indexedSegments: 0, enqueuedJobs: 0 };
68
62
  }
69
63
 
70
64
  const db = getDb();
@@ -95,8 +89,13 @@ export async function indexMessageNow(
95
89
  // Wrap all segment inserts and job enqueues in a single transaction so they
96
90
  // either all succeed or all roll back, preventing partial/orphaned state.
97
91
  let skippedEmbedJobs = 0;
92
+ let skippedShortSegments = 0;
98
93
  db.transaction((tx) => {
99
94
  for (const segment of segments) {
95
+ if (segment.text.length < MIN_SEGMENT_CHARS) {
96
+ skippedShortSegments++;
97
+ continue;
98
+ }
100
99
  const segmentId = buildSegmentId(input.messageId, segment.segmentIndex);
101
100
  const hash = createHash("sha256").update(segment.text).digest("hex");
102
101
 
@@ -151,22 +150,42 @@ export async function indexMessageNow(
151
150
  );
152
151
  }
153
152
 
154
- if (shouldExtract && isTrustedActor && !input.automated && config.extraction.useLLM) {
155
- enqueueMemoryJob(
156
- "extract_items",
157
- { messageId: input.messageId, scopeId: input.scopeId ?? "default" },
158
- Date.now(),
159
- tx,
160
- );
153
+ });
154
+
155
+ // ── Batch extraction tracking ──────────────────────────────────────
156
+ // Instead of per-message extraction, track pending unextracted messages
157
+ // and trigger batch extraction when the threshold is reached or after idle.
158
+ if (shouldExtract && isTrustedActor && !input.automated && config.extraction.useLLM) {
159
+ const pendingKey = `batch_extract:${input.conversationId}:pending_count`;
160
+ const currentVal = getMemoryCheckpoint(pendingKey);
161
+ const pendingCount = (currentVal ? parseInt(currentVal, 10) : 0) + 1;
162
+ setMemoryCheckpoint(pendingKey, String(pendingCount));
163
+
164
+ const batchSize = config.extraction.batchSize ?? 10;
165
+ const idleTimeoutMs = config.extraction.idleTimeoutMs ?? 300_000;
166
+
167
+ if (pendingCount >= batchSize) {
168
+ // Threshold reached — trigger immediate batch extraction
169
+ enqueueMemoryJob("batch_extract", {
170
+ conversationId: input.conversationId,
171
+ scopeId: input.scopeId ?? "default",
172
+ });
161
173
  }
162
174
 
175
+ // Also maintain idle debounce: enqueue a delayed batch_extract that fires
176
+ // if no new messages arrive within the idle timeout window.
163
177
  upsertDebouncedJob(
164
- "build_conversation_summary",
178
+ "batch_extract",
165
179
  { conversationId: input.conversationId },
166
- Date.now() + SUMMARY_DEBOUNCE_MS,
167
- tx,
180
+ Date.now() + idleTimeoutMs,
168
181
  );
169
- });
182
+ }
183
+
184
+ if (skippedShortSegments > 0) {
185
+ log.debug(
186
+ `Skipped ${skippedShortSegments}/${segments.length} segments shorter than ${MIN_SEGMENT_CHARS} chars`,
187
+ );
188
+ }
170
189
 
171
190
  if (skippedEmbedJobs > 0) {
172
191
  log.debug(
@@ -188,14 +207,13 @@ export async function indexMessageNow(
188
207
  log.info("Skipping extraction job: LLM extraction is disabled (useLLM=false)");
189
208
  }
190
209
 
191
- const extractionGated = !isTrustedActor || !!input.automated || !config.extraction.useLLM;
210
+ const storedSegments = segments.length - skippedShortSegments;
192
211
  const enqueuedJobs =
193
- segments.length -
212
+ storedSegments -
194
213
  skippedEmbedJobs +
195
- mediaBlocks.length +
196
- (shouldExtract && !extractionGated ? 2 : 1);
214
+ mediaBlocks.length;
197
215
  return {
198
- indexedSegments: segments.length,
216
+ indexedSegments: storedSegments,
199
217
  enqueuedJobs,
200
218
  };
201
219
  }