@vellumai/assistant 0.7.1 → 0.7.3

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 (739) hide show
  1. package/ARCHITECTURE.md +48 -50
  2. package/Dockerfile +1 -0
  3. package/README.md +1 -2
  4. package/__tests__/permissions/gateway-threshold-reader.test.ts +9 -3
  5. package/bun.lock +26 -26
  6. package/docs/architecture/memory.md +5 -2
  7. package/docs/architecture/security.md +20 -0
  8. package/docs/plugins.md +7 -9
  9. package/knip.json +1 -0
  10. package/node_modules/@vellumai/gateway-client/src/index.ts +1 -0
  11. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +52 -5
  12. package/node_modules/@vellumai/gateway-client/src/types.ts +11 -0
  13. package/node_modules/@vellumai/service-contracts/package.json +2 -0
  14. package/node_modules/@vellumai/service-contracts/src/__tests__/contracts.test.ts +4 -0
  15. package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +107 -0
  16. package/node_modules/@vellumai/service-contracts/src/index.ts +5 -1
  17. package/node_modules/@vellumai/service-contracts/src/ingress.ts +24 -0
  18. package/node_modules/@vellumai/service-contracts/src/twilio-ingress.ts +84 -0
  19. package/node_modules/@vellumai/slack-text/src/index.test.ts +18 -35
  20. package/node_modules/@vellumai/slack-text/src/index.ts +2 -48
  21. package/node_modules/@vellumai/twilio-client/bun.lock +24 -0
  22. package/node_modules/@vellumai/twilio-client/package.json +18 -0
  23. package/node_modules/@vellumai/twilio-client/src/__tests__/twilio-client.test.ts +128 -0
  24. package/node_modules/@vellumai/twilio-client/src/index.ts +179 -0
  25. package/node_modules/@vellumai/twilio-client/tsconfig.json +20 -0
  26. package/openapi.yaml +1020 -40
  27. package/package.json +6 -3
  28. package/src/__tests__/app-builder-tool-scripts.test.ts +3 -3
  29. package/src/__tests__/app-bundler.test.ts +170 -1
  30. package/src/__tests__/app-control-flow.test.ts +384 -0
  31. package/src/__tests__/app-control-no-global-cgevent.test.ts +98 -0
  32. package/src/__tests__/app-control-tool-schemas.test.ts +621 -0
  33. package/src/__tests__/app-executors.test.ts +30 -43
  34. package/src/__tests__/approval-routes-http.test.ts +23 -6
  35. package/src/__tests__/assistant-event-hub-machine-name.test.ts +146 -0
  36. package/src/__tests__/assistant-event-hub-targeted.test.ts +257 -0
  37. package/src/__tests__/assistant-event-hub.test.ts +157 -2
  38. package/src/__tests__/assistant-feature-flags-integration.test.ts +29 -7
  39. package/src/__tests__/auto-analysis-end-to-end.test.ts +62 -1
  40. package/src/__tests__/background-shell-host-bash.test.ts +14 -15
  41. package/src/__tests__/background-workers-disk-pressure.test.ts +268 -0
  42. package/src/__tests__/bootstrap-turn-cleanup.test.ts +44 -0
  43. package/src/__tests__/btw-routes.test.ts +13 -4
  44. package/src/__tests__/call-controller.test.ts +49 -1
  45. package/src/__tests__/call-conversation-messages.test.ts +8 -2
  46. package/src/__tests__/call-domain.test.ts +0 -2
  47. package/src/__tests__/call-routes-http.test.ts +0 -2
  48. package/src/__tests__/channel-inbound-disk-pressure.test.ts +537 -0
  49. package/src/__tests__/channel-readiness-service.test.ts +62 -2
  50. package/src/__tests__/checker.test.ts +3 -4
  51. package/src/__tests__/config-loader-backfill.test.ts +461 -147
  52. package/src/__tests__/config-loader-platform-defaults.test.ts +196 -0
  53. package/src/__tests__/config-schema-cmd.test.ts +0 -1
  54. package/src/__tests__/config-schema.test.ts +1 -0
  55. package/src/__tests__/config-set-platform-guard.test.ts +48 -4
  56. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +20 -11
  57. package/src/__tests__/config-watcher.test.ts +142 -71
  58. package/src/__tests__/context-search-agent-runner.test.ts +61 -3
  59. package/src/__tests__/context-search-conversations-source.test.ts +0 -24
  60. package/src/__tests__/context-search-fanout.test.ts +0 -1
  61. package/src/__tests__/context-search-memory-source.test.ts +3 -7
  62. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -2
  63. package/src/__tests__/context-search-pkb-source.test.ts +0 -1
  64. package/src/__tests__/context-search-workspace-source.test.ts +0 -1
  65. package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
  66. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +223 -0
  67. package/src/__tests__/conversation-agent-loop.test.ts +454 -5
  68. package/src/__tests__/conversation-app-control-instantiation.test.ts +392 -0
  69. package/src/__tests__/conversation-app-control-lifecycle.test.ts +237 -0
  70. package/src/__tests__/conversation-error.test.ts +150 -3
  71. package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
  72. package/src/__tests__/conversation-lifecycle.test.ts +36 -0
  73. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +283 -0
  74. package/src/__tests__/conversation-process-callsite.test.ts +43 -0
  75. package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
  76. package/src/__tests__/conversation-routes-disk-view.test.ts +6 -0
  77. package/src/__tests__/conversation-routes-guardian-reply.test.ts +120 -72
  78. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  79. package/src/__tests__/conversation-runtime-assembly.test.ts +65 -0
  80. package/src/__tests__/conversation-slash-commands.test.ts +0 -4
  81. package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
  82. package/src/__tests__/conversation-speed-override.test.ts +0 -3
  83. package/src/__tests__/conversation-store.test.ts +0 -18
  84. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +202 -0
  85. package/src/__tests__/conversation-surfaces-app-control.test.ts +328 -0
  86. package/src/__tests__/conversation-surfaces-data-persist.test.ts +404 -0
  87. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +2 -5
  88. package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
  89. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -0
  90. package/src/__tests__/credential-execution-feature-gates.test.ts +5 -12
  91. package/src/__tests__/credential-execution-managed-contract.test.ts +3 -131
  92. package/src/__tests__/credentials-cli.test.ts +12 -12
  93. package/src/__tests__/cu-unified-flow.test.ts +351 -23
  94. package/src/__tests__/daemon-credential-client.test.ts +101 -19
  95. package/src/__tests__/date-context.test.ts +164 -2
  96. package/src/__tests__/db-schedule-syntax-migration.test.ts +2 -0
  97. package/src/__tests__/disk-pressure-guard.test.ts +262 -0
  98. package/src/__tests__/disk-pressure-lifecycle.test.ts +168 -0
  99. package/src/__tests__/disk-pressure-policy.test.ts +241 -0
  100. package/src/__tests__/disk-pressure-routes.test.ts +379 -0
  101. package/src/__tests__/disk-pressure-tools.test.ts +277 -0
  102. package/src/__tests__/disk-usage.test.ts +150 -0
  103. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  104. package/src/__tests__/events-client-registration.test.ts +52 -0
  105. package/src/__tests__/events-dev-bypass-actor.test.ts +162 -0
  106. package/src/__tests__/file-write-tool.test.ts +4 -10
  107. package/src/__tests__/filing-service.test.ts +3 -4
  108. package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
  109. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -2
  110. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -2
  111. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -1
  112. package/src/__tests__/heartbeat-disk-pressure.test.ts +183 -0
  113. package/src/__tests__/heartbeat-service.test.ts +968 -2
  114. package/src/__tests__/helpers/call-route-handler.ts +7 -1
  115. package/src/__tests__/host-app-control-proxy.test.ts +772 -0
  116. package/src/__tests__/host-app-control-routes.test.ts +263 -0
  117. package/src/__tests__/host-bash-proxy.test.ts +439 -47
  118. package/src/__tests__/host-bash-routes.test.ts +459 -0
  119. package/src/__tests__/host-browser-proxy.test.ts +24 -22
  120. package/src/__tests__/host-browser-routes.test.ts +39 -13
  121. package/src/__tests__/host-cu-proxy.test.ts +248 -52
  122. package/src/__tests__/host-cu-routes-targeted.test.ts +429 -0
  123. package/src/__tests__/host-file-edit-tool.test.ts +47 -1
  124. package/src/__tests__/host-file-proxy-targeted.test.ts +378 -0
  125. package/src/__tests__/host-file-proxy.test.ts +301 -45
  126. package/src/__tests__/host-file-read-tool.test.ts +17 -0
  127. package/src/__tests__/host-file-routes-targeted.test.ts +420 -0
  128. package/src/__tests__/host-file-write-tool.test.ts +42 -1
  129. package/src/__tests__/host-proxy-base.test.ts +312 -0
  130. package/src/__tests__/host-shell-tool.test.ts +22 -4
  131. package/src/__tests__/host-transfer-proxy-targeted.test.ts +932 -0
  132. package/src/__tests__/host-transfer-proxy.test.ts +121 -22
  133. package/src/__tests__/host-transfer-routes-targeted.test.ts +662 -0
  134. package/src/__tests__/http-user-message-parity.test.ts +108 -1
  135. package/src/__tests__/identity-intro-cache.test.ts +29 -0
  136. package/src/__tests__/identity-routes.test.ts +103 -1
  137. package/src/__tests__/init-feature-flag-overrides.test.ts +26 -3
  138. package/src/__tests__/injector-chain.test.ts +18 -6
  139. package/src/__tests__/injector-disk-pressure.test.ts +224 -0
  140. package/src/__tests__/inline-command-runner.test.ts +0 -1
  141. package/src/__tests__/inline-skill-load-permissions.test.ts +5 -11
  142. package/src/__tests__/integration-status.test.ts +85 -5
  143. package/src/__tests__/intent-routing.test.ts +0 -1
  144. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +95 -5
  145. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +17 -0
  146. package/src/__tests__/managed-profile-guard.test.ts +18 -0
  147. package/src/__tests__/managed-skill-lifecycle.test.ts +0 -1
  148. package/src/__tests__/mcp-abort-signal.test.ts +130 -0
  149. package/src/__tests__/mcp-auth-routes.test.ts +197 -0
  150. package/src/__tests__/mcp-cli.test.ts +338 -2
  151. package/src/__tests__/memory-admin-recall.test.ts +3 -11
  152. package/src/__tests__/memory-jobs-worker-lanes.test.ts +188 -0
  153. package/src/__tests__/memory-retrieval-pipeline.test.ts +22 -1
  154. package/src/__tests__/migration-import-commit-http.test.ts +108 -2
  155. package/src/__tests__/mock-gateway-ipc.ts +1 -0
  156. package/src/__tests__/normalize-onboarding.test.ts +180 -0
  157. package/src/__tests__/oauth-cli.test.ts +0 -2
  158. package/src/__tests__/oauth-connect-routes.test.ts +316 -0
  159. package/src/__tests__/oauth-provider-seed-logos.test.ts +24 -2
  160. package/src/__tests__/oauth2-gateway-transport.test.ts +0 -1
  161. package/src/__tests__/onboarding-persona-write.test.ts +308 -0
  162. package/src/__tests__/openai-provider.test.ts +45 -8
  163. package/src/__tests__/persist-onboarding-artifacts.test.ts +44 -64
  164. package/src/__tests__/persistence-secret-redaction.test.ts +299 -0
  165. package/src/__tests__/platform-bash-auto-approve.test.ts +5 -9
  166. package/src/__tests__/platform-callback-registration.test.ts +21 -4
  167. package/src/__tests__/platform.test.ts +2 -1
  168. package/src/__tests__/playbook-execution.test.ts +0 -43
  169. package/src/__tests__/plugin-tool-contribution.test.ts +47 -0
  170. package/src/__tests__/prechat-onboarding-contract.test.ts +214 -25
  171. package/src/__tests__/process-message-background-slack.test.ts +2 -0
  172. package/src/__tests__/provider-commit-message-generator.test.ts +0 -1
  173. package/src/__tests__/provider-tool-name.test.ts +23 -0
  174. package/src/__tests__/public-ingress-urls.test.ts +97 -0
  175. package/src/__tests__/relay-server.test.ts +15 -4
  176. package/src/__tests__/require-fresh-approval.test.ts +0 -1
  177. package/src/__tests__/retry-backoff.test.ts +87 -0
  178. package/src/__tests__/runtime-events-sse.test.ts +2 -2
  179. package/src/__tests__/sanitize-config-for-transfer.test.ts +24 -2
  180. package/src/__tests__/schedule-retry.test.ts +715 -0
  181. package/src/__tests__/scheduler-disk-pressure.test.ts +148 -0
  182. package/src/__tests__/script-proxy-mitm-handler.test.ts +1 -1
  183. package/src/__tests__/secret-ingress-http.test.ts +1 -1
  184. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  185. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -1
  186. package/src/__tests__/skill-feature-flags.test.ts +43 -41
  187. package/src/__tests__/skill-load-feature-flag.test.ts +13 -14
  188. package/src/__tests__/skill-load-inline-command.test.ts +0 -51
  189. package/src/__tests__/skill-load-inline-includes.test.ts +0 -43
  190. package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
  191. package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -1
  192. package/src/__tests__/slack-channel-config.test.ts +9 -14
  193. package/src/__tests__/suggestion-routes.test.ts +46 -0
  194. package/src/__tests__/system-prompt-ask-mode.test.ts +0 -1
  195. package/src/__tests__/system-prompt.test.ts +0 -1
  196. package/src/__tests__/telegram-config.test.ts +0 -1
  197. package/src/__tests__/test-preload.ts +8 -0
  198. package/src/__tests__/tool-approval-handler.test.ts +3 -4
  199. package/src/__tests__/tool-audit-listener.test.ts +48 -0
  200. package/src/__tests__/tool-execute-pipeline.test.ts +0 -1
  201. package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -1
  202. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
  203. package/src/__tests__/tool-executor.test.ts +0 -1
  204. package/src/__tests__/twilio-config.test.ts +3 -16
  205. package/src/__tests__/twilio-routes.test.ts +3 -5
  206. package/src/__tests__/twilio-validation.test.ts +93 -0
  207. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -4
  208. package/src/__tests__/verification-control-plane-policy.test.ts +2 -4
  209. package/src/__tests__/voice-ingress-preflight.test.ts +19 -0
  210. package/src/__tests__/workspace-migration-006-services-config.test.ts +3 -2
  211. package/src/__tests__/workspace-migration-065-bump-stale-heartbeat-interval.test.ts +122 -0
  212. package/src/__tests__/workspace-migration-066-seed-heartbeat-callsite-cost-default.test.ts +285 -0
  213. package/src/__tests__/workspace-migration-068-release-notes-local-timezone.test.ts +90 -0
  214. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +1 -5
  215. package/src/__tests__/workspace-migration-down-functions.test.ts +8 -8
  216. package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +90 -0
  217. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +10 -6
  218. package/src/approvals/guardian-decision-primitive.ts +13 -0
  219. package/src/approvals/guardian-request-resolvers.ts +16 -17
  220. package/src/backup/__tests__/paths.test.ts +0 -22
  221. package/src/backup/__tests__/restore.test.ts +51 -151
  222. package/src/backup/paths.ts +2 -18
  223. package/src/backup/restore.ts +107 -231
  224. package/src/backup/snapshot-lock.ts +2 -27
  225. package/src/bundler/app-bundler.ts +51 -3
  226. package/src/bundler/compiler-tools.ts +3 -2
  227. package/src/calls/call-conversation-messages.ts +46 -10
  228. package/src/calls/relay-server.ts +4 -44
  229. package/src/calls/twilio-config.ts +2 -17
  230. package/src/calls/twilio-rest.ts +33 -105
  231. package/src/calls/twilio-routes.ts +11 -12
  232. package/src/channels/types.ts +8 -7
  233. package/src/cli/commands/__tests__/backup.test.ts +6 -277
  234. package/src/cli/commands/__tests__/gateway.test.ts +288 -0
  235. package/src/cli/commands/__tests__/memory-v2.test.ts +4 -0
  236. package/src/cli/commands/__tests__/webhooks.test.ts +0 -5
  237. package/src/cli/commands/backup.ts +6 -331
  238. package/src/cli/commands/bash.ts +35 -108
  239. package/src/cli/commands/clients.ts +36 -37
  240. package/src/cli/commands/contacts.ts +137 -25
  241. package/src/cli/commands/conversations.ts +2 -5
  242. package/src/cli/commands/credentials.ts +71 -7
  243. package/src/cli/commands/domain.ts +66 -15
  244. package/src/cli/commands/gateway.ts +183 -0
  245. package/src/cli/commands/keys.ts +9 -6
  246. package/src/cli/commands/mcp.ts +116 -156
  247. package/src/cli/commands/memory-v2.ts +303 -7
  248. package/src/cli/commands/oauth/__tests__/connect.test.ts +437 -1
  249. package/src/cli/commands/oauth/connect.ts +127 -1
  250. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -4
  251. package/src/cli/commands/platform/__tests__/connect.test.ts +7 -3
  252. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -3
  253. package/src/cli/commands/platform/__tests__/status.test.ts +116 -21
  254. package/src/cli/commands/platform/disconnect.ts +5 -4
  255. package/src/cli/commands/platform/index.ts +16 -25
  256. package/src/cli/commands/status.ts +57 -0
  257. package/src/cli/lib/daemon-credential-client.ts +110 -28
  258. package/src/cli/program.ts +6 -2
  259. package/src/config/assistant-feature-flags.ts +79 -12
  260. package/src/config/bundled-skills/acp/SKILL.md +6 -0
  261. package/src/config/bundled-skills/acp/TOOLS.json +1 -22
  262. package/src/config/bundled-skills/app-builder/SKILL.md +14 -109
  263. package/src/config/bundled-skills/app-builder/TOOLS.json +1 -28
  264. package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -10
  265. package/src/config/bundled-skills/app-control/SKILL.md +75 -0
  266. package/src/config/bundled-skills/app-control/TOOLS.json +299 -0
  267. package/src/config/bundled-skills/app-control/tools/app-control-click.ts +12 -0
  268. package/src/config/bundled-skills/app-control/tools/app-control-combo.ts +12 -0
  269. package/src/config/bundled-skills/app-control/tools/app-control-drag.ts +12 -0
  270. package/src/config/bundled-skills/app-control/tools/app-control-observe.ts +12 -0
  271. package/src/config/bundled-skills/app-control/tools/app-control-press.ts +12 -0
  272. package/src/config/bundled-skills/app-control/tools/app-control-sequence.ts +12 -0
  273. package/src/config/bundled-skills/app-control/tools/app-control-start.ts +12 -0
  274. package/src/config/bundled-skills/app-control/tools/app-control-stop.ts +12 -0
  275. package/src/config/bundled-skills/app-control/tools/app-control-type.ts +12 -0
  276. package/src/config/bundled-skills/computer-use/SKILL.md +6 -0
  277. package/src/config/bundled-skills/computer-use/TOOLS.json +67 -43
  278. package/src/config/bundled-skills/contacts/TOOLS.json +0 -16
  279. package/src/config/bundled-skills/document/TOOLS.json +0 -8
  280. package/src/config/bundled-skills/followups/TOOLS.json +0 -12
  281. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  282. package/src/config/bundled-skills/image-studio/TOOLS.json +0 -4
  283. package/src/config/bundled-skills/media-processing/TOOLS.json +0 -24
  284. package/src/config/bundled-skills/messaging/TOOLS.json +0 -40
  285. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -3
  286. package/src/config/bundled-skills/phone-calls/TOOLS.json +0 -12
  287. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +25 -4
  288. package/src/config/bundled-skills/playbooks/TOOLS.json +0 -16
  289. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -2
  290. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +2 -2
  291. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +2 -2
  292. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -2
  293. package/src/config/bundled-skills/schedule/TOOLS.json +14 -14
  294. package/src/config/bundled-skills/sequences/TOOLS.json +0 -36
  295. package/src/config/bundled-skills/settings/SKILL.md +4 -0
  296. package/src/config/bundled-skills/settings/TOOLS.json +0 -12
  297. package/src/config/bundled-skills/skill-management/SKILL.md +6 -0
  298. package/src/config/bundled-skills/skill-management/TOOLS.json +0 -8
  299. package/src/config/bundled-skills/subagent/SKILL.md +6 -2
  300. package/src/config/bundled-skills/subagent/TOOLS.json +0 -20
  301. package/src/config/bundled-skills/transcribe/SKILL.md +4 -0
  302. package/src/config/bundled-skills/transcribe/TOOLS.json +0 -4
  303. package/src/config/bundled-tool-registry.ts +21 -0
  304. package/src/config/env-registry.ts +0 -2
  305. package/src/config/env.ts +19 -20
  306. package/src/config/feature-flag-registry.json +47 -135
  307. package/src/config/loader.ts +197 -104
  308. package/src/config/sanitize-for-transfer.ts +2 -0
  309. package/src/config/schemas/__tests__/memory-lifecycle.test.ts +80 -0
  310. package/src/config/schemas/__tests__/memory-v2.test.ts +17 -9
  311. package/src/config/schemas/call-site-catalog.ts +14 -0
  312. package/src/config/schemas/calls.ts +0 -9
  313. package/src/config/schemas/channels.ts +0 -5
  314. package/src/config/schemas/heartbeat.ts +64 -1
  315. package/src/config/schemas/ingress.ts +10 -6
  316. package/src/config/schemas/llm.ts +7 -10
  317. package/src/config/schemas/memory-lifecycle.ts +90 -24
  318. package/src/config/schemas/memory-v2.ts +121 -13
  319. package/src/config/schemas/platform.ts +49 -3
  320. package/src/config/schemas/services.ts +29 -15
  321. package/src/config/schemas/skills.ts +0 -6
  322. package/src/config/seed-inference-profiles.ts +230 -33
  323. package/src/contacts/contact-store.ts +0 -55
  324. package/src/contacts/contacts-write.ts +0 -27
  325. package/src/context/window-manager.ts +1 -2
  326. package/src/credential-execution/feature-gates.ts +10 -10
  327. package/src/credential-execution/process-manager.ts +12 -41
  328. package/src/daemon/__tests__/conversation-tool-setup.test.ts +187 -5
  329. package/src/daemon/assistant-attachments.ts +4 -4
  330. package/src/daemon/bootstrap-turn-cleanup.ts +45 -0
  331. package/src/daemon/config-watcher.ts +89 -60
  332. package/src/daemon/conversation-agent-loop-handlers.ts +27 -3
  333. package/src/daemon/conversation-agent-loop.ts +202 -61
  334. package/src/daemon/conversation-error.ts +87 -15
  335. package/src/daemon/conversation-lifecycle.ts +9 -4
  336. package/src/daemon/conversation-process.ts +24 -11
  337. package/src/daemon/conversation-runtime-assembly.ts +28 -2
  338. package/src/daemon/conversation-store.ts +2 -2
  339. package/src/daemon/conversation-surfaces.ts +305 -4
  340. package/src/daemon/conversation-tool-setup.ts +66 -62
  341. package/src/daemon/conversation.ts +38 -24
  342. package/src/daemon/date-context.ts +71 -22
  343. package/src/daemon/disk-pressure-background-gate.ts +73 -0
  344. package/src/daemon/disk-pressure-guard.ts +343 -0
  345. package/src/daemon/disk-pressure-policy.ts +163 -0
  346. package/src/daemon/doordash-steps.ts +1 -1
  347. package/src/daemon/handlers/shared.ts +4 -2
  348. package/src/daemon/handlers/skills.ts +3 -4
  349. package/src/daemon/host-app-control-proxy.ts +389 -0
  350. package/src/daemon/host-bash-proxy.ts +117 -82
  351. package/src/daemon/host-browser-proxy.ts +67 -82
  352. package/src/daemon/host-cu-proxy.ts +127 -86
  353. package/src/daemon/host-file-proxy.ts +129 -69
  354. package/src/daemon/host-proxy-base.ts +294 -0
  355. package/src/daemon/host-proxy-preactivation.ts +82 -0
  356. package/src/daemon/host-transfer-proxy.ts +338 -129
  357. package/src/daemon/lifecycle.ts +194 -145
  358. package/src/daemon/meet-host-supervisor.ts +4 -4
  359. package/src/daemon/meet-manifest-loader.ts +0 -1
  360. package/src/daemon/memory-v2-startup.ts +14 -4
  361. package/src/daemon/message-protocol.ts +6 -8
  362. package/src/daemon/message-types/contacts.ts +23 -1
  363. package/src/daemon/message-types/conversations.ts +15 -8
  364. package/src/daemon/message-types/disk-pressure.ts +9 -0
  365. package/src/daemon/message-types/host-app-control.ts +150 -0
  366. package/src/daemon/message-types/host-bash.ts +4 -0
  367. package/src/daemon/message-types/host-cu.ts +2 -0
  368. package/src/daemon/message-types/host-file.ts +4 -0
  369. package/src/daemon/message-types/host-transfer.ts +3 -0
  370. package/src/daemon/message-types/messages.ts +3 -0
  371. package/src/daemon/message-types/schedules.ts +8 -3
  372. package/src/daemon/message-types/skills.ts +2 -2
  373. package/src/daemon/process-message.ts +18 -1
  374. package/src/daemon/profiler-run-store.ts +5 -5
  375. package/src/daemon/shutdown-handlers.ts +0 -3
  376. package/src/daemon/tool-setup-types.ts +51 -0
  377. package/src/daemon/tool-side-effects.ts +1 -1
  378. package/src/documents/document-store.ts +85 -0
  379. package/src/events/tool-audit-listener.ts +2 -1
  380. package/src/filing/filing-service.ts +30 -5
  381. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +24 -23
  382. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +252 -0
  383. package/src/heartbeat/heartbeat-run-store.ts +249 -0
  384. package/src/heartbeat/heartbeat-service.ts +459 -54
  385. package/src/home/__tests__/post-connect-feed.test.ts +99 -0
  386. package/src/home/__tests__/relationship-state-writer.test.ts +11 -9
  387. package/src/home/__tests__/suggested-prompts.test.ts +89 -0
  388. package/src/home/feed-scheduler.ts +18 -0
  389. package/src/home/post-connect-feed.ts +68 -0
  390. package/src/home/relationship-state-writer.ts +17 -92
  391. package/src/home/suggested-prompts.ts +46 -10
  392. package/src/inbound/platform-callback-registration.ts +8 -15
  393. package/src/inbound/public-ingress-urls.ts +32 -34
  394. package/src/ipc/__tests__/clients-list-ipc.test.ts +169 -0
  395. package/src/ipc/__tests__/route-error-envelope.test.ts +80 -0
  396. package/src/ipc/assistant-server.ts +70 -3
  397. package/src/ipc/cli-client.ts +32 -1
  398. package/src/ipc/gateway-client.ts +37 -3
  399. package/src/live-voice/live-voice-archive.ts +4 -4
  400. package/src/live-voice/live-voice-metrics.ts +10 -10
  401. package/src/live-voice/protocol.ts +5 -7
  402. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +304 -0
  403. package/src/mcp/mcp-auth-orchestrator.ts +213 -0
  404. package/src/mcp/mcp-auth-state.ts +133 -0
  405. package/src/mcp/mcp-oauth-provider.ts +19 -0
  406. package/src/media/image-service.ts +1 -7
  407. package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +21 -13
  408. package/src/memory/__tests__/jobs-store-job-classes.test.ts +24 -0
  409. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +52 -22
  410. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +0 -6
  411. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +272 -0
  412. package/src/memory/__tests__/qdrant-client-sentinel.test.ts +49 -0
  413. package/src/memory/__tests__/sparse-tokenize.test.ts +66 -0
  414. package/src/memory/admin.ts +5 -9
  415. package/src/memory/anisotropy.test.ts +247 -0
  416. package/src/memory/anisotropy.ts +443 -0
  417. package/src/memory/auto-analysis-constants.ts +17 -0
  418. package/src/memory/auto-analysis-guard.ts +5 -15
  419. package/src/memory/canonical-guardian-store.ts +7 -7
  420. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +122 -0
  421. package/src/memory/context-search/agent-protocol.ts +6 -6
  422. package/src/memory/context-search/agent-runner.ts +51 -9
  423. package/src/memory/context-search/sources/conversations.ts +2 -11
  424. package/src/memory/context-search/sources/memory-v2.ts +22 -9
  425. package/src/memory/context-search/sources/memory.ts +0 -1
  426. package/src/memory/context-search/types.ts +0 -1
  427. package/src/memory/conversation-crud.ts +5 -13
  428. package/src/memory/conversation-key-store.ts +2 -15
  429. package/src/memory/db-init.ts +6 -0
  430. package/src/memory/embedding-backend.ts +9 -21
  431. package/src/memory/embedding-runtime-manager.ts +119 -5
  432. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +81 -25
  433. package/src/memory/graph/conversation-graph-memory.ts +43 -78
  434. package/src/memory/graph/extraction.ts +1 -3
  435. package/src/memory/graph/graph-search.test.ts +10 -67
  436. package/src/memory/graph/graph-search.ts +9 -20
  437. package/src/memory/graph/retriever.test.ts +6 -0
  438. package/src/memory/graph/retriever.ts +34 -10
  439. package/src/memory/graph/tools.ts +1 -1
  440. package/src/memory/indexer.ts +54 -45
  441. package/src/memory/job-handlers/backfill.ts +2 -11
  442. package/src/memory/job-handlers/cleanup.ts +43 -0
  443. package/src/memory/job-handlers/embedding.ts +6 -8
  444. package/src/memory/job-handlers/summarization.ts +2 -7
  445. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +8 -2
  446. package/src/memory/jobs/embed-concept-page.ts +28 -2
  447. package/src/memory/jobs/embed-pkb-file.test.ts +2 -2
  448. package/src/memory/jobs-store.ts +114 -22
  449. package/src/memory/jobs-worker.ts +193 -106
  450. package/src/memory/memory-v2-activation-log-store.ts +33 -15
  451. package/src/memory/memory-v2-concept-frequency.ts +169 -0
  452. package/src/memory/migrations/237-heartbeat-runs.ts +45 -0
  453. package/src/memory/migrations/238-schedule-retry-policy.ts +20 -0
  454. package/src/memory/migrations/239-trace-events-created-at-index.ts +18 -0
  455. package/src/memory/migrations/index.ts +6 -0
  456. package/src/memory/migrations/registry.ts +8 -0
  457. package/src/memory/pkb/pkb-search.test.ts +6 -0
  458. package/src/memory/pkb/pkb-search.ts +7 -0
  459. package/src/memory/qdrant-client.ts +49 -32
  460. package/src/memory/rerank-local.ts +374 -0
  461. package/src/memory/schema/infrastructure.ts +15 -0
  462. package/src/memory/search/semantic.ts +13 -67
  463. package/src/memory/sparse-tokenize.ts +49 -0
  464. package/src/memory/trace-event-store.ts +1 -17
  465. package/src/memory/v2/__tests__/activation.test.ts +387 -344
  466. package/src/memory/v2/__tests__/consolidation-job.test.ts +40 -8
  467. package/src/memory/v2/__tests__/injection.test.ts +181 -169
  468. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +61 -2
  469. package/src/memory/v2/__tests__/qdrant.test.ts +16 -0
  470. package/src/memory/v2/__tests__/reranker.test.ts +338 -0
  471. package/src/memory/v2/__tests__/sim.test.ts +154 -188
  472. package/src/memory/v2/__tests__/skill-store.test.ts +71 -65
  473. package/src/memory/v2/__tests__/sparse-bm25.test.ts +292 -0
  474. package/src/memory/v2/__tests__/static-context.test.ts +76 -2
  475. package/src/memory/v2/activation.ts +213 -239
  476. package/src/memory/v2/consolidation-job.ts +65 -17
  477. package/src/memory/v2/constants.ts +7 -0
  478. package/src/memory/v2/injection.ts +123 -103
  479. package/src/memory/v2/prompts/consolidation.ts +348 -92
  480. package/src/memory/v2/qdrant.ts +198 -1
  481. package/src/memory/v2/reranker.ts +177 -0
  482. package/src/memory/v2/sim.ts +113 -77
  483. package/src/memory/v2/skill-content.ts +4 -3
  484. package/src/memory/v2/skill-store.ts +91 -53
  485. package/src/memory/v2/sparse-bm25.ts +245 -0
  486. package/src/memory/v2/static-context.ts +28 -5
  487. package/src/memory/v2/types.ts +10 -10
  488. package/src/messaging/providers/gmail/types.ts +0 -49
  489. package/src/messaging/providers/slack/adapter.ts +1 -31
  490. package/src/messaging/providers/slack/types.ts +0 -32
  491. package/src/notifications/README.md +10 -10
  492. package/src/notifications/broadcaster.ts +1 -1
  493. package/src/notifications/copy-composer.ts +13 -0
  494. package/src/notifications/guardian-question-mode.ts +5 -5
  495. package/src/notifications/signal.ts +4 -0
  496. package/src/oauth/AGENTS.md +3 -1
  497. package/src/oauth/__tests__/oauth-connect-state.test.ts +137 -0
  498. package/src/oauth/connect-orchestrator.ts +6 -0
  499. package/src/oauth/connection-resolver.test.ts +66 -1
  500. package/src/oauth/connection-resolver.ts +55 -1
  501. package/src/oauth/credential-token-resolver.ts +1 -3
  502. package/src/oauth/manual-token-connection.ts +0 -4
  503. package/src/oauth/oauth-connect-state.ts +77 -0
  504. package/src/oauth/seed-providers.ts +58 -1
  505. package/src/outbound-proxy/index.ts +1 -37
  506. package/src/outbound-proxy/logging.ts +1 -1
  507. package/src/outbound-proxy/policy.ts +6 -5
  508. package/src/outbound-proxy/router.ts +2 -1
  509. package/src/permissions/approval-policy.test.ts +6 -275
  510. package/src/permissions/approval-policy.ts +0 -51
  511. package/src/permissions/checker.test.ts +0 -1
  512. package/src/permissions/checker.ts +3 -17
  513. package/src/permissions/gateway-threshold-reader.ts +2 -0
  514. package/src/permissions/prompter.ts +34 -1
  515. package/src/permissions/secret-prompter.ts +6 -2
  516. package/src/plugins/defaults/injectors.ts +35 -2
  517. package/src/plugins/defaults/memory-retrieval.ts +5 -6
  518. package/src/plugins/types.ts +7 -0
  519. package/src/proactive-artifact/aux-message-injector.ts +74 -0
  520. package/src/proactive-artifact/decision.test.ts +226 -0
  521. package/src/proactive-artifact/decision.ts +165 -0
  522. package/src/proactive-artifact/index.ts +7 -0
  523. package/src/proactive-artifact/job.test.ts +867 -0
  524. package/src/proactive-artifact/job.ts +352 -0
  525. package/src/proactive-artifact/message-copy.ts +41 -0
  526. package/src/proactive-artifact/trigger-state.test.ts +277 -0
  527. package/src/proactive-artifact/trigger-state.ts +119 -0
  528. package/src/prompts/bootstrap-cleanup.ts +27 -0
  529. package/src/prompts/normalize-onboarding.ts +80 -0
  530. package/src/prompts/persona-resolver.ts +101 -9
  531. package/src/prompts/system-prompt.ts +23 -24
  532. package/src/prompts/templates/BOOTSTRAP.md +13 -5
  533. package/src/prompts/templates/SOUL.md +13 -1
  534. package/src/providers/__tests__/retry-callsite.test.ts +222 -1
  535. package/src/providers/model-intents.ts +7 -0
  536. package/src/providers/openrouter/client.ts +8 -0
  537. package/src/providers/retry.ts +50 -0
  538. package/src/providers/speech-to-text/provider-catalog.ts +7 -8
  539. package/src/providers/types.ts +1 -0
  540. package/src/runtime/__tests__/agent-wake.test.ts +456 -3
  541. package/src/runtime/agent-wake.ts +238 -100
  542. package/src/runtime/assistant-event-hub.ts +151 -99
  543. package/src/runtime/auth/__tests__/middleware.test.ts +11 -56
  544. package/src/runtime/auth/__tests__/route-policy.test.ts +64 -0
  545. package/src/runtime/auth/middleware.ts +0 -96
  546. package/src/runtime/auth/route-policy.ts +32 -0
  547. package/src/runtime/auth/same-actor.ts +216 -0
  548. package/src/runtime/btw-sidechain.ts +2 -3
  549. package/src/runtime/channel-invite-transport.ts +2 -48
  550. package/src/runtime/channel-invite-transports/email.ts +1 -1
  551. package/src/runtime/channel-invite-transports/slack.ts +1 -1
  552. package/src/runtime/channel-invite-transports/telegram.ts +1 -1
  553. package/src/runtime/channel-invite-transports/voice.ts +1 -1
  554. package/src/runtime/channel-invite-transports/whatsapp.ts +1 -1
  555. package/src/runtime/channel-invite-types.ts +54 -0
  556. package/src/runtime/channel-readiness-service.ts +32 -13
  557. package/src/runtime/channel-retry-sweep.ts +65 -1
  558. package/src/runtime/guardian-reply-router.ts +10 -0
  559. package/src/runtime/http-server.ts +3 -329
  560. package/src/runtime/http-types.ts +0 -5
  561. package/src/runtime/local-actor-identity.ts +52 -11
  562. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +413 -0
  563. package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +260 -0
  564. package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +189 -0
  565. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +153 -1
  566. package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +451 -0
  567. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  568. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +515 -0
  569. package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +437 -0
  570. package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +319 -0
  571. package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +51 -1
  572. package/src/runtime/migrations/migration-transport.ts +7 -7
  573. package/src/runtime/migrations/vbundle-builder.ts +327 -60
  574. package/src/runtime/migrations/vbundle-import-analyzer.ts +4 -4
  575. package/src/runtime/migrations/vbundle-import-policy.ts +172 -0
  576. package/src/runtime/migrations/vbundle-importer.ts +245 -68
  577. package/src/runtime/migrations/vbundle-streaming-importer.ts +326 -35
  578. package/src/runtime/migrations/vbundle-streaming-validator.ts +157 -4
  579. package/src/runtime/migrations/vbundle-tar-stream.ts +15 -6
  580. package/src/runtime/migrations/vbundle-validator.ts +114 -0
  581. package/src/runtime/pending-interactions.ts +43 -9
  582. package/src/runtime/routes/__tests__/backup-routes.test.ts +22 -150
  583. package/src/runtime/routes/__tests__/client-routes.test.ts +155 -0
  584. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -5
  585. package/src/runtime/routes/__tests__/gateway-log-routes.test.ts +242 -0
  586. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +112 -0
  587. package/src/runtime/routes/approval-interception-types.ts +13 -0
  588. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +1 -1
  589. package/src/runtime/routes/backup-routes.ts +15 -38
  590. package/src/runtime/routes/btw-routes.ts +14 -37
  591. package/src/runtime/routes/client-routes.ts +21 -2
  592. package/src/runtime/routes/contact-prompt-routes.ts +183 -0
  593. package/src/runtime/routes/contact-routes.ts +0 -25
  594. package/src/runtime/routes/conversation-query-routes.ts +36 -1
  595. package/src/runtime/routes/conversation-routes.ts +65 -39
  596. package/src/runtime/routes/debug-bash-routes.ts +163 -0
  597. package/src/runtime/routes/disk-pressure-routes.ts +121 -0
  598. package/src/runtime/routes/document-pdf-renderer.ts +169 -0
  599. package/src/runtime/routes/documents-routes.ts +32 -75
  600. package/src/runtime/routes/errors.ts +19 -4
  601. package/src/runtime/routes/events-routes.ts +38 -0
  602. package/src/runtime/routes/gateway-log-routes.ts +79 -0
  603. package/src/runtime/routes/guardian-approval-interception.ts +2 -8
  604. package/src/runtime/routes/heartbeat-routes.ts +103 -38
  605. package/src/runtime/routes/host-app-control-routes.ts +134 -0
  606. package/src/runtime/routes/host-bash-routes.ts +56 -6
  607. package/src/runtime/routes/host-browser-routes.ts +108 -13
  608. package/src/runtime/routes/host-cu-routes.ts +66 -9
  609. package/src/runtime/routes/host-file-routes.ts +54 -5
  610. package/src/runtime/routes/host-transfer-routes.ts +122 -19
  611. package/src/runtime/routes/http-adapter.ts +1 -0
  612. package/src/runtime/routes/identity-intro-cache.ts +30 -0
  613. package/src/runtime/routes/identity-routes.ts +21 -180
  614. package/src/runtime/routes/inbound-message-handler.ts +78 -21
  615. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -7
  616. package/src/runtime/routes/inbound-stages/edit-intercept.ts +0 -8
  617. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +3 -0
  618. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +0 -20
  619. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +5 -13
  620. package/src/runtime/routes/index.ts +14 -0
  621. package/src/runtime/routes/mcp-auth-routes.ts +132 -0
  622. package/src/runtime/routes/memory-item-routes.test.ts +41 -15
  623. package/src/runtime/routes/memory-item-routes.ts +10 -12
  624. package/src/runtime/routes/memory-v2-routes.ts +474 -1
  625. package/src/runtime/routes/migration-routes.ts +96 -0
  626. package/src/runtime/routes/oauth-connect-routes.ts +153 -0
  627. package/src/runtime/routes/schedule-routes.ts +7 -0
  628. package/src/runtime/verification-outbound-actions.ts +4 -4
  629. package/src/runtime/verification-templates.ts +4 -7
  630. package/src/schedule/integration-status.ts +66 -2
  631. package/src/schedule/recurrence-engine.ts +4 -1
  632. package/src/schedule/retry-backoff.ts +18 -0
  633. package/src/schedule/retry-policy.ts +82 -0
  634. package/src/schedule/run-script.ts +37 -5
  635. package/src/schedule/schedule-recovery.ts +64 -0
  636. package/src/schedule/schedule-store.ts +106 -2
  637. package/src/schedule/scheduler-types.ts +25 -0
  638. package/src/schedule/scheduler.ts +83 -39
  639. package/src/security/encrypted-store.ts +2 -0
  640. package/src/security/oauth-callback-registry.ts +8 -0
  641. package/src/security/secure-keys.ts +55 -0
  642. package/src/sequence/analytics.ts +5 -5
  643. package/src/sequence/engine.ts +1 -1
  644. package/src/skills/catalog-files.ts +2 -8
  645. package/src/skills/include-graph.ts +5 -5
  646. package/src/skills/remote-skill-policy.ts +10 -16
  647. package/src/skills/skill-file-provider.ts +1 -1
  648. package/src/skills/skill-file-types.ts +13 -0
  649. package/src/skills/skillssh-audit-types.ts +28 -0
  650. package/src/skills/skillssh-registry.ts +8 -21
  651. package/src/subagent/index.ts +1 -7
  652. package/src/subagent/manager.ts +1 -15
  653. package/src/tasks/task-runner.ts +0 -1
  654. package/src/tasks/task-store.ts +0 -3
  655. package/src/telemetry/types.ts +2 -0
  656. package/src/telemetry/usage-telemetry-reporter.test.ts +21 -0
  657. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  658. package/src/tools/app-control/skill-proxy-bridge.ts +28 -0
  659. package/src/tools/apps/executors.ts +56 -69
  660. package/src/tools/background-tool-registry.ts +17 -3
  661. package/src/tools/browser/__tests__/browser-status.test.ts +21 -18
  662. package/src/tools/browser/browser-execution.ts +2 -2
  663. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +55 -4
  664. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +12 -6
  665. package/src/tools/browser/cdp-client/factory.ts +23 -24
  666. package/src/tools/browser/cdp-client/index.ts +1 -14
  667. package/src/tools/computer-use/definitions.ts +42 -20
  668. package/src/tools/executor.ts +2 -0
  669. package/src/tools/host-filesystem/edit.test.ts +151 -0
  670. package/src/tools/host-filesystem/edit.ts +68 -0
  671. package/src/tools/host-filesystem/read.test.ts +129 -0
  672. package/src/tools/host-filesystem/read.ts +68 -0
  673. package/src/tools/host-filesystem/transfer.test.ts +127 -2
  674. package/src/tools/host-filesystem/transfer.ts +78 -3
  675. package/src/tools/host-filesystem/write.test.ts +134 -0
  676. package/src/tools/host-filesystem/write.ts +68 -0
  677. package/src/tools/host-terminal/host-shell.ts +66 -1
  678. package/src/tools/mcp/mcp-tool-factory.ts +2 -1
  679. package/src/tools/memory/register.test.ts +12 -9
  680. package/src/tools/memory/register.ts +1 -2
  681. package/src/tools/provider-tool-name.ts +28 -0
  682. package/src/tools/registry.ts +30 -9
  683. package/src/tools/schedule/create.ts +6 -0
  684. package/src/tools/schedule/list.ts +2 -0
  685. package/src/tools/schedule/update.ts +10 -0
  686. package/src/tools/shared/filesystem/file-ops-service.ts +2 -0
  687. package/src/tools/shared/filesystem/path-policy.ts +25 -1
  688. package/src/tools/skills/load.ts +0 -32
  689. package/src/tools/terminal/shell.ts +9 -1
  690. package/src/tools/tool-approval-handler.ts +32 -11
  691. package/src/tools/types.ts +28 -2
  692. package/src/tts/provider-catalog.ts +3 -5
  693. package/src/usage/pricing.ts +1 -1
  694. package/src/util/disk-usage.ts +138 -0
  695. package/src/util/platform.ts +21 -11
  696. package/src/util/process-liveness.ts +26 -0
  697. package/src/workspace/hatched-date.ts +86 -0
  698. package/src/workspace/heartbeat-service.ts +19 -0
  699. package/src/workspace/migrations/003-seed-device-id.ts +1 -1
  700. package/src/workspace/migrations/006-services-config.ts +8 -5
  701. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +3 -9
  702. package/src/workspace/migrations/021-move-signals-to-workspace.ts +4 -10
  703. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +4 -10
  704. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +4 -11
  705. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +3 -10
  706. package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +3 -2
  707. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +2 -1
  708. package/src/workspace/migrations/059-move-pid-to-workspace.ts +3 -8
  709. package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +3 -8
  710. package/src/workspace/migrations/065-bump-stale-heartbeat-interval.ts +60 -0
  711. package/src/workspace/migrations/066-seed-heartbeat-callsite-cost-default.ts +146 -0
  712. package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +72 -0
  713. package/src/workspace/migrations/068-release-notes-local-timezone.ts +65 -0
  714. package/src/workspace/migrations/AGENTS.md +1 -1
  715. package/src/workspace/migrations/migrate-to-workspace-volume.ts +4 -10
  716. package/src/workspace/migrations/registry.ts +8 -0
  717. package/src/workspace/migrations/utils.ts +21 -0
  718. package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +0 -167
  719. package/src/__tests__/host-browser-e2e-cloud.test.ts +0 -443
  720. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +0 -226
  721. package/src/__tests__/host-browser-ws-events-e2e.test.ts +0 -427
  722. package/src/__tests__/twilio-rest.test.ts +0 -34
  723. package/src/backup/__tests__/backup-key.test.ts +0 -152
  724. package/src/backup/__tests__/backup-worker.test.ts +0 -782
  725. package/src/backup/__tests__/offsite-writer.test.ts +0 -641
  726. package/src/backup/__tests__/stream-crypt.test.ts +0 -228
  727. package/src/backup/backup-key.ts +0 -137
  728. package/src/backup/backup-worker.ts +0 -472
  729. package/src/backup/offsite-writer.ts +0 -222
  730. package/src/backup/stream-crypt.ts +0 -263
  731. package/src/daemon/message-types/pairing.ts +0 -58
  732. package/src/memory/v2/__tests__/skill-qdrant.test.ts +0 -657
  733. package/src/memory/v2/skill-qdrant.ts +0 -395
  734. package/src/outbound-proxy/config.ts +0 -20
  735. package/src/outbound-proxy/health.ts +0 -18
  736. package/src/outbound-proxy/types.ts +0 -150
  737. package/src/runtime/capability-tokens.ts +0 -190
  738. package/src/signals/bash.ts +0 -198
  739. package/src/signals/mcp-reload.ts +0 -18
@@ -372,26 +372,58 @@ describe("memoryV2ConsolidateJob — concurrent invocations", () => {
372
372
  writeFileSync(bufferPath(), "- [Apr 27, 9:00 AM] Alice prefers VS Code.\n");
373
373
  });
374
374
 
375
- test("a stale lock file blocks a second concurrent invocation", async () => {
376
- // Pre-seed a lock file as if a prior run was still in flight. The
377
- // simple wx-based lock has no liveness probe, so this also covers
378
- // stale-lock-on-disk behavior operators clear stale locks manually.
375
+ test("a live lock holder blocks a second concurrent invocation", async () => {
376
+ // Pre-seed a lock file with the current process's PID so the liveness
377
+ // probe sees a running holder and the second invocation correctly
378
+ // reports `locked` rather than taking over.
379
379
  mkdirSync(join(memoryDir(), ".v2-state"), { recursive: true });
380
- writeFileSync(lockPath(), "9999 1700000000000\n");
380
+ writeFileSync(lockPath(), `${process.pid} 1700000000000\n`);
381
381
 
382
382
  const result = await memoryV2ConsolidateJob(makeJob(), CONFIG);
383
383
 
384
384
  expect(result.kind).toBe("locked");
385
385
  if (result.kind === "locked") {
386
- expect(result.holder).toContain("9999");
386
+ expect(result.holder).toContain(`${process.pid}`);
387
387
  }
388
388
  expect(bootstrapCalls).toBe(0);
389
389
  expect(wakeCalls).toBe(0);
390
390
  expect(enqueuedJobs).toHaveLength(0);
391
- // The pre-seeded lock must NOT be removed by a contender — only the
392
- // owner releases it.
391
+ // The live holder's lock must NOT be removed by a contender.
393
392
  expect(existsSync(lockPath())).toBe(true);
394
393
  });
394
+
395
+ test("a stale lock from a non-running PID is taken over and consolidation proceeds", async () => {
396
+ // PID 999999 is well outside the typical kernel max_pid range on macOS
397
+ // and Linux, so kill(pid, 0) reliably returns ESRCH. The takeover path
398
+ // must unlink the stale file, retry the wx create, and bootstrap the
399
+ // background conversation as if the lock had been free all along.
400
+ mkdirSync(join(memoryDir(), ".v2-state"), { recursive: true });
401
+ writeFileSync(lockPath(), "999999 1700000000000\n");
402
+
403
+ const result = await memoryV2ConsolidateJob(makeJob(), CONFIG);
404
+
405
+ expect(result.kind).toBe("invoked");
406
+ expect(bootstrapCalls).toBe(1);
407
+ expect(wakeCalls).toBe(1);
408
+ // Lock is released in the finally block after a successful run.
409
+ expect(existsSync(lockPath())).toBe(false);
410
+ });
411
+
412
+ test("an empty / corrupted lock file is treated as stale and taken over", async () => {
413
+ // A zero-byte file simulates a prior holder that crashed between the
414
+ // O_EXCL create and the PID write. With only one writer ever, an
415
+ // unparseable payload is unambiguously corruption, not a live
416
+ // mid-write — take it over.
417
+ mkdirSync(join(memoryDir(), ".v2-state"), { recursive: true });
418
+ writeFileSync(lockPath(), "");
419
+
420
+ const result = await memoryV2ConsolidateJob(makeJob(), CONFIG);
421
+
422
+ expect(result.kind).toBe("invoked");
423
+ expect(bootstrapCalls).toBe(1);
424
+ expect(wakeCalls).toBe(1);
425
+ expect(existsSync(lockPath())).toBe(false);
426
+ });
395
427
  });
396
428
 
397
429
  describe("CONSOLIDATION_PROMPT", () => {
@@ -8,18 +8,18 @@
8
8
  * - A new topic appearing on a later turn injects only the new slug.
9
9
  * - `evictCompactedTurns` re-enables a previously-injected slug —
10
10
  * after eviction the same slug appears again in `toInject`.
11
- * - Skill pipeline: skill-only block, mixed concept-page+skill block,
12
- * both-empty null, no skill dedup across turns, `top_k_skills: 0`
13
- * short-circuit.
11
+ * - Unified-pool skills: a `skills/<id>` slug ranked into the top-K is
12
+ * rendered under `### Skills You Can Use`, mixed concept-page+skill
13
+ * blocks render concept sections first then the skills suffix, both
14
+ * empty → null block, skills participate in `everInjected` so they
15
+ * deduplicate across turns just like concepts.
14
16
  *
15
17
  * Hermetic by design: the embedding backend, qdrant client, and `getConfig`
16
18
  * are mocked at the module level so the suite never reaches a real backend.
17
- * The skill activation pipeline (`selectSkillCandidates`,
18
- * `computeSkillActivation`, `selectSkillInjections`) and the skill-store
19
- * lookup (`getSkillCapability`) are also mocked at the module level so each
20
- * test can stage its skill slate without touching the dedicated skills
21
- * Qdrant collection. The activation-store uses an in-memory SQLite database
22
- * so writes are real but contained.
19
+ * The skill-store cache (`getSkillCapability`, `isSkillSlug`) is mocked so
20
+ * each test can stage skill content without touching the real catalog.
21
+ * The activation-store uses an in-memory SQLite database so writes are
22
+ * real but contained.
23
23
  *
24
24
  * Tests use a temp workspace (mkdtemp) and never touch `~/.vellum/`. Sample
25
25
  * page content uses generic placeholders (Alice, Bob, etc.) per the cross-
@@ -124,43 +124,32 @@ mock.module("@qdrant/js-client-rest", () => ({
124
124
  }));
125
125
 
126
126
  // ---------------------------------------------------------------------------
127
- // Skill pipeline mocks
127
+ // Skill-store mock
128
128
  // ---------------------------------------------------------------------------
129
129
  //
130
- // The skill side of the per-turn pipeline (`selectSkillCandidates`,
131
- // `computeSkillActivation`, `selectSkillInjections`) has its own dedicated
132
- // Qdrant collection and embedding round-trips. Rather than threading staged
133
- // hits through that whole pipeline for every test, we mock the three skill
134
- // helpers and the synchronous `getSkillCapability` lookup at the module level
135
- // and let each test stage a `topNow` ordering and the matching `SkillEntry`
136
- // content directly.
130
+ // Skills now flow through the unified pipeline under the `skills/<id>` slug
131
+ // prefix — they are scored by `simBatch` against the same Qdrant collection
132
+ // as concept pages, ranked by `selectInjections`, and rendered alongside
133
+ // concept sections. The render path branches on `isSkillSlug(slug)` to fetch
134
+ // content from the in-process cache via `getSkillCapability` instead of
135
+ // reading a page from disk. Tests stage that cache and rely on the regular
136
+ // `stageTurn` plumbing to land skill slugs in the candidate set.
137
137
 
138
138
  const skillState = {
139
- /** Ordered ids `selectSkillInjections.topNow` returns this turn. */
140
- topSkillIds: [] as string[],
141
- /** id → SkillEntry used by `getSkillCapability`. */
139
+ /** id SkillEntry consulted by `getSkillCapability`. */
142
140
  entries: new Map<string, SkillEntry>(),
143
141
  };
144
142
 
145
- const realActivation = await import("../activation.js");
146
- mock.module("../activation.js", () => ({
147
- ...realActivation,
148
- // The injection wiring only consumes `topNow` — the candidate set and
149
- // activation map are inputs to `selectSkillInjections`, not anything the
150
- // injection logic introspects. Stub them to empty so the test stays focused
151
- // on the wiring, not the pipeline internals (covered in activation.test.ts).
152
- selectSkillCandidates: async () => new Set<string>(),
153
- computeSkillActivation: async () => ({
154
- activation: new Map<string, number>(),
155
- breakdown: new Map(),
156
- }),
157
- selectSkillInjections: ({ topK }: { topK: number }) => ({
158
- topNow: skillState.topSkillIds.slice(0, topK),
159
- }),
160
- }));
161
-
162
143
  mock.module("../skill-store.js", () => ({
163
- getSkillCapability: (id: string) => skillState.entries.get(id) ?? null,
144
+ getSkillCapability: (idOrSlug: string) => {
145
+ const id = idOrSlug.startsWith("skills/")
146
+ ? idOrSlug.slice("skills/".length)
147
+ : idOrSlug;
148
+ return skillState.entries.get(id) ?? null;
149
+ },
150
+ isSkillSlug: (slug: string) => slug.startsWith("skills/"),
151
+ SKILL_SLUG_PREFIX: "skills/",
152
+ skillSlugFor: (id: string) => `skills/${id}`,
164
153
  }));
165
154
 
166
155
  // ---------------------------------------------------------------------------
@@ -292,7 +281,6 @@ function makeConfig(
292
281
  k: number;
293
282
  hops: number;
294
283
  top_k: number;
295
- top_k_skills: number;
296
284
  epsilon: number;
297
285
  dense_weight: number;
298
286
  sparse_weight: number;
@@ -307,8 +295,7 @@ function makeConfig(
307
295
  c_now: 0.2,
308
296
  k: 0.5,
309
297
  hops: 2,
310
- top_k: 20,
311
- top_k_skills: 5,
298
+ top_k: 25,
312
299
  epsilon: 0.01,
313
300
  dense_weight: 1.0,
314
301
  sparse_weight: 0.0,
@@ -331,6 +318,13 @@ function stageTurn(
331
318
  hits: Array<{ slug: string; denseScore?: number; sparseScore?: number }>,
332
319
  channels = 4,
333
320
  ): void {
321
+ // Clear any leftovers from a prior turn before staging this one so unused
322
+ // staged responses can't bleed into the next injection. The activation
323
+ // pipeline now skips the embedding round-trip for empty texts (turn 1's
324
+ // assistantMessage), so consumed-channel counts vary per turn — staging
325
+ // exclusively is the only way multi-turn tests stay aligned.
326
+ state.queryResponses.dense.length = 0;
327
+ state.queryResponses.sparse.length = 0;
334
328
  for (let i = 0; i < channels; i++) {
335
329
  state.queryResponses.dense.push({
336
330
  points: hits
@@ -350,7 +344,6 @@ function resetState(): void {
350
344
  state.sparseReturn = { indices: [1, 2, 3], values: [0.5, 0.5, 0.5] };
351
345
  state.queryResponses.dense.length = 0;
352
346
  state.queryResponses.sparse.length = 0;
353
- skillState.topSkillIds.length = 0;
354
347
  skillState.entries.clear();
355
348
  telemetryState.recordCalls.length = 0;
356
349
  telemetryState.recordShouldThrow = false;
@@ -360,10 +353,8 @@ function resetState(): void {
360
353
  _resetMemoryV2QdrantForTests();
361
354
  }
362
355
 
363
- /** Stage the next turn's skill slate and the entries the renderer will look up. */
364
- function stageSkills(ids: string[], entries: SkillEntry[] = []): void {
365
- skillState.topSkillIds.length = 0;
366
- skillState.topSkillIds.push(...ids);
356
+ /** Stage skill-store cache entries for the upcoming render. */
357
+ function stageSkills(entries: SkillEntry[]): void {
367
358
  for (const entry of entries) {
368
359
  skillState.entries.set(entry.id, entry);
369
360
  }
@@ -399,11 +390,13 @@ describe("injectMemoryV2Block", () => {
399
390
 
400
391
  expect(result.toInject).toEqual(["alice-vscode"]);
401
392
  expect(result.block).not.toBeNull();
402
- expect(result.block).toContain("<memory>");
393
+ // `block` is the unwrapped inner content; the caller adds the
394
+ // `<memory>...</memory>` wrapper exactly once at injection time.
395
+ expect(result.block).not.toContain("<memory>");
396
+ expect(result.block).not.toContain("</memory>");
403
397
  expect(result.block).not.toContain("## What I Remember Right Now");
404
398
  expect(result.block).toContain("### alice-vscode");
405
399
  expect(result.block).toContain("VS Code");
406
- expect(result.block).toContain("</memory>");
407
400
 
408
401
  // State persisted: alice's activation is above epsilon and recorded;
409
402
  // everInjected captured the new slug + currentTurn.
@@ -652,27 +645,36 @@ describe("injectMemoryV2Block", () => {
652
645
  expect(persisted!.everInjected).toEqual([
653
646
  { slug: "phantom-slug", turn: 1 },
654
647
  ]);
648
+
649
+ // Activation log marks the slug `page_missing` (not `injected`) so a
650
+ // stale Qdrant / edge-index entry pointing at a vanished page is
651
+ // visible in telemetry instead of masquerading as a successful inject.
652
+ expect(telemetryState.recordCalls.length).toBe(1);
653
+ const row = telemetryState.recordCalls[0] as {
654
+ concepts: Array<{ slug: string; status: string }>;
655
+ };
656
+ const phantom = row.concepts.find((c) => c.slug === "phantom-slug");
657
+ expect(phantom).toBeDefined();
658
+ expect(phantom!.status).toBe("page_missing");
655
659
  });
656
660
 
657
661
  // ---------------------------------------------------------------------------
658
- // Skill subsection rendering
662
+ // Unified pool — skills as `skills/<id>` slugs
659
663
  // ---------------------------------------------------------------------------
660
664
 
661
- test("renders a skill-only block in the same `<memory>` wrapper as concept-page-only blocks", async () => {
662
- // No concept-page candidates this turn — the candidate query and the three
663
- // simBatch queries all return empty. The skill pipeline is mocked to
664
- // surface a single skill.
665
- stageTurn([]);
666
- stageSkills(
667
- ["example-skill-a"],
668
- [
669
- {
670
- id: "example-skill-a",
671
- content:
672
- 'The "Example Skill A" skill (example-skill-a) is available. Helps with examples.',
673
- },
674
- ],
675
- );
665
+ test("renders a skill-only block via the skills/ slug prefix", async () => {
666
+ // No concept-page candidates this turn — the only ANN hit is a skill
667
+ // slug. The render path branches on `skills/` prefix: it pulls the
668
+ // entry from the skill-store cache (mocked) and emits the bullet under
669
+ // the `### Skills You Can Use` subsection.
670
+ stageTurn([{ slug: "skills/example-skill-a", denseScore: 0.9 }]);
671
+ stageSkills([
672
+ {
673
+ id: "example-skill-a",
674
+ content:
675
+ 'The "Example Skill A" skill (example-skill-a) is available. Helps with examples.',
676
+ },
677
+ ]);
676
678
 
677
679
  const result = await injectMemoryV2Block({
678
680
  database: db,
@@ -685,14 +687,11 @@ describe("injectMemoryV2Block", () => {
685
687
  config: makeConfig(),
686
688
  });
687
689
 
688
- expect(result.toInject).toEqual([]);
690
+ expect(result.toInject).toEqual(["skills/example-skill-a"]);
689
691
  expect(result.block).not.toBeNull();
690
- // Same outer wrapping as concept-page-only blocks.
691
- expect(result.block).toContain("<memory>");
692
+ expect(result.block).not.toContain("<memory>");
693
+ expect(result.block).not.toContain("</memory>");
692
694
  expect(result.block).not.toContain("## What I Remember Right Now");
693
- expect(result.block).toContain("</memory>");
694
- // No concept-page sections; skills subsection present with the right
695
- // bullet shape and the unconditional `→ use skill_load to activate` suffix.
696
695
  expect(result.block).not.toContain("### alice-vscode");
697
696
  expect(result.block).toContain("### Skills You Can Use");
698
697
  expect(result.block).toContain(
@@ -701,19 +700,19 @@ describe("injectMemoryV2Block", () => {
701
700
  });
702
701
 
703
702
  test("renders concept-page sections before the skills subsection in mixed blocks", async () => {
704
- // Concept page hits AND a skill — concept-page sections come first, then
703
+ // Concept page hit AND a skill — concept-page sections come first, then
705
704
  // the skills subsection.
706
- stageTurn([{ slug: "alice-vscode", denseScore: 0.9 }]);
707
- stageSkills(
708
- ["example-skill-a"],
709
- [
710
- {
711
- id: "example-skill-a",
712
- content:
713
- 'The "Example Skill A" skill (example-skill-a) is available. Helps with examples.',
714
- },
715
- ],
716
- );
705
+ stageTurn([
706
+ { slug: "alice-vscode", denseScore: 0.9 },
707
+ { slug: "skills/example-skill-a", denseScore: 0.7 },
708
+ ]);
709
+ stageSkills([
710
+ {
711
+ id: "example-skill-a",
712
+ content:
713
+ 'The "Example Skill A" skill (example-skill-a) is available. Helps with examples.',
714
+ },
715
+ ]);
717
716
 
718
717
  const result = await injectMemoryV2Block({
719
718
  database: db,
@@ -726,7 +725,10 @@ describe("injectMemoryV2Block", () => {
726
725
  config: makeConfig(),
727
726
  });
728
727
 
729
- expect(result.toInject).toEqual(["alice-vscode"]);
728
+ // Both slugs ranked into top-K and got freshly attached.
729
+ expect(new Set(result.toInject)).toEqual(
730
+ new Set(["alice-vscode", "skills/example-skill-a"]),
731
+ );
730
732
  expect(result.block).not.toBeNull();
731
733
 
732
734
  const aliceIdx = result.block!.indexOf("### alice-vscode");
@@ -735,46 +737,20 @@ describe("injectMemoryV2Block", () => {
735
737
  expect(skillsIdx).toBeGreaterThan(-1);
736
738
  expect(aliceIdx).toBeLessThan(skillsIdx);
737
739
 
738
- // The activation suffix is always appended for skills.
739
740
  expect(result.block).toContain(
740
741
  '- The "Example Skill A" skill (example-skill-a) is available. Helps with examples. → use skill_load to activate',
741
742
  );
742
743
  });
743
744
 
744
- test("returns null when both concept pages and skills are empty", async () => {
745
- // Empty concept-page candidate set (all simBatch + ANN responses empty)
746
- // AND no skill ids.
747
- stageTurn([]);
748
- stageSkills([]);
749
-
750
- const result = await injectMemoryV2Block({
751
- database: db,
752
- conversationId: "conv-1",
753
- currentTurn: 1,
754
- userMessage: "anything",
755
- assistantMessage: "",
756
- nowText: "",
757
- messageId: "msg-1",
758
- config: makeConfig(),
759
- });
760
-
761
- expect(result.toInject).toEqual([]);
762
- expect(result.block).toBeNull();
763
- });
764
-
765
- test("re-renders the same top-ranked skill on consecutive turns (no dedup)", async () => {
766
- // Skills are stateless: the same id can appear on back-to-back turns.
767
- // Stage no concept-page candidates so the block content is purely the
768
- // skills subsection.
745
+ test("skills participate in everInjected an attached skill is not re-attached on the next turn", async () => {
746
+ // Turn 1: skill ranks high, gets attached.
769
747
  const skillEntry = {
770
748
  id: "example-skill-a",
771
749
  content:
772
750
  'The "Example Skill A" skill (example-skill-a) is available. Helps with examples.',
773
751
  };
774
-
775
- // Turn 1 — only the skill.
776
- stageTurn([]);
777
- stageSkills(["example-skill-a"], [skillEntry]);
752
+ stageTurn([{ slug: "skills/example-skill-a", denseScore: 0.9 }]);
753
+ stageSkills([skillEntry]);
778
754
  const result1 = await injectMemoryV2Block({
779
755
  database: db,
780
756
  conversationId: "conv-1",
@@ -785,15 +761,14 @@ describe("injectMemoryV2Block", () => {
785
761
  messageId: "msg-1",
786
762
  config: makeConfig(),
787
763
  });
788
- expect(result1.block).not.toBeNull();
764
+ expect(result1.toInject).toEqual(["skills/example-skill-a"]);
789
765
  expect(result1.block).toContain("### Skills You Can Use");
790
- expect(result1.block).toContain("example-skill-a");
791
766
 
792
- // Turn 2 same skill ranks top again. Persisted state has advanced (the
793
- // first call wrote a fresh activation_state row), and `everInjected` was
794
- // not touched by the skill pipeline. The skill must still appear.
795
- stageTurn([]);
796
- stageSkills(["example-skill-a"], [skillEntry]);
767
+ // Turn 2: same skill ranks top again. It is already in `everInjected`, so
768
+ // `toInject` is empty and the block is null the attachment from turn 1
769
+ // remains visible to the agent via the cached prior user message.
770
+ stageTurn([{ slug: "skills/example-skill-a", denseScore: 0.9 }]);
771
+ stageSkills([skillEntry]);
797
772
  const result2 = await injectMemoryV2Block({
798
773
  database: db,
799
774
  conversationId: "conv-1",
@@ -804,21 +779,57 @@ describe("injectMemoryV2Block", () => {
804
779
  messageId: "msg-2",
805
780
  config: makeConfig(),
806
781
  });
807
- expect(result2.block).not.toBeNull();
808
- expect(result2.block).toContain("### Skills You Can Use");
809
- expect(result2.block).toContain("example-skill-a");
810
-
811
- // The skill content line is identical across the two turns — the renderer
812
- // is deterministic in `id → entry` lookup and the entry is unchanged.
813
- const skillLine =
814
- '- The "Example Skill A" skill (example-skill-a) is available. Helps with examples. → use skill_load to activate';
815
- expect(result1.block).toContain(skillLine);
816
- expect(result2.block).toContain(skillLine);
817
-
818
- // `everInjected` is untouched by the skill pipeline — both turns left it
819
- // empty (no concept pages were injected).
782
+ expect(result2.toInject).toEqual([]);
783
+ expect(result2.block).toBeNull();
784
+
820
785
  const persisted = await hydrate(db, "conv-1");
821
- expect(persisted!.everInjected).toEqual([]);
786
+ expect(persisted!.everInjected).toEqual([
787
+ { slug: "skills/example-skill-a", turn: 1 },
788
+ ]);
789
+ });
790
+
791
+ test("skill slugs whose entry is missing from the cache are dropped silently", async () => {
792
+ // The skill ranks into top-K but the in-process cache no longer knows
793
+ // its content (skill uninstalled mid-run). The render path drops it
794
+ // without surfacing it as a `missingSlugs` page-missing event — that
795
+ // status is reserved for on-disk concept pages, not catalog-derived
796
+ // skill entries.
797
+ stageTurn([{ slug: "skills/missing-skill", denseScore: 0.9 }]);
798
+ // No `stageSkills` call — cache stays empty.
799
+
800
+ const result = await injectMemoryV2Block({
801
+ database: db,
802
+ conversationId: "conv-1",
803
+ currentTurn: 1,
804
+ userMessage: "anything",
805
+ assistantMessage: "",
806
+ nowText: "Now",
807
+ messageId: "msg-1",
808
+ config: makeConfig(),
809
+ });
810
+
811
+ // `toInject` still records the slug (it ranked into top-K) but the
812
+ // block collapses to null because the only entry was a cache miss.
813
+ expect(result.toInject).toEqual(["skills/missing-skill"]);
814
+ expect(result.block).toBeNull();
815
+ });
816
+
817
+ test("returns null when both concept pages and skills are empty", async () => {
818
+ stageTurn([]);
819
+
820
+ const result = await injectMemoryV2Block({
821
+ database: db,
822
+ conversationId: "conv-1",
823
+ currentTurn: 1,
824
+ userMessage: "anything",
825
+ assistantMessage: "",
826
+ nowText: "",
827
+ messageId: "msg-1",
828
+ config: makeConfig(),
829
+ });
830
+
831
+ expect(result.toInject).toEqual([]);
832
+ expect(result.block).toBeNull();
822
833
  });
823
834
 
824
835
  test("context-load mode renders topNow even when every slug was previously injected", async () => {
@@ -910,39 +921,6 @@ describe("injectMemoryV2Block", () => {
910
921
  expect(persisted!.everInjected).toHaveLength(3);
911
922
  });
912
923
 
913
- test("`top_k_skills: 0` short-circuits to no skills subsection", async () => {
914
- // Even when the underlying mock would surface skills, the cap at 0 must
915
- // drop them via `selectSkillInjections.topK = 0` → empty `topNow`.
916
- stageTurn([{ slug: "alice-vscode", denseScore: 0.9 }]);
917
- stageSkills(
918
- ["example-skill-a"],
919
- [
920
- {
921
- id: "example-skill-a",
922
- content:
923
- 'The "Example Skill A" skill (example-skill-a) is available.',
924
- },
925
- ],
926
- );
927
-
928
- const result = await injectMemoryV2Block({
929
- database: db,
930
- conversationId: "conv-1",
931
- currentTurn: 1,
932
- userMessage: "Alice's editor",
933
- assistantMessage: "",
934
- nowText: "Now",
935
- messageId: "msg-1",
936
- config: makeConfig({ top_k_skills: 0 }),
937
- });
938
-
939
- expect(result.toInject).toEqual(["alice-vscode"]);
940
- expect(result.block).not.toBeNull();
941
- expect(result.block).toContain("### alice-vscode");
942
- expect(result.block).not.toContain("### Skills You Can Use");
943
- expect(result.block).not.toContain("example-skill-a");
944
- });
945
-
946
924
  // ---------------------------------------------------------------------------
947
925
  // Activation-log telemetry
948
926
  // ---------------------------------------------------------------------------
@@ -991,13 +969,12 @@ describe("injectMemoryV2Block", () => {
991
969
  status: string;
992
970
  source: string;
993
971
  }>;
994
- skills: unknown[];
995
972
  config: { top_k: number };
996
973
  };
997
974
  expect(row.conversationId).toBe("conv-1");
998
975
  expect(row.turn).toBe(2);
999
976
  expect(row.mode).toBe("per-turn");
1000
- expect(row.config.top_k).toBe(20);
977
+ expect(row.config.top_k).toBe(25);
1001
978
 
1002
979
  // The candidate set is the union of fromPrior (alice) and fromAnn
1003
980
  // (alice + carol) → two concept rows.
@@ -1019,6 +996,41 @@ describe("injectMemoryV2Block", () => {
1019
996
  expect(byslug.get("carol-jazz")!.status).toBe("injected");
1020
997
  });
1021
998
 
999
+ test("activation-log concepts include skill rows under the skills/ prefix", async () => {
1000
+ // Skills participate in the unified telemetry list — they live in the
1001
+ // same `concepts` array, identifiable by the `skills/` slug prefix.
1002
+ stageTurn([
1003
+ { slug: "alice-vscode", denseScore: 0.9 },
1004
+ { slug: "skills/example-skill-a", denseScore: 0.7 },
1005
+ ]);
1006
+ stageSkills([
1007
+ {
1008
+ id: "example-skill-a",
1009
+ content: "skill content",
1010
+ },
1011
+ ]);
1012
+
1013
+ await injectMemoryV2Block({
1014
+ database: db,
1015
+ conversationId: "conv-1",
1016
+ currentTurn: 1,
1017
+ userMessage: "Alice's editor",
1018
+ assistantMessage: "",
1019
+ nowText: "Now",
1020
+ messageId: "msg-1",
1021
+ config: makeConfig(),
1022
+ });
1023
+
1024
+ expect(telemetryState.recordCalls.length).toBe(1);
1025
+ const row = telemetryState.recordCalls[0] as {
1026
+ concepts: Array<{ slug: string; status: string }>;
1027
+ };
1028
+ const slugs = row.concepts.map((c) => c.slug);
1029
+ expect(new Set(slugs)).toEqual(
1030
+ new Set(["alice-vscode", "skills/example-skill-a"]),
1031
+ );
1032
+ });
1033
+
1022
1034
  test("context-load mode marks every rendered slug as `injected`, never `in_context`", async () => {
1023
1035
  // Turn 1 (per-turn): seed alice as injected so the next turn's prior
1024
1036
  // `everInjected` includes her — the same setup the per-turn telemetry
@@ -4,7 +4,14 @@
4
4
  * file-based override and falls back to the bundled prompt when the
5
5
  * override is missing/empty/unreadable.
6
6
  */
7
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
7
+ import { execFileSync } from "node:child_process";
8
+ import {
9
+ mkdirSync,
10
+ mkdtempSync,
11
+ rmSync,
12
+ symlinkSync,
13
+ writeFileSync,
14
+ } from "node:fs";
8
15
  import { homedir, tmpdir } from "node:os";
9
16
  import { join } from "node:path";
10
17
  import {
@@ -67,7 +74,14 @@ beforeEach(() => {
67
74
  });
68
75
 
69
76
  afterEach(() => {
70
- for (const entry of ["custom-prompt.md", "empty.md", "no-placeholder.md"]) {
77
+ for (const entry of [
78
+ "custom-prompt.md",
79
+ "empty.md",
80
+ "no-placeholder.md",
81
+ "huge.md",
82
+ "link.md",
83
+ "fifo",
84
+ ]) {
71
85
  rmSync(join(tmpWorkspace, entry), { force: true });
72
86
  }
73
87
  });
@@ -178,4 +192,49 @@ describe("resolveConsolidationPrompt — failure modes", () => {
178
192
  const data = warnCalls[0].data as Record<string, unknown>;
179
193
  expect(data.reason).toBe("empty_override");
180
194
  });
195
+
196
+ test("falls back to bundled prompt when the override exceeds the size limit", () => {
197
+ const path = join(tmpWorkspace, "huge.md");
198
+ // 1 MiB + 1 byte — just over the cap so we don't waste test memory.
199
+ writeFileSync(path, Buffer.alloc(1 * 1024 * 1024 + 1, 0x61));
200
+
201
+ const result = resolveConsolidationPrompt(path, CUTOFF);
202
+
203
+ expect(result).toBe(bundledPrompt());
204
+ expect(warnCalls).toHaveLength(1);
205
+ const data = warnCalls[0].data as Record<string, unknown>;
206
+ expect(data.reason).toBe("oversized_override");
207
+ expect(data.size).toBe(1 * 1024 * 1024 + 1);
208
+ });
209
+
210
+ test("falls back to bundled prompt when the override is a symlink", () => {
211
+ const target = join(tmpWorkspace, "custom-prompt.md");
212
+ writeFileSync(target, "real prompt body\n");
213
+ const link = join(tmpWorkspace, "link.md");
214
+ symlinkSync(target, link);
215
+
216
+ const result = resolveConsolidationPrompt(link, CUTOFF);
217
+
218
+ expect(result).toBe(bundledPrompt());
219
+ expect(warnCalls).toHaveLength(1);
220
+ const data = warnCalls[0].data as Record<string, unknown>;
221
+ expect(data.reason).toBe("not_regular_file");
222
+ });
223
+
224
+ test("falls back to bundled prompt when the override is a FIFO", () => {
225
+ const fifoPath = join(tmpWorkspace, "fifo");
226
+ try {
227
+ execFileSync("mkfifo", [fifoPath]);
228
+ } catch {
229
+ // mkfifo unavailable on this platform — skip without failing.
230
+ return;
231
+ }
232
+
233
+ const result = resolveConsolidationPrompt(fifoPath, CUTOFF);
234
+
235
+ expect(result).toBe(bundledPrompt());
236
+ expect(warnCalls).toHaveLength(1);
237
+ const data = warnCalls[0].data as Record<string, unknown>;
238
+ expect(data.reason).toBe("not_regular_file");
239
+ });
181
240
  });