@vellumai/assistant 0.5.6 → 0.5.8

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 (442) hide show
  1. package/.env.example +16 -2
  2. package/ARCHITECTURE.md +6 -75
  3. package/Dockerfile +3 -2
  4. package/README.md +0 -2
  5. package/bun.lock +0 -414
  6. package/docker-entrypoint.sh +9 -0
  7. package/docs/architecture/keychain-broker.md +45 -240
  8. package/docs/architecture/memory.md +13 -11
  9. package/docs/architecture/security.md +0 -17
  10. package/docs/credential-execution-service.md +2 -2
  11. package/node_modules/@vellumai/ces-contracts/package.json +1 -0
  12. package/node_modules/@vellumai/ces-contracts/src/error.ts +1 -1
  13. package/node_modules/@vellumai/ces-contracts/src/grants.ts +1 -1
  14. package/node_modules/@vellumai/ces-contracts/src/handles.ts +1 -1
  15. package/node_modules/@vellumai/ces-contracts/src/index.ts +1 -1
  16. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +120 -1
  17. package/node_modules/@vellumai/credential-storage/package.json +1 -0
  18. package/node_modules/@vellumai/egress-proxy/package.json +1 -0
  19. package/package.json +2 -3
  20. package/src/__tests__/actor-token-service.test.ts +0 -114
  21. package/src/__tests__/approval-cascade.test.ts +0 -1
  22. package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
  23. package/src/__tests__/browser-fill-credential.test.ts +1 -1
  24. package/src/__tests__/browser-skill-endstate.test.ts +6 -5
  25. package/src/__tests__/btw-routes.test.ts +0 -39
  26. package/src/__tests__/call-controller.test.ts +0 -1
  27. package/src/__tests__/call-domain.test.ts +0 -128
  28. package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
  29. package/src/__tests__/ces-startup-timeout.test.ts +40 -0
  30. package/src/__tests__/channel-approval-routes.test.ts +0 -5
  31. package/src/__tests__/channel-readiness-service.test.ts +1 -60
  32. package/src/__tests__/checker.test.ts +4 -2
  33. package/src/__tests__/cli-command-risk-guard.test.ts +112 -0
  34. package/src/__tests__/config-schema-cmd.test.ts +0 -2
  35. package/src/__tests__/config-schema.test.ts +3 -1
  36. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -1
  37. package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -2
  38. package/src/__tests__/conversation-agent-loop.test.ts +2 -4
  39. package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
  40. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -1
  41. package/src/__tests__/conversation-error.test.ts +15 -1
  42. package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
  43. package/src/__tests__/conversation-messaging-secret-redirect.test.ts +1 -1
  44. package/src/__tests__/conversation-pre-run-repair.test.ts +0 -1
  45. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -1
  46. package/src/__tests__/conversation-queue.test.ts +0 -1
  47. package/src/__tests__/conversation-skill-tools.test.ts +0 -54
  48. package/src/__tests__/conversation-slash-queue.test.ts +0 -1
  49. package/src/__tests__/conversation-slash-unknown.test.ts +0 -1
  50. package/src/__tests__/conversation-title-service.test.ts +87 -0
  51. package/src/__tests__/conversation-workspace-injection.test.ts +0 -1
  52. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -1
  53. package/src/__tests__/credential-execution-client.test.ts +5 -2
  54. package/src/__tests__/credential-execution-feature-gates.test.ts +59 -30
  55. package/src/__tests__/credential-execution-managed-contract.test.ts +35 -20
  56. package/src/__tests__/credential-security-e2e.test.ts +1 -67
  57. package/src/__tests__/credential-security-invariants.test.ts +6 -50
  58. package/src/__tests__/credentials-cli.test.ts +82 -3
  59. package/src/__tests__/daemon-credential-client.test.ts +123 -0
  60. package/src/__tests__/db-migration-rollback.test.ts +2015 -1
  61. package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -0
  62. package/src/__tests__/docker-signing-key-bootstrap.test.ts +34 -143
  63. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
  64. package/src/__tests__/gateway-client-managed-outbound.test.ts +79 -1
  65. package/src/__tests__/guardian-routing-state.test.ts +0 -5
  66. package/src/__tests__/host-shell-tool.test.ts +6 -7
  67. package/src/__tests__/http-user-message-parity.test.ts +3 -103
  68. package/src/__tests__/inbound-invite-redemption.test.ts +0 -4
  69. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -8
  70. package/src/__tests__/intent-routing.test.ts +0 -13
  71. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +178 -0
  72. package/src/__tests__/journal-context.test.ts +335 -0
  73. package/src/__tests__/keychain-broker-client.test.ts +161 -22
  74. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -3
  75. package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
  76. package/src/__tests__/memory-lifecycle-e2e.test.ts +70 -25
  77. package/src/__tests__/memory-recall-quality.test.ts +48 -17
  78. package/src/__tests__/memory-regressions.test.ts +408 -363
  79. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -3
  80. package/src/__tests__/migration-export-http.test.ts +2 -2
  81. package/src/__tests__/migration-import-commit-http.test.ts +2 -2
  82. package/src/__tests__/migration-import-preflight-http.test.ts +2 -2
  83. package/src/__tests__/migration-validate-http.test.ts +2 -2
  84. package/src/__tests__/non-member-access-request.test.ts +2 -7
  85. package/src/__tests__/notification-decision-fallback.test.ts +4 -0
  86. package/src/__tests__/notification-decision-identity.test.ts +4 -0
  87. package/src/__tests__/notification-decision-strategy.test.ts +71 -0
  88. package/src/__tests__/oauth-cli.test.ts +5 -1
  89. package/src/__tests__/permission-types.test.ts +1 -0
  90. package/src/__tests__/provider-commit-message-generator.test.ts +0 -37
  91. package/src/__tests__/provider-error-scenarios.test.ts +0 -267
  92. package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
  93. package/src/__tests__/provider-streaming.benchmark.test.ts +2 -81
  94. package/src/__tests__/qdrant-manager.test.ts +28 -2
  95. package/src/__tests__/registry.test.ts +0 -6
  96. package/src/__tests__/relay-server.test.ts +1 -2
  97. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
  98. package/src/__tests__/script-proxy-injection-runtime.test.ts +1 -1
  99. package/src/__tests__/secret-onetime-send.test.ts +1 -1
  100. package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
  101. package/src/__tests__/secure-keys.test.ts +95 -272
  102. package/src/__tests__/shell-identity.test.ts +96 -6
  103. package/src/__tests__/skill-feature-flags-integration.test.ts +22 -14
  104. package/src/__tests__/skill-feature-flags.test.ts +46 -45
  105. package/src/__tests__/skill-load-feature-flag.test.ts +7 -10
  106. package/src/__tests__/skill-load-inline-command.test.ts +8 -12
  107. package/src/__tests__/skill-load-inline-includes.test.ts +6 -10
  108. package/src/__tests__/skill-load-tool.test.ts +0 -2
  109. package/src/__tests__/skill-memory.test.ts +17 -3
  110. package/src/__tests__/skill-projection-feature-flag.test.ts +33 -29
  111. package/src/__tests__/skills.test.ts +0 -2
  112. package/src/__tests__/slack-inbound-verification.test.ts +0 -4
  113. package/src/__tests__/stale-approval-dedup.test.ts +171 -0
  114. package/src/__tests__/stt-hints.test.ts +437 -0
  115. package/src/__tests__/suggestion-routes.test.ts +1 -32
  116. package/src/__tests__/system-prompt.test.ts +0 -1
  117. package/src/__tests__/task-memory-cleanup.test.ts +14 -0
  118. package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
  119. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
  120. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
  121. package/src/__tests__/twilio-routes-twiml.test.ts +139 -1
  122. package/src/__tests__/update-bulletin.test.ts +0 -2
  123. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +6 -9
  124. package/src/__tests__/voice-quality.test.ts +58 -0
  125. package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -7
  126. package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +252 -0
  127. package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +220 -0
  128. package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
  129. package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
  130. package/src/acp/agent-process.ts +9 -1
  131. package/src/agent/loop.ts +1 -1
  132. package/src/approvals/guardian-request-resolvers.ts +164 -38
  133. package/src/calls/__tests__/tts-text-sanitizer.test.ts +254 -0
  134. package/src/calls/audio-store.test.ts +97 -0
  135. package/src/calls/audio-store.ts +205 -0
  136. package/src/calls/call-controller.ts +90 -8
  137. package/src/calls/call-domain.ts +3 -0
  138. package/src/calls/call-store.ts +10 -3
  139. package/src/calls/fish-audio-client.ts +129 -0
  140. package/src/calls/relay-server.ts +27 -0
  141. package/src/calls/stt-hints.ts +189 -0
  142. package/src/calls/tts-text-sanitizer.ts +61 -0
  143. package/src/calls/twilio-routes.ts +34 -5
  144. package/src/calls/types.ts +1 -0
  145. package/src/calls/voice-ingress-preflight.ts +0 -42
  146. package/src/calls/voice-quality.ts +38 -5
  147. package/src/calls/voice-session-bridge.ts +7 -12
  148. package/src/cli/commands/avatar.ts +2 -2
  149. package/src/cli/commands/config.ts +1 -4
  150. package/src/cli/commands/credentials.ts +128 -82
  151. package/src/cli/commands/doctor.ts +2 -2
  152. package/src/cli/commands/keys.ts +7 -7
  153. package/src/cli/commands/memory.ts +1 -1
  154. package/src/cli/commands/oauth/connections.ts +11 -29
  155. package/src/cli/commands/oauth/index.ts +7 -0
  156. package/src/cli/commands/oauth/platform.ts +525 -0
  157. package/src/cli/commands/platform.ts +3 -3
  158. package/src/cli/lib/daemon-credential-client.ts +284 -0
  159. package/src/cli.ts +1 -1
  160. package/src/config/assistant-feature-flags.ts +186 -5
  161. package/src/config/bundled-skills/AGENTS.md +34 -0
  162. package/src/config/bundled-skills/acp/SKILL.md +10 -0
  163. package/src/config/bundled-skills/app-builder/SKILL.md +0 -4
  164. package/src/config/bundled-skills/messaging/SKILL.md +5 -5
  165. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
  166. package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
  167. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +1 -0
  168. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +1 -0
  169. package/src/config/bundled-skills/settings/SKILL.md +15 -2
  170. package/src/config/bundled-skills/settings/TOOLS.json +47 -2
  171. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +59 -0
  172. package/src/config/bundled-skills/settings/tools/avatar-update.ts +80 -0
  173. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
  174. package/src/config/bundled-skills/slack/SKILL.md +1 -1
  175. package/src/config/bundled-tool-registry.ts +5 -11
  176. package/src/config/defaults.ts +0 -2
  177. package/src/config/env-registry.ts +5 -5
  178. package/src/config/env.ts +21 -14
  179. package/src/config/feature-flag-registry.json +49 -9
  180. package/src/config/loader.ts +106 -42
  181. package/src/config/schema.ts +9 -29
  182. package/src/config/schemas/calls.ts +30 -0
  183. package/src/config/schemas/fish-audio.ts +39 -0
  184. package/src/config/schemas/inference.ts +2 -2
  185. package/src/config/schemas/journal.ts +16 -0
  186. package/src/config/schemas/memory-processing.ts +2 -2
  187. package/src/config/schemas/security.ts +0 -4
  188. package/src/config/types.ts +1 -1
  189. package/src/contacts/contact-store.ts +39 -0
  190. package/src/contacts/types.ts +2 -0
  191. package/src/credential-execution/approval-bridge.ts +1 -0
  192. package/src/credential-execution/executable-discovery.ts +28 -4
  193. package/src/credential-execution/feature-gates.ts +16 -0
  194. package/src/credential-execution/process-manager.ts +38 -0
  195. package/src/credential-execution/startup-timeout.ts +36 -0
  196. package/src/daemon/approval-generators.ts +3 -9
  197. package/src/daemon/assistant-attachments.ts +9 -0
  198. package/src/daemon/config-watcher.ts +5 -0
  199. package/src/daemon/conversation-error.ts +13 -1
  200. package/src/daemon/conversation-memory.ts +1 -2
  201. package/src/daemon/conversation-process.ts +18 -1
  202. package/src/daemon/conversation-surfaces.ts +30 -1
  203. package/src/daemon/conversation-tool-setup.ts +0 -105
  204. package/src/daemon/conversation.ts +21 -1
  205. package/src/daemon/guardian-action-generators.ts +3 -9
  206. package/src/daemon/handlers/config-vercel.ts +92 -0
  207. package/src/daemon/handlers/skills.ts +2 -15
  208. package/src/daemon/install-symlink.ts +195 -0
  209. package/src/daemon/lifecycle.ts +234 -51
  210. package/src/daemon/message-types/conversations.ts +4 -4
  211. package/src/daemon/message-types/diagnostics.ts +3 -22
  212. package/src/daemon/message-types/messages.ts +0 -2
  213. package/src/daemon/message-types/upgrades.ts +8 -0
  214. package/src/daemon/server.ts +32 -95
  215. package/src/events/domain-events.ts +2 -1
  216. package/src/inbound/platform-callback-registration.ts +3 -3
  217. package/src/instrument.ts +8 -5
  218. package/src/memory/app-store.ts +31 -0
  219. package/src/memory/conversation-title-service.ts +50 -1
  220. package/src/memory/db-init.ts +16 -0
  221. package/src/memory/indexer.ts +19 -10
  222. package/src/memory/items-extractor.ts +328 -321
  223. package/src/memory/job-handlers/conversation-starters.ts +4 -1
  224. package/src/memory/job-handlers/summarization.ts +26 -16
  225. package/src/memory/jobs-store.ts +63 -6
  226. package/src/memory/jobs-worker.ts +31 -7
  227. package/src/memory/journal-memory.ts +214 -0
  228. package/src/memory/migrations/001-job-deferrals.ts +19 -0
  229. package/src/memory/migrations/004-entity-relation-dedup.ts +10 -0
  230. package/src/memory/migrations/005-fingerprint-scope-unique.ts +76 -0
  231. package/src/memory/migrations/006-scope-salted-fingerprints.ts +50 -0
  232. package/src/memory/migrations/007-assistant-id-to-self.ts +10 -0
  233. package/src/memory/migrations/008-remove-assistant-id-columns.ts +34 -0
  234. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +26 -0
  235. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +10 -0
  236. package/src/memory/migrations/015-drop-active-search-index.ts +17 -0
  237. package/src/memory/migrations/019-notification-tables-schema-migration.ts +12 -0
  238. package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +121 -0
  239. package/src/memory/migrations/024-embedding-vector-blob.ts +74 -0
  240. package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +82 -0
  241. package/src/memory/migrations/036-normalize-phone-identities.ts +11 -0
  242. package/src/memory/migrations/116-messages-fts.ts +106 -1
  243. package/src/memory/migrations/126-backfill-guardian-principal-id.ts +52 -0
  244. package/src/memory/migrations/127-guardian-principal-id-not-null.ts +77 -0
  245. package/src/memory/migrations/134-contacts-notes-column.ts +13 -0
  246. package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +20 -0
  247. package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -0
  248. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +13 -0
  249. package/src/memory/migrations/141-rename-verification-table.ts +54 -0
  250. package/src/memory/migrations/142-rename-verification-session-id-column.ts +25 -0
  251. package/src/memory/migrations/143-rename-guardian-verification-values.ts +35 -0
  252. package/src/memory/migrations/144-rename-voice-to-phone.ts +136 -0
  253. package/src/memory/migrations/145-drop-accounts-table.ts +32 -0
  254. package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +14 -1
  255. package/src/memory/migrations/148-drop-reminders-table.ts +35 -1
  256. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +69 -1
  257. package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +290 -0
  258. package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +51 -1
  259. package/src/memory/migrations/174-rename-thread-starters-table.ts +47 -1
  260. package/src/memory/migrations/176-drop-capability-card-state.ts +13 -0
  261. package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +16 -0
  262. package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +28 -1
  263. package/src/memory/migrations/190-call-session-skip-disclosure.ts +15 -0
  264. package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +64 -0
  265. package/src/memory/migrations/192-contacts-user-file-column.ts +15 -0
  266. package/src/memory/migrations/193-add-source-type-columns.ts +81 -0
  267. package/src/memory/migrations/index.ts +5 -0
  268. package/src/memory/migrations/registry.ts +98 -0
  269. package/src/memory/migrations/validate-migration-state.ts +137 -11
  270. package/src/memory/qdrant-circuit-breaker.ts +9 -0
  271. package/src/memory/qdrant-manager.ts +64 -7
  272. package/src/memory/retriever.test.ts +37 -25
  273. package/src/memory/retriever.ts +24 -49
  274. package/src/memory/schema/calls.ts +1 -0
  275. package/src/memory/schema/contacts.ts +1 -0
  276. package/src/memory/schema/memory-core.ts +2 -0
  277. package/src/memory/search/formatting.ts +7 -44
  278. package/src/memory/search/staleness.ts +4 -0
  279. package/src/memory/search/tier-classifier.ts +10 -2
  280. package/src/memory/search/types.ts +2 -5
  281. package/src/memory/task-memory-cleanup.ts +4 -3
  282. package/src/notifications/adapters/slack.ts +168 -6
  283. package/src/notifications/broadcaster.ts +1 -0
  284. package/src/notifications/copy-composer.ts +59 -2
  285. package/src/notifications/decision-engine.ts +4 -1
  286. package/src/notifications/signal.ts +2 -0
  287. package/src/notifications/types.ts +2 -0
  288. package/src/oauth/connection-resolver.ts +6 -4
  289. package/src/permissions/checker.ts +0 -38
  290. package/src/permissions/shell-identity.ts +76 -22
  291. package/src/permissions/types.ts +4 -2
  292. package/src/platform/client.ts +35 -7
  293. package/src/prompts/journal-context.ts +133 -0
  294. package/src/prompts/persona-resolver.ts +194 -0
  295. package/src/prompts/system-prompt.ts +44 -4
  296. package/src/prompts/templates/SOUL.md +10 -0
  297. package/src/prompts/templates/users/default.md +1 -0
  298. package/src/providers/provider-send-message.ts +3 -32
  299. package/src/providers/registry.ts +29 -179
  300. package/src/providers/types.ts +1 -1
  301. package/src/runtime/access-request-helper.ts +4 -0
  302. package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
  303. package/src/runtime/auth/__tests__/external-assistant-id.test.ts +13 -68
  304. package/src/runtime/auth/__tests__/guard-tests.test.ts +9 -50
  305. package/src/runtime/auth/external-assistant-id.ts +13 -59
  306. package/src/runtime/auth/route-policy.ts +17 -1
  307. package/src/runtime/auth/token-service.ts +43 -138
  308. package/src/runtime/channel-readiness-service.ts +1 -16
  309. package/src/runtime/gateway-client.ts +47 -4
  310. package/src/runtime/guardian-decision-types.ts +45 -4
  311. package/src/runtime/http-server.ts +31 -3
  312. package/src/runtime/middleware/error-handler.ts +1 -9
  313. package/src/runtime/routes/access-request-decision.ts +2 -2
  314. package/src/runtime/routes/app-management-routes.ts +2 -1
  315. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +219 -30
  316. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +37 -14
  317. package/src/runtime/routes/audio-routes.ts +40 -0
  318. package/src/runtime/routes/btw-routes.ts +0 -17
  319. package/src/runtime/routes/channel-readiness-routes.ts +9 -4
  320. package/src/runtime/routes/conversation-query-routes.ts +63 -1
  321. package/src/runtime/routes/conversation-routes.ts +4 -44
  322. package/src/runtime/routes/debug-routes.ts +12 -9
  323. package/src/runtime/routes/diagnostics-routes.ts +1 -477
  324. package/src/runtime/routes/guardian-approval-interception.ts +168 -11
  325. package/src/runtime/routes/guardian-approval-prompt.ts +6 -1
  326. package/src/runtime/routes/guardian-approval-reply-helpers.ts +103 -21
  327. package/src/runtime/routes/identity-routes.ts +19 -30
  328. package/src/runtime/routes/inbound-message-handler.ts +31 -1
  329. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +64 -5
  330. package/src/runtime/routes/inbound-stages/background-dispatch.ts +52 -40
  331. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -33
  332. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +1 -1
  333. package/src/runtime/routes/integrations/twilio.ts +52 -10
  334. package/src/runtime/routes/integrations/vercel.ts +89 -0
  335. package/src/runtime/routes/log-export-routes.ts +5 -0
  336. package/src/runtime/routes/memory-item-routes.test.ts +3 -3
  337. package/src/runtime/routes/memory-item-routes.ts +46 -14
  338. package/src/runtime/routes/migration-rollback-routes.ts +209 -0
  339. package/src/runtime/routes/migration-routes.ts +17 -1
  340. package/src/runtime/routes/notification-routes.ts +58 -0
  341. package/src/runtime/routes/schedule-routes.ts +65 -0
  342. package/src/runtime/routes/secret-routes.ts +141 -10
  343. package/src/runtime/routes/settings-routes.ts +41 -1
  344. package/src/runtime/routes/tts-routes.ts +96 -0
  345. package/src/runtime/routes/upgrade-broadcast-routes.ts +26 -2
  346. package/src/runtime/routes/workspace-commit-routes.ts +62 -0
  347. package/src/runtime/routes/workspace-routes.test.ts +22 -1
  348. package/src/runtime/routes/workspace-routes.ts +1 -1
  349. package/src/runtime/routes/workspace-utils.ts +86 -2
  350. package/src/security/ces-credential-client.ts +75 -29
  351. package/src/security/ces-rpc-credential-backend.ts +86 -0
  352. package/src/security/credential-backend.ts +22 -92
  353. package/src/security/keychain-broker-client.ts +10 -2
  354. package/src/security/secure-keys.ts +113 -115
  355. package/src/skills/catalog-install.ts +6 -32
  356. package/src/skills/skill-memory.ts +1 -0
  357. package/src/subagent/manager.ts +2 -5
  358. package/src/telemetry/usage-telemetry-reporter.ts +4 -2
  359. package/src/tools/acp/spawn.ts +78 -1
  360. package/src/tools/calls/call-start.ts +1 -0
  361. package/src/tools/credentials/vault.ts +5 -3
  362. package/src/tools/executor.ts +0 -4
  363. package/src/tools/memory/definitions.ts +3 -2
  364. package/src/tools/memory/handlers.ts +10 -7
  365. package/src/tools/network/script-proxy/session-manager.ts +19 -4
  366. package/src/tools/network/web-fetch.ts +3 -1
  367. package/src/tools/skills/execute.ts +1 -1
  368. package/src/tools/terminal/safe-env.ts +1 -0
  369. package/src/tools/types.ts +0 -8
  370. package/src/util/browser.ts +15 -0
  371. package/src/util/errors.ts +0 -12
  372. package/src/util/platform.ts +4 -51
  373. package/src/workspace/git-service.ts +5 -2
  374. package/src/workspace/migrations/001-avatar-rename.ts +15 -0
  375. package/src/workspace/migrations/003-seed-device-id.ts +17 -1
  376. package/src/workspace/migrations/004-extract-collect-usage-data.ts +33 -0
  377. package/src/workspace/migrations/005-add-send-diagnostics.ts +3 -0
  378. package/src/workspace/migrations/006-services-config.ts +49 -0
  379. package/src/workspace/migrations/007-web-search-provider-rename.ts +27 -0
  380. package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +3 -0
  381. package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +4 -0
  382. package/src/workspace/migrations/010-app-dir-rename.ts +78 -0
  383. package/src/workspace/migrations/011-backfill-installation-id.ts +11 -0
  384. package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +44 -0
  385. package/src/workspace/migrations/013-repair-conversation-disk-view.ts +5 -0
  386. package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +153 -0
  387. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +156 -0
  388. package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +150 -0
  389. package/src/workspace/migrations/017-seed-persona-dirs.ts +96 -0
  390. package/src/workspace/migrations/018-rekey-compound-credential-keys.ts +184 -0
  391. package/src/workspace/migrations/019-scope-journal-to-guardian.ts +103 -0
  392. package/src/workspace/migrations/migrate-to-workspace-volume.ts +27 -5
  393. package/src/workspace/migrations/registry.ts +12 -0
  394. package/src/workspace/migrations/runner.ts +106 -2
  395. package/src/workspace/migrations/types.ts +4 -0
  396. package/src/workspace/provider-commit-message-generator.ts +12 -21
  397. package/src/__tests__/claude-code-skill-regression.test.ts +0 -206
  398. package/src/__tests__/claude-code-tool-profiles.test.ts +0 -99
  399. package/src/__tests__/diagnostics-export.test.ts +0 -288
  400. package/src/__tests__/local-gateway-health.test.ts +0 -209
  401. package/src/__tests__/provider-fail-open-selection.test.ts +0 -271
  402. package/src/__tests__/provider-failover-actual-provider.test.ts +0 -66
  403. package/src/__tests__/secret-ingress-handler.test.ts +0 -120
  404. package/src/__tests__/swarm-conversation-integration.test.ts +0 -358
  405. package/src/__tests__/swarm-dag-pathological.test.ts +0 -547
  406. package/src/__tests__/swarm-orchestrator.test.ts +0 -463
  407. package/src/__tests__/swarm-plan-validator.test.ts +0 -384
  408. package/src/__tests__/swarm-recursion.test.ts +0 -197
  409. package/src/__tests__/swarm-router-planner.test.ts +0 -234
  410. package/src/__tests__/swarm-tool.test.ts +0 -185
  411. package/src/__tests__/swarm-worker-backend.test.ts +0 -144
  412. package/src/__tests__/swarm-worker-runner.test.ts +0 -288
  413. package/src/commands/__tests__/cc-command-registry.test.ts +0 -396
  414. package/src/commands/cc-command-registry.ts +0 -248
  415. package/src/config/bundled-skills/claude-code/SKILL.md +0 -53
  416. package/src/config/bundled-skills/claude-code/TOOLS.json +0 -47
  417. package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -12
  418. package/src/config/bundled-skills/orchestration/SKILL.md +0 -33
  419. package/src/config/bundled-skills/orchestration/TOOLS.json +0 -35
  420. package/src/config/bundled-skills/orchestration/tools/swarm-delegate.ts +0 -12
  421. package/src/config/schemas/swarm.ts +0 -82
  422. package/src/logfire.ts +0 -135
  423. package/src/memory/search/lexical.ts +0 -48
  424. package/src/providers/failover.ts +0 -186
  425. package/src/runtime/local-gateway-health.ts +0 -275
  426. package/src/security/secret-ingress.ts +0 -68
  427. package/src/swarm/backend-claude-code.ts +0 -225
  428. package/src/swarm/checkpoint.ts +0 -137
  429. package/src/swarm/graph-utils.ts +0 -53
  430. package/src/swarm/index.ts +0 -55
  431. package/src/swarm/limits.ts +0 -66
  432. package/src/swarm/orchestrator.ts +0 -424
  433. package/src/swarm/plan-validator.ts +0 -117
  434. package/src/swarm/router-planner.ts +0 -162
  435. package/src/swarm/router-prompts.ts +0 -39
  436. package/src/swarm/synthesizer.ts +0 -81
  437. package/src/swarm/types.ts +0 -72
  438. package/src/swarm/worker-backend.ts +0 -131
  439. package/src/swarm/worker-prompts.ts +0 -80
  440. package/src/swarm/worker-runner.ts +0 -170
  441. package/src/tools/claude-code/claude-code.ts +0 -610
  442. package/src/tools/swarm/delegate.ts +0 -205
@@ -337,10 +337,7 @@ describe("Memory Retriever Pipeline", () => {
337
337
  expect(result.tier1Count).toBeDefined();
338
338
  expect(result.tier2Count).toBeDefined();
339
339
  expect(result.hybridSearchMs).toBeDefined();
340
- // Recency search finds raw candidates from this conversation…
341
- expect(result.recencyHits).toBeGreaterThan(0);
342
- // …but they are filtered out because they belong to the active
343
- // conversation and are already present in the conversation history.
340
+ // Without semantic search, no candidates are found.
344
341
  expect(result.mergedCount).toBe(0);
345
342
  });
346
343
 
@@ -403,11 +400,7 @@ describe("Memory Retriever Pipeline", () => {
403
400
  );
404
401
 
405
402
  expect(result.enabled).toBe(true);
406
- // Recency search finds segments from the active conversation
407
- expect(result.recencyHits).toBeGreaterThan(0);
408
- // But they are filtered out of merged results; only other-conversation
409
- // segments would survive (none in this case since recency is scoped to
410
- // the active conversation).
403
+ // Without semantic search, no candidates are found.
411
404
  expect(result.mergedCount).toBe(0);
412
405
  });
413
406
 
@@ -506,10 +499,6 @@ describe("Memory Retriever Pipeline", () => {
506
499
  );
507
500
 
508
501
  expect(result.enabled).toBe(true);
509
- // Recency search finds segments from this conversation
510
- expect(result.recencyHits).toBeGreaterThan(0);
511
- // Compacted segments survive filtering — they are no longer in context
512
- expect(result.mergedCount).toBeGreaterThan(0);
513
502
  });
514
503
 
515
504
  // -----------------------------------------------------------------------
@@ -716,9 +705,7 @@ describe("Memory Retriever Pipeline", () => {
716
705
  expect(result.enabled).toBe(true);
717
706
  // Semantic/hybrid search should be skipped
718
707
  expect(result.semanticHits).toBe(0);
719
- // Recency search finds raw candidates…
720
- expect(result.recencyHits).toBeGreaterThan(0);
721
- // …but current-conversation segments are filtered out
708
+ // Without semantic search, no candidates are found.
722
709
  expect(result.mergedCount).toBe(0);
723
710
  });
724
711
 
@@ -752,7 +739,7 @@ describe("Memory Retriever Pipeline", () => {
752
739
  expect(result.degradation).toBeDefined();
753
740
  expect(result.degradation!.semanticUnavailable).toBe(true);
754
741
  expect(result.degradation!.reason).toBe("embedding_provider_down");
755
- expect(result.degradation!.fallbackSources).toContain("recency");
742
+ expect(result.degradation!.fallbackSources).toEqual([]);
756
743
  });
757
744
 
758
745
  // -----------------------------------------------------------------------
@@ -864,9 +851,7 @@ describe("Memory Retriever Pipeline", () => {
864
851
  // pipeline proceeds non-degraded end-to-end.
865
852
  expect(result.enabled).toBe(true);
866
853
  expect(result.degraded).toBe(false);
867
- // Recency search finds raw candidates; hybrid search returns empty from mock
868
- expect(result.recencyHits).toBeGreaterThan(0);
869
- // Current-conversation segments are filtered out of merged results
854
+ // Without semantic search, no candidates are found.
870
855
  expect(result.mergedCount).toBe(0);
871
856
  });
872
857
 
@@ -1142,8 +1127,7 @@ describe("Memory Retriever Pipeline", () => {
1142
1127
  );
1143
1128
 
1144
1129
  // Simulate Qdrant returning the parent-conversation segment as a
1145
- // semantic hit so it enters the candidate map (recency search is scoped
1146
- // to forkConv and would never find it).
1130
+ // semantic hit so it enters the candidate map.
1147
1131
  mockQdrantResults.push({
1148
1132
  id: "qdrant-fork-1",
1149
1133
  score: 0.9,
@@ -1264,6 +1248,35 @@ describe("Memory Retriever Pipeline", () => {
1264
1248
  now - 50_000,
1265
1249
  );
1266
1250
 
1251
+ // Simulate Qdrant returning both segments as semantic hits so they
1252
+ // enter the candidate map (recency search was removed).
1253
+ mockQdrantResults.push(
1254
+ {
1255
+ id: "qdrant-compact-fork-1",
1256
+ score: 0.9,
1257
+ payload: {
1258
+ target_type: "segment",
1259
+ target_id: "seg-compact-fork",
1260
+ text: "compacted parent topic detail",
1261
+ created_at: now - 100_000,
1262
+ message_id: "fork-compact-msg-1",
1263
+ conversation_id: forkConv,
1264
+ },
1265
+ },
1266
+ {
1267
+ id: "qdrant-compact-fork-2",
1268
+ score: 0.85,
1269
+ payload: {
1270
+ target_type: "segment",
1271
+ target_id: "seg-in-context-fork",
1272
+ text: "recent fork topic detail",
1273
+ created_at: now - 50_000,
1274
+ message_id: "fork-compact-msg-3",
1275
+ conversation_id: forkConv,
1276
+ },
1277
+ },
1278
+ );
1279
+
1267
1280
  const result = await buildMemoryRecall(
1268
1281
  "compacted parent topic",
1269
1282
  forkConv,
@@ -1273,7 +1286,7 @@ describe("Memory Retriever Pipeline", () => {
1273
1286
  expect(result.enabled).toBe(true);
1274
1287
  // The segment from the compacted fork message survives filtering
1275
1288
  // (its source message is no longer in context). The in-context segment
1276
- // is filtered out. Recency search returns both, but only the compacted
1289
+ // is filtered out. Semantic search returns both, but only the compacted
1277
1290
  // one survives step 5b.
1278
1291
  expect(result.mergedCount).toBeGreaterThan(0);
1279
1292
  });
@@ -1342,8 +1355,7 @@ describe("Memory Retriever Pipeline", () => {
1342
1355
  );
1343
1356
 
1344
1357
  // Simulate Qdrant returning the grandparent segment as a semantic hit
1345
- // so it enters the candidate map (recency search is scoped to childConv
1346
- // and would never find it).
1358
+ // so it enters the candidate map.
1347
1359
  mockQdrantResults.push({
1348
1360
  id: "qdrant-gp-1",
1349
1361
  score: 0.9,
@@ -29,7 +29,6 @@ import {
29
29
  IDENTITY_KINDS,
30
30
  PREFERENCE_KINDS,
31
31
  } from "./search/formatting.js";
32
- import { recencySearch } from "./search/lexical.js";
33
32
  import { isQdrantConnectionError, semanticSearch } from "./search/semantic.js";
34
33
  import { applyStaleDemotion, computeStaleness } from "./search/staleness.js";
35
34
  import {
@@ -139,7 +138,7 @@ function buildDegradationStatus(
139
138
  return {
140
139
  semanticUnavailable: true,
141
140
  reason,
142
- fallbackSources: ["recency"],
141
+ fallbackSources: [],
143
142
  };
144
143
  }
145
144
 
@@ -243,13 +242,12 @@ async function generateQueryEmbedding(
243
242
  * 1. Build query text (caller provides via buildMemoryQuery)
244
243
  * 2. Generate dense + sparse embeddings
245
244
  * 3. Hybrid search on Qdrant (dense + sparse RRF fusion)
246
- * 4. Supplement with recency search (conversation-scoped, DB only)
247
- * 5. Merge + deduplicate results
248
- * 6. Classify tiers (score > 0.8 tier 1, > 0.6 → tier 2)
249
- * 7. Enrich item candidates with metadata for staleness
250
- * 8. Compute staleness per item
251
- * 9. Demote very_stale tier 1 tier 2
252
- * 10. Build two-layer XML injection with budget allocation
245
+ * 4. Deduplicate results
246
+ * 5. Classify tiers (score > 0.6 → tier 1, > 0.4 → tier 2)
247
+ * 6. Enrich item candidates with metadata for staleness
248
+ * 7. Compute staleness per item
249
+ * 8. Demote very_stale tier 1 → tier 2
250
+ * 9. Build two-layer XML injection with budget allocation
253
251
  */
254
252
  export async function buildMemoryRecall(
255
253
  query: string,
@@ -327,21 +325,15 @@ export async function buildMemoryRecall(
327
325
  if (isQdrantConnectionError(err)) {
328
326
  log.warn({ err }, "Qdrant unavailable — hybrid search disabled");
329
327
  } else {
330
- log.warn({ err }, "Hybrid search failed, continuing with recency only");
328
+ log.warn({ err }, "Hybrid search failed");
331
329
  }
332
330
  }
333
331
  }
334
332
  const hybridSearchMs = Date.now() - hybridSearchStart;
335
333
 
336
- // ── Step 4: Recency supplement (DB only, conversation-scoped) ───
337
- const recencyLimit = 5;
338
- const recencyCandidates = conversationId
339
- ? recencySearch(conversationId, recencyLimit, excludeMessageIds, scopeIds)
340
- : [];
341
-
342
- // ── Step 5: Merge and deduplicate ──────────────────────────────
334
+ // ── Step 4: Deduplicate ────────────────────────────────────────
343
335
  const candidateMap = new Map<string, Candidate>();
344
- for (const c of [...hybridCandidates, ...recencyCandidates]) {
336
+ for (const c of [...hybridCandidates]) {
345
337
  const existing = candidateMap.get(c.key);
346
338
  if (!existing) {
347
339
  candidateMap.set(c.key, { ...c });
@@ -356,8 +348,7 @@ export async function buildMemoryRecall(
356
348
  existing.text = c.text;
357
349
  }
358
350
  // Propagate metadata that the first source may lack (e.g. legacy
359
- // Qdrant points missing conversation_id / message_id). The recency
360
- // source always has these from the DB, so merging fills the gap.
351
+ // Qdrant points missing conversation_id / message_id).
361
352
  if (c.conversationId && !existing.conversationId) {
362
353
  existing.conversationId = c.conversationId;
363
354
  }
@@ -366,7 +357,7 @@ export async function buildMemoryRecall(
366
357
  }
367
358
  }
368
359
 
369
- // ── Step 5b: Filter out current-conversation segments still in context ──
360
+ // ── Step 4b: Filter out current-conversation segments still in context ──
370
361
  // Segments whose source message is still in the conversation's context
371
362
  // window are redundant (already visible to the model). However, segments
372
363
  // from messages that were removed by context compaction should be kept —
@@ -443,39 +434,26 @@ export async function buildMemoryRecall(
443
434
  // Compute RRF-style final scores for the merged candidates
444
435
  const allCandidates = [...candidateMap.values()];
445
436
  for (const c of allCandidates) {
446
- // Simple weighted combination hybrid search already applies RRF fusion
447
- // at the Qdrant level; here we combine the fused semantic score with recency.
448
- c.finalScore = c.semantic * 0.7 + c.recency * 0.2 + c.confidence * 0.1;
437
+ // Multiplicative scoring: importance, confidence, and recency amplify semantic
438
+ // relevance but can't substitute for it. An irrelevant item (semantic 0)
439
+ // stays low regardless of metadata. Multiplier range: 0.4 (all zero) to 1.0.
440
+ const metadataMultiplier =
441
+ 0.4 + c.importance * 0.25 + c.confidence * 0.15 + c.recency * 0.2;
442
+ c.finalScore = c.semantic * metadataMultiplier;
449
443
  }
450
444
  allCandidates.sort((a, b) => b.finalScore - a.finalScore);
451
445
 
452
- // ── Step 6: Tier classification ─────────────────────────────────
453
- // Recency-only candidates (semantic=0) can never reach the tier 2 threshold
454
- // (>0.6) since their max finalScore is 0.3. Promote them directly to tier 2
455
- // so recent conversation context is preserved even without semantic signal.
456
- const recencyOnlyKeys = new Set(
457
- allCandidates
458
- .filter((c) => c.semantic === 0 && c.recency > 0)
459
- .map((c) => c.key),
460
- );
446
+ // ── Step 5: Tier classification ─────────────────────────────────
461
447
  const tiered = classifyTiers(allCandidates);
462
- if (recencyOnlyKeys.size > 0) {
463
- const alreadyTiered = new Set(tiered.map((c) => c.key));
464
- for (const c of allCandidates) {
465
- if (recencyOnlyKeys.has(c.key) && !alreadyTiered.has(c.key)) {
466
- tiered.push({ ...c, tier: 2 });
467
- }
468
- }
469
- }
470
448
 
471
- // ── Step 6b: Enrich candidates with source labels ──────────────
449
+ // ── Step 5b: Enrich candidates with source labels ──────────────
472
450
  enrichSourceLabels(tiered);
473
451
 
474
- // ── Step 7: Enrich with item metadata for staleness ─────────────
452
+ // ── Step 6: Enrich with item metadata for staleness ─────────────
475
453
  const itemIds = tiered.filter((c) => c.type === "item").map((c) => c.id);
476
454
  const itemMetadataMap = enrichItemMetadata(itemIds);
477
455
 
478
- // ── Step 8: Compute staleness per item ──────────────────────────
456
+ // ── Step 7: Compute staleness per item ──────────────────────────
479
457
  const now = Date.now();
480
458
  for (const c of tiered) {
481
459
  if (c.type !== "item") continue;
@@ -492,10 +470,10 @@ export async function buildMemoryRecall(
492
470
  c.staleness = level;
493
471
  }
494
472
 
495
- // ── Step 9: Demote very_stale tier 1 → tier 2 ──────────────────
473
+ // ── Step 8: Demote very_stale tier 1 → tier 2 ──────────────────
496
474
  const afterDemotion = applyStaleDemotion(tiered);
497
475
 
498
- // ── Step 10: Budget allocation and two-layer injection ──────────
476
+ // ── Step 9: Budget allocation and two-layer injection ──────────
499
477
  const maxInjectTokens = Math.max(
500
478
  1,
501
479
  Math.floor(
@@ -581,7 +559,6 @@ export async function buildMemoryRecall(
581
559
  {
582
560
  query: truncate(query, 120),
583
561
  hybridHits: hybridCandidates.length,
584
- recencyHits: recencyCandidates.length,
585
562
  mergedCount: allCandidates.length,
586
563
  tier1Count,
587
564
  tier2Count,
@@ -602,7 +579,6 @@ export async function buildMemoryRecall(
602
579
  provider: embeddingResult.provider,
603
580
  model: embeddingResult.model,
604
581
  semanticHits: hybridCandidates.length,
605
- recencyHits: recencyCandidates.length,
606
582
  mergedCount: allCandidates.length,
607
583
  selectedCount,
608
584
  injectedTokens: estimateTextTokens(injectedText),
@@ -887,7 +863,6 @@ function emptyResult(
887
863
  provider: init.provider,
888
864
  model: init.model,
889
865
  semanticHits: 0,
890
- recencyHits: 0,
891
866
  mergedCount: 0,
892
867
  selectedCount: 0,
893
868
  injectedTokens: 0,
@@ -27,6 +27,7 @@ export const callSessions = sqliteTable(
27
27
  inviteGuardianName: text("invite_guardian_name"),
28
28
  callerIdentityMode: text("caller_identity_mode"),
29
29
  callerIdentitySource: text("caller_identity_source"),
30
+ skipDisclosure: integer("skip_disclosure").notNull().default(0),
30
31
  initiatedFromConversationId: text("initiated_from_conversation_id"),
31
32
  startedAt: integer("started_at"),
32
33
  endedAt: integer("ended_at"),
@@ -10,6 +10,7 @@ export const contacts = sqliteTable("contacts", {
10
10
  updatedAt: integer("updated_at").notNull(),
11
11
  role: text("role").notNull().default("contact"), // 'guardian' | 'contact'
12
12
  principalId: text("principal_id"), // internal auth principal (nullable)
13
+ userFile: text("user_file"), // workspace-relative path to per-user persona file
13
14
  contactType: text("contact_type").notNull().default("human"), // 'human' | 'assistant'
14
15
  });
15
16
 
@@ -56,6 +56,8 @@ export const memoryItems = sqliteTable(
56
56
  supersedes: text("supersedes"),
57
57
  supersededBy: text("superseded_by"),
58
58
  overrideConfidence: text("override_confidence").default("inferred"),
59
+ sourceType: text("source_type").notNull().default("extraction"),
60
+ sourceMessageRole: text("source_message_role"),
59
61
  },
60
62
  (table) => [
61
63
  index("idx_memory_items_scope_id").on(table.scopeId),
@@ -80,15 +80,6 @@ export const PREFERENCE_KINDS = new Set(["preference", "constraint"]);
80
80
  /** Kinds classified as capabilities for the <available_capabilities> section. */
81
81
  export const CAPABILITY_KINDS = new Set(["capability"]);
82
82
 
83
- /** Per-item token budget for tier 1 items. */
84
- const TIER1_PER_ITEM_TOKENS = 150;
85
-
86
- /** Per-item token budget for tier 2 items. */
87
- const TIER2_PER_ITEM_TOKENS = 100;
88
-
89
- /** Approximate chars-per-token for truncation (matches token-estimator). */
90
- const CHARS_PER_TOKEN = 4;
91
-
92
83
  /**
93
84
  * Build a two-layer XML injection block from tiered candidates.
94
85
  *
@@ -151,38 +142,21 @@ export function buildTwoLayerInjection(params: {
151
142
  : Infinity;
152
143
 
153
144
  // Render tier 1 items first (identity, relevant context, preferences)
154
- const identityLines = renderPlainStatements(
155
- identityItems,
156
- TIER1_PER_ITEM_TOKENS,
157
- remainingTokens,
158
- );
145
+ const identityLines = renderPlainStatements(identityItems, remainingTokens);
159
146
  remainingTokens -= estimateTextTokens(identityLines.join("\n"));
160
147
 
161
- const relevantEpisodes = renderEpisodes(
162
- tier1Candidates,
163
- TIER1_PER_ITEM_TOKENS,
164
- remainingTokens,
165
- );
148
+ const relevantEpisodes = renderEpisodes(tier1Candidates, remainingTokens);
166
149
  remainingTokens -= estimateTextTokens(relevantEpisodes.join("\n"));
167
150
 
168
- const preferenceLines = renderPlainStatements(
169
- preferences,
170
- TIER1_PER_ITEM_TOKENS,
171
- remainingTokens,
172
- );
151
+ const preferenceLines = renderPlainStatements(preferences, remainingTokens);
173
152
  remainingTokens -= estimateTextTokens(preferenceLines.join("\n"));
174
153
 
175
- const capabilityLines = renderPlainStatements(
176
- capabilities,
177
- TIER1_PER_ITEM_TOKENS,
178
- remainingTokens,
179
- );
154
+ const capabilityLines = renderPlainStatements(capabilities, remainingTokens);
180
155
  remainingTokens -= estimateTextTokens(capabilityLines.join("\n"));
181
156
 
182
157
  // Tier 2 uses remaining budget
183
158
  const possiblyRelevantEpisodes = renderEpisodesWithStaleness(
184
159
  tier2Candidates,
185
- TIER2_PER_ITEM_TOKENS,
186
160
  remainingTokens,
187
161
  );
188
162
 
@@ -229,15 +203,13 @@ export function buildTwoLayerInjection(params: {
229
203
  */
230
204
  function renderPlainStatements(
231
205
  items: TieredCandidate[],
232
- perItemBudgetTokens: number,
233
206
  remainingBudget: number,
234
207
  ): string[] {
235
208
  const lines: string[] = [];
236
209
  let used = 0;
237
210
  for (const item of items) {
238
211
  if (used >= remainingBudget) break;
239
- const maxChars = perItemBudgetTokens * CHARS_PER_TOKEN;
240
- const text = escapeXmlTags(truncate(item.text, maxChars));
212
+ const text = escapeXmlTags(item.text);
241
213
  const tokens = estimateTextTokens(text);
242
214
  if (used + tokens > remainingBudget) break;
243
215
  lines.push(text);
@@ -251,15 +223,13 @@ function renderPlainStatements(
251
223
  */
252
224
  function renderEpisodes(
253
225
  items: TieredCandidate[],
254
- perItemBudgetTokens: number,
255
226
  remainingBudget: number,
256
227
  ): string[] {
257
228
  const lines: string[] = [];
258
229
  let used = 0;
259
230
  for (const item of items) {
260
231
  if (used >= remainingBudget) break;
261
- const maxChars = perItemBudgetTokens * CHARS_PER_TOKEN;
262
- const text = escapeXmlTags(truncate(item.text, maxChars));
232
+ const text = escapeXmlTags(item.text);
263
233
  const sourceAttr = buildSourceAttr(item);
264
234
  const line = `<episode${sourceAttr}>\n${text}\n</episode>`;
265
235
  const tokens = estimateTextTokens(line);
@@ -275,15 +245,13 @@ function renderEpisodes(
275
245
  */
276
246
  function renderEpisodesWithStaleness(
277
247
  items: TieredCandidate[],
278
- perItemBudgetTokens: number,
279
248
  remainingBudget: number,
280
249
  ): string[] {
281
250
  const lines: string[] = [];
282
251
  let used = 0;
283
252
  for (const item of items) {
284
253
  if (used >= remainingBudget) break;
285
- const maxChars = perItemBudgetTokens * CHARS_PER_TOKEN;
286
- const text = escapeXmlTags(truncate(item.text, maxChars));
254
+ const text = escapeXmlTags(item.text);
287
255
  const sourceAttr = buildSourceAttr(item);
288
256
  const stalenessAttr =
289
257
  item.staleness && item.staleness !== "fresh"
@@ -347,8 +315,3 @@ function formatShortDate(epochMs: number): string {
347
315
  }
348
316
  return `${month} ${day} ${date.getFullYear()}`;
349
317
  }
350
-
351
- function truncate(text: string, max: number): string {
352
- if (text.length <= max) return text;
353
- return `${text.slice(0, max - 3)}...`;
354
- }
@@ -8,6 +8,10 @@ const BASE_LIFETIME_MS: Record<string, number> = {
8
8
  project: 14 * 86_400_000, // 2 weeks
9
9
  decision: 14 * 86_400_000, // 2 weeks
10
10
  event: 3 * 86_400_000, // 3 days
11
+ // Journals are experiential reflections and forward-looking notes — more
12
+ // durable than ephemeral events or decisions, but not as permanent as
13
+ // identity. 90 days mirrors "preference" lifetime.
14
+ journal: 90 * 86_400_000, // 3 months
11
15
  capability: Infinity,
12
16
  };
13
17
 
@@ -8,9 +8,17 @@ export interface TieredCandidate extends Candidate {
8
8
  sourceLabel?: string;
9
9
  }
10
10
 
11
+ /**
12
+ * Map a composite relevance score to an injection tier.
13
+ *
14
+ * Thresholds are intentionally set lower than raw-embedding ceilings because
15
+ * the multiplicative scoring pipeline (semantic × recency × metadata) compresses
16
+ * the effective score range. Lowering the gates lets moderately-relevant items
17
+ * surface rather than being silently dropped.
18
+ */
11
19
  export function classifyTier(score: number): Tier | null {
12
- if (score > 0.8) return 1;
13
- if (score > 0.6) return 2;
20
+ if (score > 0.6) return 1;
21
+ if (score > 0.4) return 2;
14
22
  return null;
15
23
  }
16
24
 
@@ -1,5 +1,5 @@
1
1
  export type CandidateType = "segment" | "item" | "summary" | "media";
2
- export type CandidateSource = "semantic" | "recency";
2
+ export type CandidateSource = "semantic";
3
3
 
4
4
  export type StalenessLevel = "fresh" | "aging" | "stale" | "very_stale";
5
5
 
@@ -39,12 +39,10 @@ export type DegradationReason =
39
39
  | "qdrant_unavailable"
40
40
  | "embedding_generation_failed";
41
41
 
42
- export type FallbackSource = "recency";
43
-
44
42
  export interface DegradationStatus {
45
43
  semanticUnavailable: boolean;
46
44
  reason: DegradationReason;
47
- fallbackSources: FallbackSource[];
45
+ fallbackSources: string[];
48
46
  }
49
47
 
50
48
  export interface MemoryRecallResult {
@@ -55,7 +53,6 @@ export interface MemoryRecallResult {
55
53
  provider?: string;
56
54
  model?: string;
57
55
  semanticHits: number;
58
- recencyHits: number;
59
56
  mergedCount: number;
60
57
  selectedCount: number;
61
58
  injectedTokens: number;
@@ -24,14 +24,14 @@ export function isConversationFailed(conversationId: string): boolean {
24
24
  }
25
25
 
26
26
  /**
27
- * Invalidate `assistant_inferred` memory items sourced *exclusively* from
27
+ * Invalidate assistant-extracted memory items sourced *exclusively* from
28
28
  * messages in the given conversation. Called when a background task or
29
29
  * schedule fails — the assistant's optimistic claims (e.g., "I booked an
30
30
  * appointment") are not trustworthy if the task didn't complete.
31
31
  *
32
32
  * The failed state is derived from durable storage (task_runs / cron_runs),
33
33
  * so any pending or future extraction jobs for this conversation are blocked
34
- * from creating new `assistant_inferred` items — even after daemon restarts.
34
+ * from creating new assistant-extracted items — even after daemon restarts.
35
35
  *
36
36
  * Items that also have sources from other conversations are left alone
37
37
  * only when those conversations come from non-failed task/schedule runs
@@ -55,7 +55,8 @@ export function invalidateAssistantInferredItemsForConversation(
55
55
  `UPDATE memory_items
56
56
  SET status = 'invalidated',
57
57
  invalid_at = ?
58
- WHERE verification_state = 'assistant_inferred'
58
+ WHERE source_type = 'extraction'
59
+ AND source_message_role = 'assistant'
59
60
  AND status = 'active'
60
61
  AND id IN (
61
62
  SELECT mis.memory_item_id