@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.
- package/eslint.config.mjs +2 -2
- package/package.json +1 -1
- package/src/__tests__/access-request-decision.test.ts +128 -120
- package/src/__tests__/account-registry.test.ts +121 -110
- package/src/__tests__/active-skill-tools.test.ts +200 -172
- package/src/__tests__/actor-token-service.test.ts +341 -274
- package/src/__tests__/agent-loop-thinking.test.ts +28 -19
- package/src/__tests__/agent-loop.test.ts +798 -378
- package/src/__tests__/anthropic-provider.test.ts +405 -247
- package/src/__tests__/app-builder-tool-scripts.test.ts +97 -97
- package/src/__tests__/app-bundler.test.ts +112 -79
- package/src/__tests__/app-executors.test.ts +205 -178
- package/src/__tests__/app-git-history.test.ts +90 -73
- package/src/__tests__/app-git-service.test.ts +67 -53
- package/src/__tests__/app-open-proxy.test.ts +29 -25
- package/src/__tests__/approval-conversation-turn.test.ts +100 -81
- package/src/__tests__/approval-hardcoded-copy-guard.test.ts +45 -17
- package/src/__tests__/approval-message-composer.test.ts +119 -119
- package/src/__tests__/approval-primitive.test.ts +264 -233
- package/src/__tests__/approval-routes-http.test.ts +4 -3
- package/src/__tests__/asset-materialize-tool.test.ts +250 -178
- package/src/__tests__/asset-search-tool.test.ts +251 -191
- package/src/__tests__/assistant-attachment-directive.test.ts +187 -142
- package/src/__tests__/assistant-attachments.test.ts +254 -186
- package/src/__tests__/assistant-event-hub.test.ts +105 -63
- package/src/__tests__/assistant-event.test.ts +66 -58
- package/src/__tests__/assistant-events-sse-hardening.test.ts +113 -73
- package/src/__tests__/assistant-feature-flag-guard.test.ts +78 -52
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +48 -45
- package/src/__tests__/assistant-feature-flags-integration.test.ts +118 -77
- package/src/__tests__/assistant-id-boundary-guard.test.ts +158 -104
- package/src/__tests__/attachments-store.test.ts +240 -183
- package/src/__tests__/attachments.test.ts +70 -62
- package/src/__tests__/audit-log-rotation.test.ts +50 -35
- package/src/__tests__/browser-fill-credential.test.ts +169 -101
- package/src/__tests__/browser-manager.test.ts +97 -75
- package/src/__tests__/browser-runtime-check.test.ts +16 -15
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +12 -10
- package/src/__tests__/browser-skill-endstate.test.ts +97 -72
- package/src/__tests__/bundle-scanner.test.ts +47 -22
- package/src/__tests__/bundled-asset.test.ts +74 -47
- package/src/__tests__/call-constants.test.ts +19 -19
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/call-conversation-messages.test.ts +90 -65
- package/src/__tests__/call-domain.test.ts +149 -121
- package/src/__tests__/call-pointer-message-composer.test.ts +113 -83
- package/src/__tests__/call-pointer-messages.test.ts +213 -154
- package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +9 -10
- package/src/__tests__/call-recovery.test.ts +232 -212
- package/src/__tests__/call-routes-http.test.ts +0 -1
- package/src/__tests__/call-start-guardian-guard.test.ts +32 -30
- package/src/__tests__/call-state-machine.test.ts +62 -51
- package/src/__tests__/call-state.test.ts +89 -75
- package/src/__tests__/call-store.test.ts +387 -316
- package/src/__tests__/callback-handoff-copy.test.ts +84 -82
- package/src/__tests__/canonical-guardian-store.test.ts +331 -280
- package/src/__tests__/channel-approval-routes.test.ts +1643 -1115
- package/src/__tests__/channel-approval.test.ts +139 -137
- package/src/__tests__/channel-approvals.test.ts +0 -1
- package/src/__tests__/channel-delivery-store.test.ts +232 -194
- package/src/__tests__/channel-guardian.test.ts +5 -3
- package/src/__tests__/channel-invite-transport.test.ts +107 -92
- package/src/__tests__/channel-policy.test.ts +42 -38
- package/src/__tests__/channel-readiness-service.test.ts +119 -102
- package/src/__tests__/channel-reply-delivery.test.ts +147 -118
- package/src/__tests__/channel-retry-sweep.test.ts +153 -110
- package/src/__tests__/checker.test.ts +3309 -1850
- package/src/__tests__/clarification-resolver.test.ts +91 -79
- package/src/__tests__/classifier.test.ts +64 -54
- package/src/__tests__/claude-code-skill-regression.test.ts +42 -37
- package/src/__tests__/claude-code-tool-profiles.test.ts +31 -29
- package/src/__tests__/clawhub.test.ts +92 -82
- package/src/__tests__/cli.test.ts +30 -30
- package/src/__tests__/clipboard.test.ts +53 -46
- package/src/__tests__/commit-guarantee.test.ts +59 -52
- package/src/__tests__/commit-message-enrichment-service.test.ts +203 -75
- package/src/__tests__/compaction.benchmark.test.ts +33 -31
- package/src/__tests__/computer-use-session-compaction.test.ts +60 -50
- package/src/__tests__/computer-use-session-lifecycle.test.ts +145 -117
- package/src/__tests__/computer-use-session-working-dir.test.ts +62 -48
- package/src/__tests__/computer-use-skill-baseline.test.ts +22 -19
- package/src/__tests__/computer-use-skill-endstate.test.ts +45 -31
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +121 -88
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +65 -42
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +33 -18
- package/src/__tests__/computer-use-tools.test.ts +121 -98
- package/src/__tests__/config-schema.test.ts +443 -347
- package/src/__tests__/config-watcher.test.ts +96 -81
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +148 -133
- package/src/__tests__/conflict-intent-tokenization.test.ts +96 -78
- package/src/__tests__/conflict-policy.test.ts +151 -80
- package/src/__tests__/conflict-store.test.ts +203 -157
- package/src/__tests__/connection-policy.test.ts +89 -59
- package/src/__tests__/contacts-tools.test.ts +247 -178
- package/src/__tests__/context-memory-e2e.test.ts +306 -214
- package/src/__tests__/context-token-estimator.test.ts +114 -74
- package/src/__tests__/context-window-manager.test.ts +269 -167
- package/src/__tests__/contradiction-checker.test.ts +161 -135
- package/src/__tests__/conversation-attention-store.test.ts +350 -290
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-pairing.test.ts +220 -113
- package/src/__tests__/conversation-store.test.ts +390 -235
- package/src/__tests__/credential-broker-browser-fill.test.ts +325 -250
- package/src/__tests__/credential-broker-server-use.test.ts +283 -243
- package/src/__tests__/credential-broker.test.ts +128 -74
- package/src/__tests__/credential-host-pattern-match.test.ts +64 -44
- package/src/__tests__/credential-metadata-store.test.ts +360 -311
- package/src/__tests__/credential-policy-validate.test.ts +81 -65
- package/src/__tests__/credential-resolve.test.ts +212 -145
- package/src/__tests__/credential-security-e2e.test.ts +144 -103
- package/src/__tests__/credential-security-invariants.test.ts +253 -208
- package/src/__tests__/credential-selection.test.ts +254 -146
- package/src/__tests__/credential-vault-unit.test.ts +531 -341
- package/src/__tests__/credential-vault.test.ts +761 -484
- package/src/__tests__/daemon-assistant-events.test.ts +91 -66
- package/src/__tests__/daemon-lifecycle.test.ts +258 -190
- package/src/__tests__/daemon-server-session-init.test.ts +0 -1
- package/src/__tests__/date-context.test.ts +314 -249
- package/src/__tests__/db-migration-rollback.test.ts +259 -130
- package/src/__tests__/db-schedule-syntax-migration.test.ts +78 -41
- package/src/__tests__/delete-managed-skill-tool.test.ts +77 -53
- package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -1
- package/src/__tests__/dictation-mode-detection.test.ts +77 -55
- package/src/__tests__/dictation-profile-store.test.ts +70 -56
- package/src/__tests__/dictation-text-processing.test.ts +53 -35
- package/src/__tests__/diff.test.ts +102 -98
- package/src/__tests__/domain-normalize.test.ts +54 -54
- package/src/__tests__/domain-policy.test.ts +71 -55
- package/src/__tests__/dynamic-page-surface.test.ts +31 -33
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +69 -69
- package/src/__tests__/edit-engine.test.ts +56 -56
- package/src/__tests__/elevenlabs-client.test.ts +117 -91
- package/src/__tests__/elevenlabs-config.test.ts +32 -31
- package/src/__tests__/email-classifier.test.ts +15 -12
- package/src/__tests__/email-cli.test.ts +121 -108
- package/src/__tests__/emit-signal-routing-intent.test.ts +76 -69
- package/src/__tests__/encrypted-store.test.ts +180 -154
- package/src/__tests__/entity-extractor.test.ts +108 -87
- package/src/__tests__/entity-search.test.ts +664 -258
- package/src/__tests__/ephemeral-permissions.test.ts +224 -188
- package/src/__tests__/event-bus.test.ts +81 -77
- package/src/__tests__/extract-email.test.ts +29 -20
- package/src/__tests__/file-edit-tool.test.ts +62 -44
- package/src/__tests__/file-ops-service.test.ts +131 -114
- package/src/__tests__/file-read-tool.test.ts +48 -31
- package/src/__tests__/file-write-tool.test.ts +43 -37
- package/src/__tests__/filesystem-tools.test.ts +238 -209
- package/src/__tests__/followup-tools.test.ts +237 -162
- package/src/__tests__/forbidden-legacy-symbols.test.ts +19 -20
- package/src/__tests__/frontmatter.test.ts +96 -81
- package/src/__tests__/fuzzy-match-property.test.ts +75 -81
- package/src/__tests__/fuzzy-match.test.ts +71 -65
- package/src/__tests__/gateway-client-managed-outbound.test.ts +76 -57
- package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/gemini-image-service.test.ts +113 -100
- package/src/__tests__/gemini-provider.test.ts +297 -220
- package/src/__tests__/get-weather.test.ts +188 -114
- package/src/__tests__/gmail-integration.test.ts +0 -1
- package/src/__tests__/guardian-action-conversation-turn.test.ts +226 -171
- package/src/__tests__/guardian-action-copy-generator.test.ts +111 -93
- package/src/__tests__/guardian-action-followup-executor.test.ts +0 -1
- package/src/__tests__/guardian-action-followup-store.test.ts +199 -167
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +297 -250
- package/src/__tests__/guardian-action-late-reply.test.ts +462 -316
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +23 -18
- package/src/__tests__/guardian-action-store.test.ts +158 -109
- package/src/__tests__/guardian-action-sweep.test.ts +114 -100
- package/src/__tests__/guardian-actions-endpoint.test.ts +440 -256
- package/src/__tests__/guardian-control-plane-policy.test.ts +497 -331
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +217 -215
- package/src/__tests__/guardian-dispatch.test.ts +316 -256
- package/src/__tests__/guardian-grant-minting.test.ts +247 -178
- package/src/__tests__/guardian-outbound-http.test.ts +5 -3
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +99 -96
- package/src/__tests__/guardian-question-copy.test.ts +17 -17
- package/src/__tests__/guardian-question-mode.test.ts +134 -100
- package/src/__tests__/guardian-routing-invariants.test.ts +0 -1
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/guardian-verification-intent-routing.test.ts +94 -88
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
- package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +0 -1
- package/src/__tests__/handle-user-message-secret-resume.test.ts +0 -1
- package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +92 -76
- package/src/__tests__/handlers-cu-observation-blob.test.ts +103 -70
- package/src/__tests__/handlers-ipc-blob-probe.test.ts +77 -51
- package/src/__tests__/handlers-slack-config.test.ts +63 -54
- package/src/__tests__/handlers-task-submit-slash.test.ts +18 -18
- package/src/__tests__/handlers-telegram-config.test.ts +662 -329
- package/src/__tests__/handlers-twitter-config.test.ts +525 -298
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -2
- package/src/__tests__/headless-browser-interactions.test.ts +444 -280
- package/src/__tests__/headless-browser-navigate.test.ts +116 -79
- package/src/__tests__/headless-browser-read-tools.test.ts +123 -86
- package/src/__tests__/headless-browser-snapshot.test.ts +71 -56
- package/src/__tests__/heartbeat-service.test.ts +76 -58
- package/src/__tests__/history-repair-observability.test.ts +14 -14
- package/src/__tests__/history-repair.test.ts +171 -167
- package/src/__tests__/home-base-bootstrap.test.ts +30 -27
- package/src/__tests__/hooks-blocking.test.ts +86 -37
- package/src/__tests__/hooks-cli.test.ts +104 -68
- package/src/__tests__/hooks-config.test.ts +81 -43
- package/src/__tests__/hooks-discovery.test.ts +106 -96
- package/src/__tests__/hooks-integration.test.ts +78 -72
- package/src/__tests__/hooks-manager.test.ts +99 -61
- package/src/__tests__/hooks-runner.test.ts +94 -71
- package/src/__tests__/hooks-settings.test.ts +69 -64
- package/src/__tests__/hooks-templates.test.ts +85 -54
- package/src/__tests__/hooks-ts-runner.test.ts +82 -45
- package/src/__tests__/hooks-watch.test.ts +32 -22
- package/src/__tests__/host-file-edit-tool.test.ts +190 -148
- package/src/__tests__/host-file-read-tool.test.ts +86 -63
- package/src/__tests__/host-file-write-tool.test.ts +98 -64
- package/src/__tests__/host-shell-tool.test.ts +342 -233
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -1
- package/src/__tests__/ingress-member-store.test.ts +163 -159
- package/src/__tests__/ingress-reconcile.test.ts +0 -1
- package/src/__tests__/ingress-routes-http.test.ts +441 -356
- package/src/__tests__/ingress-url-consistency.test.ts +125 -64
- package/src/__tests__/integration-status.test.ts +93 -73
- package/src/__tests__/intent-routing.test.ts +148 -118
- package/src/__tests__/invite-redemption-service.test.ts +163 -121
- package/src/__tests__/ipc-blob-store.test.ts +104 -91
- package/src/__tests__/ipc-contract-inventory.test.ts +27 -15
- package/src/__tests__/ipc-contract.test.ts +24 -23
- package/src/__tests__/ipc-protocol.test.ts +52 -46
- package/src/__tests__/ipc-roundtrip.benchmark.test.ts +61 -50
- package/src/__tests__/ipc-snapshot.test.ts +1135 -1056
- package/src/__tests__/ipc-validate.test.ts +240 -179
- package/src/__tests__/key-migration.test.ts +123 -90
- package/src/__tests__/keychain.test.ts +150 -123
- package/src/__tests__/lifecycle-docs-guard.test.ts +65 -64
- package/src/__tests__/llm-usage-store.test.ts +112 -87
- package/src/__tests__/managed-skill-lifecycle.test.ts +147 -108
- package/src/__tests__/managed-store.test.ts +411 -360
- package/src/__tests__/mcp-cli.test.ts +189 -123
- package/src/__tests__/mcp-health-check.test.ts +26 -21
- package/src/__tests__/media-generate-image.test.ts +122 -99
- package/src/__tests__/media-reuse-story.e2e.test.ts +282 -214
- package/src/__tests__/media-visibility-policy.test.ts +86 -38
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +146 -100
- package/src/__tests__/memory-lifecycle-e2e.test.ts +385 -297
- package/src/__tests__/memory-query-builder.test.ts +32 -33
- package/src/__tests__/memory-recall-quality.test.ts +761 -407
- package/src/__tests__/memory-regressions.experimental.test.ts +443 -380
- package/src/__tests__/memory-regressions.test.ts +3725 -2642
- package/src/__tests__/memory-retrieval-budget.test.ts +7 -8
- package/src/__tests__/memory-retrieval.benchmark.test.ts +144 -109
- package/src/__tests__/memory-upsert-concurrency.test.ts +292 -201
- package/src/__tests__/messaging-send-tool.test.ts +36 -29
- package/src/__tests__/migration-cli-flows.test.ts +69 -53
- package/src/__tests__/migration-ordering.test.ts +103 -86
- package/src/__tests__/mime-builder.test.ts +55 -32
- package/src/__tests__/mock-signup-server.test.ts +384 -246
- package/src/__tests__/model-intents.test.ts +61 -37
- package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +9 -12
- package/src/__tests__/no-is-trusted-guard.test.ts +24 -21
- package/src/__tests__/non-member-access-request.test.ts +3 -2
- package/src/__tests__/notification-broadcaster.test.ts +99 -81
- package/src/__tests__/notification-decision-fallback.test.ts +223 -178
- package/src/__tests__/notification-decision-strategy.test.ts +375 -337
- package/src/__tests__/notification-deep-link.test.ts +67 -61
- package/src/__tests__/notification-guardian-path.test.ts +248 -206
- package/src/__tests__/notification-routing-intent.test.ts +166 -93
- package/src/__tests__/notification-thread-candidate-validation.test.ts +78 -75
- package/src/__tests__/notification-thread-candidates.test.ts +64 -61
- package/src/__tests__/oauth-callback-registry.test.ts +40 -30
- package/src/__tests__/oauth-connect-handler.test.ts +109 -89
- package/src/__tests__/oauth-scope-policy.test.ts +63 -55
- package/src/__tests__/oauth2-gateway-transport.test.ts +252 -174
- package/src/__tests__/onboarding-starter-tasks.test.ts +93 -89
- package/src/__tests__/onboarding-template-contract.test.ts +93 -94
- package/src/__tests__/openai-provider.test.ts +366 -274
- package/src/__tests__/pairing-concurrent.test.ts +18 -12
- package/src/__tests__/pairing-routes.test.ts +45 -41
- package/src/__tests__/parallel-tool.benchmark.test.ts +108 -58
- package/src/__tests__/parser.test.ts +316 -226
- package/src/__tests__/path-classifier.test.ts +24 -25
- package/src/__tests__/path-policy.test.ts +187 -147
- package/src/__tests__/phone.test.ts +36 -36
- package/src/__tests__/platform-move-helper.test.ts +48 -40
- package/src/__tests__/platform-socket-path.test.ts +23 -24
- package/src/__tests__/platform-workspace-migration.test.ts +464 -414
- package/src/__tests__/platform.test.ts +61 -53
- package/src/__tests__/playbook-execution.test.ts +397 -265
- package/src/__tests__/playbook-tools.test.ts +267 -196
- package/src/__tests__/prebuilt-home-base-seed.test.ts +30 -27
- package/src/__tests__/pricing.test.ts +316 -136
- package/src/__tests__/profile-compiler.test.ts +206 -188
- package/src/__tests__/provider-commit-message-generator.test.ts +114 -106
- package/src/__tests__/provider-error-scenarios.test.ts +212 -158
- package/src/__tests__/provider-fail-open-selection.test.ts +51 -44
- package/src/__tests__/provider-registry-ollama.test.ts +13 -9
- package/src/__tests__/provider-streaming.benchmark.test.ts +232 -183
- package/src/__tests__/proxy-approval-callback.test.ts +180 -119
- package/src/__tests__/public-ingress-urls.test.ts +112 -94
- package/src/__tests__/qdrant-manager.test.ts +147 -98
- package/src/__tests__/ratelimit.test.ts +152 -82
- package/src/__tests__/recording-handler.test.ts +273 -151
- package/src/__tests__/recording-intent-fallback.test.ts +94 -75
- package/src/__tests__/recording-intent-handler.test.ts +0 -1
- package/src/__tests__/recording-intent.test.ts +578 -379
- package/src/__tests__/recording-state-machine.test.ts +530 -316
- package/src/__tests__/recurrence-engine-rruleset.test.ts +150 -92
- package/src/__tests__/recurrence-engine.test.ts +81 -41
- package/src/__tests__/recurrence-types.test.ts +63 -44
- package/src/__tests__/relay-server.test.ts +2131 -1602
- package/src/__tests__/reminder-store.test.ts +158 -80
- package/src/__tests__/reminder.test.ts +113 -109
- package/src/__tests__/remote-skill-policy.test.ts +96 -72
- package/src/__tests__/request-file-tool.test.ts +74 -67
- package/src/__tests__/response-tier.test.ts +131 -74
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/runtime-events-sse-parity.test.ts +167 -145
- package/src/__tests__/runtime-events-sse.test.ts +0 -1
- package/src/__tests__/sandbox-diagnostics.test.ts +66 -56
- package/src/__tests__/sandbox-host-parity.test.ts +377 -301
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +213 -161
- package/src/__tests__/schedule-store.test.ts +268 -205
- package/src/__tests__/schedule-tools.test.ts +702 -524
- package/src/__tests__/scheduler-recurrence.test.ts +240 -130
- package/src/__tests__/scoped-approval-grants.test.ts +258 -168
- package/src/__tests__/scoped-grant-security-matrix.test.ts +160 -146
- package/src/__tests__/script-proxy-certs.test.ts +38 -35
- package/src/__tests__/script-proxy-connect-tunnel.test.ts +71 -46
- package/src/__tests__/script-proxy-decision-trace.test.ts +161 -84
- package/src/__tests__/script-proxy-http-forwarder.test.ts +146 -129
- package/src/__tests__/script-proxy-injection-runtime.test.ts +139 -113
- package/src/__tests__/script-proxy-mitm-handler.test.ts +226 -142
- package/src/__tests__/script-proxy-policy-runtime.test.ts +126 -86
- package/src/__tests__/script-proxy-policy.test.ts +308 -153
- package/src/__tests__/script-proxy-rewrite-specificity.test.ts +74 -62
- package/src/__tests__/script-proxy-router.test.ts +111 -77
- package/src/__tests__/script-proxy-session-manager.test.ts +156 -113
- package/src/__tests__/script-proxy-session-runtime.test.ts +28 -24
- package/src/__tests__/secret-allowlist.test.ts +105 -90
- package/src/__tests__/secret-ingress-handler.test.ts +41 -30
- package/src/__tests__/secret-onetime-send.test.ts +67 -50
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +35 -31
- package/src/__tests__/secret-response-routing.test.ts +50 -41
- package/src/__tests__/secret-scanner-executor.test.ts +152 -111
- package/src/__tests__/secret-scanner.test.ts +495 -413
- package/src/__tests__/secure-keys.test.ts +132 -121
- package/src/__tests__/send-endpoint-busy.test.ts +0 -1
- package/src/__tests__/send-notification-tool.test.ts +43 -42
- package/src/__tests__/sensitive-output-placeholders.test.ts +72 -64
- package/src/__tests__/sequence-store.test.ts +335 -167
- package/src/__tests__/server-history-render.test.ts +341 -202
- package/src/__tests__/session-abort-tool-results.test.ts +133 -70
- package/src/__tests__/session-confirmation-signals.test.ts +252 -160
- package/src/__tests__/session-conflict-gate.test.ts +775 -585
- package/src/__tests__/session-error.test.ts +222 -191
- package/src/__tests__/session-evictor.test.ts +79 -62
- package/src/__tests__/session-init.benchmark.test.ts +170 -108
- package/src/__tests__/session-load-history-repair.test.ts +273 -139
- package/src/__tests__/session-messaging-secret-redirect.test.ts +130 -90
- package/src/__tests__/session-pre-run-repair.test.ts +106 -59
- package/src/__tests__/session-profile-injection.test.ts +198 -130
- package/src/__tests__/session-provider-retry-repair.test.ts +223 -141
- package/src/__tests__/session-queue.test.ts +624 -321
- package/src/__tests__/session-runtime-assembly.test.ts +425 -329
- package/src/__tests__/session-runtime-workspace.test.ts +69 -61
- package/src/__tests__/session-skill-tools.test.ts +973 -678
- package/src/__tests__/session-slash-known.test.ts +185 -133
- package/src/__tests__/session-slash-queue.test.ts +147 -81
- package/src/__tests__/session-slash-unknown.test.ts +135 -90
- package/src/__tests__/session-surfaces-task-progress.test.ts +122 -87
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +338 -177
- package/src/__tests__/session-tool-setup-memory-scope.test.ts +63 -40
- package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +60 -37
- package/src/__tests__/session-tool-setup-tools-disabled.test.ts +28 -26
- package/src/__tests__/session-undo.test.ts +43 -30
- package/src/__tests__/session-workspace-cache-state.test.ts +108 -67
- package/src/__tests__/session-workspace-injection.test.ts +245 -117
- package/src/__tests__/session-workspace-tool-tracking.test.ts +260 -93
- package/src/__tests__/shared-filesystem-errors.test.ts +47 -47
- package/src/__tests__/shell-credential-ref.test.ts +126 -90
- package/src/__tests__/shell-identity.test.ts +134 -111
- package/src/__tests__/shell-parser-fuzz.test.ts +263 -179
- package/src/__tests__/shell-parser-property.test.ts +435 -288
- package/src/__tests__/shell-tool-proxy-mode.test.ts +142 -70
- package/src/__tests__/size-guard.test.ts +42 -44
- package/src/__tests__/skill-feature-flags-integration.test.ts +79 -52
- package/src/__tests__/skill-feature-flags.test.ts +75 -47
- package/src/__tests__/skill-include-graph.test.ts +143 -148
- package/src/__tests__/skill-load-feature-flag.test.ts +94 -59
- package/src/__tests__/skill-load-tool.test.ts +371 -199
- package/src/__tests__/skill-projection-feature-flag.test.ts +131 -88
- package/src/__tests__/skill-projection.benchmark.test.ts +93 -65
- package/src/__tests__/skill-script-runner-host.test.ts +460 -250
- package/src/__tests__/skill-script-runner-sandbox.test.ts +168 -108
- package/src/__tests__/skill-script-runner.test.ts +115 -74
- package/src/__tests__/skill-tool-factory.test.ts +140 -96
- package/src/__tests__/skill-tool-manifest.test.ts +306 -210
- package/src/__tests__/skill-version-hash.test.ts +70 -56
- package/src/__tests__/skills.test.ts +0 -1
- package/src/__tests__/slack-channel-config.test.ts +127 -84
- package/src/__tests__/slack-skill.test.ts +60 -47
- package/src/__tests__/slash-commands-catalog.test.ts +37 -31
- package/src/__tests__/slash-commands-parser.test.ts +71 -64
- package/src/__tests__/slash-commands-resolver.test.ts +143 -107
- package/src/__tests__/slash-commands-rewrite.test.ts +22 -22
- package/src/__tests__/speaker-identification.test.ts +28 -25
- package/src/__tests__/starter-bundle.test.ts +27 -23
- package/src/__tests__/starter-task-flow.test.ts +67 -52
- package/src/__tests__/subagent-manager-notify.test.ts +154 -108
- package/src/__tests__/subagent-tools.test.ts +311 -270
- package/src/__tests__/subagent-types.test.ts +40 -40
- package/src/__tests__/surface-mutex-cleanup.test.ts +42 -30
- package/src/__tests__/swarm-dag-pathological.test.ts +122 -111
- package/src/__tests__/swarm-orchestrator.test.ts +135 -101
- package/src/__tests__/swarm-plan-validator.test.ts +125 -73
- package/src/__tests__/swarm-recursion.test.ts +58 -46
- package/src/__tests__/swarm-router-planner.test.ts +99 -74
- package/src/__tests__/swarm-session-integration.test.ts +148 -91
- package/src/__tests__/swarm-tool.test.ts +65 -45
- package/src/__tests__/swarm-worker-backend.test.ts +59 -45
- package/src/__tests__/swarm-worker-runner.test.ts +133 -118
- package/src/__tests__/system-prompt.test.ts +290 -256
- package/src/__tests__/task-compiler.test.ts +176 -120
- package/src/__tests__/task-management-tools.test.ts +561 -456
- package/src/__tests__/task-memory-cleanup.test.ts +627 -362
- package/src/__tests__/task-runner.test.ts +117 -94
- package/src/__tests__/task-scheduler.test.ts +113 -84
- package/src/__tests__/task-tools.test.ts +349 -264
- package/src/__tests__/terminal-sandbox.test.ts +138 -108
- package/src/__tests__/terminal-tools.test.ts +350 -305
- package/src/__tests__/thread-seed-composer.test.ts +307 -180
- package/src/__tests__/tool-approval-handler.test.ts +238 -137
- package/src/__tests__/tool-audit-listener.test.ts +69 -69
- package/src/__tests__/tool-domain-event-publisher.test.ts +142 -132
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +153 -146
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +136 -105
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +355 -239
- package/src/__tests__/tool-executor-redaction.test.ts +112 -109
- package/src/__tests__/tool-executor-shell-integration.test.ts +130 -79
- package/src/__tests__/tool-executor.test.ts +1274 -674
- package/src/__tests__/tool-grant-request-escalation.test.ts +401 -283
- package/src/__tests__/tool-metrics-listener.test.ts +97 -85
- package/src/__tests__/tool-notification-listener.test.ts +42 -25
- package/src/__tests__/tool-permission-simulate-handler.test.ts +137 -113
- package/src/__tests__/tool-policy.test.ts +44 -25
- package/src/__tests__/tool-profiling-listener.test.ts +99 -93
- package/src/__tests__/tool-result-truncation.test.ts +5 -4
- package/src/__tests__/tool-trace-listener.test.ts +131 -111
- package/src/__tests__/top-level-renderer.test.ts +62 -58
- package/src/__tests__/top-level-scanner.test.ts +68 -64
- package/src/__tests__/trace-emitter.test.ts +56 -56
- package/src/__tests__/trust-context-guards.test.ts +65 -65
- package/src/__tests__/trust-store.test.ts +1239 -806
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +3 -2
- package/src/__tests__/trusted-contact-multichannel.test.ts +3 -2
- package/src/__tests__/trusted-contact-verification.test.ts +251 -231
- package/src/__tests__/turn-commit.test.ts +259 -200
- package/src/__tests__/twilio-provider.test.ts +140 -126
- package/src/__tests__/twilio-rest.test.ts +22 -18
- package/src/__tests__/twilio-routes-elevenlabs.test.ts +0 -1
- package/src/__tests__/twilio-routes-twiml.test.ts +55 -55
- package/src/__tests__/twilio-routes.test.ts +0 -1
- package/src/__tests__/twitter-auth-handler.test.ts +184 -139
- package/src/__tests__/twitter-cli-error-shaping.test.ts +88 -73
- package/src/__tests__/twitter-cli-routing.test.ts +146 -99
- package/src/__tests__/twitter-oauth-client.test.ts +82 -65
- package/src/__tests__/update-bulletin-format.test.ts +69 -66
- package/src/__tests__/update-bulletin-state.test.ts +66 -60
- package/src/__tests__/update-bulletin.test.ts +150 -114
- package/src/__tests__/update-template-contract.test.ts +15 -10
- package/src/__tests__/url-safety.test.ts +288 -265
- package/src/__tests__/user-reference.test.ts +32 -32
- package/src/__tests__/view-image-tool.test.ts +118 -96
- package/src/__tests__/voice-invite-redemption.test.ts +111 -106
- package/src/__tests__/voice-quality.test.ts +117 -102
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +204 -146
- package/src/__tests__/voice-session-bridge.test.ts +351 -216
- package/src/__tests__/weather-skill-regression.test.ts +170 -120
- package/src/__tests__/web-fetch.test.ts +664 -526
- package/src/__tests__/web-search.test.ts +379 -213
- package/src/__tests__/work-item-output.test.ts +90 -53
- package/src/__tests__/workspace-git-service.test.ts +437 -356
- package/src/__tests__/workspace-heartbeat-service.test.ts +125 -91
- package/src/__tests__/workspace-lifecycle.test.ts +98 -64
- package/src/__tests__/workspace-policy.test.ts +139 -71
- package/src/commands/__tests__/cc-command-registry.test.ts +142 -134
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +48 -39
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +25 -10
- package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +0 -1
- package/src/config/bundled-skills/messaging/SKILL.md +4 -3
- package/src/config/bundled-skills/messaging/tools/gmail-outreach-scan.ts +15 -5
- package/src/config/bundled-skills/messaging/tools/gmail-sender-digest.ts +16 -5
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +34 -32
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/env.ts +3 -4
- package/src/memory/db-connection.ts +16 -10
- package/src/messaging/providers/gmail/adapter.ts +10 -3
- package/src/messaging/providers/gmail/client.ts +280 -72
- package/src/runtime/auth/__tests__/context.test.ts +75 -65
- package/src/runtime/auth/__tests__/credential-service.test.ts +137 -114
- package/src/runtime/auth/__tests__/guard-tests.test.ts +84 -90
- package/src/runtime/auth/__tests__/ipc-auth-context.test.ts +40 -40
- package/src/runtime/auth/__tests__/middleware.test.ts +80 -74
- package/src/runtime/auth/__tests__/policy.test.ts +9 -9
- package/src/runtime/auth/__tests__/route-policy.test.ts +76 -65
- package/src/runtime/auth/__tests__/scopes.test.ts +68 -60
- package/src/runtime/auth/__tests__/subject.test.ts +54 -54
- package/src/runtime/auth/__tests__/token-service.test.ts +115 -108
- package/src/runtime/auth/scopes.ts +3 -0
- package/src/runtime/auth/token-service.ts +4 -1
- package/src/runtime/auth/types.ts +2 -1
- package/src/runtime/http-server.ts +2 -1
- package/src/security/secure-keys.ts +103 -53
- package/src/tools/browser/__tests__/auth-cache.test.ts +69 -63
- package/src/tools/browser/__tests__/auth-detector.test.ts +218 -157
- package/src/tools/browser/__tests__/jit-auth.test.ts +83 -99
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { describe, expect,test } from
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
import {
|
|
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(
|
|
27
|
-
test(
|
|
28
|
-
const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone:
|
|
29
|
-
expect(result).toStartWith(
|
|
30
|
-
expect(result).toEndWith(
|
|
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(
|
|
34
|
-
const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone:
|
|
35
|
-
expect(result).toContain(
|
|
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(
|
|
39
|
-
const result = buildTemporalContext({
|
|
40
|
-
|
|
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(
|
|
44
|
-
const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone:
|
|
45
|
-
expect(result).toContain(
|
|
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(
|
|
49
|
-
const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone:
|
|
50
|
-
expect(result).toContain(
|
|
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(
|
|
54
|
-
const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone:
|
|
55
|
-
expect(result).toContain(
|
|
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(
|
|
64
|
+
test("uses user timezone when provided and records source metadata", () => {
|
|
59
65
|
const result = buildTemporalContext({
|
|
60
66
|
nowMs: WED_FEB_18,
|
|
61
|
-
hostTimeZone:
|
|
62
|
-
userTimeZone:
|
|
67
|
+
hostTimeZone: "UTC",
|
|
68
|
+
userTimeZone: "America/New_York",
|
|
63
69
|
});
|
|
64
|
-
expect(result).toContain(
|
|
65
|
-
expect(result).toContain(
|
|
66
|
-
expect(result).toContain(
|
|
67
|
-
expect(result).toContain(
|
|
68
|
-
expect(result).toContain(
|
|
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(
|
|
77
|
+
test("uses configured user timezone when profile timezone is unavailable", () => {
|
|
72
78
|
const result = buildTemporalContext({
|
|
73
79
|
nowMs: WED_FEB_18,
|
|
74
|
-
hostTimeZone:
|
|
75
|
-
configuredUserTimeZone:
|
|
80
|
+
hostTimeZone: "UTC",
|
|
81
|
+
configuredUserTimeZone: "America/Chicago",
|
|
76
82
|
userTimeZone: null,
|
|
77
83
|
});
|
|
78
|
-
expect(result).toContain(
|
|
79
|
-
expect(result).toContain(
|
|
80
|
-
expect(result).toContain(
|
|
81
|
-
expect(result).toContain(
|
|
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(
|
|
90
|
+
test("configured user timezone takes precedence over profile timezone", () => {
|
|
85
91
|
const result = buildTemporalContext({
|
|
86
92
|
nowMs: WED_FEB_18,
|
|
87
|
-
hostTimeZone:
|
|
88
|
-
configuredUserTimeZone:
|
|
89
|
-
userTimeZone:
|
|
93
|
+
hostTimeZone: "UTC",
|
|
94
|
+
configuredUserTimeZone: "America/Los_Angeles",
|
|
95
|
+
userTimeZone: "America/New_York",
|
|
90
96
|
});
|
|
91
|
-
expect(result).toContain(
|
|
92
|
-
expect(result).toContain(
|
|
93
|
-
expect(result).toContain(
|
|
94
|
-
expect(result).toContain(
|
|
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(
|
|
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:
|
|
106
|
+
hostTimeZone: "UTC",
|
|
101
107
|
userTimeZone: null,
|
|
102
108
|
});
|
|
103
|
-
expect(result).toContain(
|
|
104
|
-
expect(result).toContain(
|
|
105
|
-
expect(result).toContain(
|
|
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(
|
|
114
|
+
test("accepts UTC/GMT offset-style user timezone values", () => {
|
|
109
115
|
const result = buildTemporalContext({
|
|
110
116
|
nowMs: WED_FEB_18,
|
|
111
|
-
hostTimeZone:
|
|
112
|
-
userTimeZone:
|
|
117
|
+
hostTimeZone: "UTC",
|
|
118
|
+
userTimeZone: "UTC+2",
|
|
113
119
|
});
|
|
114
|
-
expect(result).toContain(
|
|
115
|
-
expect(result).toContain(
|
|
116
|
-
expect(result).toContain(
|
|
117
|
-
expect(result).toContain(
|
|
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(
|
|
126
|
+
test("accepts fractional UTC/GMT offset-style user timezone values", () => {
|
|
121
127
|
const result = buildTemporalContext({
|
|
122
128
|
nowMs: WED_FEB_18,
|
|
123
|
-
hostTimeZone:
|
|
124
|
-
userTimeZone:
|
|
129
|
+
hostTimeZone: "UTC",
|
|
130
|
+
userTimeZone: "UTC+5:30",
|
|
125
131
|
});
|
|
126
|
-
expect(result).toContain(
|
|
127
|
-
expect(result).toContain(
|
|
128
|
-
expect(result).toContain(
|
|
129
|
-
expect(result).toContain(
|
|
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(
|
|
133
|
-
const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone:
|
|
134
|
-
expect(result).toContain(
|
|
135
|
-
expect(result).toContain(
|
|
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(
|
|
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({
|
|
141
|
-
|
|
142
|
-
|
|
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(
|
|
147
|
-
test(
|
|
155
|
+
describe("extractUserTimeZoneFromDynamicProfile", () => {
|
|
156
|
+
test("extracts canonical timezone from explicit timezone profile line", () => {
|
|
148
157
|
const profile = [
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
].join(
|
|
153
|
-
expect(extractUserTimeZoneFromDynamicProfile(profile)).toBe(
|
|
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(
|
|
167
|
+
test("extracts timezone token from generic profile text when explicit line is absent", () => {
|
|
157
168
|
const profile = [
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
].join(
|
|
162
|
-
expect(extractUserTimeZoneFromDynamicProfile(profile)).toBe(
|
|
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(
|
|
176
|
+
test("returns null when no valid timezone is present", () => {
|
|
166
177
|
const profile = [
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
].join(
|
|
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(
|
|
185
|
+
test("extracts UTC/GMT offset tokens from explicit timezone profile line", () => {
|
|
175
186
|
const profile = [
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
].join(
|
|
180
|
-
expect(extractUserTimeZoneFromDynamicProfile(profile)).toBe(
|
|
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(
|
|
194
|
+
test("extracts GMT negative offset tokens from generic profile text", () => {
|
|
184
195
|
const profile = [
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
].join(
|
|
189
|
-
expect(extractUserTimeZoneFromDynamicProfile(profile)).toBe(
|
|
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(
|
|
203
|
+
test("extracts fractional UTC offset tokens from explicit timezone profile line", () => {
|
|
193
204
|
const profile = [
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
].join(
|
|
198
|
-
expect(extractUserTimeZoneFromDynamicProfile(profile)).toBe(
|
|
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(
|
|
212
|
+
test("extracts fractional GMT offset tokens from generic profile text", () => {
|
|
202
213
|
const profile = [
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
].join(
|
|
207
|
-
expect(extractUserTimeZoneFromDynamicProfile(profile)).toBe(
|
|
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(
|
|
221
|
+
test("prefers IANA timezone tokens over UTC/GMT offsets in the same profile line", () => {
|
|
211
222
|
const profile = [
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
].join(
|
|
216
|
-
expect(extractUserTimeZoneFromDynamicProfile(profile)).toBe(
|
|
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(
|
|
225
|
-
test(
|
|
226
|
-
const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone:
|
|
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(
|
|
239
|
+
expect(result).toContain("Next weekend: 2026-02-21 – 2026-02-22");
|
|
229
240
|
});
|
|
230
241
|
|
|
231
|
-
test(
|
|
232
|
-
const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone:
|
|
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(
|
|
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(
|
|
243
|
-
test(
|
|
244
|
-
const result = buildTemporalContext({ nowMs: SAT_FEB_21, timeZone:
|
|
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(
|
|
257
|
+
expect(result).toContain("Next weekend: 2026-02-28 – 2026-03-01");
|
|
247
258
|
});
|
|
248
259
|
|
|
249
|
-
test(
|
|
250
|
-
const result = buildTemporalContext({ nowMs: SAT_FEB_21, timeZone:
|
|
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(
|
|
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(
|
|
261
|
-
test(
|
|
262
|
-
const result = buildTemporalContext({ nowMs: SUN_FEB_22, timeZone:
|
|
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(
|
|
275
|
+
expect(result).toContain("Next weekend: 2026-02-28 – 2026-03-01");
|
|
265
276
|
});
|
|
266
277
|
|
|
267
|
-
test(
|
|
268
|
-
const result = buildTemporalContext({ nowMs: SUN_FEB_22, timeZone:
|
|
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(
|
|
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(
|
|
279
|
-
test(
|
|
280
|
-
const result = buildTemporalContext({ nowMs: FRI_FEB_27, timeZone:
|
|
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(
|
|
293
|
+
expect(result).toContain("Next weekend: 2026-02-28 – 2026-03-01");
|
|
283
294
|
});
|
|
284
295
|
|
|
285
|
-
test(
|
|
286
|
-
const result = buildTemporalContext({ nowMs: FRI_FEB_27, timeZone:
|
|
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(
|
|
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(
|
|
297
|
-
test(
|
|
298
|
-
const result = buildTemporalContext({ nowMs: TUE_DEC_29, timeZone:
|
|
299
|
-
expect(result).toContain(
|
|
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(
|
|
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(
|
|
316
|
+
expect(result).toContain("Next work week: 2027-01-04 – 2027-01-08");
|
|
306
317
|
});
|
|
307
318
|
|
|
308
|
-
test(
|
|
309
|
-
const result = buildTemporalContext({
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
expect(result).toContain(
|
|
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(
|
|
323
|
-
test(
|
|
324
|
-
const result = buildTemporalContext({
|
|
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(
|
|
329
|
-
const result = buildTemporalContext({
|
|
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(
|
|
336
|
-
const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone:
|
|
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(
|
|
343
|
-
const result = buildTemporalContext({
|
|
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(
|
|
355
|
-
test(
|
|
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({
|
|
358
|
-
|
|
359
|
-
|
|
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(
|
|
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({
|
|
367
|
-
|
|
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(
|
|
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({
|
|
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(
|
|
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(
|
|
379
|
-
expect(result).toContain(
|
|
380
|
-
expect(result).toContain(
|
|
381
|
-
expect(result).toContain(
|
|
382
|
-
expect(result).toContain(
|
|
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(
|
|
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({
|
|
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(
|
|
432
|
+
expect(result).toContain("Today: 2026-11-01 (Sunday)");
|
|
392
433
|
// Horizon should have 3 consecutive days
|
|
393
|
-
expect(result).toContain(
|
|
394
|
-
expect(result).toContain(
|
|
395
|
-
expect(result).toContain(
|
|
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(
|
|
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({
|
|
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(
|
|
447
|
+
expect(result).toContain("Today: 2026-02-19 (Thursday)");
|
|
403
448
|
// Horizon should show consecutive days without +1 shift
|
|
404
|
-
expect(result).toContain(
|
|
405
|
-
expect(result).toContain(
|
|
406
|
-
expect(result).toContain(
|
|
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(
|
|
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({
|
|
413
|
-
|
|
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(
|
|
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:
|
|
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(
|
|
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(
|
|
429
|
-
expect(result).toContain(
|
|
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:
|
|
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(
|
|
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:
|
|
440
|
-
expect(result).toContain(
|
|
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:
|
|
445
|
-
expect(result).toContain(
|
|
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(
|
|
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:
|
|
456
|
-
expect(result).toContain(
|
|
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:
|
|
463
|
-
expect(result).toContain(
|
|
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:
|
|
468
|
-
expect(result).toContain(
|
|
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(
|
|
477
|
-
test(
|
|
478
|
-
const result = buildTemporalContext({ nowMs: WED_FEB_18, timeZone:
|
|
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(
|
|
531
|
+
expect(result).toContain("2026-02-18");
|
|
484
532
|
});
|
|
485
533
|
|
|
486
|
-
test(
|
|
487
|
-
const result = buildTemporalContext({
|
|
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(
|
|
494
|
-
expect(dateStr >
|
|
545
|
+
const dateStr = line.trim().split(" ")[0];
|
|
546
|
+
expect(dateStr > "2026-02-18").toBe(true);
|
|
495
547
|
}
|
|
496
548
|
});
|
|
497
549
|
|
|
498
|
-
test(
|
|
499
|
-
const result = buildTemporalContext({
|
|
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(
|
|
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(
|
|
507
|
-
const result = buildTemporalContext({
|
|
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(
|
|
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(
|
|
520
|
-
test(
|
|
521
|
-
const result = buildTemporalContext({ nowMs: FRI_FEB_27, timeZone:
|
|
522
|
-
expect(result).toContain(
|
|
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(
|
|
526
|
-
const result = buildTemporalContext({ nowMs: TUE_DEC_29, timeZone:
|
|
527
|
-
expect(result).toContain(
|
|
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(
|
|
536
|
-
test(
|
|
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({
|
|
540
|
-
|
|
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(
|
|
604
|
+
expect(result).toContain("Next weekend: 2026-03-07 – 2026-03-08");
|
|
543
605
|
});
|
|
544
606
|
|
|
545
|
-
test(
|
|
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({
|
|
549
|
-
|
|
550
|
-
|
|
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
|
});
|