@vellumai/assistant 0.8.3 → 0.8.4

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 (342) hide show
  1. package/docker-entrypoint.sh +0 -1
  2. package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
  3. package/openapi.yaml +610 -16
  4. package/package.json +1 -1
  5. package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
  6. package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
  7. package/src/__tests__/agent-loop.test.ts +88 -3
  8. package/src/__tests__/anthropic-provider.test.ts +272 -0
  9. package/src/__tests__/approval-cascade.test.ts +1 -1
  10. package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
  11. package/src/__tests__/channel-delivery-store.test.ts +193 -0
  12. package/src/__tests__/channel-reply-delivery.test.ts +284 -5
  13. package/src/__tests__/channel-retry-sweep.test.ts +274 -1
  14. package/src/__tests__/compaction-events.test.ts +1 -1
  15. package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
  16. package/src/__tests__/config-watcher.test.ts +1 -1
  17. package/src/__tests__/context-token-estimator.test.ts +91 -1
  18. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  19. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
  20. package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
  21. package/src/__tests__/conversation-agent-loop.test.ts +25 -7
  22. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
  23. package/src/__tests__/conversation-clean-command.test.ts +137 -0
  24. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
  25. package/src/__tests__/conversation-fork-crud.test.ts +161 -0
  26. package/src/__tests__/conversation-lifecycle.test.ts +1 -1
  27. package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
  28. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  29. package/src/__tests__/conversation-pairing.test.ts +2 -2
  30. package/src/__tests__/conversation-process-callsite.test.ts +1 -1
  31. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
  32. package/src/__tests__/conversation-queue.test.ts +1 -1
  33. package/src/__tests__/conversation-runtime-assembly.test.ts +264 -81
  34. package/src/__tests__/conversation-seed-composer.test.ts +66 -4
  35. package/src/__tests__/conversation-slash-commands.test.ts +36 -8
  36. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  37. package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
  38. package/src/__tests__/conversation-speed-override.test.ts +1 -1
  39. package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
  40. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
  41. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  42. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  43. package/src/__tests__/credential-security-invariants.test.ts +6 -0
  44. package/src/__tests__/cu-unified-flow.test.ts +10 -1
  45. package/src/__tests__/dm-backfill.test.ts +64 -0
  46. package/src/__tests__/dm-persistence.test.ts +33 -0
  47. package/src/__tests__/document-find-replace.test.ts +501 -0
  48. package/src/__tests__/first-greeting.test.ts +23 -2
  49. package/src/__tests__/headless-browser-navigate.test.ts +172 -0
  50. package/src/__tests__/host-bash-proxy.test.ts +6 -0
  51. package/src/__tests__/host-browser-proxy.test.ts +10 -0
  52. package/src/__tests__/host-cu-proxy.test.ts +8 -1
  53. package/src/__tests__/host-file-proxy.test.ts +8 -1
  54. package/src/__tests__/host-transfer-proxy.test.ts +8 -1
  55. package/src/__tests__/identity-routes.test.ts +57 -0
  56. package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
  57. package/src/__tests__/injector-chain.test.ts +2 -0
  58. package/src/__tests__/injector-document-comments.test.ts +378 -0
  59. package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
  60. package/src/__tests__/list-messages-attachments.test.ts +21 -17
  61. package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
  62. package/src/__tests__/list-messages-page-latest.test.ts +130 -14
  63. package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
  64. package/src/__tests__/llm-context-normalization.test.ts +0 -2
  65. package/src/__tests__/llm-resolver.test.ts +85 -1
  66. package/src/__tests__/log-export-routes.test.ts +99 -2
  67. package/src/__tests__/message-queue-steer.test.ts +114 -0
  68. package/src/__tests__/openai-provider.test.ts +105 -0
  69. package/src/__tests__/openai-responses-provider.test.ts +4 -4
  70. package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
  71. package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
  72. package/src/__tests__/platform.test.ts +0 -3
  73. package/src/__tests__/plugin-source-watcher.test.ts +302 -0
  74. package/src/__tests__/process-message-background-slack.test.ts +1 -51
  75. package/src/__tests__/process-message-display-content.test.ts +21 -16
  76. package/src/__tests__/server-history-render.test.ts +83 -4
  77. package/src/__tests__/steer-tool-repair.test.ts +249 -0
  78. package/src/__tests__/system-prompt.test.ts +51 -28
  79. package/src/__tests__/terminal-tools.test.ts +11 -1
  80. package/src/__tests__/thinking-block-replay.test.ts +113 -0
  81. package/src/__tests__/thread-backfill.test.ts +370 -22
  82. package/src/__tests__/tool-executor.test.ts +90 -1
  83. package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
  84. package/src/__tests__/twilio-routes.test.ts +1 -1
  85. package/src/__tests__/web-fetch.test.ts +2 -2
  86. package/src/__tests__/workspace-git-service.test.ts +88 -5
  87. package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
  88. package/src/agent/attachments.ts +1 -0
  89. package/src/agent/loop.ts +57 -20
  90. package/src/background-wake/next-wake.test.ts +289 -0
  91. package/src/background-wake/next-wake.ts +172 -0
  92. package/src/browser/operations.ts +15 -0
  93. package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
  94. package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
  95. package/src/cli/commands/conversations.ts +128 -1
  96. package/src/cli/commands/inference-providers.ts +147 -1
  97. package/src/cli/commands/memory-v2.ts +308 -0
  98. package/src/cli/commands/notifications.ts +24 -2
  99. package/src/cli/utils/conversation-id.ts +17 -5
  100. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  101. package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
  102. package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
  103. package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
  104. package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
  105. package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
  106. package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
  107. package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
  108. package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
  109. package/src/config/bundled-skills/schedule/SKILL.md +8 -0
  110. package/src/config/bundled-tool-registry.ts +22 -12
  111. package/src/config/call-site-defaults.ts +19 -0
  112. package/src/config/feature-flag-registry.json +99 -3
  113. package/src/config/llm-resolver.ts +16 -2
  114. package/src/config/schemas/__tests__/memory-v2.test.ts +4 -0
  115. package/src/config/schemas/call-site-catalog.ts +21 -0
  116. package/src/config/schemas/llm.ts +3 -0
  117. package/src/config/schemas/memory-v2.ts +48 -1
  118. package/src/context/compactor.ts +8 -1
  119. package/src/context/token-estimator.ts +47 -4
  120. package/src/context/window-manager.ts +25 -0
  121. package/src/credential-health/credential-health-service.ts +34 -19
  122. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
  123. package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
  124. package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
  125. package/src/daemon/conversation-agent-loop-handlers.ts +153 -23
  126. package/src/daemon/conversation-agent-loop.ts +223 -54
  127. package/src/daemon/conversation-lifecycle.ts +142 -116
  128. package/src/daemon/conversation-messaging.ts +3 -0
  129. package/src/daemon/conversation-process.ts +273 -0
  130. package/src/daemon/conversation-queue-manager.ts +14 -0
  131. package/src/daemon/conversation-runtime-assembly.ts +135 -75
  132. package/src/daemon/conversation-slash.ts +37 -5
  133. package/src/daemon/conversation-surfaces.ts +45 -2
  134. package/src/daemon/conversation-tool-setup.ts +7 -0
  135. package/src/daemon/conversation.ts +42 -5
  136. package/src/daemon/first-greeting.ts +10 -0
  137. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
  138. package/src/daemon/handlers/config-a2a.ts +160 -0
  139. package/src/daemon/handlers/config-model.test.ts +1 -0
  140. package/src/daemon/handlers/conversations.ts +79 -0
  141. package/src/daemon/handlers/shared.ts +92 -29
  142. package/src/daemon/host-bash-proxy.ts +1 -1
  143. package/src/daemon/host-cu-proxy.ts +1 -1
  144. package/src/daemon/host-file-proxy.ts +1 -1
  145. package/src/daemon/host-transfer-proxy.ts +1 -1
  146. package/src/daemon/lifecycle.ts +18 -4
  147. package/src/daemon/message-protocol.ts +4 -0
  148. package/src/daemon/message-types/conversations.ts +8 -0
  149. package/src/daemon/message-types/document-comments.ts +50 -0
  150. package/src/daemon/message-types/messages.ts +68 -1
  151. package/src/daemon/message-types/surfaces.ts +3 -1
  152. package/src/daemon/message-types/web-activity.ts +57 -0
  153. package/src/daemon/plugin-source-watcher.ts +135 -3
  154. package/src/daemon/process-message.ts +69 -12
  155. package/src/daemon/query-complexity-router.ts +75 -0
  156. package/src/daemon/trust-context.ts +6 -0
  157. package/src/documents/document-comments-store.test.ts +338 -0
  158. package/src/documents/document-comments-store.ts +237 -0
  159. package/src/documents/document-store.ts +202 -0
  160. package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
  161. package/src/heartbeat/heartbeat-service.ts +1 -0
  162. package/src/home/__tests__/suggested-prompts.test.ts +33 -2
  163. package/src/home/feed-types.ts +6 -1
  164. package/src/home/home-content-refresh.ts +52 -0
  165. package/src/home/home-greeting-cache.ts +69 -0
  166. package/src/home/home-greeting.ts +94 -0
  167. package/src/home/suggested-prompts.ts +177 -9
  168. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
  169. package/src/memory/__tests__/memory-retrospective-job.test.ts +320 -6
  170. package/src/memory/conversation-crud.ts +133 -43
  171. package/src/memory/db-init.ts +16 -0
  172. package/src/memory/delivery-crud.ts +41 -0
  173. package/src/memory/delivery-status.ts +141 -15
  174. package/src/memory/external-conversation-store.ts +32 -1
  175. package/src/memory/jobs-worker.ts +21 -1
  176. package/src/memory/memory-retrospective-constants.ts +28 -0
  177. package/src/memory/memory-retrospective-enqueue.ts +3 -2
  178. package/src/memory/memory-retrospective-job.ts +408 -18
  179. package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
  180. package/src/memory/memory-v2-activation-log-store.ts +26 -8
  181. package/src/memory/migrations/100-core-tables.ts +1 -0
  182. package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
  183. package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
  184. package/src/memory/migrations/253-document-comments.ts +47 -0
  185. package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
  186. package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
  187. package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
  188. package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
  189. package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
  190. package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
  191. package/src/memory/migrations/index.ts +17 -0
  192. package/src/memory/migrations/registry.ts +25 -0
  193. package/src/memory/onboarding-events-store.ts +7 -0
  194. package/src/memory/schema/calls.ts +1 -0
  195. package/src/memory/schema/conversations.ts +3 -0
  196. package/src/memory/schema/infrastructure.ts +1 -0
  197. package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
  198. package/src/memory/v2/__tests__/injection.test.ts +31 -14
  199. package/src/memory/v2/__tests__/page-index.test.ts +365 -1
  200. package/src/memory/v2/__tests__/router.test.ts +489 -1
  201. package/src/memory/v2/consolidation-job.ts +14 -0
  202. package/src/memory/v2/injection-events.ts +101 -0
  203. package/src/memory/v2/injection.ts +21 -10
  204. package/src/memory/v2/page-index.ts +209 -7
  205. package/src/memory/v2/page-store.ts +18 -0
  206. package/src/memory/v2/router.ts +209 -55
  207. package/src/messaging/providers/index.ts +7 -1
  208. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
  209. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
  210. package/src/messaging/providers/slack/adapter.ts +178 -25
  211. package/src/messaging/providers/slack/api.test.ts +54 -0
  212. package/src/messaging/providers/slack/api.ts +119 -3
  213. package/src/messaging/providers/slack/client.ts +12 -0
  214. package/src/messaging/providers/slack/deep-link.ts +20 -1
  215. package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
  216. package/src/messaging/providers/slack/message-metadata.ts +156 -0
  217. package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
  218. package/src/messaging/providers/slack/render-transcript.ts +176 -49
  219. package/src/messaging/providers/slack/send.test.ts +77 -0
  220. package/src/messaging/providers/slack/send.ts +8 -2
  221. package/src/messaging/providers/slack/types.ts +14 -0
  222. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +4 -1
  223. package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
  224. package/src/notifications/conversation-seed-composer.ts +14 -2
  225. package/src/notifications/deferred-emit.ts +135 -0
  226. package/src/notifications/emit-signal.ts +9 -1
  227. package/src/notifications/home-feed-side-effect.ts +60 -30
  228. package/src/oauth/connect-orchestrator.ts +3 -0
  229. package/src/oauth/credential-token-resolver.ts +2 -0
  230. package/src/oauth/manual-token-connection.ts +19 -0
  231. package/src/oauth/oauth-store.ts +12 -0
  232. package/src/oauth/seed-providers.ts +22 -0
  233. package/src/permissions/prompter.ts +5 -2
  234. package/src/permissions/secret-prompter.ts +4 -1
  235. package/src/plugins/defaults/injectors.ts +82 -9
  236. package/src/prompts/__tests__/system-prompt.test.ts +46 -2
  237. package/src/prompts/normalize-onboarding.ts +40 -0
  238. package/src/prompts/sections.ts +32 -14
  239. package/src/prompts/system-prompt.ts +105 -68
  240. package/src/prompts/template-detection.ts +37 -0
  241. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
  242. package/src/prompts/templates/BOOTSTRAP.md +8 -0
  243. package/src/prompts/templates/VOICE.md +3 -0
  244. package/src/prompts/templates/system-sections.ts +53 -3
  245. package/src/providers/anthropic/client.ts +132 -5
  246. package/src/providers/fireworks/client.ts +20 -2
  247. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
  248. package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
  249. package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
  250. package/src/providers/inference/adapter-factory.ts +15 -1
  251. package/src/providers/inference/auth.ts +3 -3
  252. package/src/providers/inference/codex-token-refresh.ts +128 -0
  253. package/src/providers/inference/resolve-auth.ts +49 -6
  254. package/src/providers/model-catalog.ts +48 -1
  255. package/src/providers/openai/chat-completions-provider.ts +57 -20
  256. package/src/providers/openai/responses-provider.ts +9 -3
  257. package/src/providers/openrouter/client.ts +5 -1
  258. package/src/providers/types.ts +25 -0
  259. package/src/runtime/__tests__/agent-wake.test.ts +214 -0
  260. package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
  261. package/src/runtime/agent-wake.ts +151 -56
  262. package/src/runtime/auth/route-policy.ts +7 -3
  263. package/src/runtime/background-job-runner.ts +26 -0
  264. package/src/runtime/channel-reply-delivery.ts +182 -47
  265. package/src/runtime/channel-retry-sweep.ts +141 -16
  266. package/src/runtime/http-types.ts +7 -4
  267. package/src/runtime/pending-interactions.ts +51 -8
  268. package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
  269. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +55 -1
  270. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
  271. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
  272. package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
  273. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
  274. package/src/runtime/routes/approval-routes.ts +4 -1
  275. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
  276. package/src/runtime/routes/content-source-routes.ts +78 -0
  277. package/src/runtime/routes/conversation-cli-routes.ts +146 -1
  278. package/src/runtime/routes/conversation-query-routes.ts +60 -1
  279. package/src/runtime/routes/conversation-routes.ts +281 -76
  280. package/src/runtime/routes/document-comments-routes.ts +287 -0
  281. package/src/runtime/routes/documents-routes.ts +33 -0
  282. package/src/runtime/routes/home-feed-routes.ts +6 -3
  283. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  284. package/src/runtime/routes/host-browser-routes.ts +8 -1
  285. package/src/runtime/routes/identity-routes.ts +21 -0
  286. package/src/runtime/routes/inbound-message-handler.ts +288 -58
  287. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
  288. package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
  289. package/src/runtime/routes/index.ts +12 -4
  290. package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
  291. package/src/runtime/routes/integrations/a2a.ts +60 -1
  292. package/src/runtime/routes/log-export-routes.ts +39 -0
  293. package/src/runtime/routes/memory-v2-routes.ts +217 -0
  294. package/src/runtime/routes/notification-routes.ts +19 -2
  295. package/src/runtime/routes/question-routes.ts +4 -1
  296. package/src/runtime/routes/sanity-routes.ts +159 -0
  297. package/src/runtime/routes/slack-channel-routes.ts +187 -0
  298. package/src/runtime/services/conversation-serializer.ts +30 -4
  299. package/src/schedule/integration-status.ts +3 -1
  300. package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
  301. package/src/security/oauth2-device-code.ts +307 -0
  302. package/src/security/oauth2.ts +26 -9
  303. package/src/security/secure-keys.ts +5 -0
  304. package/src/skills/catalog-install.ts +6 -2
  305. package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
  306. package/src/tools/browser/browser-execution.ts +93 -0
  307. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
  308. package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
  309. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
  310. package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
  311. package/src/tools/browser/cdp-client/factory.ts +87 -3
  312. package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
  313. package/src/tools/browser/cdp-client/types.ts +36 -0
  314. package/src/tools/browser/pinned-tabs.ts +90 -0
  315. package/src/tools/document/document-comment-tool.test.ts +379 -0
  316. package/src/tools/document/document-comment-tool.ts +156 -0
  317. package/src/tools/document/document-tool.ts +128 -2
  318. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
  319. package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
  320. package/src/tools/network/domain-normalize.ts +17 -0
  321. package/src/tools/network/web-fetch.ts +213 -64
  322. package/src/tools/network/web-search.ts +191 -66
  323. package/src/tools/terminal/safe-env.ts +3 -2
  324. package/src/tools/tool-approval-handler.ts +19 -12
  325. package/src/tools/types.ts +4 -0
  326. package/src/tools/ui-surface/definitions.ts +3 -1
  327. package/src/types/onboarding-context.ts +4 -0
  328. package/src/util/__tests__/favicon.test.ts +84 -0
  329. package/src/util/favicon.ts +40 -0
  330. package/src/util/platform.ts +0 -5
  331. package/src/workspace/git-service.ts +75 -4
  332. package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
  333. package/src/workspace/migrations/registry.ts +2 -0
  334. package/src/config/bundled-skills/document/SKILL.md +0 -54
  335. package/src/config/bundled-skills/document/TOOLS.json +0 -106
  336. package/src/daemon/seed-files.ts +0 -18
  337. package/src/runtime/routes/interface-routes.ts +0 -43
  338. /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
  339. /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
  340. /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
  341. /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
  342. /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
@@ -195,6 +195,9 @@ mock.module("../memory/conversation-crud.js", () => ({
195
195
  updateMessageContent: () => {},
196
196
  updateMessageMetadata: () => {},
197
197
  clearStrippedInjectionMetadataForConversation: () => {},
198
+ setLastNotifiedInferenceProfile: () => {},
199
+ getLastUserTimestampBefore: () => 0,
200
+ getConversationOverrideProfileFromRow: () => undefined,
198
201
  }));
199
202
 
200
203
  afterAll(() => {
@@ -301,6 +304,7 @@ mock.module("../daemon/history-repair.js", () => ({
301
304
  }));
302
305
 
303
306
  const recordUsageMock = mock((..._args: unknown[]) => {});
307
+ const setAgentLoopExitReasonOnLatestLogMock = mock(() => {});
304
308
  mock.module("../daemon/conversation-usage.js", () => ({
305
309
  recordUsage: recordUsageMock,
306
310
  }));
@@ -349,12 +353,23 @@ mock.module("../workspace/git-service.js", () => ({
349
353
  }));
350
354
 
351
355
  mock.module("../daemon/conversation-error.js", () => ({
352
- classifyConversationError: (_err: unknown, _ctx: unknown) => ({
353
- code: "CONVERSATION_PROCESSING_FAILED",
354
- userMessage: "Something went wrong processing your message.",
355
- retryable: false,
356
- errorCategory: "processing_failed",
357
- }),
356
+ classifyConversationError: (err: unknown, _ctx: unknown) => {
357
+ const message = err instanceof Error ? err.message : String(err);
358
+ if (/context.?length.?exceeded/i.test(message)) {
359
+ return {
360
+ code: "CONTEXT_TOO_LARGE",
361
+ userMessage: "Context too large.",
362
+ retryable: false,
363
+ errorCategory: "context_too_large",
364
+ };
365
+ }
366
+ return {
367
+ code: "CONVERSATION_PROCESSING_FAILED",
368
+ userMessage: "Something went wrong processing your message.",
369
+ retryable: false,
370
+ errorCategory: "processing_failed",
371
+ };
372
+ },
358
373
  isUserCancellation: (err: unknown, ctx: { aborted?: boolean }) => {
359
374
  if (!ctx.aborted) return false;
360
375
  if (err instanceof DOMException && err.name === "AbortError") return true;
@@ -394,6 +409,7 @@ mock.module("../agent/message-types.js", () => ({
394
409
  mock.module("../memory/llm-request-log-store.js", () => ({
395
410
  recordRequestLog: () => {},
396
411
  backfillMessageIdOnLogs: () => {},
412
+ setAgentLoopExitReasonOnLatestLog: setAgentLoopExitReasonOnLatestLogMock,
397
413
  }));
398
414
 
399
415
  mock.module("../memory/archive-store.js", () => ({
@@ -616,6 +632,7 @@ beforeEach(() => {
616
632
  mockOverflowAction = "fail_gracefully";
617
633
  mockApplyRuntimeInjections = (msgs) => msgs;
618
634
  recordUsageMock.mockClear();
635
+ setAgentLoopExitReasonOnLatestLogMock.mockClear();
619
636
  // Reset the plugin registry and re-register every default so the
620
637
  // orchestrator's pipelines (`overflowReduce`, `persistence`, …) dispatch to
621
638
  // the default middleware, which in turn hits the mocked collaborators
@@ -1907,6 +1924,10 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1907
1924
  // After exhausting mid-loop attempts, the convergence loop should
1908
1925
  // have been triggered (contextTooLargeDetected set to true)
1909
1926
  expect(convergenceReducerCalled).toBe(true);
1927
+ expect(setAgentLoopExitReasonOnLatestLogMock).toHaveBeenCalledWith(
1928
+ "test-conv",
1929
+ "context_too_large",
1930
+ );
1910
1931
  });
1911
1932
 
1912
1933
  // ── Test 9 ────────────────────────────────────────────────────────
@@ -2068,6 +2089,10 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
2068
2089
 
2069
2090
  // Agent loop: 1 initial + 3 mid-loop re-entries + 2 convergence re-runs = 6 calls
2070
2091
  expect(agentLoopCallCount).toBe(6);
2092
+ expect(setAgentLoopExitReasonOnLatestLogMock).toHaveBeenCalledWith(
2093
+ "test-conv",
2094
+ "context_too_large",
2095
+ );
2071
2096
  });
2072
2097
 
2073
2098
  // ── Test 8 ────────────────────────────────────────────────────────
@@ -283,6 +283,7 @@ let mockSlackChronologicalContext: {
283
283
  renderedMessages: Array<{
284
284
  message: Message;
285
285
  sourceChannelTs: string | null;
286
+ tagLineProvenance: "none" | "slack-reaction" | "slack-timezone-message";
286
287
  }>;
287
288
  messages: Message[];
288
289
  compactableStartIndex: number;
@@ -387,6 +388,7 @@ mock.module("../daemon/history-repair.js", () => ({
387
388
  const recordUsageMock = mock(() => {});
388
389
  const recordRequestLogMock = mock(() => {});
389
390
  const backfillMessageIdOnLogsMock = mock(() => {});
391
+ const setAgentLoopExitReasonOnLatestLogMock = mock(() => {});
390
392
  mock.module("../daemon/conversation-usage.js", () => ({
391
393
  recordUsage: recordUsageMock,
392
394
  }));
@@ -484,6 +486,7 @@ mock.module("../memory/archive-store.js", () => ({
484
486
  mock.module("../memory/llm-request-log-store.js", () => ({
485
487
  recordRequestLog: recordRequestLogMock,
486
488
  backfillMessageIdOnLogs: backfillMessageIdOnLogsMock,
489
+ setAgentLoopExitReasonOnLatestLog: setAgentLoopExitReasonOnLatestLogMock,
487
490
  }));
488
491
 
489
492
  let mockHasProactiveArtifactCompleted = true;
@@ -660,6 +663,7 @@ beforeEach(() => {
660
663
  recordUsageMock.mockClear();
661
664
  recordRequestLogMock.mockClear();
662
665
  backfillMessageIdOnLogsMock.mockClear();
666
+ setAgentLoopExitReasonOnLatestLogMock.mockClear();
663
667
  syncMessageToDiskMock.mockClear();
664
668
  rebuildConversationDiskViewFromDbStateMock.mockClear();
665
669
  updateMessageMetadataMock.mockClear();
@@ -2300,6 +2304,10 @@ describe("session-agent-loop", () => {
2300
2304
 
2301
2305
  const handoff = events.find((e) => e.type === "generation_handoff");
2302
2306
  expect(handoff).toBeDefined();
2307
+ expect(setAgentLoopExitReasonOnLatestLogMock).toHaveBeenCalledWith(
2308
+ "test-conv",
2309
+ "checkpoint_handoff",
2310
+ );
2303
2311
  });
2304
2312
 
2305
2313
  test("continues when canHandoffAtCheckpoint returns false", async () => {
@@ -2359,6 +2367,10 @@ describe("session-agent-loop", () => {
2359
2367
 
2360
2368
  const handoff = events.find((e) => e.type === "generation_handoff");
2361
2369
  expect(handoff).toBeUndefined();
2370
+ expect(setAgentLoopExitReasonOnLatestLogMock).not.toHaveBeenCalledWith(
2371
+ "test-conv",
2372
+ "checkpoint_handoff",
2373
+ );
2362
2374
  const complete = events.find((e) => e.type === "message_complete");
2363
2375
  expect(complete).toBeDefined();
2364
2376
  });
@@ -2903,11 +2915,8 @@ describe("session-agent-loop", () => {
2903
2915
  // (from the mocked `addMessage` -> `{ id: "mock-msg-id" }`) into the
2904
2916
  // backfill primitive, scoped to this conversation.
2905
2917
  expect(backfillMessageIdOnLogsMock).toHaveBeenCalledTimes(1);
2906
- const backfillCall =
2907
- backfillMessageIdOnLogsMock.mock.calls[0] as unknown as [
2908
- string,
2909
- string,
2910
- ];
2918
+ const backfillCall = backfillMessageIdOnLogsMock.mock
2919
+ .calls[0] as unknown as [string, string];
2911
2920
  expect(backfillCall[0]).toBe("test-conv");
2912
2921
  expect(backfillCall[1]).toBe("mock-msg-id");
2913
2922
  });
@@ -3023,6 +3032,7 @@ describe("session-agent-loop", () => {
3023
3032
  "1700000020.000000",
3024
3033
  "1700000030.000000",
3025
3034
  ][index]!,
3035
+ tagLineProvenance: "none",
3026
3036
  })),
3027
3037
  compactableStartIndex: 0,
3028
3038
  };
@@ -3122,6 +3132,7 @@ describe("session-agent-loop", () => {
3122
3132
  "1700000020.000000",
3123
3133
  "1700000030.000000",
3124
3134
  ][index]!,
3135
+ tagLineProvenance: "none",
3125
3136
  })),
3126
3137
  compactableStartIndex: 0,
3127
3138
  };
@@ -3238,6 +3249,7 @@ describe("session-agent-loop", () => {
3238
3249
  "1700000030.000000",
3239
3250
  "1700000040.000000",
3240
3251
  ][index]!,
3252
+ tagLineProvenance: "none",
3241
3253
  })),
3242
3254
  compactableStartIndex: 0,
3243
3255
  };
@@ -3343,9 +3355,10 @@ describe("session-agent-loop", () => {
3343
3355
  {
3344
3356
  message: firstSummaryMessage,
3345
3357
  sourceChannelTs: null,
3358
+ tagLineProvenance: "none",
3346
3359
  },
3347
- mockSlackChronologicalContext.renderedMessages[2],
3348
- mockSlackChronologicalContext.renderedMessages[3],
3360
+ mockSlackChronologicalContext!.renderedMessages[2],
3361
+ mockSlackChronologicalContext!.renderedMessages[3],
3349
3362
  ],
3350
3363
  messages: firstCompactedMessages,
3351
3364
  compactableStartIndex: 1,
@@ -3384,6 +3397,7 @@ describe("session-agent-loop", () => {
3384
3397
  "1700000020.000000",
3385
3398
  "1700000030.000000",
3386
3399
  ][index]!,
3400
+ tagLineProvenance: "none",
3387
3401
  })),
3388
3402
  compactableStartIndex: 0,
3389
3403
  };
@@ -3541,6 +3555,7 @@ describe("session-agent-loop", () => {
3541
3555
  ],
3542
3556
  },
3543
3557
  sourceChannelTs: null,
3558
+ tagLineProvenance: "none",
3544
3559
  },
3545
3560
  {
3546
3561
  message: {
@@ -3548,6 +3563,7 @@ describe("session-agent-loop", () => {
3548
3563
  content: [{ type: "text", text: "after watermark reply" }],
3549
3564
  },
3550
3565
  sourceChannelTs: "1700000020.000000",
3566
+ tagLineProvenance: "none",
3551
3567
  },
3552
3568
  ],
3553
3569
  compactableStartIndex: 1,
@@ -3642,6 +3658,7 @@ describe("session-agent-loop", () => {
3642
3658
  ],
3643
3659
  },
3644
3660
  sourceChannelTs: null,
3661
+ tagLineProvenance: "none",
3645
3662
  },
3646
3663
  {
3647
3664
  message: {
@@ -3654,6 +3671,7 @@ describe("session-agent-loop", () => {
3654
3671
  ],
3655
3672
  },
3656
3673
  sourceChannelTs: "1700000121.000000",
3674
+ tagLineProvenance: "none",
3657
3675
  },
3658
3676
  ],
3659
3677
  compactableStartIndex: 1,
@@ -22,7 +22,7 @@ mock.module("../util/logger.js", () => ({
22
22
 
23
23
  mock.module("../providers/registry.js", () => ({
24
24
  getProvider: () => ({ name: "mock-provider" }),
25
- initializeProviders: () => {},
25
+ initializeProviders: async () => {},
26
26
  }));
27
27
 
28
28
  mock.module("../config/loader.js", () => ({
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Tests for the `/clean` slash command primitives.
3
+ *
4
+ * `/clean` is wired up so that:
5
+ * 1. Runtime injection prefixes are stripped from user-message text blocks
6
+ * (same allowlist `/compact` uses).
7
+ * 2. Assistant turns, tool_use blocks, and tool_result blocks are preserved
8
+ * verbatim — `/clean` never alters history shape.
9
+ * 3. The user-facing result message reports tokens reclaimed and the
10
+ * number of messages preserved.
11
+ *
12
+ * The slash-routing layer is covered by `conversation-slash-commands.test.ts`;
13
+ * the graph-memory eviction hook is covered by the v2 routing tests. This
14
+ * file focuses on the formatter and the strip behavior that defines the
15
+ * "no history loss" contract.
16
+ */
17
+ import { describe, expect, test } from "bun:test";
18
+
19
+ import { formatCleanResult } from "../daemon/conversation-process.js";
20
+ import { stripInjectionsForCompaction } from "../daemon/conversation-runtime-assembly.js";
21
+ import type { Message } from "../providers/types.js";
22
+
23
+ describe("formatCleanResult", () => {
24
+ test("formats token reclamation and preserved-message count", () => {
25
+ const out = formatCleanResult({
26
+ previousEstimatedInputTokens: 100_000,
27
+ estimatedInputTokens: 95_000,
28
+ maxInputTokens: 200_000,
29
+ preservedMessages: 250,
30
+ });
31
+ expect(out).toContain("Context Cleaned");
32
+ expect(out).toContain("100,000 → 95,000 (5,000 reclaimed)");
33
+ expect(out).toContain("95,000 / 200,000 tokens");
34
+ expect(out).toContain("250 preserved");
35
+ });
36
+
37
+ test("renders zero reclaimed when nothing was stripped", () => {
38
+ const out = formatCleanResult({
39
+ previousEstimatedInputTokens: 12_345,
40
+ estimatedInputTokens: 12_345,
41
+ maxInputTokens: 200_000,
42
+ preservedMessages: 10,
43
+ });
44
+ expect(out).toContain("12,345 → 12,345 (0 reclaimed)");
45
+ expect(out).toContain("10 preserved");
46
+ });
47
+ });
48
+
49
+ describe("stripInjectionsForCompaction preserves history shape", () => {
50
+ test("strips known injection prefixes from user text blocks", () => {
51
+ const messages: Message[] = [
52
+ {
53
+ role: "user",
54
+ content: [
55
+ {
56
+ type: "text",
57
+ text: "<NOW.md Always keep this up to date>\nfoo\n</NOW.md>",
58
+ },
59
+ { type: "text", text: "Hello, please help with X." },
60
+ ],
61
+ },
62
+ ];
63
+ const out = stripInjectionsForCompaction(messages);
64
+ expect(out).toHaveLength(1);
65
+ expect(out[0].content).toHaveLength(1);
66
+ expect(out[0].content[0]).toEqual({
67
+ type: "text",
68
+ text: "Hello, please help with X.",
69
+ });
70
+ });
71
+
72
+ test("preserves assistant turns and tool_use/tool_result blocks verbatim", () => {
73
+ const messages: Message[] = [
74
+ {
75
+ role: "user",
76
+ content: [
77
+ {
78
+ type: "text",
79
+ text: "<knowledge_base>\nstale kb\n</knowledge_base>",
80
+ },
81
+ { type: "text", text: "Run the calculator." },
82
+ ],
83
+ },
84
+ {
85
+ role: "assistant",
86
+ content: [
87
+ { type: "text", text: "Using the calculator." },
88
+ {
89
+ type: "tool_use",
90
+ id: "tool-1",
91
+ name: "calculator",
92
+ input: { expr: "1 + 2" },
93
+ },
94
+ ],
95
+ },
96
+ {
97
+ role: "user",
98
+ content: [
99
+ {
100
+ type: "tool_result",
101
+ tool_use_id: "tool-1",
102
+ content: "3",
103
+ },
104
+ ],
105
+ },
106
+ {
107
+ role: "assistant",
108
+ content: [{ type: "text", text: "The answer is 3." }],
109
+ },
110
+ ];
111
+
112
+ const out = stripInjectionsForCompaction(messages);
113
+
114
+ expect(out).toHaveLength(4);
115
+ expect(out[0].content).toEqual([
116
+ { type: "text", text: "Run the calculator." },
117
+ ]);
118
+ expect(out[1]).toEqual(messages[1]);
119
+ expect(out[2]).toEqual(messages[2]);
120
+ expect(out[3]).toEqual(messages[3]);
121
+ });
122
+
123
+ test("leaves <turn_context>, <workspace>, and <memory __injected> alone", () => {
124
+ const messages: Message[] = [
125
+ {
126
+ role: "user",
127
+ content: [
128
+ { type: "text", text: "<turn_context>\nnow\n</turn_context>" },
129
+ { type: "text", text: "<workspace>\nfiles\n</workspace>" },
130
+ { type: "text", text: "<memory __injected>\nrecent\n</memory>" },
131
+ ],
132
+ },
133
+ ];
134
+ const out = stripInjectionsForCompaction(messages);
135
+ expect(out[0].content).toEqual(messages[0].content);
136
+ });
137
+ });
@@ -51,7 +51,7 @@ mock.module("../memory/guardian-action-store.js", () => ({
51
51
 
52
52
  mock.module("../providers/registry.js", () => ({
53
53
  getProvider: () => ({ name: "mock-provider" }),
54
- initializeProviders: () => {},
54
+ initializeProviders: async () => {},
55
55
  }));
56
56
 
57
57
  mock.module("../config/loader.js", () => ({
@@ -49,6 +49,7 @@ import {
49
49
  getRetrospectiveState,
50
50
  upsertRetrospectiveState,
51
51
  } from "../memory/memory-retrospective-state.js";
52
+ import { rawGet, rawRun } from "../memory/raw-query.js";
52
53
  import {
53
54
  activationState,
54
55
  channelInboundEvents,
@@ -296,6 +297,114 @@ describe("forkConversation", () => {
296
297
  ]);
297
298
  });
298
299
 
300
+ test("inherits cleanedAt when forking past the clean event", async () => {
301
+ const source = createConversation("Clean thread");
302
+ await addMessage(source.id, "user", "Message 1", undefined, {
303
+ skipIndexing: true,
304
+ });
305
+ const preClean = await addMessage(
306
+ source.id,
307
+ "assistant",
308
+ "Message 2",
309
+ undefined,
310
+ { skipIndexing: true },
311
+ );
312
+
313
+ const cleanedAt = preClean.createdAt + 1;
314
+ getDb()
315
+ .update(conversations)
316
+ .set({ cleanedAt })
317
+ .where(eq(conversations.id, source.id))
318
+ .run();
319
+
320
+ const postClean = await addMessage(
321
+ source.id,
322
+ "user",
323
+ "Message 3",
324
+ undefined,
325
+ { skipIndexing: true },
326
+ );
327
+ expect(postClean.createdAt).toBeGreaterThanOrEqual(cleanedAt);
328
+
329
+ const fork = forkConversation({
330
+ conversationId: source.id,
331
+ throughMessageId: postClean.id,
332
+ });
333
+
334
+ expect(fork.cleanedAt).toBe(cleanedAt);
335
+ });
336
+
337
+ test("does not inherit cleanedAt when forking before the clean event", async () => {
338
+ const source = createConversation("Clean thread");
339
+ await addMessage(source.id, "user", "Message 1", undefined, {
340
+ skipIndexing: true,
341
+ });
342
+ const preClean = await addMessage(
343
+ source.id,
344
+ "assistant",
345
+ "Message 2",
346
+ undefined,
347
+ { skipIndexing: true },
348
+ );
349
+
350
+ const cleanedAt = preClean.createdAt + 1;
351
+ getDb()
352
+ .update(conversations)
353
+ .set({ cleanedAt })
354
+ .where(eq(conversations.id, source.id))
355
+ .run();
356
+
357
+ await addMessage(source.id, "user", "Message 3", undefined, {
358
+ skipIndexing: true,
359
+ });
360
+
361
+ const fork = forkConversation({
362
+ conversationId: source.id,
363
+ throughMessageId: preClean.id,
364
+ });
365
+
366
+ expect(fork.cleanedAt).toBeNull();
367
+ });
368
+
369
+ test("inherits cleanedAt on a full-history fork", async () => {
370
+ const source = createConversation("Clean thread");
371
+ await addMessage(source.id, "user", "Message 1", undefined, {
372
+ skipIndexing: true,
373
+ });
374
+ const last = await addMessage(
375
+ source.id,
376
+ "assistant",
377
+ "Message 2",
378
+ undefined,
379
+ { skipIndexing: true },
380
+ );
381
+
382
+ const cleanedAt = last.createdAt - 1;
383
+ getDb()
384
+ .update(conversations)
385
+ .set({ cleanedAt })
386
+ .where(eq(conversations.id, source.id))
387
+ .run();
388
+
389
+ const fork = forkConversation({ conversationId: source.id });
390
+
391
+ expect(fork.cleanedAt).toBe(cleanedAt);
392
+ });
393
+
394
+ test("leaves cleanedAt null when the source has no clean event", async () => {
395
+ const source = createConversation("Unclean thread");
396
+ await addMessage(source.id, "user", "Message 1", undefined, {
397
+ skipIndexing: true,
398
+ });
399
+ await addMessage(source.id, "assistant", "Message 2", undefined, {
400
+ skipIndexing: true,
401
+ });
402
+
403
+ const fork = forkConversation({ conversationId: source.id });
404
+
405
+ expect(fork.cleanedAt).toBeNull();
406
+ });
407
+
299
408
  test("rejects forks when the source conversation has no persisted messages", () => {
300
409
  const source = createConversation("Empty thread");
301
410
 
@@ -661,6 +770,58 @@ describe("forkConversation", () => {
661
770
  expect(loadGraphMemoryState(fork.id)).toBeNull();
662
771
  });
663
772
 
773
+ test("defaults conversationType to standard and inherits the parent's group", async () => {
774
+ const source = createConversation("Default inheritance thread");
775
+ await addMessage(source.id, "user", "first message", undefined, {
776
+ skipIndexing: true,
777
+ });
778
+ rawRun(
779
+ "UPDATE conversations SET group_id = ? WHERE id = ?",
780
+ "system:pinned",
781
+ source.id,
782
+ );
783
+
784
+ const fork = forkConversation({ conversationId: source.id });
785
+ const row = getDb()
786
+ .select()
787
+ .from(conversations)
788
+ .where(eq(conversations.id, fork.id))
789
+ .get();
790
+ const groupIdRow = rawGet<{ group_id: string | null }>(
791
+ "SELECT group_id FROM conversations WHERE id = ?",
792
+ fork.id,
793
+ );
794
+
795
+ expect(row?.conversationType).toBe("standard");
796
+ expect(groupIdRow?.group_id).toBe("system:pinned");
797
+ });
798
+
799
+ test("honors conversationType and groupId overrides on the fork", async () => {
800
+ const source = createConversation("Override thread");
801
+ await addMessage(source.id, "user", "first message", undefined, {
802
+ skipIndexing: true,
803
+ });
804
+
805
+ const fork = forkConversation({
806
+ conversationId: source.id,
807
+ conversationType: "background",
808
+ groupId: "system:background",
809
+ });
810
+
811
+ const row = getDb()
812
+ .select()
813
+ .from(conversations)
814
+ .where(eq(conversations.id, fork.id))
815
+ .get();
816
+ const groupIdRow = rawGet<{ group_id: string | null }>(
817
+ "SELECT group_id FROM conversations WHERE id = ?",
818
+ fork.id,
819
+ );
820
+
821
+ expect(row?.conversationType).toBe("background");
822
+ expect(groupIdRow?.group_id).toBe("system:background");
823
+ });
824
+
664
825
  test("copies memory state when throughMessageId points at the last message", async () => {
665
826
  const source = createConversation("Through-last thread");
666
827
  const lastMessage = await addMessage(
@@ -8,7 +8,7 @@ mock.module("../util/logger.js", () => ({
8
8
 
9
9
  mock.module("../providers/registry.js", () => ({
10
10
  getProvider: () => ({ name: "mock-provider" }),
11
- initializeProviders: () => {},
11
+ initializeProviders: async () => {},
12
12
  }));
13
13
 
14
14
  mock.module("../config/loader.js", () => ({