@vellumai/assistant 0.4.17 → 0.4.18

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 (515) hide show
  1. package/eslint.config.mjs +2 -2
  2. package/package.json +1 -1
  3. package/src/__tests__/access-request-decision.test.ts +128 -120
  4. package/src/__tests__/account-registry.test.ts +121 -110
  5. package/src/__tests__/active-skill-tools.test.ts +200 -172
  6. package/src/__tests__/actor-token-service.test.ts +341 -274
  7. package/src/__tests__/agent-loop-thinking.test.ts +28 -19
  8. package/src/__tests__/agent-loop.test.ts +798 -378
  9. package/src/__tests__/anthropic-provider.test.ts +405 -247
  10. package/src/__tests__/app-builder-tool-scripts.test.ts +97 -97
  11. package/src/__tests__/app-bundler.test.ts +112 -79
  12. package/src/__tests__/app-executors.test.ts +205 -178
  13. package/src/__tests__/app-git-history.test.ts +90 -73
  14. package/src/__tests__/app-git-service.test.ts +67 -53
  15. package/src/__tests__/app-open-proxy.test.ts +29 -25
  16. package/src/__tests__/approval-conversation-turn.test.ts +100 -81
  17. package/src/__tests__/approval-hardcoded-copy-guard.test.ts +45 -17
  18. package/src/__tests__/approval-message-composer.test.ts +119 -119
  19. package/src/__tests__/approval-primitive.test.ts +264 -233
  20. package/src/__tests__/approval-routes-http.test.ts +4 -3
  21. package/src/__tests__/asset-materialize-tool.test.ts +250 -178
  22. package/src/__tests__/asset-search-tool.test.ts +251 -191
  23. package/src/__tests__/assistant-attachment-directive.test.ts +187 -142
  24. package/src/__tests__/assistant-attachments.test.ts +254 -186
  25. package/src/__tests__/assistant-event-hub.test.ts +105 -63
  26. package/src/__tests__/assistant-event.test.ts +66 -58
  27. package/src/__tests__/assistant-events-sse-hardening.test.ts +113 -73
  28. package/src/__tests__/assistant-feature-flag-guard.test.ts +78 -52
  29. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +48 -45
  30. package/src/__tests__/assistant-feature-flags-integration.test.ts +118 -77
  31. package/src/__tests__/assistant-id-boundary-guard.test.ts +158 -104
  32. package/src/__tests__/attachments-store.test.ts +240 -183
  33. package/src/__tests__/attachments.test.ts +70 -62
  34. package/src/__tests__/audit-log-rotation.test.ts +50 -35
  35. package/src/__tests__/browser-fill-credential.test.ts +169 -101
  36. package/src/__tests__/browser-manager.test.ts +97 -75
  37. package/src/__tests__/browser-runtime-check.test.ts +16 -15
  38. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +12 -10
  39. package/src/__tests__/browser-skill-endstate.test.ts +97 -72
  40. package/src/__tests__/bundle-scanner.test.ts +47 -22
  41. package/src/__tests__/bundled-asset.test.ts +74 -47
  42. package/src/__tests__/call-constants.test.ts +19 -19
  43. package/src/__tests__/call-controller.test.ts +0 -1
  44. package/src/__tests__/call-conversation-messages.test.ts +90 -65
  45. package/src/__tests__/call-domain.test.ts +149 -121
  46. package/src/__tests__/call-pointer-message-composer.test.ts +113 -83
  47. package/src/__tests__/call-pointer-messages.test.ts +213 -154
  48. package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +9 -10
  49. package/src/__tests__/call-recovery.test.ts +232 -212
  50. package/src/__tests__/call-routes-http.test.ts +0 -1
  51. package/src/__tests__/call-start-guardian-guard.test.ts +32 -30
  52. package/src/__tests__/call-state-machine.test.ts +62 -51
  53. package/src/__tests__/call-state.test.ts +89 -75
  54. package/src/__tests__/call-store.test.ts +387 -316
  55. package/src/__tests__/callback-handoff-copy.test.ts +84 -82
  56. package/src/__tests__/canonical-guardian-store.test.ts +331 -280
  57. package/src/__tests__/channel-approval-routes.test.ts +1643 -1115
  58. package/src/__tests__/channel-approval.test.ts +139 -137
  59. package/src/__tests__/channel-approvals.test.ts +0 -1
  60. package/src/__tests__/channel-delivery-store.test.ts +232 -194
  61. package/src/__tests__/channel-guardian.test.ts +5 -3
  62. package/src/__tests__/channel-invite-transport.test.ts +107 -92
  63. package/src/__tests__/channel-policy.test.ts +42 -38
  64. package/src/__tests__/channel-readiness-service.test.ts +119 -102
  65. package/src/__tests__/channel-reply-delivery.test.ts +147 -118
  66. package/src/__tests__/channel-retry-sweep.test.ts +153 -110
  67. package/src/__tests__/checker.test.ts +3309 -1850
  68. package/src/__tests__/clarification-resolver.test.ts +91 -79
  69. package/src/__tests__/classifier.test.ts +64 -54
  70. package/src/__tests__/claude-code-skill-regression.test.ts +42 -37
  71. package/src/__tests__/claude-code-tool-profiles.test.ts +31 -29
  72. package/src/__tests__/clawhub.test.ts +92 -82
  73. package/src/__tests__/cli.test.ts +30 -30
  74. package/src/__tests__/clipboard.test.ts +53 -46
  75. package/src/__tests__/commit-guarantee.test.ts +59 -52
  76. package/src/__tests__/commit-message-enrichment-service.test.ts +203 -75
  77. package/src/__tests__/compaction.benchmark.test.ts +33 -31
  78. package/src/__tests__/computer-use-session-compaction.test.ts +60 -50
  79. package/src/__tests__/computer-use-session-lifecycle.test.ts +145 -117
  80. package/src/__tests__/computer-use-session-working-dir.test.ts +62 -48
  81. package/src/__tests__/computer-use-skill-baseline.test.ts +22 -19
  82. package/src/__tests__/computer-use-skill-endstate.test.ts +45 -31
  83. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +121 -88
  84. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +65 -42
  85. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +33 -18
  86. package/src/__tests__/computer-use-tools.test.ts +121 -98
  87. package/src/__tests__/config-schema.test.ts +443 -347
  88. package/src/__tests__/config-watcher.test.ts +96 -81
  89. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +148 -133
  90. package/src/__tests__/conflict-intent-tokenization.test.ts +96 -78
  91. package/src/__tests__/conflict-policy.test.ts +151 -80
  92. package/src/__tests__/conflict-store.test.ts +203 -157
  93. package/src/__tests__/connection-policy.test.ts +89 -59
  94. package/src/__tests__/contacts-tools.test.ts +247 -178
  95. package/src/__tests__/context-memory-e2e.test.ts +306 -214
  96. package/src/__tests__/context-token-estimator.test.ts +114 -74
  97. package/src/__tests__/context-window-manager.test.ts +269 -167
  98. package/src/__tests__/contradiction-checker.test.ts +161 -135
  99. package/src/__tests__/conversation-attention-store.test.ts +350 -290
  100. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  101. package/src/__tests__/conversation-pairing.test.ts +220 -113
  102. package/src/__tests__/conversation-store.test.ts +390 -235
  103. package/src/__tests__/credential-broker-browser-fill.test.ts +325 -250
  104. package/src/__tests__/credential-broker-server-use.test.ts +283 -243
  105. package/src/__tests__/credential-broker.test.ts +128 -74
  106. package/src/__tests__/credential-host-pattern-match.test.ts +64 -44
  107. package/src/__tests__/credential-metadata-store.test.ts +360 -311
  108. package/src/__tests__/credential-policy-validate.test.ts +81 -65
  109. package/src/__tests__/credential-resolve.test.ts +212 -145
  110. package/src/__tests__/credential-security-e2e.test.ts +144 -103
  111. package/src/__tests__/credential-security-invariants.test.ts +253 -208
  112. package/src/__tests__/credential-selection.test.ts +254 -146
  113. package/src/__tests__/credential-vault-unit.test.ts +531 -341
  114. package/src/__tests__/credential-vault.test.ts +761 -484
  115. package/src/__tests__/daemon-assistant-events.test.ts +91 -66
  116. package/src/__tests__/daemon-lifecycle.test.ts +258 -190
  117. package/src/__tests__/daemon-server-session-init.test.ts +0 -1
  118. package/src/__tests__/date-context.test.ts +314 -249
  119. package/src/__tests__/db-migration-rollback.test.ts +259 -130
  120. package/src/__tests__/db-schedule-syntax-migration.test.ts +78 -41
  121. package/src/__tests__/delete-managed-skill-tool.test.ts +77 -53
  122. package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -1
  123. package/src/__tests__/dictation-mode-detection.test.ts +77 -55
  124. package/src/__tests__/dictation-profile-store.test.ts +70 -56
  125. package/src/__tests__/dictation-text-processing.test.ts +53 -35
  126. package/src/__tests__/diff.test.ts +102 -98
  127. package/src/__tests__/domain-normalize.test.ts +54 -54
  128. package/src/__tests__/domain-policy.test.ts +71 -55
  129. package/src/__tests__/dynamic-page-surface.test.ts +31 -33
  130. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +69 -69
  131. package/src/__tests__/edit-engine.test.ts +56 -56
  132. package/src/__tests__/elevenlabs-client.test.ts +117 -91
  133. package/src/__tests__/elevenlabs-config.test.ts +32 -31
  134. package/src/__tests__/email-classifier.test.ts +15 -12
  135. package/src/__tests__/email-cli.test.ts +121 -108
  136. package/src/__tests__/emit-signal-routing-intent.test.ts +76 -69
  137. package/src/__tests__/encrypted-store.test.ts +180 -154
  138. package/src/__tests__/entity-extractor.test.ts +108 -87
  139. package/src/__tests__/entity-search.test.ts +664 -258
  140. package/src/__tests__/ephemeral-permissions.test.ts +224 -188
  141. package/src/__tests__/event-bus.test.ts +81 -77
  142. package/src/__tests__/extract-email.test.ts +29 -20
  143. package/src/__tests__/file-edit-tool.test.ts +62 -44
  144. package/src/__tests__/file-ops-service.test.ts +131 -114
  145. package/src/__tests__/file-read-tool.test.ts +48 -31
  146. package/src/__tests__/file-write-tool.test.ts +43 -37
  147. package/src/__tests__/filesystem-tools.test.ts +238 -209
  148. package/src/__tests__/followup-tools.test.ts +237 -162
  149. package/src/__tests__/forbidden-legacy-symbols.test.ts +19 -20
  150. package/src/__tests__/frontmatter.test.ts +96 -81
  151. package/src/__tests__/fuzzy-match-property.test.ts +75 -81
  152. package/src/__tests__/fuzzy-match.test.ts +71 -65
  153. package/src/__tests__/gateway-client-managed-outbound.test.ts +76 -57
  154. package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
  155. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  156. package/src/__tests__/gemini-image-service.test.ts +113 -100
  157. package/src/__tests__/gemini-provider.test.ts +297 -220
  158. package/src/__tests__/get-weather.test.ts +188 -114
  159. package/src/__tests__/gmail-integration.test.ts +0 -1
  160. package/src/__tests__/guardian-action-conversation-turn.test.ts +226 -171
  161. package/src/__tests__/guardian-action-copy-generator.test.ts +111 -93
  162. package/src/__tests__/guardian-action-followup-executor.test.ts +0 -1
  163. package/src/__tests__/guardian-action-followup-store.test.ts +199 -167
  164. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +297 -250
  165. package/src/__tests__/guardian-action-late-reply.test.ts +462 -316
  166. package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +23 -18
  167. package/src/__tests__/guardian-action-store.test.ts +158 -109
  168. package/src/__tests__/guardian-action-sweep.test.ts +114 -100
  169. package/src/__tests__/guardian-actions-endpoint.test.ts +440 -256
  170. package/src/__tests__/guardian-control-plane-policy.test.ts +497 -331
  171. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +217 -215
  172. package/src/__tests__/guardian-dispatch.test.ts +316 -256
  173. package/src/__tests__/guardian-grant-minting.test.ts +247 -178
  174. package/src/__tests__/guardian-outbound-http.test.ts +5 -3
  175. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +99 -96
  176. package/src/__tests__/guardian-question-copy.test.ts +17 -17
  177. package/src/__tests__/guardian-question-mode.test.ts +134 -100
  178. package/src/__tests__/guardian-routing-invariants.test.ts +0 -1
  179. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  180. package/src/__tests__/guardian-verification-intent-routing.test.ts +94 -88
  181. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
  182. package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +0 -1
  183. package/src/__tests__/handle-user-message-secret-resume.test.ts +0 -1
  184. package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +92 -76
  185. package/src/__tests__/handlers-cu-observation-blob.test.ts +103 -70
  186. package/src/__tests__/handlers-ipc-blob-probe.test.ts +77 -51
  187. package/src/__tests__/handlers-slack-config.test.ts +63 -54
  188. package/src/__tests__/handlers-task-submit-slash.test.ts +18 -18
  189. package/src/__tests__/handlers-telegram-config.test.ts +662 -329
  190. package/src/__tests__/handlers-twitter-config.test.ts +525 -298
  191. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -2
  192. package/src/__tests__/headless-browser-interactions.test.ts +444 -280
  193. package/src/__tests__/headless-browser-navigate.test.ts +116 -79
  194. package/src/__tests__/headless-browser-read-tools.test.ts +123 -86
  195. package/src/__tests__/headless-browser-snapshot.test.ts +71 -56
  196. package/src/__tests__/heartbeat-service.test.ts +76 -58
  197. package/src/__tests__/history-repair-observability.test.ts +14 -14
  198. package/src/__tests__/history-repair.test.ts +171 -167
  199. package/src/__tests__/home-base-bootstrap.test.ts +30 -27
  200. package/src/__tests__/hooks-blocking.test.ts +86 -37
  201. package/src/__tests__/hooks-cli.test.ts +104 -68
  202. package/src/__tests__/hooks-config.test.ts +81 -43
  203. package/src/__tests__/hooks-discovery.test.ts +106 -96
  204. package/src/__tests__/hooks-integration.test.ts +78 -72
  205. package/src/__tests__/hooks-manager.test.ts +99 -61
  206. package/src/__tests__/hooks-runner.test.ts +94 -71
  207. package/src/__tests__/hooks-settings.test.ts +69 -64
  208. package/src/__tests__/hooks-templates.test.ts +85 -54
  209. package/src/__tests__/hooks-ts-runner.test.ts +82 -45
  210. package/src/__tests__/hooks-watch.test.ts +32 -22
  211. package/src/__tests__/host-file-edit-tool.test.ts +190 -148
  212. package/src/__tests__/host-file-read-tool.test.ts +86 -63
  213. package/src/__tests__/host-file-write-tool.test.ts +98 -64
  214. package/src/__tests__/host-shell-tool.test.ts +342 -233
  215. package/src/__tests__/inbound-invite-redemption.test.ts +0 -1
  216. package/src/__tests__/ingress-member-store.test.ts +163 -159
  217. package/src/__tests__/ingress-reconcile.test.ts +0 -1
  218. package/src/__tests__/ingress-routes-http.test.ts +441 -356
  219. package/src/__tests__/ingress-url-consistency.test.ts +125 -64
  220. package/src/__tests__/integration-status.test.ts +93 -73
  221. package/src/__tests__/intent-routing.test.ts +148 -118
  222. package/src/__tests__/invite-redemption-service.test.ts +163 -121
  223. package/src/__tests__/ipc-blob-store.test.ts +104 -91
  224. package/src/__tests__/ipc-contract-inventory.test.ts +27 -15
  225. package/src/__tests__/ipc-contract.test.ts +24 -23
  226. package/src/__tests__/ipc-protocol.test.ts +52 -46
  227. package/src/__tests__/ipc-roundtrip.benchmark.test.ts +61 -50
  228. package/src/__tests__/ipc-snapshot.test.ts +1135 -1056
  229. package/src/__tests__/ipc-validate.test.ts +240 -179
  230. package/src/__tests__/key-migration.test.ts +123 -90
  231. package/src/__tests__/keychain.test.ts +150 -123
  232. package/src/__tests__/lifecycle-docs-guard.test.ts +65 -64
  233. package/src/__tests__/llm-usage-store.test.ts +112 -87
  234. package/src/__tests__/managed-skill-lifecycle.test.ts +147 -108
  235. package/src/__tests__/managed-store.test.ts +411 -360
  236. package/src/__tests__/mcp-cli.test.ts +189 -123
  237. package/src/__tests__/mcp-health-check.test.ts +26 -21
  238. package/src/__tests__/media-generate-image.test.ts +122 -99
  239. package/src/__tests__/media-reuse-story.e2e.test.ts +282 -214
  240. package/src/__tests__/media-visibility-policy.test.ts +86 -38
  241. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +146 -100
  242. package/src/__tests__/memory-lifecycle-e2e.test.ts +385 -297
  243. package/src/__tests__/memory-query-builder.test.ts +32 -33
  244. package/src/__tests__/memory-recall-quality.test.ts +761 -407
  245. package/src/__tests__/memory-regressions.experimental.test.ts +443 -380
  246. package/src/__tests__/memory-regressions.test.ts +3725 -2642
  247. package/src/__tests__/memory-retrieval-budget.test.ts +7 -8
  248. package/src/__tests__/memory-retrieval.benchmark.test.ts +144 -109
  249. package/src/__tests__/memory-upsert-concurrency.test.ts +292 -201
  250. package/src/__tests__/messaging-send-tool.test.ts +36 -29
  251. package/src/__tests__/migration-cli-flows.test.ts +69 -53
  252. package/src/__tests__/migration-ordering.test.ts +103 -86
  253. package/src/__tests__/mime-builder.test.ts +55 -32
  254. package/src/__tests__/mock-signup-server.test.ts +384 -246
  255. package/src/__tests__/model-intents.test.ts +61 -37
  256. package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +9 -12
  257. package/src/__tests__/no-is-trusted-guard.test.ts +24 -21
  258. package/src/__tests__/non-member-access-request.test.ts +3 -2
  259. package/src/__tests__/notification-broadcaster.test.ts +99 -81
  260. package/src/__tests__/notification-decision-fallback.test.ts +223 -178
  261. package/src/__tests__/notification-decision-strategy.test.ts +375 -337
  262. package/src/__tests__/notification-deep-link.test.ts +67 -61
  263. package/src/__tests__/notification-guardian-path.test.ts +248 -206
  264. package/src/__tests__/notification-routing-intent.test.ts +166 -93
  265. package/src/__tests__/notification-thread-candidate-validation.test.ts +78 -75
  266. package/src/__tests__/notification-thread-candidates.test.ts +64 -61
  267. package/src/__tests__/oauth-callback-registry.test.ts +40 -30
  268. package/src/__tests__/oauth-connect-handler.test.ts +109 -89
  269. package/src/__tests__/oauth-scope-policy.test.ts +63 -55
  270. package/src/__tests__/oauth2-gateway-transport.test.ts +252 -174
  271. package/src/__tests__/onboarding-starter-tasks.test.ts +93 -89
  272. package/src/__tests__/onboarding-template-contract.test.ts +93 -94
  273. package/src/__tests__/openai-provider.test.ts +366 -274
  274. package/src/__tests__/pairing-concurrent.test.ts +18 -12
  275. package/src/__tests__/pairing-routes.test.ts +45 -41
  276. package/src/__tests__/parallel-tool.benchmark.test.ts +108 -58
  277. package/src/__tests__/parser.test.ts +316 -226
  278. package/src/__tests__/path-classifier.test.ts +24 -25
  279. package/src/__tests__/path-policy.test.ts +187 -147
  280. package/src/__tests__/phone.test.ts +36 -36
  281. package/src/__tests__/platform-move-helper.test.ts +48 -40
  282. package/src/__tests__/platform-socket-path.test.ts +23 -24
  283. package/src/__tests__/platform-workspace-migration.test.ts +464 -414
  284. package/src/__tests__/platform.test.ts +61 -53
  285. package/src/__tests__/playbook-execution.test.ts +397 -265
  286. package/src/__tests__/playbook-tools.test.ts +267 -196
  287. package/src/__tests__/prebuilt-home-base-seed.test.ts +30 -27
  288. package/src/__tests__/pricing.test.ts +316 -136
  289. package/src/__tests__/profile-compiler.test.ts +206 -188
  290. package/src/__tests__/provider-commit-message-generator.test.ts +114 -106
  291. package/src/__tests__/provider-error-scenarios.test.ts +212 -158
  292. package/src/__tests__/provider-fail-open-selection.test.ts +51 -44
  293. package/src/__tests__/provider-registry-ollama.test.ts +13 -9
  294. package/src/__tests__/provider-streaming.benchmark.test.ts +232 -183
  295. package/src/__tests__/proxy-approval-callback.test.ts +180 -119
  296. package/src/__tests__/public-ingress-urls.test.ts +112 -94
  297. package/src/__tests__/qdrant-manager.test.ts +147 -98
  298. package/src/__tests__/ratelimit.test.ts +152 -82
  299. package/src/__tests__/recording-handler.test.ts +273 -151
  300. package/src/__tests__/recording-intent-fallback.test.ts +94 -75
  301. package/src/__tests__/recording-intent-handler.test.ts +0 -1
  302. package/src/__tests__/recording-intent.test.ts +578 -379
  303. package/src/__tests__/recording-state-machine.test.ts +530 -316
  304. package/src/__tests__/recurrence-engine-rruleset.test.ts +150 -92
  305. package/src/__tests__/recurrence-engine.test.ts +81 -41
  306. package/src/__tests__/recurrence-types.test.ts +63 -44
  307. package/src/__tests__/relay-server.test.ts +2131 -1602
  308. package/src/__tests__/reminder-store.test.ts +158 -80
  309. package/src/__tests__/reminder.test.ts +113 -109
  310. package/src/__tests__/remote-skill-policy.test.ts +96 -72
  311. package/src/__tests__/request-file-tool.test.ts +74 -67
  312. package/src/__tests__/response-tier.test.ts +131 -74
  313. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  314. package/src/__tests__/runtime-events-sse-parity.test.ts +167 -145
  315. package/src/__tests__/runtime-events-sse.test.ts +0 -1
  316. package/src/__tests__/sandbox-diagnostics.test.ts +66 -56
  317. package/src/__tests__/sandbox-host-parity.test.ts +377 -301
  318. package/src/__tests__/scaffold-managed-skill-tool.test.ts +213 -161
  319. package/src/__tests__/schedule-store.test.ts +268 -205
  320. package/src/__tests__/schedule-tools.test.ts +702 -524
  321. package/src/__tests__/scheduler-recurrence.test.ts +240 -130
  322. package/src/__tests__/scoped-approval-grants.test.ts +258 -168
  323. package/src/__tests__/scoped-grant-security-matrix.test.ts +160 -146
  324. package/src/__tests__/script-proxy-certs.test.ts +38 -35
  325. package/src/__tests__/script-proxy-connect-tunnel.test.ts +71 -46
  326. package/src/__tests__/script-proxy-decision-trace.test.ts +161 -84
  327. package/src/__tests__/script-proxy-http-forwarder.test.ts +146 -129
  328. package/src/__tests__/script-proxy-injection-runtime.test.ts +139 -113
  329. package/src/__tests__/script-proxy-mitm-handler.test.ts +226 -142
  330. package/src/__tests__/script-proxy-policy-runtime.test.ts +126 -86
  331. package/src/__tests__/script-proxy-policy.test.ts +308 -153
  332. package/src/__tests__/script-proxy-rewrite-specificity.test.ts +74 -62
  333. package/src/__tests__/script-proxy-router.test.ts +111 -77
  334. package/src/__tests__/script-proxy-session-manager.test.ts +156 -113
  335. package/src/__tests__/script-proxy-session-runtime.test.ts +28 -24
  336. package/src/__tests__/secret-allowlist.test.ts +105 -90
  337. package/src/__tests__/secret-ingress-handler.test.ts +41 -30
  338. package/src/__tests__/secret-onetime-send.test.ts +67 -50
  339. package/src/__tests__/secret-prompt-log-hygiene.test.ts +35 -31
  340. package/src/__tests__/secret-response-routing.test.ts +50 -41
  341. package/src/__tests__/secret-scanner-executor.test.ts +152 -111
  342. package/src/__tests__/secret-scanner.test.ts +495 -413
  343. package/src/__tests__/secure-keys.test.ts +132 -121
  344. package/src/__tests__/send-endpoint-busy.test.ts +0 -1
  345. package/src/__tests__/send-notification-tool.test.ts +43 -42
  346. package/src/__tests__/sensitive-output-placeholders.test.ts +72 -64
  347. package/src/__tests__/sequence-store.test.ts +335 -167
  348. package/src/__tests__/server-history-render.test.ts +341 -202
  349. package/src/__tests__/session-abort-tool-results.test.ts +133 -70
  350. package/src/__tests__/session-confirmation-signals.test.ts +252 -160
  351. package/src/__tests__/session-conflict-gate.test.ts +775 -585
  352. package/src/__tests__/session-error.test.ts +222 -191
  353. package/src/__tests__/session-evictor.test.ts +79 -62
  354. package/src/__tests__/session-init.benchmark.test.ts +170 -108
  355. package/src/__tests__/session-load-history-repair.test.ts +273 -139
  356. package/src/__tests__/session-messaging-secret-redirect.test.ts +130 -90
  357. package/src/__tests__/session-pre-run-repair.test.ts +106 -59
  358. package/src/__tests__/session-profile-injection.test.ts +198 -130
  359. package/src/__tests__/session-provider-retry-repair.test.ts +223 -141
  360. package/src/__tests__/session-queue.test.ts +624 -321
  361. package/src/__tests__/session-runtime-assembly.test.ts +425 -329
  362. package/src/__tests__/session-runtime-workspace.test.ts +69 -61
  363. package/src/__tests__/session-skill-tools.test.ts +973 -678
  364. package/src/__tests__/session-slash-known.test.ts +185 -133
  365. package/src/__tests__/session-slash-queue.test.ts +147 -81
  366. package/src/__tests__/session-slash-unknown.test.ts +135 -90
  367. package/src/__tests__/session-surfaces-task-progress.test.ts +122 -87
  368. package/src/__tests__/session-tool-setup-app-refresh.test.ts +338 -177
  369. package/src/__tests__/session-tool-setup-memory-scope.test.ts +63 -40
  370. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +60 -37
  371. package/src/__tests__/session-tool-setup-tools-disabled.test.ts +28 -26
  372. package/src/__tests__/session-undo.test.ts +43 -30
  373. package/src/__tests__/session-workspace-cache-state.test.ts +108 -67
  374. package/src/__tests__/session-workspace-injection.test.ts +245 -117
  375. package/src/__tests__/session-workspace-tool-tracking.test.ts +260 -93
  376. package/src/__tests__/shared-filesystem-errors.test.ts +47 -47
  377. package/src/__tests__/shell-credential-ref.test.ts +126 -90
  378. package/src/__tests__/shell-identity.test.ts +134 -111
  379. package/src/__tests__/shell-parser-fuzz.test.ts +263 -179
  380. package/src/__tests__/shell-parser-property.test.ts +435 -288
  381. package/src/__tests__/shell-tool-proxy-mode.test.ts +142 -70
  382. package/src/__tests__/size-guard.test.ts +42 -44
  383. package/src/__tests__/skill-feature-flags-integration.test.ts +79 -52
  384. package/src/__tests__/skill-feature-flags.test.ts +75 -47
  385. package/src/__tests__/skill-include-graph.test.ts +143 -148
  386. package/src/__tests__/skill-load-feature-flag.test.ts +94 -59
  387. package/src/__tests__/skill-load-tool.test.ts +371 -199
  388. package/src/__tests__/skill-projection-feature-flag.test.ts +131 -88
  389. package/src/__tests__/skill-projection.benchmark.test.ts +93 -65
  390. package/src/__tests__/skill-script-runner-host.test.ts +460 -250
  391. package/src/__tests__/skill-script-runner-sandbox.test.ts +168 -108
  392. package/src/__tests__/skill-script-runner.test.ts +115 -74
  393. package/src/__tests__/skill-tool-factory.test.ts +140 -96
  394. package/src/__tests__/skill-tool-manifest.test.ts +306 -210
  395. package/src/__tests__/skill-version-hash.test.ts +70 -56
  396. package/src/__tests__/skills.test.ts +0 -1
  397. package/src/__tests__/slack-channel-config.test.ts +127 -84
  398. package/src/__tests__/slack-skill.test.ts +60 -47
  399. package/src/__tests__/slash-commands-catalog.test.ts +37 -31
  400. package/src/__tests__/slash-commands-parser.test.ts +71 -64
  401. package/src/__tests__/slash-commands-resolver.test.ts +143 -107
  402. package/src/__tests__/slash-commands-rewrite.test.ts +22 -22
  403. package/src/__tests__/speaker-identification.test.ts +28 -25
  404. package/src/__tests__/starter-bundle.test.ts +27 -23
  405. package/src/__tests__/starter-task-flow.test.ts +67 -52
  406. package/src/__tests__/subagent-manager-notify.test.ts +154 -108
  407. package/src/__tests__/subagent-tools.test.ts +311 -270
  408. package/src/__tests__/subagent-types.test.ts +40 -40
  409. package/src/__tests__/surface-mutex-cleanup.test.ts +42 -30
  410. package/src/__tests__/swarm-dag-pathological.test.ts +122 -111
  411. package/src/__tests__/swarm-orchestrator.test.ts +135 -101
  412. package/src/__tests__/swarm-plan-validator.test.ts +125 -73
  413. package/src/__tests__/swarm-recursion.test.ts +58 -46
  414. package/src/__tests__/swarm-router-planner.test.ts +99 -74
  415. package/src/__tests__/swarm-session-integration.test.ts +148 -91
  416. package/src/__tests__/swarm-tool.test.ts +65 -45
  417. package/src/__tests__/swarm-worker-backend.test.ts +59 -45
  418. package/src/__tests__/swarm-worker-runner.test.ts +133 -118
  419. package/src/__tests__/system-prompt.test.ts +290 -256
  420. package/src/__tests__/task-compiler.test.ts +176 -120
  421. package/src/__tests__/task-management-tools.test.ts +561 -456
  422. package/src/__tests__/task-memory-cleanup.test.ts +627 -362
  423. package/src/__tests__/task-runner.test.ts +117 -94
  424. package/src/__tests__/task-scheduler.test.ts +113 -84
  425. package/src/__tests__/task-tools.test.ts +349 -264
  426. package/src/__tests__/terminal-sandbox.test.ts +138 -108
  427. package/src/__tests__/terminal-tools.test.ts +350 -305
  428. package/src/__tests__/thread-seed-composer.test.ts +307 -180
  429. package/src/__tests__/tool-approval-handler.test.ts +238 -137
  430. package/src/__tests__/tool-audit-listener.test.ts +69 -69
  431. package/src/__tests__/tool-domain-event-publisher.test.ts +142 -132
  432. package/src/__tests__/tool-execution-abort-cleanup.test.ts +153 -146
  433. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +136 -105
  434. package/src/__tests__/tool-executor-lifecycle-events.test.ts +355 -239
  435. package/src/__tests__/tool-executor-redaction.test.ts +112 -109
  436. package/src/__tests__/tool-executor-shell-integration.test.ts +130 -79
  437. package/src/__tests__/tool-executor.test.ts +1274 -674
  438. package/src/__tests__/tool-grant-request-escalation.test.ts +401 -283
  439. package/src/__tests__/tool-metrics-listener.test.ts +97 -85
  440. package/src/__tests__/tool-notification-listener.test.ts +42 -25
  441. package/src/__tests__/tool-permission-simulate-handler.test.ts +137 -113
  442. package/src/__tests__/tool-policy.test.ts +44 -25
  443. package/src/__tests__/tool-profiling-listener.test.ts +99 -93
  444. package/src/__tests__/tool-result-truncation.test.ts +5 -4
  445. package/src/__tests__/tool-trace-listener.test.ts +131 -111
  446. package/src/__tests__/top-level-renderer.test.ts +62 -58
  447. package/src/__tests__/top-level-scanner.test.ts +68 -64
  448. package/src/__tests__/trace-emitter.test.ts +56 -56
  449. package/src/__tests__/trust-context-guards.test.ts +65 -65
  450. package/src/__tests__/trust-store.test.ts +1239 -806
  451. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  452. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
  453. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +3 -2
  454. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -2
  455. package/src/__tests__/trusted-contact-verification.test.ts +251 -231
  456. package/src/__tests__/turn-commit.test.ts +259 -200
  457. package/src/__tests__/twilio-provider.test.ts +140 -126
  458. package/src/__tests__/twilio-rest.test.ts +22 -18
  459. package/src/__tests__/twilio-routes-elevenlabs.test.ts +0 -1
  460. package/src/__tests__/twilio-routes-twiml.test.ts +55 -55
  461. package/src/__tests__/twilio-routes.test.ts +0 -1
  462. package/src/__tests__/twitter-auth-handler.test.ts +184 -139
  463. package/src/__tests__/twitter-cli-error-shaping.test.ts +88 -73
  464. package/src/__tests__/twitter-cli-routing.test.ts +146 -99
  465. package/src/__tests__/twitter-oauth-client.test.ts +82 -65
  466. package/src/__tests__/update-bulletin-format.test.ts +69 -66
  467. package/src/__tests__/update-bulletin-state.test.ts +66 -60
  468. package/src/__tests__/update-bulletin.test.ts +150 -114
  469. package/src/__tests__/update-template-contract.test.ts +15 -10
  470. package/src/__tests__/url-safety.test.ts +288 -265
  471. package/src/__tests__/user-reference.test.ts +32 -32
  472. package/src/__tests__/view-image-tool.test.ts +118 -96
  473. package/src/__tests__/voice-invite-redemption.test.ts +111 -106
  474. package/src/__tests__/voice-quality.test.ts +117 -102
  475. package/src/__tests__/voice-scoped-grant-consumer.test.ts +204 -146
  476. package/src/__tests__/voice-session-bridge.test.ts +351 -216
  477. package/src/__tests__/weather-skill-regression.test.ts +170 -120
  478. package/src/__tests__/web-fetch.test.ts +664 -526
  479. package/src/__tests__/web-search.test.ts +379 -213
  480. package/src/__tests__/work-item-output.test.ts +90 -53
  481. package/src/__tests__/workspace-git-service.test.ts +437 -356
  482. package/src/__tests__/workspace-heartbeat-service.test.ts +125 -91
  483. package/src/__tests__/workspace-lifecycle.test.ts +98 -64
  484. package/src/__tests__/workspace-policy.test.ts +139 -71
  485. package/src/commands/__tests__/cc-command-registry.test.ts +142 -134
  486. package/src/config/__tests__/feature-flag-registry-guard.test.ts +48 -39
  487. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +25 -10
  488. package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +0 -1
  489. package/src/config/bundled-skills/messaging/SKILL.md +4 -3
  490. package/src/config/bundled-skills/messaging/tools/gmail-outreach-scan.ts +15 -5
  491. package/src/config/bundled-skills/messaging/tools/gmail-sender-digest.ts +16 -5
  492. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +34 -32
  493. package/src/config/bundled-tool-registry.ts +2 -0
  494. package/src/config/env.ts +3 -4
  495. package/src/memory/db-connection.ts +16 -10
  496. package/src/messaging/providers/gmail/adapter.ts +10 -3
  497. package/src/messaging/providers/gmail/client.ts +280 -72
  498. package/src/runtime/auth/__tests__/context.test.ts +75 -65
  499. package/src/runtime/auth/__tests__/credential-service.test.ts +137 -114
  500. package/src/runtime/auth/__tests__/guard-tests.test.ts +84 -90
  501. package/src/runtime/auth/__tests__/ipc-auth-context.test.ts +40 -40
  502. package/src/runtime/auth/__tests__/middleware.test.ts +80 -74
  503. package/src/runtime/auth/__tests__/policy.test.ts +9 -9
  504. package/src/runtime/auth/__tests__/route-policy.test.ts +76 -65
  505. package/src/runtime/auth/__tests__/scopes.test.ts +68 -60
  506. package/src/runtime/auth/__tests__/subject.test.ts +54 -54
  507. package/src/runtime/auth/__tests__/token-service.test.ts +115 -108
  508. package/src/runtime/auth/scopes.ts +3 -0
  509. package/src/runtime/auth/token-service.ts +4 -1
  510. package/src/runtime/auth/types.ts +2 -1
  511. package/src/runtime/http-server.ts +2 -1
  512. package/src/security/secure-keys.ts +103 -53
  513. package/src/tools/browser/__tests__/auth-cache.test.ts +69 -63
  514. package/src/tools/browser/__tests__/auth-detector.test.ts +218 -157
  515. package/src/tools/browser/__tests__/jit-auth.test.ts +83 -99
@@ -1,6 +1,9 @@
1
- import { describe, expect,test } from 'bun:test';
1
+ import { describe, expect, test } from "bun:test";
2
2
 
3
- import { buildTemporalContext, extractUserTimeZoneFromDynamicProfile } from '../daemon/date-context.js';
3
+ import {
4
+ buildTemporalContext,
5
+ extractUserTimeZoneFromDynamicProfile,
6
+ } from "../daemon/date-context.js";
4
7
 
5
8
  // Fixed timestamps for deterministic assertions (all UTC midday to avoid DST edge cases).
6
9
 
@@ -23,197 +26,205 @@ const FRI_FEB_27 = Date.UTC(2026, 1, 27, 12, 0, 0);
23
26
  // Basic structure
24
27
  // ---------------------------------------------------------------------------
25
28
 
26
- describe('buildTemporalContext', () => {
27
- test('returns output wrapped in <temporal_context> tags', () => {
28
- const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: 'UTC' });
29
- expect(result).toStartWith('<temporal_context>');
30
- expect(result).toEndWith('</temporal_context>');
29
+ describe("buildTemporalContext", () => {
30
+ test("returns output wrapped in <temporal_context> tags", () => {
31
+ const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: "UTC" });
32
+ expect(result).toStartWith("<temporal_context>");
33
+ expect(result).toEndWith("</temporal_context>");
31
34
  });
32
35
 
33
- test('includes today date and weekday', () => {
34
- const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: 'UTC' });
35
- expect(result).toContain('Today: 2026-02-18 (Wednesday)');
36
+ test("includes today date and weekday", () => {
37
+ const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: "UTC" });
38
+ expect(result).toContain("Today: 2026-02-18 (Wednesday)");
36
39
  });
37
40
 
38
- test('includes timezone', () => {
39
- const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: 'America/New_York' });
40
- expect(result).toContain('Timezone: America/New_York');
41
+ test("includes timezone", () => {
42
+ const result = buildTemporalContext({
43
+ nowMs: WED_FEB_18,
44
+ timeZone: "America/New_York",
45
+ });
46
+ expect(result).toContain("Timezone: America/New_York");
41
47
  });
42
48
 
43
- test('includes current local time as ISO 8601 with offset', () => {
44
- const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: 'UTC' });
45
- expect(result).toContain('Current local time: 2026-02-18T12:00:00+00:00');
49
+ test("includes current local time as ISO 8601 with offset", () => {
50
+ const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: "UTC" });
51
+ expect(result).toContain("Current local time: 2026-02-18T12:00:00+00:00");
46
52
  });
47
53
 
48
- test('includes current UTC time from assistant host clock', () => {
49
- const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: 'UTC' });
50
- expect(result).toContain('Current UTC time: 2026-02-18T12:00:00.000Z');
54
+ test("includes current UTC time from assistant host clock", () => {
55
+ const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: "UTC" });
56
+ expect(result).toContain("Current UTC time: 2026-02-18T12:00:00.000Z");
51
57
  });
52
58
 
53
- test('documents assistant host as the authoritative clock source', () => {
54
- const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: 'UTC' });
55
- expect(result).toContain('Clock source: assistant host machine');
59
+ test("documents assistant host as the authoritative clock source", () => {
60
+ const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: "UTC" });
61
+ expect(result).toContain("Clock source: assistant host machine");
56
62
  });
57
63
 
58
- test('uses user timezone when provided and records source metadata', () => {
64
+ test("uses user timezone when provided and records source metadata", () => {
59
65
  const result = buildTemporalContext({
60
66
  nowMs: WED_FEB_18,
61
- hostTimeZone: 'UTC',
62
- userTimeZone: 'America/New_York',
67
+ hostTimeZone: "UTC",
68
+ userTimeZone: "America/New_York",
63
69
  });
64
- expect(result).toContain('Timezone: America/New_York');
65
- expect(result).toContain('Current local time: 2026-02-18T07:00:00-05:00');
66
- expect(result).toContain('Assistant host timezone: UTC');
67
- expect(result).toContain('User timezone: America/New_York');
68
- expect(result).toContain('Timezone source: user_profile_memory');
70
+ expect(result).toContain("Timezone: America/New_York");
71
+ expect(result).toContain("Current local time: 2026-02-18T07:00:00-05:00");
72
+ expect(result).toContain("Assistant host timezone: UTC");
73
+ expect(result).toContain("User timezone: America/New_York");
74
+ expect(result).toContain("Timezone source: user_profile_memory");
69
75
  });
70
76
 
71
- test('uses configured user timezone when profile timezone is unavailable', () => {
77
+ test("uses configured user timezone when profile timezone is unavailable", () => {
72
78
  const result = buildTemporalContext({
73
79
  nowMs: WED_FEB_18,
74
- hostTimeZone: 'UTC',
75
- configuredUserTimeZone: 'America/Chicago',
80
+ hostTimeZone: "UTC",
81
+ configuredUserTimeZone: "America/Chicago",
76
82
  userTimeZone: null,
77
83
  });
78
- expect(result).toContain('Timezone: America/Chicago');
79
- expect(result).toContain('Current local time: 2026-02-18T06:00:00-06:00');
80
- expect(result).toContain('User timezone: America/Chicago');
81
- expect(result).toContain('Timezone source: user_settings');
84
+ expect(result).toContain("Timezone: America/Chicago");
85
+ expect(result).toContain("Current local time: 2026-02-18T06:00:00-06:00");
86
+ expect(result).toContain("User timezone: America/Chicago");
87
+ expect(result).toContain("Timezone source: user_settings");
82
88
  });
83
89
 
84
- test('configured user timezone takes precedence over profile timezone', () => {
90
+ test("configured user timezone takes precedence over profile timezone", () => {
85
91
  const result = buildTemporalContext({
86
92
  nowMs: WED_FEB_18,
87
- hostTimeZone: 'UTC',
88
- configuredUserTimeZone: 'America/Los_Angeles',
89
- userTimeZone: 'America/New_York',
93
+ hostTimeZone: "UTC",
94
+ configuredUserTimeZone: "America/Los_Angeles",
95
+ userTimeZone: "America/New_York",
90
96
  });
91
- expect(result).toContain('Timezone: America/Los_Angeles');
92
- expect(result).toContain('Current local time: 2026-02-18T04:00:00-08:00');
93
- expect(result).toContain('User timezone: America/Los_Angeles');
94
- expect(result).toContain('Timezone source: user_settings');
97
+ expect(result).toContain("Timezone: America/Los_Angeles");
98
+ expect(result).toContain("Current local time: 2026-02-18T04:00:00-08:00");
99
+ expect(result).toContain("User timezone: America/Los_Angeles");
100
+ expect(result).toContain("Timezone source: user_settings");
95
101
  });
96
102
 
97
- test('falls back to host timezone when user timezone is unavailable', () => {
103
+ test("falls back to host timezone when user timezone is unavailable", () => {
98
104
  const result = buildTemporalContext({
99
105
  nowMs: WED_FEB_18,
100
- hostTimeZone: 'UTC',
106
+ hostTimeZone: "UTC",
101
107
  userTimeZone: null,
102
108
  });
103
- expect(result).toContain('Timezone: UTC');
104
- expect(result).toContain('User timezone: unknown');
105
- expect(result).toContain('Timezone source: assistant_host_fallback');
109
+ expect(result).toContain("Timezone: UTC");
110
+ expect(result).toContain("User timezone: unknown");
111
+ expect(result).toContain("Timezone source: assistant_host_fallback");
106
112
  });
107
113
 
108
- test('accepts UTC/GMT offset-style user timezone values', () => {
114
+ test("accepts UTC/GMT offset-style user timezone values", () => {
109
115
  const result = buildTemporalContext({
110
116
  nowMs: WED_FEB_18,
111
- hostTimeZone: 'UTC',
112
- userTimeZone: 'UTC+2',
117
+ hostTimeZone: "UTC",
118
+ userTimeZone: "UTC+2",
113
119
  });
114
- expect(result).toContain('Timezone: Etc/GMT-2');
115
- expect(result).toContain('Current local time: 2026-02-18T14:00:00+02:00');
116
- expect(result).toContain('User timezone: Etc/GMT-2');
117
- expect(result).toContain('Timezone source: user_profile_memory');
120
+ expect(result).toContain("Timezone: Etc/GMT-2");
121
+ expect(result).toContain("Current local time: 2026-02-18T14:00:00+02:00");
122
+ expect(result).toContain("User timezone: Etc/GMT-2");
123
+ expect(result).toContain("Timezone source: user_profile_memory");
118
124
  });
119
125
 
120
- test('accepts fractional UTC/GMT offset-style user timezone values', () => {
126
+ test("accepts fractional UTC/GMT offset-style user timezone values", () => {
121
127
  const result = buildTemporalContext({
122
128
  nowMs: WED_FEB_18,
123
- hostTimeZone: 'UTC',
124
- userTimeZone: 'UTC+5:30',
129
+ hostTimeZone: "UTC",
130
+ userTimeZone: "UTC+5:30",
125
131
  });
126
- expect(result).toContain('Timezone: +05:30');
127
- expect(result).toContain('Current local time: 2026-02-18T17:30:00+05:30');
128
- expect(result).toContain('User timezone: +05:30');
129
- expect(result).toContain('Timezone source: user_profile_memory');
132
+ expect(result).toContain("Timezone: +05:30");
133
+ expect(result).toContain("Current local time: 2026-02-18T17:30:00+05:30");
134
+ expect(result).toContain("User timezone: +05:30");
135
+ expect(result).toContain("Timezone source: user_profile_memory");
130
136
  });
131
137
 
132
- test('includes week definitions', () => {
133
- const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: 'UTC' });
134
- expect(result).toContain('work week = Monday–Friday');
135
- expect(result).toContain('weekend = Saturday–Sunday');
138
+ test("includes week definitions", () => {
139
+ const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: "UTC" });
140
+ expect(result).toContain("work week = Monday–Friday");
141
+ expect(result).toContain("weekend = Saturday–Sunday");
136
142
  });
137
143
 
138
- test('formats midnight hours as 00 (never 24) in local ISO output', () => {
144
+ test("formats midnight hours as 00 (never 24) in local ISO output", () => {
139
145
  const justAfterMidnight = Date.UTC(2026, 1, 19, 0, 5, 0);
140
- const result = buildTemporalContext({ nowMs: justAfterMidnight, timeZone: 'UTC' });
141
- expect(result).toContain('Current local time: 2026-02-19T00:05:00+00:00');
142
- expect(result).not.toContain('T24:05:00');
146
+ const result = buildTemporalContext({
147
+ nowMs: justAfterMidnight,
148
+ timeZone: "UTC",
149
+ });
150
+ expect(result).toContain("Current local time: 2026-02-19T00:05:00+00:00");
151
+ expect(result).not.toContain("T24:05:00");
143
152
  });
144
153
  });
145
154
 
146
- describe('extractUserTimeZoneFromDynamicProfile', () => {
147
- test('extracts canonical timezone from explicit timezone profile line', () => {
155
+ describe("extractUserTimeZoneFromDynamicProfile", () => {
156
+ test("extracts canonical timezone from explicit timezone profile line", () => {
148
157
  const profile = [
149
- '<dynamic-user-profile>',
150
- '- timezone: Timezone is America/New_York.',
151
- '</dynamic-user-profile>',
152
- ].join('\n');
153
- expect(extractUserTimeZoneFromDynamicProfile(profile)).toBe('America/New_York');
158
+ "<dynamic-user-profile>",
159
+ "- timezone: Timezone is America/New_York.",
160
+ "</dynamic-user-profile>",
161
+ ].join("\n");
162
+ expect(extractUserTimeZoneFromDynamicProfile(profile)).toBe(
163
+ "America/New_York",
164
+ );
154
165
  });
155
166
 
156
- test('extracts timezone token from generic profile text when explicit line is absent', () => {
167
+ test("extracts timezone token from generic profile text when explicit line is absent", () => {
157
168
  const profile = [
158
- '<dynamic-user-profile>',
159
- '- location: Travels often between Europe and Asia (currently Europe/Paris).',
160
- '</dynamic-user-profile>',
161
- ].join('\n');
162
- expect(extractUserTimeZoneFromDynamicProfile(profile)).toBe('Europe/Paris');
169
+ "<dynamic-user-profile>",
170
+ "- location: Travels often between Europe and Asia (currently Europe/Paris).",
171
+ "</dynamic-user-profile>",
172
+ ].join("\n");
173
+ expect(extractUserTimeZoneFromDynamicProfile(profile)).toBe("Europe/Paris");
163
174
  });
164
175
 
165
- test('returns null when no valid timezone is present', () => {
176
+ test("returns null when no valid timezone is present", () => {
166
177
  const profile = [
167
- '<dynamic-user-profile>',
168
- '- timezone: Pacific time',
169
- '</dynamic-user-profile>',
170
- ].join('\n');
178
+ "<dynamic-user-profile>",
179
+ "- timezone: Pacific time",
180
+ "</dynamic-user-profile>",
181
+ ].join("\n");
171
182
  expect(extractUserTimeZoneFromDynamicProfile(profile)).toBeNull();
172
183
  });
173
184
 
174
- test('extracts UTC/GMT offset tokens from explicit timezone profile line', () => {
185
+ test("extracts UTC/GMT offset tokens from explicit timezone profile line", () => {
175
186
  const profile = [
176
- '<dynamic-user-profile>',
177
- '- timezone: UTC+2',
178
- '</dynamic-user-profile>',
179
- ].join('\n');
180
- expect(extractUserTimeZoneFromDynamicProfile(profile)).toBe('Etc/GMT-2');
187
+ "<dynamic-user-profile>",
188
+ "- timezone: UTC+2",
189
+ "</dynamic-user-profile>",
190
+ ].join("\n");
191
+ expect(extractUserTimeZoneFromDynamicProfile(profile)).toBe("Etc/GMT-2");
181
192
  });
182
193
 
183
- test('extracts GMT negative offset tokens from generic profile text', () => {
194
+ test("extracts GMT negative offset tokens from generic profile text", () => {
184
195
  const profile = [
185
- '<dynamic-user-profile>',
186
- '- preferences: schedule notifications in GMT-5 whenever possible.',
187
- '</dynamic-user-profile>',
188
- ].join('\n');
189
- expect(extractUserTimeZoneFromDynamicProfile(profile)).toBe('Etc/GMT+5');
196
+ "<dynamic-user-profile>",
197
+ "- preferences: schedule notifications in GMT-5 whenever possible.",
198
+ "</dynamic-user-profile>",
199
+ ].join("\n");
200
+ expect(extractUserTimeZoneFromDynamicProfile(profile)).toBe("Etc/GMT+5");
190
201
  });
191
202
 
192
- test('extracts fractional UTC offset tokens from explicit timezone profile line', () => {
203
+ test("extracts fractional UTC offset tokens from explicit timezone profile line", () => {
193
204
  const profile = [
194
- '<dynamic-user-profile>',
195
- '- timezone: UTC+5:30',
196
- '</dynamic-user-profile>',
197
- ].join('\n');
198
- expect(extractUserTimeZoneFromDynamicProfile(profile)).toBe('+05:30');
205
+ "<dynamic-user-profile>",
206
+ "- timezone: UTC+5:30",
207
+ "</dynamic-user-profile>",
208
+ ].join("\n");
209
+ expect(extractUserTimeZoneFromDynamicProfile(profile)).toBe("+05:30");
199
210
  });
200
211
 
201
- test('extracts fractional GMT offset tokens from generic profile text', () => {
212
+ test("extracts fractional GMT offset tokens from generic profile text", () => {
202
213
  const profile = [
203
- '<dynamic-user-profile>',
204
- '- preferences: default reminders to GMT+5:45.',
205
- '</dynamic-user-profile>',
206
- ].join('\n');
207
- expect(extractUserTimeZoneFromDynamicProfile(profile)).toBe('+05:45');
214
+ "<dynamic-user-profile>",
215
+ "- preferences: default reminders to GMT+5:45.",
216
+ "</dynamic-user-profile>",
217
+ ].join("\n");
218
+ expect(extractUserTimeZoneFromDynamicProfile(profile)).toBe("+05:45");
208
219
  });
209
220
 
210
- test('prefers IANA timezone tokens over UTC/GMT offsets in the same profile line', () => {
221
+ test("prefers IANA timezone tokens over UTC/GMT offsets in the same profile line", () => {
211
222
  const profile = [
212
- '<dynamic-user-profile>',
213
- '- timezone: UTC+1 (Europe/Paris)',
214
- '</dynamic-user-profile>',
215
- ].join('\n');
216
- expect(extractUserTimeZoneFromDynamicProfile(profile)).toBe('Europe/Paris');
223
+ "<dynamic-user-profile>",
224
+ "- timezone: UTC+1 (Europe/Paris)",
225
+ "</dynamic-user-profile>",
226
+ ].join("\n");
227
+ expect(extractUserTimeZoneFromDynamicProfile(profile)).toBe("Europe/Paris");
217
228
  });
218
229
  });
219
230
 
@@ -221,17 +232,17 @@ describe('extractUserTimeZoneFromDynamicProfile', () => {
221
232
  // Weekday baseline — today is Wednesday
222
233
  // ---------------------------------------------------------------------------
223
234
 
224
- describe('weekday baseline (Wednesday)', () => {
225
- test('next weekend is the upcoming Saturday-Sunday', () => {
226
- const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: 'UTC' });
235
+ describe("weekday baseline (Wednesday)", () => {
236
+ test("next weekend is the upcoming Saturday-Sunday", () => {
237
+ const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: "UTC" });
227
238
  // Wednesday Feb 18 → next Saturday is Feb 21, Sunday is Feb 22
228
- expect(result).toContain('Next weekend: 2026-02-21 – 2026-02-22');
239
+ expect(result).toContain("Next weekend: 2026-02-21 – 2026-02-22");
229
240
  });
230
241
 
231
- test('next work week is the following Monday-Friday', () => {
232
- const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: 'UTC' });
242
+ test("next work week is the following Monday-Friday", () => {
243
+ const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: "UTC" });
233
244
  // Wednesday Feb 18 → next Monday is Feb 23, Friday is Feb 27
234
- expect(result).toContain('Next work week: 2026-02-23 – 2026-02-27');
245
+ expect(result).toContain("Next work week: 2026-02-23 – 2026-02-27");
235
246
  });
236
247
  });
237
248
 
@@ -239,17 +250,17 @@ describe('weekday baseline (Wednesday)', () => {
239
250
  // Weekend baseline — today is Saturday
240
251
  // ---------------------------------------------------------------------------
241
252
 
242
- describe('weekend baseline (Saturday)', () => {
243
- test('next weekend is the *following* Saturday-Sunday, not today', () => {
244
- const result = buildTemporalContext({ nowMs: SAT_FEB_21, timeZone: 'UTC' });
253
+ describe("weekend baseline (Saturday)", () => {
254
+ test("next weekend is the *following* Saturday-Sunday, not today", () => {
255
+ const result = buildTemporalContext({ nowMs: SAT_FEB_21, timeZone: "UTC" });
245
256
  // Saturday Feb 21 → next Saturday is Feb 28, Sunday is Mar 1
246
- expect(result).toContain('Next weekend: 2026-02-28 – 2026-03-01');
257
+ expect(result).toContain("Next weekend: 2026-02-28 – 2026-03-01");
247
258
  });
248
259
 
249
- test('next work week is the upcoming Monday-Friday', () => {
250
- const result = buildTemporalContext({ nowMs: SAT_FEB_21, timeZone: 'UTC' });
260
+ test("next work week is the upcoming Monday-Friday", () => {
261
+ const result = buildTemporalContext({ nowMs: SAT_FEB_21, timeZone: "UTC" });
251
262
  // Saturday Feb 21 → next Monday is Feb 23, Friday is Feb 27
252
- expect(result).toContain('Next work week: 2026-02-23 – 2026-02-27');
263
+ expect(result).toContain("Next work week: 2026-02-23 – 2026-02-27");
253
264
  });
254
265
  });
255
266
 
@@ -257,17 +268,17 @@ describe('weekend baseline (Saturday)', () => {
257
268
  // Weekend baseline — today is Sunday
258
269
  // ---------------------------------------------------------------------------
259
270
 
260
- describe('weekend baseline (Sunday)', () => {
261
- test('next weekend is the following Saturday-Sunday', () => {
262
- const result = buildTemporalContext({ nowMs: SUN_FEB_22, timeZone: 'UTC' });
271
+ describe("weekend baseline (Sunday)", () => {
272
+ test("next weekend is the following Saturday-Sunday", () => {
273
+ const result = buildTemporalContext({ nowMs: SUN_FEB_22, timeZone: "UTC" });
263
274
  // Sunday Feb 22 → next Saturday is Feb 28, Sunday is Mar 1
264
- expect(result).toContain('Next weekend: 2026-02-28 – 2026-03-01');
275
+ expect(result).toContain("Next weekend: 2026-02-28 – 2026-03-01");
265
276
  });
266
277
 
267
- test('next work week is the upcoming Monday-Friday', () => {
268
- const result = buildTemporalContext({ nowMs: SUN_FEB_22, timeZone: 'UTC' });
278
+ test("next work week is the upcoming Monday-Friday", () => {
279
+ const result = buildTemporalContext({ nowMs: SUN_FEB_22, timeZone: "UTC" });
269
280
  // Sunday Feb 22 → next Monday is Feb 23, Friday is Feb 27
270
- expect(result).toContain('Next work week: 2026-02-23 – 2026-02-27');
281
+ expect(result).toContain("Next work week: 2026-02-23 – 2026-02-27");
271
282
  });
272
283
  });
273
284
 
@@ -275,17 +286,17 @@ describe('weekend baseline (Sunday)', () => {
275
286
  // Friday baseline
276
287
  // ---------------------------------------------------------------------------
277
288
 
278
- describe('Friday baseline', () => {
279
- test('next weekend is tomorrow (Saturday) and Sunday', () => {
280
- const result = buildTemporalContext({ nowMs: FRI_FEB_27, timeZone: 'UTC' });
289
+ describe("Friday baseline", () => {
290
+ test("next weekend is tomorrow (Saturday) and Sunday", () => {
291
+ const result = buildTemporalContext({ nowMs: FRI_FEB_27, timeZone: "UTC" });
281
292
  // Friday Feb 27 → next Saturday is Feb 28, Sunday is Mar 1
282
- expect(result).toContain('Next weekend: 2026-02-28 – 2026-03-01');
293
+ expect(result).toContain("Next weekend: 2026-02-28 – 2026-03-01");
283
294
  });
284
295
 
285
- test('next work week is the following Monday-Friday', () => {
286
- const result = buildTemporalContext({ nowMs: FRI_FEB_27, timeZone: 'UTC' });
296
+ test("next work week is the following Monday-Friday", () => {
297
+ const result = buildTemporalContext({ nowMs: FRI_FEB_27, timeZone: "UTC" });
287
298
  // Friday Feb 27 → next Monday is Mar 2, Friday is Mar 6
288
- expect(result).toContain('Next work week: 2026-03-02 – 2026-03-06');
299
+ expect(result).toContain("Next work week: 2026-03-02 – 2026-03-06");
289
300
  });
290
301
  });
291
302
 
@@ -293,25 +304,29 @@ describe('Friday baseline', () => {
293
304
  // Month / year boundary
294
305
  // ---------------------------------------------------------------------------
295
306
 
296
- describe('month/year boundary', () => {
297
- test('handles year boundary correctly', () => {
298
- const result = buildTemporalContext({ nowMs: TUE_DEC_29, timeZone: 'UTC' });
299
- expect(result).toContain('Today: 2026-12-29 (Tuesday)');
307
+ describe("month/year boundary", () => {
308
+ test("handles year boundary correctly", () => {
309
+ const result = buildTemporalContext({ nowMs: TUE_DEC_29, timeZone: "UTC" });
310
+ expect(result).toContain("Today: 2026-12-29 (Tuesday)");
300
311
  // Tuesday Dec 29 → next Saturday is Jan 2 2027
301
- expect(result).toContain('Next weekend: 2027-01-02 – 2027-01-03');
312
+ expect(result).toContain("Next weekend: 2027-01-02 – 2027-01-03");
302
313
  // Next Monday is Jan 4 2027 (skips current work week)
303
314
  // Wait — Dec 29 is Tuesday, so next Monday = Jan 4? Let me think:
304
315
  // Dec 29 Tue → Mon is (1-2+7)%7 = 6 days → Jan 4 Mon
305
- expect(result).toContain('Next work week: 2027-01-04 – 2027-01-08');
316
+ expect(result).toContain("Next work week: 2027-01-04 – 2027-01-08");
306
317
  });
307
318
 
308
- test('horizon entries cross year boundary', () => {
309
- const result = buildTemporalContext({ nowMs: TUE_DEC_29, timeZone: 'UTC', horizonDays: 5 });
310
- expect(result).toContain('2026-12-30 Wednesday');
311
- expect(result).toContain('2026-12-31 Thursday');
312
- expect(result).toContain('2027-01-01 Friday');
313
- expect(result).toContain('2027-01-02 Saturday');
314
- expect(result).toContain('2027-01-03 Sunday');
319
+ test("horizon entries cross year boundary", () => {
320
+ const result = buildTemporalContext({
321
+ nowMs: TUE_DEC_29,
322
+ timeZone: "UTC",
323
+ horizonDays: 5,
324
+ });
325
+ expect(result).toContain("2026-12-30 Wednesday");
326
+ expect(result).toContain("2026-12-31 Thursday");
327
+ expect(result).toContain("2027-01-01 Friday");
328
+ expect(result).toContain("2027-01-02 Saturday");
329
+ expect(result).toContain("2027-01-03 Sunday");
315
330
  });
316
331
  });
317
332
 
@@ -319,28 +334,40 @@ describe('month/year boundary', () => {
319
334
  // Output size caps
320
335
  // ---------------------------------------------------------------------------
321
336
 
322
- describe('output size caps', () => {
323
- test('output is at most 1500 characters', () => {
324
- const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: 'UTC', horizonDays: 14 });
337
+ describe("output size caps", () => {
338
+ test("output is at most 1500 characters", () => {
339
+ const result = buildTemporalContext({
340
+ nowMs: WED_FEB_18,
341
+ timeZone: "UTC",
342
+ horizonDays: 14,
343
+ });
325
344
  expect(result.length).toBeLessThanOrEqual(1500);
326
345
  });
327
346
 
328
- test('horizon entries are capped at 14 even if more requested', () => {
329
- const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: 'UTC', horizonDays: 30 });
347
+ test("horizon entries are capped at 14 even if more requested", () => {
348
+ const result = buildTemporalContext({
349
+ nowMs: WED_FEB_18,
350
+ timeZone: "UTC",
351
+ horizonDays: 30,
352
+ });
330
353
  const horizonMatches = result.match(/^\s+\d{4}-\d{2}-\d{2} \w+$/gm);
331
354
  expect(horizonMatches).not.toBeNull();
332
355
  expect(horizonMatches!.length).toBeLessThanOrEqual(14);
333
356
  });
334
357
 
335
- test('default horizon is 14 days', () => {
336
- const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: 'UTC' });
358
+ test("default horizon is 14 days", () => {
359
+ const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: "UTC" });
337
360
  const horizonMatches = result.match(/^\s+\d{4}-\d{2}-\d{2} \w+$/gm);
338
361
  expect(horizonMatches).not.toBeNull();
339
362
  expect(horizonMatches!.length).toBe(14);
340
363
  });
341
364
 
342
- test('respects smaller horizonDays', () => {
343
- const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: 'UTC', horizonDays: 3 });
365
+ test("respects smaller horizonDays", () => {
366
+ const result = buildTemporalContext({
367
+ nowMs: WED_FEB_18,
368
+ timeZone: "UTC",
369
+ horizonDays: 3,
370
+ });
344
371
  const horizonMatches = result.match(/^\s+\d{4}-\d{2}-\d{2} \w+$/gm);
345
372
  expect(horizonMatches).not.toBeNull();
346
373
  expect(horizonMatches!.length).toBe(3);
@@ -351,66 +378,87 @@ describe('output size caps', () => {
351
378
  // DST-safe timezone behavior
352
379
  // ---------------------------------------------------------------------------
353
380
 
354
- describe('DST-safe timezone behavior', () => {
355
- test('date labels are correct in US Eastern timezone', () => {
381
+ describe("DST-safe timezone behavior", () => {
382
+ test("date labels are correct in US Eastern timezone", () => {
356
383
  // Feb 18 12:00 UTC = Feb 18 07:00 EST (same calendar date)
357
- const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: 'America/New_York' });
358
- expect(result).toContain('Today: 2026-02-18 (Wednesday)');
359
- expect(result).toContain('Current local time: 2026-02-18T07:00:00-05:00');
384
+ const result = buildTemporalContext({
385
+ nowMs: WED_FEB_18,
386
+ timeZone: "America/New_York",
387
+ });
388
+ expect(result).toContain("Today: 2026-02-18 (Wednesday)");
389
+ expect(result).toContain("Current local time: 2026-02-18T07:00:00-05:00");
360
390
  });
361
391
 
362
- test('date labels are correct in timezone ahead of UTC', () => {
392
+ test("date labels are correct in timezone ahead of UTC", () => {
363
393
  // Use a timestamp near midnight UTC so the local date differs
364
394
  // Feb 18 23:00 UTC = Feb 19 08:00 JST
365
395
  const nearMidnight = Date.UTC(2026, 1, 18, 23, 0, 0);
366
- const result = buildTemporalContext({ nowMs: nearMidnight, timeZone: 'Asia/Tokyo' });
367
- expect(result).toContain('Today: 2026-02-19 (Thursday)');
396
+ const result = buildTemporalContext({
397
+ nowMs: nearMidnight,
398
+ timeZone: "Asia/Tokyo",
399
+ });
400
+ expect(result).toContain("Today: 2026-02-19 (Thursday)");
368
401
  });
369
402
 
370
- test('addDays is correct across DST spring-forward boundary', () => {
403
+ test("addDays is correct across DST spring-forward boundary", () => {
371
404
  // 2026-03-08 is spring-forward day in America/New_York (clocks jump 2:00→3:00 AM).
372
405
  // Use a timestamp at local 23:30 on Friday March 6 (04:30 UTC March 7).
373
406
  const preDST = Date.UTC(2026, 2, 7, 4, 30, 0); // local: Fri Mar 6 23:30 EST
374
- const result = buildTemporalContext({ nowMs: preDST, timeZone: 'America/New_York', horizonDays: 5 });
407
+ const result = buildTemporalContext({
408
+ nowMs: preDST,
409
+ timeZone: "America/New_York",
410
+ horizonDays: 5,
411
+ });
375
412
  // Today should be Friday March 6
376
- expect(result).toContain('Today: 2026-03-06 (Friday)');
413
+ expect(result).toContain("Today: 2026-03-06 (Friday)");
377
414
  // Horizon should have 5 consecutive days with no duplicates/skips
378
- expect(result).toContain('2026-03-07 Saturday');
379
- expect(result).toContain('2026-03-08 Sunday');
380
- expect(result).toContain('2026-03-09 Monday');
381
- expect(result).toContain('2026-03-10 Tuesday');
382
- expect(result).toContain('2026-03-11 Wednesday');
415
+ expect(result).toContain("2026-03-07 Saturday");
416
+ expect(result).toContain("2026-03-08 Sunday");
417
+ expect(result).toContain("2026-03-09 Monday");
418
+ expect(result).toContain("2026-03-10 Tuesday");
419
+ expect(result).toContain("2026-03-11 Wednesday");
383
420
  });
384
421
 
385
- test('addDays is correct across DST fall-back boundary', () => {
422
+ test("addDays is correct across DST fall-back boundary", () => {
386
423
  // 2026-11-01 is fall-back day in America/New_York (clocks jump 2:00→1:00 AM).
387
424
  // Use a timestamp at local 00:30 on Sunday Nov 1 (04:30 UTC Nov 1).
388
425
  const preFallback = Date.UTC(2026, 10, 1, 4, 30, 0); // local: Sun Nov 1 00:30 EDT
389
- const result = buildTemporalContext({ nowMs: preFallback, timeZone: 'America/New_York', horizonDays: 3 });
426
+ const result = buildTemporalContext({
427
+ nowMs: preFallback,
428
+ timeZone: "America/New_York",
429
+ horizonDays: 3,
430
+ });
390
431
  // Today should be Sunday Nov 1
391
- expect(result).toContain('Today: 2026-11-01 (Sunday)');
432
+ expect(result).toContain("Today: 2026-11-01 (Sunday)");
392
433
  // Horizon should have 3 consecutive days
393
- expect(result).toContain('2026-11-02 Monday');
394
- expect(result).toContain('2026-11-03 Tuesday');
395
- expect(result).toContain('2026-11-04 Wednesday');
434
+ expect(result).toContain("2026-11-02 Monday");
435
+ expect(result).toContain("2026-11-03 Tuesday");
436
+ expect(result).toContain("2026-11-04 Wednesday");
396
437
  });
397
438
 
398
- test('dates are correct in far-east UTC+13 timezone (Pacific/Auckland NZDT)', () => {
439
+ test("dates are correct in far-east UTC+13 timezone (Pacific/Auckland NZDT)", () => {
399
440
  // Feb 18 12:00 UTC = Feb 19 01:00 NZDT (UTC+13 during daylight saving)
400
- const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: 'Pacific/Auckland', horizonDays: 3 });
441
+ const result = buildTemporalContext({
442
+ nowMs: WED_FEB_18,
443
+ timeZone: "Pacific/Auckland",
444
+ horizonDays: 3,
445
+ });
401
446
  // In Auckland, Feb 18 12:00 UTC is already Feb 19 (Thursday)
402
- expect(result).toContain('Today: 2026-02-19 (Thursday)');
447
+ expect(result).toContain("Today: 2026-02-19 (Thursday)");
403
448
  // Horizon should show consecutive days without +1 shift
404
- expect(result).toContain('2026-02-20 Friday');
405
- expect(result).toContain('2026-02-21 Saturday');
406
- expect(result).toContain('2026-02-22 Sunday');
449
+ expect(result).toContain("2026-02-20 Friday");
450
+ expect(result).toContain("2026-02-21 Saturday");
451
+ expect(result).toContain("2026-02-22 Sunday");
407
452
  });
408
453
 
409
- test('local offset tracks daylight saving changes', () => {
454
+ test("local offset tracks daylight saving changes", () => {
410
455
  // Jul 1 12:00 UTC = Jul 1 08:00 EDT
411
456
  const summer = Date.UTC(2026, 6, 1, 12, 0, 0);
412
- const result = buildTemporalContext({ nowMs: summer, timeZone: 'America/New_York' });
413
- expect(result).toContain('Current local time: 2026-07-01T08:00:00-04:00');
457
+ const result = buildTemporalContext({
458
+ nowMs: summer,
459
+ timeZone: "America/New_York",
460
+ });
461
+ expect(result).toContain("Current local time: 2026-07-01T08:00:00-04:00");
414
462
  });
415
463
  });
416
464
 
@@ -418,31 +466,31 @@ describe('DST-safe timezone behavior', () => {
418
466
  // Trip-planning regression: "next weekend" resolution
419
467
  // ---------------------------------------------------------------------------
420
468
 
421
- describe('trip-planning: next weekend resolution', () => {
469
+ describe("trip-planning: next weekend resolution", () => {
422
470
  test('Wednesday → "next weekend" anchors resolve to upcoming Sat-Sun', () => {
423
- const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: 'UTC' });
471
+ const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: "UTC" });
424
472
  // A user asking "plan a trip for next weekend" on Wednesday Feb 18
425
473
  // expects Sat Feb 21 – Sun Feb 22.
426
- expect(result).toContain('Next weekend: 2026-02-21 – 2026-02-22');
474
+ expect(result).toContain("Next weekend: 2026-02-21 – 2026-02-22");
427
475
  // Both dates must appear in the horizon so the model can reference them.
428
- expect(result).toContain('2026-02-21 Saturday');
429
- expect(result).toContain('2026-02-22 Sunday');
476
+ expect(result).toContain("2026-02-21 Saturday");
477
+ expect(result).toContain("2026-02-22 Sunday");
430
478
  });
431
479
 
432
480
  test('Saturday → "next weekend" skips current weekend', () => {
433
- const result = buildTemporalContext({ nowMs: SAT_FEB_21, timeZone: 'UTC' });
481
+ const result = buildTemporalContext({ nowMs: SAT_FEB_21, timeZone: "UTC" });
434
482
  // Already on Saturday → "next weekend" means the *following* weekend.
435
- expect(result).toContain('Next weekend: 2026-02-28 – 2026-03-01');
483
+ expect(result).toContain("Next weekend: 2026-02-28 – 2026-03-01");
436
484
  });
437
485
 
438
486
  test('Sunday → "next weekend" skips current weekend', () => {
439
- const result = buildTemporalContext({ nowMs: SUN_FEB_22, timeZone: 'UTC' });
440
- expect(result).toContain('Next weekend: 2026-02-28 – 2026-03-01');
487
+ const result = buildTemporalContext({ nowMs: SUN_FEB_22, timeZone: "UTC" });
488
+ expect(result).toContain("Next weekend: 2026-02-28 – 2026-03-01");
441
489
  });
442
490
 
443
491
  test('Friday → "next weekend" is tomorrow', () => {
444
- const result = buildTemporalContext({ nowMs: FRI_FEB_27, timeZone: 'UTC' });
445
- expect(result).toContain('Next weekend: 2026-02-28 – 2026-03-01');
492
+ const result = buildTemporalContext({ nowMs: FRI_FEB_27, timeZone: "UTC" });
493
+ expect(result).toContain("Next weekend: 2026-02-28 – 2026-03-01");
446
494
  });
447
495
  });
448
496
 
@@ -450,22 +498,22 @@ describe('trip-planning: next weekend resolution', () => {
450
498
  // Trip-planning regression: "next work week" resolution
451
499
  // ---------------------------------------------------------------------------
452
500
 
453
- describe('trip-planning: next work week resolution', () => {
501
+ describe("trip-planning: next work week resolution", () => {
454
502
  test('Wednesday → "next work week" skips remainder of current week', () => {
455
- const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: 'UTC' });
456
- expect(result).toContain('Next work week: 2026-02-23 – 2026-02-27');
503
+ const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: "UTC" });
504
+ expect(result).toContain("Next work week: 2026-02-23 – 2026-02-27");
457
505
  });
458
506
 
459
507
  test('Monday → "next work week" is the following Monday-Friday', () => {
460
508
  /** Monday 2026-02-23 12:00 UTC */
461
509
  const MON_FEB_23 = Date.UTC(2026, 1, 23, 12, 0, 0);
462
- const result = buildTemporalContext({ nowMs: MON_FEB_23, timeZone: 'UTC' });
463
- expect(result).toContain('Next work week: 2026-03-02 – 2026-03-06');
510
+ const result = buildTemporalContext({ nowMs: MON_FEB_23, timeZone: "UTC" });
511
+ expect(result).toContain("Next work week: 2026-03-02 – 2026-03-06");
464
512
  });
465
513
 
466
514
  test('Saturday → "next work week" is the upcoming Monday-Friday', () => {
467
- const result = buildTemporalContext({ nowMs: SAT_FEB_21, timeZone: 'UTC' });
468
- expect(result).toContain('Next work week: 2026-02-23 – 2026-02-27');
515
+ const result = buildTemporalContext({ nowMs: SAT_FEB_21, timeZone: "UTC" });
516
+ expect(result).toContain("Next work week: 2026-02-23 – 2026-02-27");
469
517
  });
470
518
  });
471
519
 
@@ -473,42 +521,53 @@ describe('trip-planning: next work week resolution', () => {
473
521
  // Trip-planning regression: month-without-year disambiguation
474
522
  // ---------------------------------------------------------------------------
475
523
 
476
- describe('trip-planning: month-without-year disambiguation via temporal anchors', () => {
477
- test('Today line includes full YYYY-MM-DD format with year for month disambiguation', () => {
478
- const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: 'UTC' });
524
+ describe("trip-planning: month-without-year disambiguation via temporal anchors", () => {
525
+ test("Today line includes full YYYY-MM-DD format with year for month disambiguation", () => {
526
+ const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: "UTC" });
479
527
  // The Today line must include the full year so the model can resolve bare
480
528
  // month names (e.g. "May" → May 2026 because today is Feb 2026).
481
529
  // Regex ensures YYYY-MM-DD format is present (regression if year is dropped).
482
530
  expect(result).toMatch(/Today: \d{4}-\d{2}-\d{2} \(\w+\)/);
483
- expect(result).toContain('2026-02-18');
531
+ expect(result).toContain("2026-02-18");
484
532
  });
485
533
 
486
- test('future-month anchors: horizon dates are all in the future relative to today', () => {
487
- const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: 'UTC', horizonDays: 14 });
534
+ test("future-month anchors: horizon dates are all in the future relative to today", () => {
535
+ const result = buildTemporalContext({
536
+ nowMs: WED_FEB_18,
537
+ timeZone: "UTC",
538
+ horizonDays: 14,
539
+ });
488
540
  // Extract all horizon dates (indented YYYY-MM-DD lines)
489
541
  const horizonDates = result.match(/^\s+(\d{4}-\d{2}-\d{2}) \w+$/gm);
490
542
  expect(horizonDates).not.toBeNull();
491
543
  // All horizon dates must be after today (2026-02-18)
492
544
  for (const line of horizonDates!) {
493
- const dateStr = line.trim().split(' ')[0];
494
- expect(dateStr > '2026-02-18').toBe(true);
545
+ const dateStr = line.trim().split(" ")[0];
546
+ expect(dateStr > "2026-02-18").toBe(true);
495
547
  }
496
548
  });
497
549
 
498
- test('year-end context: horizon spans into next year for Dec disambiguation', () => {
499
- const result = buildTemporalContext({ nowMs: TUE_DEC_29, timeZone: 'UTC', horizonDays: 14 });
550
+ test("year-end context: horizon spans into next year for Dec disambiguation", () => {
551
+ const result = buildTemporalContext({
552
+ nowMs: TUE_DEC_29,
553
+ timeZone: "UTC",
554
+ horizonDays: 14,
555
+ });
500
556
  // Today is Dec 29 2026 — horizon must include 2027 dates so the model can
501
557
  // distinguish "January" (Jan 2027) from past January (Jan 2026).
502
- expect(result).toContain('Today: 2026-12-29');
558
+ expect(result).toContain("Today: 2026-12-29");
503
559
  expect(result).toMatch(/2027-01-\d{2} \w+/); // At least one January 2027 date
504
560
  });
505
561
 
506
- test('timezone is always present for correct local-month resolution', () => {
507
- const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone: 'America/New_York' });
562
+ test("timezone is always present for correct local-month resolution", () => {
563
+ const result = buildTemporalContext({
564
+ nowMs: WED_FEB_18,
565
+ timeZone: "America/New_York",
566
+ });
508
567
  // Timezone must be present so the model resolves months in the user's
509
568
  // local calendar, not UTC.
510
569
  expect(result).toMatch(/Timezone: .+/);
511
- expect(result).toContain('America/New_York');
570
+ expect(result).toContain("America/New_York");
512
571
  });
513
572
  });
514
573
 
@@ -516,15 +575,15 @@ describe('trip-planning: month-without-year disambiguation via temporal anchors'
516
575
  // Trip-planning regression: cross-month weekend resolution
517
576
  // ---------------------------------------------------------------------------
518
577
 
519
- describe('trip-planning: cross-month weekend resolution', () => {
520
- test('weekend that spans a month boundary (Feb → Mar)', () => {
521
- const result = buildTemporalContext({ nowMs: FRI_FEB_27, timeZone: 'UTC' });
522
- expect(result).toContain('Next weekend: 2026-02-28 – 2026-03-01');
578
+ describe("trip-planning: cross-month weekend resolution", () => {
579
+ test("weekend that spans a month boundary (Feb → Mar)", () => {
580
+ const result = buildTemporalContext({ nowMs: FRI_FEB_27, timeZone: "UTC" });
581
+ expect(result).toContain("Next weekend: 2026-02-28 – 2026-03-01");
523
582
  });
524
583
 
525
- test('year-boundary weekend (Dec 2026 → Jan 2027)', () => {
526
- const result = buildTemporalContext({ nowMs: TUE_DEC_29, timeZone: 'UTC' });
527
- expect(result).toContain('Next weekend: 2027-01-02 – 2027-01-03');
584
+ test("year-boundary weekend (Dec 2026 → Jan 2027)", () => {
585
+ const result = buildTemporalContext({ nowMs: TUE_DEC_29, timeZone: "UTC" });
586
+ expect(result).toContain("Next weekend: 2027-01-02 – 2027-01-03");
528
587
  });
529
588
  });
530
589
 
@@ -532,21 +591,27 @@ describe('trip-planning: cross-month weekend resolution', () => {
532
591
  // Trip-planning regression: timezone-shifted weekend anchors
533
592
  // ---------------------------------------------------------------------------
534
593
 
535
- describe('trip-planning: timezone-shifted weekend anchors', () => {
536
- test('late Friday UTC is already Saturday in Auckland → skips to next weekend', () => {
594
+ describe("trip-planning: timezone-shifted weekend anchors", () => {
595
+ test("late Friday UTC is already Saturday in Auckland → skips to next weekend", () => {
537
596
  // Friday Feb 27 23:00 UTC = Saturday Feb 28 12:00 NZDT
538
597
  const lateFriUTC = Date.UTC(2026, 1, 27, 23, 0, 0);
539
- const result = buildTemporalContext({ nowMs: lateFriUTC, timeZone: 'Pacific/Auckland' });
540
- expect(result).toContain('Today: 2026-02-28 (Saturday)');
598
+ const result = buildTemporalContext({
599
+ nowMs: lateFriUTC,
600
+ timeZone: "Pacific/Auckland",
601
+ });
602
+ expect(result).toContain("Today: 2026-02-28 (Saturday)");
541
603
  // "Next weekend" skips current weekend → Mar 7-8.
542
- expect(result).toContain('Next weekend: 2026-03-07 – 2026-03-08');
604
+ expect(result).toContain("Next weekend: 2026-03-07 – 2026-03-08");
543
605
  });
544
606
 
545
- test('early Saturday UTC is still Friday in US Pacific → next weekend is tomorrow', () => {
607
+ test("early Saturday UTC is still Friday in US Pacific → next weekend is tomorrow", () => {
546
608
  // Saturday Feb 28 02:00 UTC = Friday Feb 27 18:00 PST
547
609
  const earlySatUTC = Date.UTC(2026, 1, 28, 2, 0, 0);
548
- const result = buildTemporalContext({ nowMs: earlySatUTC, timeZone: 'America/Los_Angeles' });
549
- expect(result).toContain('Today: 2026-02-27 (Friday)');
550
- expect(result).toContain('Next weekend: 2026-02-28 – 2026-03-01');
610
+ const result = buildTemporalContext({
611
+ nowMs: earlySatUTC,
612
+ timeZone: "America/Los_Angeles",
613
+ });
614
+ expect(result).toContain("Today: 2026-02-27 (Friday)");
615
+ expect(result).toContain("Next weekend: 2026-02-28 – 2026-03-01");
551
616
  });
552
617
  });