@vellumai/assistant 0.4.16 → 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 (536) hide show
  1. package/Dockerfile +6 -6
  2. package/README.md +1 -2
  3. package/eslint.config.mjs +2 -2
  4. package/package.json +1 -1
  5. package/src/__tests__/access-request-decision.test.ts +128 -120
  6. package/src/__tests__/account-registry.test.ts +121 -110
  7. package/src/__tests__/active-skill-tools.test.ts +200 -172
  8. package/src/__tests__/actor-token-service.test.ts +341 -274
  9. package/src/__tests__/agent-loop-thinking.test.ts +28 -19
  10. package/src/__tests__/agent-loop.test.ts +798 -378
  11. package/src/__tests__/anthropic-provider.test.ts +405 -247
  12. package/src/__tests__/app-builder-tool-scripts.test.ts +97 -97
  13. package/src/__tests__/app-bundler.test.ts +112 -79
  14. package/src/__tests__/app-executors.test.ts +205 -178
  15. package/src/__tests__/app-git-history.test.ts +90 -73
  16. package/src/__tests__/app-git-service.test.ts +67 -53
  17. package/src/__tests__/app-open-proxy.test.ts +29 -25
  18. package/src/__tests__/approval-conversation-turn.test.ts +100 -81
  19. package/src/__tests__/approval-hardcoded-copy-guard.test.ts +45 -17
  20. package/src/__tests__/approval-message-composer.test.ts +119 -119
  21. package/src/__tests__/approval-primitive.test.ts +264 -233
  22. package/src/__tests__/approval-routes-http.test.ts +4 -3
  23. package/src/__tests__/asset-materialize-tool.test.ts +250 -178
  24. package/src/__tests__/asset-search-tool.test.ts +251 -191
  25. package/src/__tests__/assistant-attachment-directive.test.ts +187 -142
  26. package/src/__tests__/assistant-attachments.test.ts +254 -186
  27. package/src/__tests__/assistant-event-hub.test.ts +105 -63
  28. package/src/__tests__/assistant-event.test.ts +66 -58
  29. package/src/__tests__/assistant-events-sse-hardening.test.ts +113 -73
  30. package/src/__tests__/assistant-feature-flag-guard.test.ts +78 -52
  31. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +48 -45
  32. package/src/__tests__/assistant-feature-flags-integration.test.ts +118 -77
  33. package/src/__tests__/assistant-id-boundary-guard.test.ts +158 -104
  34. package/src/__tests__/attachments-store.test.ts +240 -183
  35. package/src/__tests__/attachments.test.ts +70 -62
  36. package/src/__tests__/audit-log-rotation.test.ts +50 -35
  37. package/src/__tests__/browser-fill-credential.test.ts +169 -101
  38. package/src/__tests__/browser-manager.test.ts +97 -75
  39. package/src/__tests__/browser-runtime-check.test.ts +16 -15
  40. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +12 -10
  41. package/src/__tests__/browser-skill-endstate.test.ts +97 -72
  42. package/src/__tests__/bundle-scanner.test.ts +47 -22
  43. package/src/__tests__/bundled-asset.test.ts +74 -47
  44. package/src/__tests__/call-constants.test.ts +19 -19
  45. package/src/__tests__/call-controller.test.ts +1073 -751
  46. package/src/__tests__/call-conversation-messages.test.ts +90 -65
  47. package/src/__tests__/call-domain.test.ts +149 -121
  48. package/src/__tests__/call-pointer-message-composer.test.ts +113 -83
  49. package/src/__tests__/call-pointer-messages.test.ts +213 -154
  50. package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +9 -10
  51. package/src/__tests__/call-recovery.test.ts +232 -212
  52. package/src/__tests__/call-routes-http.test.ts +328 -279
  53. package/src/__tests__/call-start-guardian-guard.test.ts +32 -30
  54. package/src/__tests__/call-state-machine.test.ts +62 -51
  55. package/src/__tests__/call-state.test.ts +89 -75
  56. package/src/__tests__/call-store.test.ts +387 -316
  57. package/src/__tests__/callback-handoff-copy.test.ts +84 -82
  58. package/src/__tests__/canonical-guardian-store.test.ts +331 -280
  59. package/src/__tests__/channel-approval-routes.test.ts +1643 -1126
  60. package/src/__tests__/channel-approval.test.ts +139 -137
  61. package/src/__tests__/channel-approvals.test.ts +226 -182
  62. package/src/__tests__/channel-delivery-store.test.ts +232 -194
  63. package/src/__tests__/channel-guardian.test.ts +6 -3
  64. package/src/__tests__/channel-invite-transport.test.ts +107 -92
  65. package/src/__tests__/channel-policy.test.ts +42 -38
  66. package/src/__tests__/channel-readiness-service.test.ts +119 -102
  67. package/src/__tests__/channel-reply-delivery.test.ts +147 -118
  68. package/src/__tests__/channel-retry-sweep.test.ts +153 -110
  69. package/src/__tests__/checker.test.ts +3309 -1850
  70. package/src/__tests__/clarification-resolver.test.ts +91 -79
  71. package/src/__tests__/classifier.test.ts +64 -54
  72. package/src/__tests__/claude-code-skill-regression.test.ts +42 -37
  73. package/src/__tests__/claude-code-tool-profiles.test.ts +31 -29
  74. package/src/__tests__/clawhub.test.ts +92 -82
  75. package/src/__tests__/cli.test.ts +30 -30
  76. package/src/__tests__/clipboard.test.ts +53 -46
  77. package/src/__tests__/commit-guarantee.test.ts +59 -52
  78. package/src/__tests__/commit-message-enrichment-service.test.ts +203 -75
  79. package/src/__tests__/compaction.benchmark.test.ts +33 -31
  80. package/src/__tests__/computer-use-session-compaction.test.ts +60 -50
  81. package/src/__tests__/computer-use-session-lifecycle.test.ts +145 -117
  82. package/src/__tests__/computer-use-session-working-dir.test.ts +62 -48
  83. package/src/__tests__/computer-use-skill-baseline.test.ts +22 -19
  84. package/src/__tests__/computer-use-skill-endstate.test.ts +45 -31
  85. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +121 -88
  86. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +65 -42
  87. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +33 -18
  88. package/src/__tests__/computer-use-tools.test.ts +121 -98
  89. package/src/__tests__/config-schema.test.ts +443 -347
  90. package/src/__tests__/config-watcher.test.ts +96 -81
  91. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +148 -133
  92. package/src/__tests__/conflict-intent-tokenization.test.ts +96 -78
  93. package/src/__tests__/conflict-policy.test.ts +151 -80
  94. package/src/__tests__/conflict-store.test.ts +203 -157
  95. package/src/__tests__/connection-policy.test.ts +89 -59
  96. package/src/__tests__/contacts-tools.test.ts +247 -178
  97. package/src/__tests__/context-memory-e2e.test.ts +306 -214
  98. package/src/__tests__/context-token-estimator.test.ts +114 -74
  99. package/src/__tests__/context-window-manager.test.ts +269 -167
  100. package/src/__tests__/contradiction-checker.test.ts +161 -135
  101. package/src/__tests__/conversation-attention-store.test.ts +350 -290
  102. package/src/__tests__/conversation-attention-telegram.test.ts +156 -114
  103. package/src/__tests__/conversation-pairing.test.ts +220 -113
  104. package/src/__tests__/conversation-routes-guardian-reply.test.ts +164 -104
  105. package/src/__tests__/conversation-routes.test.ts +71 -41
  106. package/src/__tests__/conversation-store.test.ts +390 -235
  107. package/src/__tests__/credential-broker-browser-fill.test.ts +325 -250
  108. package/src/__tests__/credential-broker-server-use.test.ts +283 -243
  109. package/src/__tests__/credential-broker.test.ts +128 -74
  110. package/src/__tests__/credential-host-pattern-match.test.ts +64 -44
  111. package/src/__tests__/credential-metadata-store.test.ts +360 -311
  112. package/src/__tests__/credential-policy-validate.test.ts +81 -65
  113. package/src/__tests__/credential-resolve.test.ts +212 -145
  114. package/src/__tests__/credential-security-e2e.test.ts +144 -103
  115. package/src/__tests__/credential-security-invariants.test.ts +253 -208
  116. package/src/__tests__/credential-selection.test.ts +254 -146
  117. package/src/__tests__/credential-vault-unit.test.ts +531 -341
  118. package/src/__tests__/credential-vault.test.ts +761 -484
  119. package/src/__tests__/daemon-assistant-events.test.ts +91 -66
  120. package/src/__tests__/daemon-lifecycle.test.ts +258 -190
  121. package/src/__tests__/daemon-server-session-init.test.ts +257 -191
  122. package/src/__tests__/date-context.test.ts +314 -249
  123. package/src/__tests__/db-migration-rollback.test.ts +259 -130
  124. package/src/__tests__/db-schedule-syntax-migration.test.ts +78 -41
  125. package/src/__tests__/delete-managed-skill-tool.test.ts +77 -53
  126. package/src/__tests__/deterministic-verification-control-plane.test.ts +183 -135
  127. package/src/__tests__/dictation-mode-detection.test.ts +77 -55
  128. package/src/__tests__/dictation-profile-store.test.ts +70 -56
  129. package/src/__tests__/dictation-text-processing.test.ts +53 -35
  130. package/src/__tests__/diff.test.ts +102 -98
  131. package/src/__tests__/domain-normalize.test.ts +54 -54
  132. package/src/__tests__/domain-policy.test.ts +71 -55
  133. package/src/__tests__/dynamic-page-surface.test.ts +31 -33
  134. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +69 -69
  135. package/src/__tests__/edit-engine.test.ts +56 -56
  136. package/src/__tests__/elevenlabs-client.test.ts +117 -91
  137. package/src/__tests__/elevenlabs-config.test.ts +32 -31
  138. package/src/__tests__/email-classifier.test.ts +15 -12
  139. package/src/__tests__/email-cli.test.ts +121 -108
  140. package/src/__tests__/emit-signal-routing-intent.test.ts +76 -69
  141. package/src/__tests__/encrypted-store.test.ts +180 -154
  142. package/src/__tests__/entity-extractor.test.ts +108 -87
  143. package/src/__tests__/entity-search.test.ts +664 -258
  144. package/src/__tests__/ephemeral-permissions.test.ts +224 -188
  145. package/src/__tests__/event-bus.test.ts +81 -77
  146. package/src/__tests__/extract-email.test.ts +51 -0
  147. package/src/__tests__/file-edit-tool.test.ts +62 -44
  148. package/src/__tests__/file-ops-service.test.ts +131 -114
  149. package/src/__tests__/file-read-tool.test.ts +48 -31
  150. package/src/__tests__/file-write-tool.test.ts +43 -37
  151. package/src/__tests__/filesystem-tools.test.ts +238 -209
  152. package/src/__tests__/followup-tools.test.ts +237 -162
  153. package/src/__tests__/forbidden-legacy-symbols.test.ts +19 -20
  154. package/src/__tests__/frontmatter.test.ts +96 -81
  155. package/src/__tests__/fuzzy-match-property.test.ts +75 -81
  156. package/src/__tests__/fuzzy-match.test.ts +71 -65
  157. package/src/__tests__/gateway-client-managed-outbound.test.ts +76 -57
  158. package/src/__tests__/gateway-only-enforcement.test.ts +467 -369
  159. package/src/__tests__/gateway-only-guard.test.ts +54 -56
  160. package/src/__tests__/gemini-image-service.test.ts +113 -100
  161. package/src/__tests__/gemini-provider.test.ts +297 -220
  162. package/src/__tests__/get-weather.test.ts +188 -114
  163. package/src/__tests__/gmail-integration.test.ts +47 -46
  164. package/src/__tests__/guardian-action-conversation-turn.test.ts +226 -171
  165. package/src/__tests__/guardian-action-copy-generator.test.ts +111 -93
  166. package/src/__tests__/guardian-action-followup-executor.test.ts +215 -151
  167. package/src/__tests__/guardian-action-followup-store.test.ts +199 -167
  168. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +297 -250
  169. package/src/__tests__/guardian-action-late-reply.test.ts +462 -316
  170. package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +23 -18
  171. package/src/__tests__/guardian-action-store.test.ts +158 -109
  172. package/src/__tests__/guardian-action-sweep.test.ts +114 -100
  173. package/src/__tests__/guardian-actions-endpoint.test.ts +440 -256
  174. package/src/__tests__/guardian-control-plane-policy.test.ts +497 -331
  175. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +217 -215
  176. package/src/__tests__/guardian-dispatch.test.ts +316 -256
  177. package/src/__tests__/guardian-grant-minting.test.ts +247 -178
  178. package/src/__tests__/guardian-outbound-http.test.ts +337 -209
  179. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +99 -96
  180. package/src/__tests__/guardian-question-copy.test.ts +17 -17
  181. package/src/__tests__/guardian-question-mode.test.ts +134 -100
  182. package/src/__tests__/guardian-routing-invariants.test.ts +679 -613
  183. package/src/__tests__/guardian-routing-state.test.ts +256 -209
  184. package/src/__tests__/guardian-verification-intent-routing.test.ts +94 -88
  185. package/src/__tests__/guardian-verification-voice-binding.test.ts +47 -41
  186. package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +0 -1
  187. package/src/__tests__/handle-user-message-secret-resume.test.ts +43 -21
  188. package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +92 -76
  189. package/src/__tests__/handlers-cu-observation-blob.test.ts +103 -70
  190. package/src/__tests__/handlers-ipc-blob-probe.test.ts +77 -51
  191. package/src/__tests__/handlers-slack-config.test.ts +63 -54
  192. package/src/__tests__/handlers-task-submit-slash.test.ts +18 -18
  193. package/src/__tests__/handlers-telegram-config.test.ts +662 -329
  194. package/src/__tests__/handlers-twitter-config.test.ts +525 -298
  195. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +270 -195
  196. package/src/__tests__/headless-browser-interactions.test.ts +444 -280
  197. package/src/__tests__/headless-browser-navigate.test.ts +116 -79
  198. package/src/__tests__/headless-browser-read-tools.test.ts +123 -86
  199. package/src/__tests__/headless-browser-snapshot.test.ts +71 -56
  200. package/src/__tests__/heartbeat-service.test.ts +76 -58
  201. package/src/__tests__/history-repair-observability.test.ts +14 -14
  202. package/src/__tests__/history-repair.test.ts +171 -167
  203. package/src/__tests__/home-base-bootstrap.test.ts +30 -27
  204. package/src/__tests__/hooks-blocking.test.ts +86 -37
  205. package/src/__tests__/hooks-cli.test.ts +104 -68
  206. package/src/__tests__/hooks-config.test.ts +81 -43
  207. package/src/__tests__/hooks-discovery.test.ts +106 -96
  208. package/src/__tests__/hooks-integration.test.ts +78 -72
  209. package/src/__tests__/hooks-manager.test.ts +99 -61
  210. package/src/__tests__/hooks-runner.test.ts +94 -71
  211. package/src/__tests__/hooks-settings.test.ts +69 -64
  212. package/src/__tests__/hooks-templates.test.ts +85 -54
  213. package/src/__tests__/hooks-ts-runner.test.ts +82 -45
  214. package/src/__tests__/hooks-watch.test.ts +32 -22
  215. package/src/__tests__/host-file-edit-tool.test.ts +190 -148
  216. package/src/__tests__/host-file-read-tool.test.ts +86 -63
  217. package/src/__tests__/host-file-write-tool.test.ts +98 -64
  218. package/src/__tests__/host-shell-tool.test.ts +342 -233
  219. package/src/__tests__/inbound-invite-redemption.test.ts +194 -152
  220. package/src/__tests__/ingress-member-store.test.ts +163 -159
  221. package/src/__tests__/ingress-reconcile.test.ts +183 -142
  222. package/src/__tests__/ingress-routes-http.test.ts +441 -356
  223. package/src/__tests__/ingress-url-consistency.test.ts +125 -64
  224. package/src/__tests__/integration-status.test.ts +93 -73
  225. package/src/__tests__/intent-routing.test.ts +148 -118
  226. package/src/__tests__/invite-redemption-service.test.ts +163 -121
  227. package/src/__tests__/ipc-blob-store.test.ts +104 -91
  228. package/src/__tests__/ipc-contract-inventory.test.ts +27 -15
  229. package/src/__tests__/ipc-contract.test.ts +24 -23
  230. package/src/__tests__/ipc-protocol.test.ts +52 -46
  231. package/src/__tests__/ipc-roundtrip.benchmark.test.ts +61 -50
  232. package/src/__tests__/ipc-snapshot.test.ts +1135 -1056
  233. package/src/__tests__/ipc-validate.test.ts +240 -179
  234. package/src/__tests__/key-migration.test.ts +123 -90
  235. package/src/__tests__/keychain.test.ts +150 -123
  236. package/src/__tests__/lifecycle-docs-guard.test.ts +65 -64
  237. package/src/__tests__/llm-usage-store.test.ts +112 -87
  238. package/src/__tests__/managed-skill-lifecycle.test.ts +147 -108
  239. package/src/__tests__/managed-store.test.ts +411 -360
  240. package/src/__tests__/mcp-cli.test.ts +189 -123
  241. package/src/__tests__/mcp-health-check.test.ts +26 -21
  242. package/src/__tests__/media-generate-image.test.ts +122 -99
  243. package/src/__tests__/media-reuse-story.e2e.test.ts +282 -214
  244. package/src/__tests__/media-visibility-policy.test.ts +86 -38
  245. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +146 -100
  246. package/src/__tests__/memory-lifecycle-e2e.test.ts +385 -297
  247. package/src/__tests__/memory-query-builder.test.ts +32 -33
  248. package/src/__tests__/memory-recall-quality.test.ts +761 -407
  249. package/src/__tests__/memory-regressions.experimental.test.ts +443 -380
  250. package/src/__tests__/memory-regressions.test.ts +3725 -2642
  251. package/src/__tests__/memory-retrieval-budget.test.ts +7 -8
  252. package/src/__tests__/memory-retrieval.benchmark.test.ts +144 -109
  253. package/src/__tests__/memory-upsert-concurrency.test.ts +292 -201
  254. package/src/__tests__/messaging-send-tool.test.ts +36 -29
  255. package/src/__tests__/migration-cli-flows.test.ts +69 -53
  256. package/src/__tests__/migration-ordering.test.ts +103 -86
  257. package/src/__tests__/mime-builder.test.ts +55 -32
  258. package/src/__tests__/mock-signup-server.test.ts +384 -246
  259. package/src/__tests__/model-intents.test.ts +61 -37
  260. package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +9 -12
  261. package/src/__tests__/no-is-trusted-guard.test.ts +24 -21
  262. package/src/__tests__/non-member-access-request.test.ts +294 -249
  263. package/src/__tests__/notification-broadcaster.test.ts +99 -81
  264. package/src/__tests__/notification-decision-fallback.test.ts +223 -178
  265. package/src/__tests__/notification-decision-strategy.test.ts +375 -337
  266. package/src/__tests__/notification-deep-link.test.ts +67 -61
  267. package/src/__tests__/notification-guardian-path.test.ts +248 -206
  268. package/src/__tests__/notification-routing-intent.test.ts +166 -93
  269. package/src/__tests__/notification-telegram-adapter.test.ts +60 -46
  270. package/src/__tests__/notification-thread-candidate-validation.test.ts +78 -75
  271. package/src/__tests__/notification-thread-candidates.test.ts +64 -61
  272. package/src/__tests__/oauth-callback-registry.test.ts +40 -30
  273. package/src/__tests__/oauth-connect-handler.test.ts +109 -89
  274. package/src/__tests__/oauth-scope-policy.test.ts +63 -55
  275. package/src/__tests__/oauth2-gateway-transport.test.ts +252 -174
  276. package/src/__tests__/onboarding-starter-tasks.test.ts +93 -89
  277. package/src/__tests__/onboarding-template-contract.test.ts +93 -94
  278. package/src/__tests__/openai-provider.test.ts +366 -274
  279. package/src/__tests__/pairing-concurrent.test.ts +18 -12
  280. package/src/__tests__/pairing-routes.test.ts +45 -41
  281. package/src/__tests__/parallel-tool.benchmark.test.ts +108 -58
  282. package/src/__tests__/parser.test.ts +316 -226
  283. package/src/__tests__/path-classifier.test.ts +24 -25
  284. package/src/__tests__/path-policy.test.ts +187 -147
  285. package/src/__tests__/phone.test.ts +36 -36
  286. package/src/__tests__/platform-move-helper.test.ts +48 -40
  287. package/src/__tests__/platform-socket-path.test.ts +23 -24
  288. package/src/__tests__/platform-workspace-migration.test.ts +464 -414
  289. package/src/__tests__/platform.test.ts +61 -53
  290. package/src/__tests__/playbook-execution.test.ts +397 -265
  291. package/src/__tests__/playbook-tools.test.ts +267 -196
  292. package/src/__tests__/prebuilt-home-base-seed.test.ts +30 -27
  293. package/src/__tests__/pricing.test.ts +316 -136
  294. package/src/__tests__/profile-compiler.test.ts +206 -188
  295. package/src/__tests__/provider-commit-message-generator.test.ts +114 -106
  296. package/src/__tests__/provider-error-scenarios.test.ts +212 -158
  297. package/src/__tests__/provider-fail-open-selection.test.ts +51 -44
  298. package/src/__tests__/provider-registry-ollama.test.ts +13 -9
  299. package/src/__tests__/provider-streaming.benchmark.test.ts +232 -183
  300. package/src/__tests__/proxy-approval-callback.test.ts +180 -119
  301. package/src/__tests__/public-ingress-urls.test.ts +112 -94
  302. package/src/__tests__/qdrant-manager.test.ts +147 -98
  303. package/src/__tests__/ratelimit.test.ts +152 -82
  304. package/src/__tests__/recording-handler.test.ts +273 -151
  305. package/src/__tests__/recording-intent-fallback.test.ts +94 -75
  306. package/src/__tests__/recording-intent-handler.test.ts +422 -292
  307. package/src/__tests__/recording-intent.test.ts +578 -379
  308. package/src/__tests__/recording-state-machine.test.ts +530 -316
  309. package/src/__tests__/recurrence-engine-rruleset.test.ts +150 -92
  310. package/src/__tests__/recurrence-engine.test.ts +81 -41
  311. package/src/__tests__/recurrence-types.test.ts +63 -44
  312. package/src/__tests__/relay-server.test.ts +2131 -1602
  313. package/src/__tests__/reminder-store.test.ts +158 -80
  314. package/src/__tests__/reminder.test.ts +113 -109
  315. package/src/__tests__/remote-skill-policy.test.ts +96 -72
  316. package/src/__tests__/request-file-tool.test.ts +74 -67
  317. package/src/__tests__/response-tier.test.ts +131 -74
  318. package/src/__tests__/runtime-attachment-metadata.test.ts +107 -70
  319. package/src/__tests__/runtime-events-sse-parity.test.ts +167 -145
  320. package/src/__tests__/runtime-events-sse.test.ts +67 -51
  321. package/src/__tests__/sandbox-diagnostics.test.ts +66 -56
  322. package/src/__tests__/sandbox-host-parity.test.ts +377 -301
  323. package/src/__tests__/scaffold-managed-skill-tool.test.ts +213 -161
  324. package/src/__tests__/schedule-store.test.ts +268 -205
  325. package/src/__tests__/schedule-tools.test.ts +702 -524
  326. package/src/__tests__/scheduler-recurrence.test.ts +240 -130
  327. package/src/__tests__/scoped-approval-grants.test.ts +258 -168
  328. package/src/__tests__/scoped-grant-security-matrix.test.ts +160 -146
  329. package/src/__tests__/script-proxy-certs.test.ts +38 -35
  330. package/src/__tests__/script-proxy-connect-tunnel.test.ts +71 -46
  331. package/src/__tests__/script-proxy-decision-trace.test.ts +161 -84
  332. package/src/__tests__/script-proxy-http-forwarder.test.ts +146 -129
  333. package/src/__tests__/script-proxy-injection-runtime.test.ts +139 -113
  334. package/src/__tests__/script-proxy-mitm-handler.test.ts +226 -142
  335. package/src/__tests__/script-proxy-policy-runtime.test.ts +126 -86
  336. package/src/__tests__/script-proxy-policy.test.ts +308 -153
  337. package/src/__tests__/script-proxy-rewrite-specificity.test.ts +74 -62
  338. package/src/__tests__/script-proxy-router.test.ts +111 -77
  339. package/src/__tests__/script-proxy-session-manager.test.ts +156 -113
  340. package/src/__tests__/script-proxy-session-runtime.test.ts +28 -24
  341. package/src/__tests__/secret-allowlist.test.ts +105 -90
  342. package/src/__tests__/secret-ingress-handler.test.ts +41 -30
  343. package/src/__tests__/secret-onetime-send.test.ts +67 -50
  344. package/src/__tests__/secret-prompt-log-hygiene.test.ts +35 -31
  345. package/src/__tests__/secret-response-routing.test.ts +50 -41
  346. package/src/__tests__/secret-scanner-executor.test.ts +152 -111
  347. package/src/__tests__/secret-scanner.test.ts +495 -413
  348. package/src/__tests__/secure-keys.test.ts +132 -121
  349. package/src/__tests__/send-endpoint-busy.test.ts +313 -232
  350. package/src/__tests__/send-notification-tool.test.ts +43 -42
  351. package/src/__tests__/sensitive-output-placeholders.test.ts +72 -64
  352. package/src/__tests__/sequence-store.test.ts +335 -167
  353. package/src/__tests__/server-history-render.test.ts +341 -202
  354. package/src/__tests__/session-abort-tool-results.test.ts +133 -70
  355. package/src/__tests__/session-approval-overrides.test.ts +93 -91
  356. package/src/__tests__/session-confirmation-signals.test.ts +252 -160
  357. package/src/__tests__/session-conflict-gate.test.ts +775 -585
  358. package/src/__tests__/session-error.test.ts +222 -191
  359. package/src/__tests__/session-evictor.test.ts +79 -62
  360. package/src/__tests__/session-init.benchmark.test.ts +170 -108
  361. package/src/__tests__/session-load-history-repair.test.ts +273 -139
  362. package/src/__tests__/session-messaging-secret-redirect.test.ts +130 -90
  363. package/src/__tests__/session-pre-run-repair.test.ts +106 -59
  364. package/src/__tests__/session-profile-injection.test.ts +198 -130
  365. package/src/__tests__/session-provider-retry-repair.test.ts +223 -141
  366. package/src/__tests__/session-queue.test.ts +624 -321
  367. package/src/__tests__/session-runtime-assembly.test.ts +425 -329
  368. package/src/__tests__/session-runtime-workspace.test.ts +69 -61
  369. package/src/__tests__/session-skill-tools.test.ts +973 -678
  370. package/src/__tests__/session-slash-known.test.ts +185 -133
  371. package/src/__tests__/session-slash-queue.test.ts +147 -81
  372. package/src/__tests__/session-slash-unknown.test.ts +135 -90
  373. package/src/__tests__/session-surfaces-task-progress.test.ts +122 -87
  374. package/src/__tests__/session-tool-setup-app-refresh.test.ts +338 -177
  375. package/src/__tests__/session-tool-setup-memory-scope.test.ts +63 -40
  376. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +60 -37
  377. package/src/__tests__/session-tool-setup-tools-disabled.test.ts +28 -26
  378. package/src/__tests__/session-undo.test.ts +43 -30
  379. package/src/__tests__/session-workspace-cache-state.test.ts +108 -67
  380. package/src/__tests__/session-workspace-injection.test.ts +245 -117
  381. package/src/__tests__/session-workspace-tool-tracking.test.ts +260 -93
  382. package/src/__tests__/shared-filesystem-errors.test.ts +47 -47
  383. package/src/__tests__/shell-credential-ref.test.ts +126 -90
  384. package/src/__tests__/shell-identity.test.ts +134 -111
  385. package/src/__tests__/shell-parser-fuzz.test.ts +263 -179
  386. package/src/__tests__/shell-parser-property.test.ts +435 -288
  387. package/src/__tests__/shell-tool-proxy-mode.test.ts +142 -70
  388. package/src/__tests__/size-guard.test.ts +42 -44
  389. package/src/__tests__/skill-feature-flags-integration.test.ts +79 -52
  390. package/src/__tests__/skill-feature-flags.test.ts +75 -47
  391. package/src/__tests__/skill-include-graph.test.ts +143 -148
  392. package/src/__tests__/skill-load-feature-flag.test.ts +94 -59
  393. package/src/__tests__/skill-load-tool.test.ts +371 -199
  394. package/src/__tests__/skill-projection-feature-flag.test.ts +131 -88
  395. package/src/__tests__/skill-projection.benchmark.test.ts +93 -65
  396. package/src/__tests__/skill-script-runner-host.test.ts +460 -250
  397. package/src/__tests__/skill-script-runner-sandbox.test.ts +168 -108
  398. package/src/__tests__/skill-script-runner.test.ts +115 -74
  399. package/src/__tests__/skill-tool-factory.test.ts +140 -96
  400. package/src/__tests__/skill-tool-manifest.test.ts +306 -210
  401. package/src/__tests__/skill-version-hash.test.ts +70 -56
  402. package/src/__tests__/skills.test.ts +0 -1
  403. package/src/__tests__/slack-channel-config.test.ts +127 -84
  404. package/src/__tests__/slack-skill.test.ts +60 -47
  405. package/src/__tests__/slash-commands-catalog.test.ts +37 -31
  406. package/src/__tests__/slash-commands-parser.test.ts +71 -64
  407. package/src/__tests__/slash-commands-resolver.test.ts +143 -107
  408. package/src/__tests__/slash-commands-rewrite.test.ts +22 -22
  409. package/src/__tests__/sms-messaging-provider.test.ts +74 -47
  410. package/src/__tests__/speaker-identification.test.ts +28 -25
  411. package/src/__tests__/starter-bundle.test.ts +27 -23
  412. package/src/__tests__/starter-task-flow.test.ts +67 -52
  413. package/src/__tests__/subagent-manager-notify.test.ts +154 -108
  414. package/src/__tests__/subagent-tools.test.ts +311 -270
  415. package/src/__tests__/subagent-types.test.ts +40 -40
  416. package/src/__tests__/surface-mutex-cleanup.test.ts +42 -30
  417. package/src/__tests__/swarm-dag-pathological.test.ts +122 -111
  418. package/src/__tests__/swarm-orchestrator.test.ts +135 -101
  419. package/src/__tests__/swarm-plan-validator.test.ts +125 -73
  420. package/src/__tests__/swarm-recursion.test.ts +58 -46
  421. package/src/__tests__/swarm-router-planner.test.ts +99 -74
  422. package/src/__tests__/swarm-session-integration.test.ts +148 -91
  423. package/src/__tests__/swarm-tool.test.ts +65 -45
  424. package/src/__tests__/swarm-worker-backend.test.ts +59 -45
  425. package/src/__tests__/swarm-worker-runner.test.ts +133 -118
  426. package/src/__tests__/system-prompt.test.ts +290 -256
  427. package/src/__tests__/task-compiler.test.ts +176 -120
  428. package/src/__tests__/task-management-tools.test.ts +561 -456
  429. package/src/__tests__/task-memory-cleanup.test.ts +627 -362
  430. package/src/__tests__/task-runner.test.ts +117 -94
  431. package/src/__tests__/task-scheduler.test.ts +113 -84
  432. package/src/__tests__/task-tools.test.ts +349 -264
  433. package/src/__tests__/terminal-sandbox.test.ts +138 -108
  434. package/src/__tests__/terminal-tools.test.ts +350 -305
  435. package/src/__tests__/thread-seed-composer.test.ts +307 -180
  436. package/src/__tests__/tool-approval-handler.test.ts +238 -137
  437. package/src/__tests__/tool-audit-listener.test.ts +69 -69
  438. package/src/__tests__/tool-domain-event-publisher.test.ts +142 -132
  439. package/src/__tests__/tool-execution-abort-cleanup.test.ts +153 -146
  440. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +136 -105
  441. package/src/__tests__/tool-executor-lifecycle-events.test.ts +355 -239
  442. package/src/__tests__/tool-executor-redaction.test.ts +112 -109
  443. package/src/__tests__/tool-executor-shell-integration.test.ts +130 -79
  444. package/src/__tests__/tool-executor.test.ts +1274 -674
  445. package/src/__tests__/tool-grant-request-escalation.test.ts +401 -283
  446. package/src/__tests__/tool-metrics-listener.test.ts +97 -85
  447. package/src/__tests__/tool-notification-listener.test.ts +42 -25
  448. package/src/__tests__/tool-permission-simulate-handler.test.ts +137 -113
  449. package/src/__tests__/tool-policy.test.ts +44 -25
  450. package/src/__tests__/tool-profiling-listener.test.ts +99 -93
  451. package/src/__tests__/tool-result-truncation.test.ts +5 -4
  452. package/src/__tests__/tool-trace-listener.test.ts +131 -111
  453. package/src/__tests__/top-level-renderer.test.ts +62 -58
  454. package/src/__tests__/top-level-scanner.test.ts +68 -64
  455. package/src/__tests__/trace-emitter.test.ts +56 -56
  456. package/src/__tests__/trust-context-guards.test.ts +65 -65
  457. package/src/__tests__/trust-store.test.ts +1239 -806
  458. package/src/__tests__/trusted-contact-approval-notifier.test.ts +339 -275
  459. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +484 -373
  460. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +264 -241
  461. package/src/__tests__/trusted-contact-multichannel.test.ts +182 -142
  462. package/src/__tests__/trusted-contact-verification.test.ts +251 -231
  463. package/src/__tests__/turn-commit.test.ts +259 -200
  464. package/src/__tests__/twilio-config.test.ts +49 -41
  465. package/src/__tests__/twilio-provider.test.ts +140 -126
  466. package/src/__tests__/twilio-rest.test.ts +22 -18
  467. package/src/__tests__/twilio-routes-elevenlabs.test.ts +188 -162
  468. package/src/__tests__/twilio-routes-twiml.test.ts +55 -55
  469. package/src/__tests__/twilio-routes.test.ts +389 -281
  470. package/src/__tests__/twitter-auth-handler.test.ts +184 -139
  471. package/src/__tests__/twitter-cli-error-shaping.test.ts +88 -73
  472. package/src/__tests__/twitter-cli-routing.test.ts +146 -99
  473. package/src/__tests__/twitter-oauth-client.test.ts +82 -65
  474. package/src/__tests__/update-bulletin-format.test.ts +69 -66
  475. package/src/__tests__/update-bulletin-state.test.ts +66 -60
  476. package/src/__tests__/update-bulletin.test.ts +150 -114
  477. package/src/__tests__/update-template-contract.test.ts +15 -10
  478. package/src/__tests__/url-safety.test.ts +288 -265
  479. package/src/__tests__/user-reference.test.ts +32 -32
  480. package/src/__tests__/view-image-tool.test.ts +118 -96
  481. package/src/__tests__/voice-invite-redemption.test.ts +111 -106
  482. package/src/__tests__/voice-quality.test.ts +117 -102
  483. package/src/__tests__/voice-scoped-grant-consumer.test.ts +204 -146
  484. package/src/__tests__/voice-session-bridge.test.ts +351 -216
  485. package/src/__tests__/weather-skill-regression.test.ts +170 -120
  486. package/src/__tests__/web-fetch.test.ts +664 -526
  487. package/src/__tests__/web-search.test.ts +379 -213
  488. package/src/__tests__/work-item-output.test.ts +90 -53
  489. package/src/__tests__/workspace-git-service.test.ts +437 -356
  490. package/src/__tests__/workspace-heartbeat-service.test.ts +125 -91
  491. package/src/__tests__/workspace-lifecycle.test.ts +98 -64
  492. package/src/__tests__/workspace-policy.test.ts +139 -71
  493. package/src/commands/__tests__/cc-command-registry.test.ts +142 -134
  494. package/src/config/__tests__/feature-flag-registry-guard.test.ts +48 -39
  495. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +44 -4
  496. package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +0 -1
  497. package/src/config/bundled-skills/messaging/SKILL.md +9 -7
  498. package/src/config/bundled-skills/messaging/tools/gmail-outreach-scan.ts +15 -5
  499. package/src/config/bundled-skills/messaging/tools/gmail-sender-digest.ts +16 -5
  500. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +11 -7
  501. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +34 -32
  502. package/src/config/bundled-tool-registry.ts +2 -0
  503. package/src/config/env.ts +38 -29
  504. package/src/daemon/handlers/skills.ts +18 -10
  505. package/src/daemon/ipc-contract/messages.ts +1 -0
  506. package/src/daemon/ipc-contract/surfaces.ts +7 -1
  507. package/src/daemon/session-agent-loop-handlers.ts +5 -0
  508. package/src/daemon/session-agent-loop.ts +1 -1
  509. package/src/daemon/session-process.ts +1 -1
  510. package/src/daemon/session-surfaces.ts +42 -2
  511. package/src/memory/db-connection.ts +16 -10
  512. package/src/messaging/providers/gmail/adapter.ts +10 -3
  513. package/src/messaging/providers/gmail/client.ts +280 -72
  514. package/src/runtime/auth/__tests__/context.test.ts +75 -65
  515. package/src/runtime/auth/__tests__/credential-service.test.ts +137 -114
  516. package/src/runtime/auth/__tests__/guard-tests.test.ts +84 -90
  517. package/src/runtime/auth/__tests__/ipc-auth-context.test.ts +40 -40
  518. package/src/runtime/auth/__tests__/middleware.test.ts +80 -74
  519. package/src/runtime/auth/__tests__/policy.test.ts +9 -9
  520. package/src/runtime/auth/__tests__/route-policy.test.ts +76 -65
  521. package/src/runtime/auth/__tests__/scopes.test.ts +68 -60
  522. package/src/runtime/auth/__tests__/subject.test.ts +54 -54
  523. package/src/runtime/auth/__tests__/token-service.test.ts +115 -108
  524. package/src/runtime/auth/scopes.ts +3 -0
  525. package/src/runtime/auth/token-service.ts +78 -48
  526. package/src/runtime/auth/types.ts +2 -1
  527. package/src/runtime/http-server.ts +2 -1
  528. package/src/security/secure-keys.ts +103 -53
  529. package/src/sequence/reply-matcher.ts +10 -6
  530. package/src/skills/frontmatter.ts +9 -6
  531. package/src/tools/browser/__tests__/auth-cache.test.ts +69 -63
  532. package/src/tools/browser/__tests__/auth-detector.test.ts +218 -157
  533. package/src/tools/browser/__tests__/jit-auth.test.ts +83 -99
  534. package/src/tools/ui-surface/definitions.ts +2 -1
  535. package/src/util/platform.ts +0 -12
  536. package/docs/architecture/http-token-refresh.md +0 -274
@@ -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
  });