@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
@@ -35,10 +35,17 @@ const slackFileMetadataSchema = z.object({
35
35
  export const slackMessageMetadataSchema = z.object({
36
36
  source: z.literal("slack"),
37
37
  channelId: z.string(),
38
+ channelName: z.string().optional(),
38
39
  channelTs: z.string(),
39
40
  threadTs: z.string().optional(),
40
41
  displayName: z.string().optional(),
41
42
  actorExternalUserId: z.string().optional(),
43
+ actorTimezone: z.string().optional(),
44
+ actorTimezoneLabel: z.string().optional(),
45
+ actorTimezoneOffsetSeconds: z.number().optional(),
46
+ timestampTimezone: z.string().optional(),
47
+ timestampTimezoneLabel: z.string().optional(),
48
+ speakerTimezoneLabel: z.string().optional(),
42
49
  eventKind: z.enum(["message", "reaction"]),
43
50
  reaction: slackReactionMetadataSchema.optional(),
44
51
  editedAt: z.number().optional(),
@@ -50,6 +57,155 @@ export type SlackReactionMetadata = z.infer<typeof slackReactionMetadataSchema>;
50
57
  export type SlackFileMetadata = z.infer<typeof slackFileMetadataSchema>;
51
58
  export type SlackMessageMetadata = z.infer<typeof slackMessageMetadataSchema>;
52
59
 
60
+ type SlackTimezoneMetadata = Pick<
61
+ SlackMessageMetadata,
62
+ | "actorTimezone"
63
+ | "actorTimezoneLabel"
64
+ | "actorTimezoneOffsetSeconds"
65
+ | "timestampTimezone"
66
+ | "timestampTimezoneLabel"
67
+ | "speakerTimezoneLabel"
68
+ >;
69
+ type SlackTimezoneMetadataInput = Omit<
70
+ Partial<SlackTimezoneMetadata>,
71
+ "actorTimezoneOffsetSeconds"
72
+ > & {
73
+ actorTimezoneOffsetSeconds?: unknown;
74
+ };
75
+
76
+ const COMMON_SLACK_TIMEZONE_LABEL_BY_IANA = new Map<string, string>([
77
+ ["America/New_York", "ET"],
78
+ ["America/Detroit", "ET"],
79
+ ["America/Indiana/Indianapolis", "ET"],
80
+ ["America/Kentucky/Louisville", "ET"],
81
+ ["America/Toronto", "ET"],
82
+ ["America/Montreal", "ET"],
83
+ ["America/Chicago", "CT"],
84
+ ["America/Winnipeg", "CT"],
85
+ ["America/Mexico_City", "CT"],
86
+ ["America/Denver", "MT"],
87
+ ["America/Boise", "MT"],
88
+ ["America/Phoenix", "MT"],
89
+ ["America/Edmonton", "MT"],
90
+ ["America/Los_Angeles", "PT"],
91
+ ["America/Vancouver", "PT"],
92
+ ["America/Tijuana", "PT"],
93
+ ]);
94
+
95
+ const COMPACT_SLACK_TIMEZONE_LABEL_BY_NAME = new Map<string, string>([
96
+ ["EASTERN TIME", "ET"],
97
+ ["EASTERN STANDARD TIME", "ET"],
98
+ ["EASTERN DAYLIGHT TIME", "ET"],
99
+ ["EST", "ET"],
100
+ ["EDT", "ET"],
101
+ ["CENTRAL TIME", "CT"],
102
+ ["CENTRAL STANDARD TIME", "CT"],
103
+ ["CENTRAL DAYLIGHT TIME", "CT"],
104
+ ["CST", "CT"],
105
+ ["CDT", "CT"],
106
+ ["MOUNTAIN TIME", "MT"],
107
+ ["MOUNTAIN STANDARD TIME", "MT"],
108
+ ["MOUNTAIN DAYLIGHT TIME", "MT"],
109
+ ["MST", "MT"],
110
+ ["MDT", "MT"],
111
+ ["PACIFIC TIME", "PT"],
112
+ ["PACIFIC STANDARD TIME", "PT"],
113
+ ["PACIFIC DAYLIGHT TIME", "PT"],
114
+ ["PST", "PT"],
115
+ ["PDT", "PT"],
116
+ ]);
117
+
118
+ const slackShortTimeZoneFormatters = new Map<string, Intl.DateTimeFormat>();
119
+
120
+ function compactStoredSlackTimezoneLabel(
121
+ label: string | null | undefined,
122
+ ): string | null {
123
+ const trimmed = label?.trim();
124
+ if (!trimmed) return null;
125
+ return (
126
+ COMPACT_SLACK_TIMEZONE_LABEL_BY_NAME.get(trimmed.toUpperCase()) ?? trimmed
127
+ );
128
+ }
129
+
130
+ function getSlackShortTimeZoneFormatter(timeZone: string): Intl.DateTimeFormat {
131
+ let formatter = slackShortTimeZoneFormatters.get(timeZone);
132
+ if (!formatter) {
133
+ formatter = new Intl.DateTimeFormat("en-US", {
134
+ timeZone,
135
+ timeZoneName: "short",
136
+ });
137
+ slackShortTimeZoneFormatters.set(timeZone, formatter);
138
+ }
139
+ return formatter;
140
+ }
141
+
142
+ function extractSlackShortTimeZoneName(
143
+ timeZone: string,
144
+ nowMs: number,
145
+ ): string | null {
146
+ try {
147
+ const part = getSlackShortTimeZoneFormatter(timeZone)
148
+ .formatToParts(new Date(nowMs))
149
+ .find((p) => p.type === "timeZoneName");
150
+ return part?.value ?? null;
151
+ } catch {
152
+ return null;
153
+ }
154
+ }
155
+
156
+ export function formatSlackTimezoneLabel(
157
+ timeZone: string | null | undefined,
158
+ opts: { persistedLabel?: string | null; nowMs?: number } = {},
159
+ ): string | null {
160
+ const persisted = compactStoredSlackTimezoneLabel(opts.persistedLabel);
161
+ if (persisted) return persisted;
162
+
163
+ const trimmedTimezone = timeZone?.trim();
164
+ if (!trimmedTimezone) return null;
165
+ const mapped = COMMON_SLACK_TIMEZONE_LABEL_BY_IANA.get(trimmedTimezone);
166
+ if (mapped) return mapped;
167
+ return (
168
+ extractSlackShortTimeZoneName(trimmedTimezone, opts.nowMs ?? Date.now()) ??
169
+ trimmedTimezone
170
+ );
171
+ }
172
+
173
+ function trimmedOptionalString(
174
+ value: string | null | undefined,
175
+ ): string | undefined {
176
+ const trimmed = value?.trim();
177
+ return trimmed ? trimmed : undefined;
178
+ }
179
+
180
+ export function buildSlackTimezoneMetadata(
181
+ input: SlackTimezoneMetadataInput,
182
+ ): Partial<SlackTimezoneMetadata> {
183
+ const actorTimezone = trimmedOptionalString(input.actorTimezone);
184
+ const actorTimezoneLabel = trimmedOptionalString(input.actorTimezoneLabel);
185
+ const timestampTimezone = trimmedOptionalString(input.timestampTimezone);
186
+ const timestampTimezoneLabel = trimmedOptionalString(
187
+ input.timestampTimezoneLabel,
188
+ );
189
+ const speakerTimezoneLabel = trimmedOptionalString(
190
+ input.speakerTimezoneLabel,
191
+ );
192
+ const actorTimezoneOffsetSeconds =
193
+ typeof input.actorTimezoneOffsetSeconds === "number" &&
194
+ Number.isFinite(input.actorTimezoneOffsetSeconds)
195
+ ? input.actorTimezoneOffsetSeconds
196
+ : undefined;
197
+ return {
198
+ ...(actorTimezone ? { actorTimezone } : {}),
199
+ ...(actorTimezoneLabel ? { actorTimezoneLabel } : {}),
200
+ ...(actorTimezoneOffsetSeconds !== undefined
201
+ ? { actorTimezoneOffsetSeconds }
202
+ : {}),
203
+ ...(timestampTimezone ? { timestampTimezone } : {}),
204
+ ...(timestampTimezoneLabel ? { timestampTimezoneLabel } : {}),
205
+ ...(speakerTimezoneLabel ? { speakerTimezoneLabel } : {}),
206
+ };
207
+ }
208
+
53
209
  /**
54
210
  * Parse a JSON string into `SlackMessageMetadata`. Returns `null` on parse
55
211
  * error, on non-object payloads, when `source !== "slack"`, or when any
@@ -12,7 +12,6 @@ import { describe, expect, test } from "bun:test";
12
12
  import type { Message } from "../../../providers/types.js";
13
13
  import {
14
14
  extractTagLineTexts,
15
- isReactionTagLine,
16
15
  parentAlias,
17
16
  type RenderableSlackMessage,
18
17
  renderSlackTranscript,
@@ -114,19 +113,108 @@ describe("renderSlackTranscript — basics", () => {
114
113
  expect(renderSlackTranscript([])).toEqual([]);
115
114
  });
116
115
 
116
+ test("renders timezone-aware rows with compact local timestamps and speaker suffixes", () => {
117
+ const out = renderSlackTranscript([
118
+ {
119
+ ...userMsg("1772681640.000001", "aaron", "hey there"),
120
+ metadata: {
121
+ source: "slack",
122
+ channelId: CHANNEL,
123
+ channelTs: "1772681640.000001",
124
+ eventKind: "message",
125
+ displayName: "aaron",
126
+ actorTimezone: "America/Denver",
127
+ actorTimezoneLabel: "MT",
128
+ timestampTimezone: "America/Denver",
129
+ timestampTimezoneLabel: "MT",
130
+ },
131
+ },
132
+ {
133
+ ...userMsg("1772681760.000002", "jordan", "whatsup"),
134
+ metadata: {
135
+ source: "slack",
136
+ channelId: CHANNEL,
137
+ channelTs: "1772681760.000002",
138
+ eventKind: "message",
139
+ displayName: "jordan",
140
+ actorTimezone: "America/New_York",
141
+ actorTimezoneLabel: "Eastern Time",
142
+ timestampTimezone: "America/Denver",
143
+ timestampTimezoneLabel: "MT",
144
+ speakerTimezoneLabel: "Eastern Time",
145
+ },
146
+ },
147
+ {
148
+ ...userMsg(
149
+ "1772681820.000003",
150
+ "aaron",
151
+ "nm, i wonder how my assistant is doing",
152
+ ),
153
+ metadata: {
154
+ source: "slack",
155
+ channelId: CHANNEL,
156
+ channelTs: "1772681820.000003",
157
+ eventKind: "message",
158
+ displayName: "aaron",
159
+ actorTimezone: "America/Denver",
160
+ actorTimezoneLabel: "MT",
161
+ timestampTimezone: "America/Denver",
162
+ timestampTimezoneLabel: "MT",
163
+ },
164
+ },
165
+ {
166
+ ...userMsg("1772681880.000004", null, "i'm good", {
167
+ role: "assistant",
168
+ }),
169
+ metadata: {
170
+ source: "slack",
171
+ channelId: CHANNEL,
172
+ channelTs: "1772681880.000004",
173
+ eventKind: "message",
174
+ timestampTimezone: "America/Denver",
175
+ timestampTimezoneLabel: "Mountain Time",
176
+ speakerTimezoneLabel: "ET",
177
+ },
178
+ },
179
+ {
180
+ ...userMsg("1772681940.000005", "jordan", "ayeeeee"),
181
+ metadata: {
182
+ source: "slack",
183
+ channelId: CHANNEL,
184
+ channelTs: "1772681940.000005",
185
+ eventKind: "message",
186
+ displayName: "jordan",
187
+ actorTimezone: "America/New_York",
188
+ actorTimezoneLabel: "ET",
189
+ timestampTimezone: "America/Denver",
190
+ timestampTimezoneLabel: "MT",
191
+ speakerTimezoneLabel: "ET",
192
+ },
193
+ },
194
+ ]);
195
+
196
+ expect(out).toEqual([
197
+ textMsg("user", "[mar 4 2026 8:34 PM MT aaron] hey there"),
198
+ textMsg("user", "[mar 4 2026 8:36 PM MT jordan (ET)] whatsup"),
199
+ textMsg(
200
+ "user",
201
+ "[mar 4 2026 8:37 PM MT aaron] nm, i wonder how my assistant is doing",
202
+ ),
203
+ textMsg("assistant", "[mar 4 2026 8:38 PM MT assistant] i'm good"),
204
+ textMsg("user", "[mar 4 2026 8:39 PM MT jordan (ET)] ayeeeee"),
205
+ ]);
206
+ });
207
+
117
208
  test("renders top-level message with MM/DD/YY HH:MM tag", () => {
118
209
  const out = renderSlackTranscript([userMsg(TS_14_25, "@alice", "hi")]);
119
210
  expect(out).toEqual([textMsg("user", "[11/14/23 14:25 @alice]: hi")]);
120
211
  });
121
212
 
122
- test("renders thread reply with parent alias arrow", () => {
213
+ test("renders thread reply without parent alias arrow", () => {
123
214
  const out = renderSlackTranscript([
124
215
  userMsg(TS_14_28, "@bob", "got it", { threadTs: TS_14_25 }),
125
216
  ]);
126
- const alias = parentAlias(TS_14_25);
127
- expect(out).toEqual([
128
- textMsg("user", `[11/14/23 14:28 @bob → ${alias}]: got it`),
129
- ]);
217
+ expect(out).toEqual([textMsg("user", "[11/14/23 14:28 @bob]: got it")]);
130
218
  });
131
219
 
132
220
  test("renders edited message with editedAt suffix", () => {
@@ -153,18 +241,17 @@ describe("renderSlackTranscript — basics", () => {
153
241
  ]);
154
242
  });
155
243
 
156
- test("edited message in a thread renders both arrow and edit suffix", () => {
244
+ test("edited message in a thread renders edit suffix without thread arrow", () => {
157
245
  const out = renderSlackTranscript([
158
246
  userMsg(TS_14_28, "@bob", "got it (edit)", {
159
247
  threadTs: TS_14_25,
160
248
  editedAt: MS_14_30,
161
249
  }),
162
250
  ]);
163
- const alias = parentAlias(TS_14_25);
164
251
  expect(out).toEqual([
165
252
  textMsg(
166
253
  "user",
167
- `[11/14/23 14:28 @bob → ${alias}, edited 11/14/23 14:30]: got it (edit)`,
254
+ "[11/14/23 14:28 @bob, edited 11/14/23 14:30]: got it (edit)",
168
255
  ),
169
256
  ]);
170
257
  });
@@ -292,10 +379,9 @@ describe("renderSlackTranscript — basics", () => {
292
379
  TS_14_25,
293
380
  TS_14_28,
294
381
  ]);
295
- expect(out.renderedMessages.map((entry) => entry.sourceChannelTs)).toEqual([
296
- TS_14_25,
297
- TS_14_28,
298
- ]);
382
+ expect(
383
+ out.renderedMessages.map((entry) => entry.tagLineProvenance),
384
+ ).toEqual(["none", "none"]);
299
385
  });
300
386
 
301
387
  test("omits sender label for user-role message with null senderLabel (no displayName)", () => {
@@ -575,50 +661,6 @@ describe("parentAlias", () => {
575
661
  });
576
662
  });
577
663
 
578
- // ── isReactionTagLine ────────────────────────────────────────────────────────
579
-
580
- describe("isReactionTagLine", () => {
581
- // Pinned to the exact shapes `renderReaction` and the overflow trailer
582
- // produce. The helper is the public contract that lets consumers
583
- // re-label the transcript without double-attributing reaction lines,
584
- // so drift here silently breaks `buildActiveThreadBlockFromRenderable`.
585
- const alias = parentAlias("1700000000.000100");
586
-
587
- test("matches reaction-add line", () => {
588
- expect(
589
- isReactionTagLine(`[11/14/23 14:28 @bob reacted 👍 to ${alias}]`),
590
- ).toBe(true);
591
- });
592
-
593
- test("matches reaction-remove line", () => {
594
- expect(
595
- isReactionTagLine(`[11/14/23 14:28 @bob removed 👍 from ${alias}]`),
596
- ).toBe(true);
597
- });
598
-
599
- test("matches overflow trailer line", () => {
600
- expect(isReactionTagLine(`[…and 2 more reactions to ${alias}]`)).toBe(true);
601
- });
602
-
603
- test("does not match a regular message tag line", () => {
604
- expect(isReactionTagLine("[11/14/23 14:25 @alice]: hi")).toBe(false);
605
- });
606
-
607
- test("does not match content-only assistant output", () => {
608
- expect(isReactionTagLine("on it. here's the answer")).toBe(false);
609
- });
610
-
611
- test("does not match the `[deleted]` sentinel", () => {
612
- expect(isReactionTagLine("[deleted]")).toBe(false);
613
- });
614
-
615
- test("does not match a user-deleted marker", () => {
616
- expect(
617
- isReactionTagLine("[11/14/23 14:25 @alice — deleted 11/14/23 14:32]"),
618
- ).toBe(false);
619
- });
620
- });
621
-
622
664
  // ── reaction cap ─────────────────────────────────────────────────────────────
623
665
 
624
666
  describe("renderSlackTranscript — reaction cap", () => {
@@ -865,13 +907,12 @@ describe("renderSlackTranscript — four design-brief scenarios", () => {
865
907
  userMsg(replyTs, "@dan", "I'll join", { threadTs: aliceTopTs }),
866
908
  ];
867
909
  const out = renderSlackTranscript(messages);
868
- const aliceAlias = parentAlias(aliceTopTs);
869
910
  expect(extractTagLineTexts(out)).toEqual([
870
911
  "[11/14/23 14:25 @alice]: lunch?",
871
- `[11/14/23 14:26 @bob → ${aliceAlias}]: yes!`,
872
- `[11/14/23 14:27 @alice → ${aliceAlias}]: 12:30 ok?`,
912
+ "[11/14/23 14:26 @bob]: yes!",
913
+ "[11/14/23 14:27 @alice]: 12:30 ok?",
873
914
  "[11/14/23 14:28 @carol]: standup soon",
874
- `[11/14/23 14:28 @dan → ${aliceAlias}]: I'll join`,
915
+ "[11/14/23 14:28 @dan]: I'll join",
875
916
  ]);
876
917
  });
877
918
 
@@ -883,12 +924,8 @@ describe("renderSlackTranscript — four design-brief scenarios", () => {
883
924
  userMsg(replyTs, "@ed", "joining now", { threadTs: carolTopTs }),
884
925
  ];
885
926
  const out = renderSlackTranscript(messages);
886
- const carolAlias = parentAlias(carolTopTs);
887
927
  const texts = extractTagLineTexts(out);
888
- // The reply tag points at carol's alias; carol's top stays untagged.
889
- expect(texts[texts.length - 1]).toBe(
890
- `[11/14/23 14:28 @ed → ${carolAlias}]: joining now`,
891
- );
928
+ expect(texts[texts.length - 1]).toBe("[11/14/23 14:28 @ed]: joining now");
892
929
  expect(texts[3]).toBe("[11/14/23 14:28 @carol]: standup soon");
893
930
  });
894
931
 
@@ -900,11 +937,8 @@ describe("renderSlackTranscript — four design-brief scenarios", () => {
900
937
  userMsg(replyTs, "@frank", "+1", { threadTs: carolTopTs }),
901
938
  ];
902
939
  const out = renderSlackTranscript(messages);
903
- const carolAlias = parentAlias(carolTopTs);
904
940
  const texts = extractTagLineTexts(out);
905
- expect(texts[texts.length - 1]).toBe(
906
- `[11/14/23 14:28 @frank → ${carolAlias}]: +1`,
907
- );
941
+ expect(texts[texts.length - 1]).toBe("[11/14/23 14:28 @frank]: +1");
908
942
  });
909
943
 
910
944
  test("scenario: new top-level message (no threadTs)", () => {
@@ -924,7 +958,7 @@ describe("renderSlackTranscript — four design-brief scenarios", () => {
924
958
  // ── mixed legacy + post-upgrade fixture ──────────────────────────────────────
925
959
 
926
960
  describe("renderSlackTranscript — mixed legacy + post-upgrade", () => {
927
- test("legacy rows render flat with no thread tag and intermix chronologically", () => {
961
+ test("legacy rows intermix chronologically with post-upgrade rows", () => {
928
962
  const messages: RenderableSlackMessage[] = [
929
963
  // Post-upgrade: 14:28 reply in alice's thread
930
964
  userMsg("1699972080.000900", "@bob", "yes!", { threadTs: TS_14_25 }),
@@ -935,16 +969,14 @@ describe("renderSlackTranscript — mixed legacy + post-upgrade", () => {
935
969
  userMsg(TS_14_25, "@alice", "lunch?"),
936
970
  ];
937
971
  const out = renderSlackTranscript(messages);
938
- const alias = parentAlias(TS_14_25);
939
972
 
940
973
  const texts = extractTagLineTexts(out);
941
974
  expect(texts).toEqual([
942
975
  "[11/14/23 14:25 @alice]: lunch?",
943
976
  "[11/14/23 14:26 @dana]: drive-by note",
944
- `[11/14/23 14:28 @bob → ${alias}]: yes!`,
977
+ "[11/14/23 14:28 @bob]: yes!",
945
978
  ]);
946
- // Ensure the legacy row has no arrow.
947
- expect(texts[1].includes("→")).toBe(false);
979
+ expect(texts.every((text) => !text.includes("→"))).toBe(true);
948
980
  });
949
981
 
950
982
  test("legacy assistant row carries assistant role and emits content verbatim", () => {