@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
@@ -12,10 +12,32 @@
12
12
  * through a static singleton so tool side-effects can call
13
13
  * `PluginSourceWatcher.getInstance().ensureStarted()` directly without an
14
14
  * intermediate module-level injection.
15
+ *
16
+ * ## Linux/Bun recursive-watch caveat
17
+ *
18
+ * `fs.watch(dir, { recursive: true })` on Linux (and the equivalent under
19
+ * Bun, which uses the platform inotify path) does **not** dynamically
20
+ * attach to subdirectories created after the watch was established. The
21
+ * kernel fires exactly one event when the new top-level entry appears,
22
+ * and any writes inside that new subtree are silently dropped.
23
+ *
24
+ * Reproducer (against an empty watched root):
25
+ *
26
+ * mkdir plugin/ → event "plugin"
27
+ * mkdir plugin/hooks/ → no event delivered
28
+ * echo x > plugin/hooks/file.ts → no event delivered
29
+ *
30
+ * To work around this we close + reopen the watcher on every event (with
31
+ * a coalescing debounce). Reopening walks the tree from scratch and
32
+ * re-subscribes to every directory that currently exists, so subsequent
33
+ * writes anywhere under the new subtree are delivered normally. We also
34
+ * rescan top-level entries after the swap and dispatch a rebuild for any
35
+ * plugin not yet in the registry, which closes the close→reopen gap.
15
36
  */
16
37
 
17
- import { type FSWatcher, mkdirSync, watch } from "node:fs";
38
+ import { type FSWatcher, mkdirSync, readdirSync, watch } from "node:fs";
18
39
 
40
+ import { getRegisteredPlugin } from "../plugins/registry.js";
19
41
  import { DebouncerMap } from "../util/debounce.js";
20
42
  import { getLogger } from "../util/logger.js";
21
43
  import { getWorkspacePluginsDir } from "../util/platform.js";
@@ -25,6 +47,12 @@ const log = getLogger("plugin-source-watcher");
25
47
 
26
48
  const PLUGIN_SOURCE_DEBOUNCE_MS = 500;
27
49
 
50
+ /**
51
+ * Single shared debouncer key for the "restart the watcher" path. Bursty
52
+ * events for many plugin names all collapse to one restart.
53
+ */
54
+ const WATCHER_RESTART_KEY = "__restart__";
55
+
28
56
  /**
29
57
  * Extract the plugin's top-level directory name from a relative path within
30
58
  * the plugins root. Returns null for a stray file directly in `plugins/`
@@ -36,10 +64,12 @@ function resolvePluginNameFromRelPath(relPath: string): string | null {
36
64
  // Bare entry under plugins/ — fs.watch reports these when a new
37
65
  // directory is first created. We treat the name as the plugin name and
38
66
  // let the install path decide whether it's a real plugin.
39
- return relPath.length > 0 ? relPath : null;
67
+ if (relPath.length === 0 || relPath.startsWith(".")) return null;
68
+ return relPath;
40
69
  }
41
70
  const dirName = relPath.slice(0, slashIdx);
42
- return dirName.length > 0 ? dirName : null;
71
+ if (dirName.length === 0 || dirName.startsWith(".")) return null;
72
+ return dirName;
43
73
  }
44
74
 
45
75
  export class PluginSourceWatcher {
@@ -70,6 +100,14 @@ export class PluginSourceWatcher {
70
100
  defaultDelayMs: PLUGIN_SOURCE_DEBOUNCE_MS,
71
101
  maxEntries: 50,
72
102
  });
103
+ /**
104
+ * Coalesces watcher-restart requests across bursty events. See
105
+ * {@link restartWatcher} for why we restart on every event.
106
+ */
107
+ private restartDebouncer = new DebouncerMap({
108
+ defaultDelayMs: PLUGIN_SOURCE_DEBOUNCE_MS,
109
+ maxEntries: 1,
110
+ });
73
111
 
74
112
  start(): void {
75
113
  this.started = true;
@@ -89,6 +127,7 @@ export class PluginSourceWatcher {
89
127
  stop(): void {
90
128
  this.started = false;
91
129
  this.debouncer.cancelAll();
130
+ this.restartDebouncer.cancelAll();
92
131
  if (this.watcher) {
93
132
  this.watcher.close();
94
133
  this.watcher = null;
@@ -99,6 +138,89 @@ export class PluginSourceWatcher {
99
138
  return reregisterExternalPlugin(pluginName);
100
139
  }
101
140
 
141
+ /**
142
+ * Close the current FSWatcher and open a fresh one on the same root.
143
+ *
144
+ * Workaround for the Linux/Bun recursive-watch limitation documented at
145
+ * the top of this file. Reopening re-walks the tree and re-subscribes
146
+ * to every directory that exists at that moment, including any subtree
147
+ * that grew under the watched root since the previous watcher started.
148
+ *
149
+ * Belt-and-suspenders: after the swap, we rescan top-level entries and
150
+ * dispatch a rebuild for any plugin not yet in the registry — this
151
+ * closes the (sub-millisecond) gap between close and reopen during
152
+ * which a brand-new plugin's first event could be lost.
153
+ *
154
+ * Failure mode: if the reopen fails, keep the previous watcher active.
155
+ * It may still miss newly-created subtrees, but it preserves existing
156
+ * plugin source coverage instead of degrading to no watcher at all.
157
+ */
158
+ private restartWatcher(): void {
159
+ if (!this.started) return;
160
+
161
+ const oldWatcher = this.watcher;
162
+ this.watcher = null; // tryWatch returns early when non-null
163
+ this.tryWatch();
164
+
165
+ if (this.watcher === null) {
166
+ // Keep the previous watcher alive if the replacement failed. It may not
167
+ // cover newly-created subtrees, but it is still better than dropping all
168
+ // plugin source coverage.
169
+ this.watcher = oldWatcher;
170
+ log.warn(
171
+ "Plugin source watcher restart failed; keeping previous watcher active",
172
+ );
173
+ return;
174
+ }
175
+
176
+ if (oldWatcher) {
177
+ try {
178
+ oldWatcher.close();
179
+ } catch (err) {
180
+ log.warn({ err }, "Failed to close previous plugin watcher");
181
+ }
182
+ }
183
+
184
+ this.rescanPlugins();
185
+ }
186
+
187
+ /**
188
+ * Schedule a rebuild for every top-level entry under the plugins dir
189
+ * that isn't already in the registry. Called after a successful
190
+ * {@link restartWatcher} to catch new-plugin events that may have been
191
+ * lost during the close→reopen swap.
192
+ *
193
+ * Existing plugins are skipped — their normal change events are
194
+ * delivered through the freshly-attached watcher.
195
+ */
196
+ private rescanPlugins(): void {
197
+ let pluginsDir: string;
198
+ try {
199
+ pluginsDir = getWorkspacePluginsDir();
200
+ } catch {
201
+ return;
202
+ }
203
+
204
+ let entries: string[];
205
+ try {
206
+ entries = readdirSync(pluginsDir);
207
+ } catch (err) {
208
+ log.warn({ err, pluginsDir }, "Failed to rescan plugins directory");
209
+ return;
210
+ }
211
+
212
+ for (const entry of entries) {
213
+ // Skip dotfiles / dot-directories (e.g. macOS `.DS_Store`, npm cache).
214
+ if (entry.startsWith(".")) continue;
215
+ // Existing plugins ride the watcher's normal event delivery. The
216
+ // rescan is purely to catch installs whose first event was lost.
217
+ if (getRegisteredPlugin(entry) !== undefined) continue;
218
+ this.debouncer.schedule(`plugin:${entry}`, () => {
219
+ void this.onChange(entry);
220
+ });
221
+ }
222
+ }
223
+
102
224
  private tryWatch(): void {
103
225
  if (this.watcher) return;
104
226
 
@@ -130,9 +252,19 @@ export class PluginSourceWatcher {
130
252
  if (!filename) return;
131
253
  const pluginName = resolvePluginNameFromRelPath(filename);
132
254
  if (!pluginName) return;
255
+
256
+ // Per-plugin rebuild — debounced under the plugin name so bursty
257
+ // edits collapse to a single rebuild.
133
258
  this.debouncer.schedule(`plugin:${pluginName}`, () => {
134
259
  void this.onChange(pluginName);
135
260
  });
261
+
262
+ // Refresh the watcher to pick up any subtree that grew under us.
263
+ // Coalesced across all plugin names; single watcher restart per
264
+ // event burst. See restartWatcher for the rationale.
265
+ this.restartDebouncer.schedule(WATCHER_RESTART_KEY, () => {
266
+ this.restartWatcher();
267
+ });
136
268
  },
137
269
  );
138
270
  log.info({ pluginsDir }, "Plugin source watcher started");
@@ -38,7 +38,10 @@ import {
38
38
  buildSlackMetaForPersistence,
39
39
  serializePersistedUserMessageContent,
40
40
  } from "./conversation-messaging.js";
41
- import { formatCompactResult } from "./conversation-process.js";
41
+ import {
42
+ formatCleanResult,
43
+ formatCompactResult,
44
+ } from "./conversation-process.js";
42
45
  import { resolveChannelCapabilities } from "./conversation-runtime-assembly.js";
43
46
  import {
44
47
  buildSlashContextForContent,
@@ -254,7 +257,7 @@ export async function processMessage(
254
257
  options?: ProcessMessageOptions,
255
258
  sourceChannel?: string,
256
259
  sourceInterface?: string,
257
- ): Promise<{ messageId: string }> {
260
+ ): Promise<{ messageId: string; assistantMessageId?: string }> {
258
261
  const conversationOptions = stripPerTurnObservers(options);
259
262
  const { conversation, attachments } = await prepareConversationForMessage(
260
263
  conversationId,
@@ -370,7 +373,7 @@ export async function processMessage(
370
373
  }
371
374
 
372
375
  const assistantMsg = createAssistantMessage(slashResult.message);
373
- await addMessage(
376
+ const persistedAssistant = await addMessage(
374
377
  conversationId,
375
378
  "assistant",
376
379
  JSON.stringify(assistantMsg.content),
@@ -378,7 +381,10 @@ export async function processMessage(
378
381
  );
379
382
  conversation.getMessages().push(assistantMsg);
380
383
  publishConversationMessagesChanged(conversationId);
381
- return { messageId: persisted.id };
384
+ return {
385
+ messageId: persisted.id,
386
+ assistantMessageId: persistedAssistant.id,
387
+ };
382
388
  }
383
389
 
384
390
  if (slashResult.kind === "compact") {
@@ -428,7 +434,7 @@ export async function processMessage(
428
434
  });
429
435
  const responseText = formatCompactResult(result);
430
436
  const assistantMsg = createAssistantMessage(responseText);
431
- await addMessage(
437
+ const persistedAssistant = await addMessage(
432
438
  conversationId,
433
439
  "assistant",
434
440
  JSON.stringify(assistantMsg.content),
@@ -436,7 +442,64 @@ export async function processMessage(
436
442
  );
437
443
  conversation.getMessages().push(assistantMsg);
438
444
  publishConversationMessagesChanged(conversationId);
439
- return { messageId: persisted.id };
445
+ return {
446
+ messageId: persisted.id,
447
+ assistantMessageId: persistedAssistant.id,
448
+ };
449
+ }
450
+
451
+ if (slashResult.kind === "clean") {
452
+ const serverTurnCtx = conversation.getTurnChannelContext();
453
+ const serverProvenance = provenanceFromTrustContext(
454
+ conversation.trustContext,
455
+ );
456
+ const cleanChannelMeta = {
457
+ ...serverProvenance,
458
+ ...(serverTurnCtx
459
+ ? {
460
+ userMessageChannel: serverTurnCtx.userMessageChannel,
461
+ assistantMessageChannel: serverTurnCtx.assistantMessageChannel,
462
+ }
463
+ : {}),
464
+ ...(serverInterfaceCtx
465
+ ? {
466
+ userMessageInterface: serverInterfaceCtx.userMessageInterface,
467
+ assistantMessageInterface:
468
+ serverInterfaceCtx.assistantMessageInterface,
469
+ }
470
+ : {}),
471
+ };
472
+ const cleanUserMeta = slackMeta
473
+ ? { ...cleanChannelMeta, slackMeta }
474
+ : cleanChannelMeta;
475
+ const cleanMsg = createUserMessage(content, attachments);
476
+ const persisted = await addMessage(
477
+ conversationId,
478
+ "user",
479
+ serializePersistedUserMessageContent(
480
+ content,
481
+ attachments,
482
+ options?.displayContent,
483
+ ),
484
+ cleanUserMeta,
485
+ );
486
+ conversation.getMessages().push(cleanMsg);
487
+
488
+ const result = await conversation.forceClean();
489
+ const responseText = formatCleanResult(result);
490
+ const assistantMsg = createAssistantMessage(responseText);
491
+ const persistedAssistant = await addMessage(
492
+ conversationId,
493
+ "assistant",
494
+ JSON.stringify(assistantMsg.content),
495
+ cleanChannelMeta,
496
+ );
497
+ conversation.getMessages().push(assistantMsg);
498
+ publishConversationMessagesChanged(conversationId);
499
+ return {
500
+ messageId: persisted.id,
501
+ assistantMessageId: persistedAssistant.id,
502
+ };
440
503
  }
441
504
 
442
505
  const resolvedContent = slashResult.content;
@@ -460,16 +523,12 @@ export async function processMessage(
460
523
  }
461
524
 
462
525
  try {
463
- conversation.setSlackRuntimeContextNotice(
464
- options?.slackRuntimeContextNotice,
465
- );
466
526
  await conversation.runAgentLoop(resolvedContent, messageId, emitEvent, {
467
527
  isInteractive: options?.isInteractive ?? false,
468
528
  isUserMessage: true,
469
529
  ...(options?.callSite ? { callSite: options.callSite } : {}),
470
530
  });
471
531
  } finally {
472
- conversation.setSlackRuntimeContextNotice(undefined);
473
532
  if (
474
533
  options?.isInteractive === true &&
475
534
  conversation.getCurrentSender() === broadcastMessage
@@ -526,7 +585,6 @@ export async function processMessageInBackground(
526
585
  getSubagentManager().updateParentSender(conversationId, broadcastMessage);
527
586
  }
528
587
 
529
- conversation.setSlackRuntimeContextNotice(options?.slackRuntimeContextNotice);
530
588
  conversation
531
589
  .runAgentLoop(content, messageId, emitEvent, {
532
590
  isInteractive: options?.isInteractive ?? false,
@@ -534,7 +592,6 @@ export async function processMessageInBackground(
534
592
  ...(options?.callSite ? { callSite: options.callSite } : {}),
535
593
  })
536
594
  .finally(() => {
537
- conversation.setSlackRuntimeContextNotice(undefined);
538
595
  if (
539
596
  options?.isInteractive === true &&
540
597
  conversation.getCurrentSender() === broadcastMessage
@@ -0,0 +1,75 @@
1
+ import {
2
+ createTimeout,
3
+ extractText,
4
+ getConfiguredProvider,
5
+ userMessage,
6
+ } from "../providers/provider-send-message.js";
7
+ import { getLogger } from "../util/logger.js";
8
+
9
+ const log = getLogger("query-complexity-router");
10
+
11
+ export type ComplexityTier = "speed" | "balanced" | "quality";
12
+
13
+ const CLASSIFICATION_TIMEOUT_MS = 5_000;
14
+
15
+ const SYSTEM_PROMPT = `You are a query complexity classifier. Given a user message, classify its complexity into exactly one tier.
16
+
17
+ Reply with a single word — one of: speed, balanced, quality
18
+
19
+ - speed: trivial queries — greetings, acknowledgements, simple yes/no questions, basic factual lookups, short commands, small talk
20
+ - balanced: moderate queries — explanations, summaries, standard coding tasks, general conversation, most everyday requests
21
+ - quality: complex queries — deep analysis, long-form creative writing, complex multi-step reasoning, debugging intricate code, architectural design, research synthesis
22
+
23
+ When uncertain, reply "balanced".`;
24
+
25
+ export async function classifyQueryComplexity(
26
+ messageText: string,
27
+ ): Promise<ComplexityTier | null> {
28
+ const provider = await getConfiguredProvider("queryComplexityRouter");
29
+ if (!provider) {
30
+ log.warn("No provider available for query complexity routing");
31
+ return null;
32
+ }
33
+
34
+ const truncated =
35
+ messageText.length > 2000 ? messageText.slice(0, 2000) : messageText;
36
+
37
+ const { signal, cleanup } = createTimeout(CLASSIFICATION_TIMEOUT_MS);
38
+ try {
39
+ const response = await provider.sendMessage(
40
+ [userMessage(truncated)],
41
+ undefined,
42
+ SYSTEM_PROMPT,
43
+ { signal },
44
+ );
45
+ const text = extractText(response).toLowerCase().trim();
46
+ if (text === "speed" || text === "balanced" || text === "quality") {
47
+ return text;
48
+ }
49
+ // Parse partial matches (model might say "speed." or "quality - because...")
50
+ if (text.startsWith("speed")) return "speed";
51
+ if (text.startsWith("quality")) return "quality";
52
+ if (text.startsWith("balanced")) return "balanced";
53
+ log.warn({ raw: text }, "Unexpected classifier output, defaulting to null");
54
+ return null;
55
+ } catch (err) {
56
+ if (signal.aborted) {
57
+ log.warn("Query complexity classification timed out");
58
+ } else {
59
+ log.warn({ err }, "Query complexity classification failed");
60
+ }
61
+ return null;
62
+ } finally {
63
+ cleanup();
64
+ }
65
+ }
66
+
67
+ const PROFILE_MAP: Record<ComplexityTier, string> = {
68
+ speed: "cost-optimized",
69
+ balanced: "balanced",
70
+ quality: "quality-optimized",
71
+ };
72
+
73
+ export function complexityTierToProfileKey(tier: ComplexityTier): string {
74
+ return PROFILE_MAP[tier];
75
+ }
@@ -25,6 +25,12 @@ export interface TrustContext {
25
25
  requesterSenderDisplayName?: string;
26
26
  /** Guardian-managed display name from the contact record. */
27
27
  requesterMemberDisplayName?: string;
28
+ /** Raw timezone for the requester, when supplied by the source channel. */
29
+ requesterTimezone?: string;
30
+ /** Compact timezone label for the requester, when supplied by the source channel. */
31
+ requesterTimezoneLabel?: string;
32
+ /** Raw timezone offset in seconds for the requester, when supplied by the source channel. */
33
+ requesterTimezoneOffsetSeconds?: number;
28
34
  /** Canonical external user ID of the requester (the current actor). */
29
35
  requesterExternalUserId?: string;
30
36
  /** Chat/conversation ID the requester is interacting through. */