@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,24 +1,38 @@
|
|
|
1
|
-
import { execFileSync } from
|
|
2
|
-
import { existsSync,mkdirSync, rmSync, writeFileSync } from
|
|
3
|
-
import { tmpdir } from
|
|
4
|
-
import { join } from
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import {
|
|
6
|
+
afterAll,
|
|
7
|
+
afterEach,
|
|
8
|
+
beforeEach,
|
|
9
|
+
describe,
|
|
10
|
+
expect,
|
|
11
|
+
test,
|
|
12
|
+
} from "bun:test";
|
|
7
13
|
|
|
8
14
|
import {
|
|
9
15
|
_resetEnrichmentService,
|
|
10
16
|
CommitEnrichmentService,
|
|
11
17
|
getEnrichmentService,
|
|
12
|
-
} from
|
|
13
|
-
import type { CommitContext } from
|
|
14
|
-
import {
|
|
18
|
+
} from "../workspace/commit-message-enrichment-service.js";
|
|
19
|
+
import type { CommitContext } from "../workspace/commit-message-provider.js";
|
|
20
|
+
import {
|
|
21
|
+
_resetGitServiceRegistry,
|
|
22
|
+
WorkspaceGitService,
|
|
23
|
+
} from "../workspace/git-service.js";
|
|
15
24
|
|
|
16
|
-
describe(
|
|
25
|
+
describe("CommitEnrichmentService", () => {
|
|
17
26
|
let testDir: string;
|
|
18
27
|
let gitService: WorkspaceGitService;
|
|
19
28
|
|
|
20
29
|
beforeEach(async () => {
|
|
21
|
-
testDir = join(
|
|
30
|
+
testDir = join(
|
|
31
|
+
tmpdir(),
|
|
32
|
+
`vellum-enrichment-test-${Date.now()}-${Math.random()
|
|
33
|
+
.toString(36)
|
|
34
|
+
.slice(2)}`,
|
|
35
|
+
);
|
|
22
36
|
mkdirSync(testDir, { recursive: true });
|
|
23
37
|
_resetGitServiceRegistry();
|
|
24
38
|
_resetEnrichmentService();
|
|
@@ -28,7 +42,11 @@ describe('CommitEnrichmentService', () => {
|
|
|
28
42
|
});
|
|
29
43
|
|
|
30
44
|
afterEach(async () => {
|
|
31
|
-
try {
|
|
45
|
+
try {
|
|
46
|
+
await getEnrichmentService().shutdown();
|
|
47
|
+
} catch {
|
|
48
|
+
/* ignore */
|
|
49
|
+
}
|
|
32
50
|
_resetEnrichmentService();
|
|
33
51
|
if (existsSync(testDir)) {
|
|
34
52
|
rmSync(testDir, { recursive: true, force: true });
|
|
@@ -36,39 +54,48 @@ describe('CommitEnrichmentService', () => {
|
|
|
36
54
|
});
|
|
37
55
|
|
|
38
56
|
afterAll(async () => {
|
|
39
|
-
try {
|
|
57
|
+
try {
|
|
58
|
+
await getEnrichmentService().shutdown();
|
|
59
|
+
} catch {
|
|
60
|
+
/* ignore */
|
|
61
|
+
}
|
|
40
62
|
_resetEnrichmentService();
|
|
41
63
|
});
|
|
42
64
|
|
|
43
65
|
function makeContext(overrides?: Partial<CommitContext>): CommitContext {
|
|
44
66
|
return {
|
|
45
67
|
workspaceDir: testDir,
|
|
46
|
-
trigger:
|
|
47
|
-
sessionId:
|
|
68
|
+
trigger: "turn",
|
|
69
|
+
sessionId: "sess_test",
|
|
48
70
|
turnNumber: 1,
|
|
49
|
-
changedFiles: [
|
|
71
|
+
changedFiles: ["file.txt"],
|
|
50
72
|
timestampMs: Date.now(),
|
|
51
73
|
...overrides,
|
|
52
74
|
};
|
|
53
75
|
}
|
|
54
76
|
|
|
55
77
|
async function createCommit(): Promise<string> {
|
|
56
|
-
writeFileSync(join(testDir, `file-${Date.now()}.txt`),
|
|
57
|
-
await gitService.commitChanges(
|
|
78
|
+
writeFileSync(join(testDir, `file-${Date.now()}.txt`), "content");
|
|
79
|
+
await gitService.commitChanges("test commit");
|
|
58
80
|
return await gitService.getHeadHash();
|
|
59
81
|
}
|
|
60
82
|
|
|
61
|
-
async function waitForDrain(
|
|
83
|
+
async function waitForDrain(
|
|
84
|
+
service: CommitEnrichmentService,
|
|
85
|
+
timeoutMs = 5000,
|
|
86
|
+
): Promise<void> {
|
|
62
87
|
const started = Date.now();
|
|
63
88
|
while (service._getQueueSize() > 0 || service._getActiveWorkers() > 0) {
|
|
64
89
|
if (Date.now() - started > timeoutMs) {
|
|
65
|
-
throw new Error(
|
|
90
|
+
throw new Error(
|
|
91
|
+
`Timed out waiting for enrichment queue to drain after ${timeoutMs}ms`,
|
|
92
|
+
);
|
|
66
93
|
}
|
|
67
|
-
await new Promise(resolve => setTimeout(resolve, 50));
|
|
94
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
68
95
|
}
|
|
69
96
|
}
|
|
70
97
|
|
|
71
|
-
test(
|
|
98
|
+
test("enqueue and execute writes git note on success", async () => {
|
|
72
99
|
const commitHash = await createCommit();
|
|
73
100
|
const service = new CommitEnrichmentService({
|
|
74
101
|
maxQueueSize: 10,
|
|
@@ -88,21 +115,25 @@ describe('CommitEnrichmentService', () => {
|
|
|
88
115
|
await service.shutdown();
|
|
89
116
|
|
|
90
117
|
// Verify git note was written
|
|
91
|
-
const noteContent = execFileSync(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
118
|
+
const noteContent = execFileSync(
|
|
119
|
+
"git",
|
|
120
|
+
["notes", "--ref=vellum", "show", commitHash],
|
|
121
|
+
{
|
|
122
|
+
cwd: testDir,
|
|
123
|
+
encoding: "utf-8",
|
|
124
|
+
},
|
|
125
|
+
);
|
|
95
126
|
|
|
96
127
|
const note = JSON.parse(noteContent);
|
|
97
128
|
expect(note.enriched).toBe(true);
|
|
98
|
-
expect(note.trigger).toBe(
|
|
99
|
-
expect(note.sessionId).toBe(
|
|
129
|
+
expect(note.trigger).toBe("turn");
|
|
130
|
+
expect(note.sessionId).toBe("sess_test");
|
|
100
131
|
expect(note.turnNumber).toBe(1);
|
|
101
132
|
expect(note.filesChanged).toBe(1);
|
|
102
133
|
expect(service._getSucceededCount()).toBe(1);
|
|
103
134
|
});
|
|
104
135
|
|
|
105
|
-
test(
|
|
136
|
+
test("queue overflow drops oldest job", async () => {
|
|
106
137
|
const service = new CommitEnrichmentService({
|
|
107
138
|
maxQueueSize: 2,
|
|
108
139
|
maxConcurrency: 1,
|
|
@@ -116,9 +147,24 @@ describe('CommitEnrichmentService', () => {
|
|
|
116
147
|
|
|
117
148
|
// Enqueue 3 jobs — hash1 starts immediately (active worker),
|
|
118
149
|
// hash2 goes to queue (size=1), hash3 goes to queue (size=2), no overflow drop.
|
|
119
|
-
service.enqueue({
|
|
120
|
-
|
|
121
|
-
|
|
150
|
+
service.enqueue({
|
|
151
|
+
workspaceDir: testDir,
|
|
152
|
+
commitHash: hash1,
|
|
153
|
+
context: makeContext(),
|
|
154
|
+
gitService,
|
|
155
|
+
});
|
|
156
|
+
service.enqueue({
|
|
157
|
+
workspaceDir: testDir,
|
|
158
|
+
commitHash: hash2,
|
|
159
|
+
context: makeContext(),
|
|
160
|
+
gitService,
|
|
161
|
+
});
|
|
162
|
+
service.enqueue({
|
|
163
|
+
workspaceDir: testDir,
|
|
164
|
+
commitHash: hash3,
|
|
165
|
+
context: makeContext(),
|
|
166
|
+
gitService,
|
|
167
|
+
});
|
|
122
168
|
|
|
123
169
|
// No overflow drops — queue size 2 can hold 2 pending while 1 is active
|
|
124
170
|
expect(service._getDroppedCount()).toBe(0);
|
|
@@ -130,7 +176,7 @@ describe('CommitEnrichmentService', () => {
|
|
|
130
176
|
expect(service._getSucceededCount()).toBe(1);
|
|
131
177
|
});
|
|
132
178
|
|
|
133
|
-
test(
|
|
179
|
+
test("queue overflow actually drops when truly full", async () => {
|
|
134
180
|
// Create a service where the worker is slow
|
|
135
181
|
const service = new CommitEnrichmentService({
|
|
136
182
|
maxQueueSize: 1,
|
|
@@ -146,16 +192,31 @@ describe('CommitEnrichmentService', () => {
|
|
|
146
192
|
// hash1 starts processing immediately (active worker = 1, queue empty)
|
|
147
193
|
// hash2 goes to queue (queue size = 1)
|
|
148
194
|
// hash3 tries to go to queue but it's full → drops hash2, adds hash3
|
|
149
|
-
service.enqueue({
|
|
150
|
-
|
|
151
|
-
|
|
195
|
+
service.enqueue({
|
|
196
|
+
workspaceDir: testDir,
|
|
197
|
+
commitHash: hash1,
|
|
198
|
+
context: makeContext(),
|
|
199
|
+
gitService,
|
|
200
|
+
});
|
|
201
|
+
service.enqueue({
|
|
202
|
+
workspaceDir: testDir,
|
|
203
|
+
commitHash: hash2,
|
|
204
|
+
context: makeContext(),
|
|
205
|
+
gitService,
|
|
206
|
+
});
|
|
207
|
+
service.enqueue({
|
|
208
|
+
workspaceDir: testDir,
|
|
209
|
+
commitHash: hash3,
|
|
210
|
+
context: makeContext(),
|
|
211
|
+
gitService,
|
|
212
|
+
});
|
|
152
213
|
|
|
153
214
|
expect(service._getDroppedCount()).toBe(1);
|
|
154
215
|
|
|
155
216
|
await service.shutdown();
|
|
156
217
|
});
|
|
157
218
|
|
|
158
|
-
test(
|
|
219
|
+
test("fire-and-forget enqueue does not block caller", async () => {
|
|
159
220
|
const commitHash = await createCommit();
|
|
160
221
|
const service = new CommitEnrichmentService({
|
|
161
222
|
maxQueueSize: 10,
|
|
@@ -179,7 +240,7 @@ describe('CommitEnrichmentService', () => {
|
|
|
179
240
|
await service.shutdown();
|
|
180
241
|
});
|
|
181
242
|
|
|
182
|
-
test(
|
|
243
|
+
test("graceful shutdown drains in-flight and discards pending", async () => {
|
|
183
244
|
const hash1 = await createCommit();
|
|
184
245
|
const hash2 = await createCommit();
|
|
185
246
|
|
|
@@ -190,8 +251,18 @@ describe('CommitEnrichmentService', () => {
|
|
|
190
251
|
maxRetries: 0,
|
|
191
252
|
});
|
|
192
253
|
|
|
193
|
-
service.enqueue({
|
|
194
|
-
|
|
254
|
+
service.enqueue({
|
|
255
|
+
workspaceDir: testDir,
|
|
256
|
+
commitHash: hash1,
|
|
257
|
+
context: makeContext(),
|
|
258
|
+
gitService,
|
|
259
|
+
});
|
|
260
|
+
service.enqueue({
|
|
261
|
+
workspaceDir: testDir,
|
|
262
|
+
commitHash: hash2,
|
|
263
|
+
context: makeContext(),
|
|
264
|
+
gitService,
|
|
265
|
+
});
|
|
195
266
|
|
|
196
267
|
// Shutdown should complete without hanging
|
|
197
268
|
await service.shutdown();
|
|
@@ -203,7 +274,7 @@ describe('CommitEnrichmentService', () => {
|
|
|
203
274
|
expect(service._getQueueSize()).toBe(0);
|
|
204
275
|
});
|
|
205
276
|
|
|
206
|
-
test(
|
|
277
|
+
test("shutdown discards all pending jobs and counts them as dropped", async () => {
|
|
207
278
|
// Use maxConcurrency 1 so only one job starts processing; the rest stay pending.
|
|
208
279
|
const hashes: string[] = [];
|
|
209
280
|
for (let i = 0; i < 5; i++) {
|
|
@@ -218,7 +289,12 @@ describe('CommitEnrichmentService', () => {
|
|
|
218
289
|
});
|
|
219
290
|
|
|
220
291
|
for (const hash of hashes) {
|
|
221
|
-
service.enqueue({
|
|
292
|
+
service.enqueue({
|
|
293
|
+
workspaceDir: testDir,
|
|
294
|
+
commitHash: hash,
|
|
295
|
+
context: makeContext(),
|
|
296
|
+
gitService,
|
|
297
|
+
});
|
|
222
298
|
}
|
|
223
299
|
|
|
224
300
|
// First job is in-flight, remaining 4 are pending
|
|
@@ -229,7 +305,7 @@ describe('CommitEnrichmentService', () => {
|
|
|
229
305
|
expect(service._getDroppedCount()).toBe(4);
|
|
230
306
|
});
|
|
231
307
|
|
|
232
|
-
test(
|
|
308
|
+
test("shutdown does not cause concurrency spike", async () => {
|
|
233
309
|
const hashes: string[] = [];
|
|
234
310
|
for (let i = 0; i < 3; i++) {
|
|
235
311
|
hashes.push(await createCommit());
|
|
@@ -243,7 +319,12 @@ describe('CommitEnrichmentService', () => {
|
|
|
243
319
|
});
|
|
244
320
|
|
|
245
321
|
for (const hash of hashes) {
|
|
246
|
-
service.enqueue({
|
|
322
|
+
service.enqueue({
|
|
323
|
+
workspaceDir: testDir,
|
|
324
|
+
commitHash: hash,
|
|
325
|
+
context: makeContext(),
|
|
326
|
+
gitService,
|
|
327
|
+
});
|
|
247
328
|
}
|
|
248
329
|
|
|
249
330
|
await service.shutdown();
|
|
@@ -252,7 +333,7 @@ describe('CommitEnrichmentService', () => {
|
|
|
252
333
|
expect(service._getActiveWorkers()).toBe(0);
|
|
253
334
|
});
|
|
254
335
|
|
|
255
|
-
test(
|
|
336
|
+
test("discards jobs enqueued after shutdown", async () => {
|
|
256
337
|
const commitHash = await createCommit();
|
|
257
338
|
const service = new CommitEnrichmentService({
|
|
258
339
|
maxQueueSize: 10,
|
|
@@ -275,7 +356,7 @@ describe('CommitEnrichmentService', () => {
|
|
|
275
356
|
expect(service._getSucceededCount()).toBe(0);
|
|
276
357
|
});
|
|
277
358
|
|
|
278
|
-
test(
|
|
359
|
+
test("multiple successful enrichments write separate git notes", async () => {
|
|
279
360
|
const hash1 = await createCommit();
|
|
280
361
|
const hash2 = await createCommit();
|
|
281
362
|
|
|
@@ -304,19 +385,25 @@ describe('CommitEnrichmentService', () => {
|
|
|
304
385
|
await service.shutdown();
|
|
305
386
|
|
|
306
387
|
// Both notes should exist
|
|
307
|
-
const note1 = JSON.parse(
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
388
|
+
const note1 = JSON.parse(
|
|
389
|
+
execFileSync("git", ["notes", "--ref=vellum", "show", hash1], {
|
|
390
|
+
cwd: testDir,
|
|
391
|
+
encoding: "utf-8",
|
|
392
|
+
}),
|
|
393
|
+
);
|
|
394
|
+
const note2 = JSON.parse(
|
|
395
|
+
execFileSync("git", ["notes", "--ref=vellum", "show", hash2], {
|
|
396
|
+
cwd: testDir,
|
|
397
|
+
encoding: "utf-8",
|
|
398
|
+
}),
|
|
399
|
+
);
|
|
313
400
|
|
|
314
401
|
expect(note1.turnNumber).toBe(1);
|
|
315
402
|
expect(note2.turnNumber).toBe(2);
|
|
316
403
|
expect(service._getSucceededCount()).toBe(2);
|
|
317
404
|
});
|
|
318
405
|
|
|
319
|
-
test(
|
|
406
|
+
test("job timeout triggers retry with backoff then fails after max retries", async () => {
|
|
320
407
|
const service = new CommitEnrichmentService({
|
|
321
408
|
maxQueueSize: 10,
|
|
322
409
|
maxConcurrency: 1,
|
|
@@ -349,7 +436,7 @@ describe('CommitEnrichmentService', () => {
|
|
|
349
436
|
expect(service._getSucceededCount()).toBe(0);
|
|
350
437
|
}, 15000); // Allow up to 15s for backoff delays
|
|
351
438
|
|
|
352
|
-
test(
|
|
439
|
+
test("queue overflow drop behavior is deterministic", async () => {
|
|
353
440
|
// With maxQueueSize=2 and maxConcurrency=1:
|
|
354
441
|
// - Job A starts processing immediately (in-flight)
|
|
355
442
|
// - Job B enters queue (size=1)
|
|
@@ -369,17 +456,42 @@ describe('CommitEnrichmentService', () => {
|
|
|
369
456
|
const hashD = await createCommit();
|
|
370
457
|
const hashE = await createCommit();
|
|
371
458
|
|
|
372
|
-
service.enqueue({
|
|
373
|
-
|
|
374
|
-
|
|
459
|
+
service.enqueue({
|
|
460
|
+
workspaceDir: testDir,
|
|
461
|
+
commitHash: hashA,
|
|
462
|
+
context: makeContext({ turnNumber: 1 }),
|
|
463
|
+
gitService,
|
|
464
|
+
});
|
|
465
|
+
service.enqueue({
|
|
466
|
+
workspaceDir: testDir,
|
|
467
|
+
commitHash: hashB,
|
|
468
|
+
context: makeContext({ turnNumber: 2 }),
|
|
469
|
+
gitService,
|
|
470
|
+
});
|
|
471
|
+
service.enqueue({
|
|
472
|
+
workspaceDir: testDir,
|
|
473
|
+
commitHash: hashC,
|
|
474
|
+
context: makeContext({ turnNumber: 3 }),
|
|
475
|
+
gitService,
|
|
476
|
+
});
|
|
375
477
|
// No drops yet: A is in-flight, B and C in queue (size=2)
|
|
376
478
|
expect(service._getDroppedCount()).toBe(0);
|
|
377
479
|
|
|
378
|
-
service.enqueue({
|
|
480
|
+
service.enqueue({
|
|
481
|
+
workspaceDir: testDir,
|
|
482
|
+
commitHash: hashD,
|
|
483
|
+
context: makeContext({ turnNumber: 4 }),
|
|
484
|
+
gitService,
|
|
485
|
+
});
|
|
379
486
|
// Queue was full (2), so oldest (B) was dropped
|
|
380
487
|
expect(service._getDroppedCount()).toBe(1);
|
|
381
488
|
|
|
382
|
-
service.enqueue({
|
|
489
|
+
service.enqueue({
|
|
490
|
+
workspaceDir: testDir,
|
|
491
|
+
commitHash: hashE,
|
|
492
|
+
context: makeContext({ turnNumber: 5 }),
|
|
493
|
+
gitService,
|
|
494
|
+
});
|
|
383
495
|
// Queue was full again (2), so oldest (C) was dropped
|
|
384
496
|
expect(service._getDroppedCount()).toBe(2);
|
|
385
497
|
|
|
@@ -394,7 +506,7 @@ describe('CommitEnrichmentService', () => {
|
|
|
394
506
|
expect(service._getDroppedCount()).toBe(4);
|
|
395
507
|
});
|
|
396
508
|
|
|
397
|
-
test(
|
|
509
|
+
test("timed-out enrichment work is cancelled via AbortSignal", async () => {
|
|
398
510
|
// Track whether the slow enrichment work actually ran to completion
|
|
399
511
|
let enrichmentCompleted = false;
|
|
400
512
|
const commitHash = await createCommit();
|
|
@@ -411,17 +523,25 @@ describe('CommitEnrichmentService', () => {
|
|
|
411
523
|
// child process on abort. This mock replicates that behavior by rejecting
|
|
412
524
|
// when the signal fires.
|
|
413
525
|
const originalWriteNote = gitService.writeNote.bind(gitService);
|
|
414
|
-
gitService.writeNote = async (
|
|
526
|
+
gitService.writeNote = async (
|
|
527
|
+
_hash: string,
|
|
528
|
+
_note: string,
|
|
529
|
+
signal?: AbortSignal,
|
|
530
|
+
) => {
|
|
415
531
|
// Simulate slow work that is cancellable via AbortSignal
|
|
416
532
|
await new Promise<void>((resolve, reject) => {
|
|
417
533
|
const timer = setTimeout(() => {
|
|
418
534
|
enrichmentCompleted = true;
|
|
419
535
|
resolve();
|
|
420
536
|
}, 2000);
|
|
421
|
-
signal?.addEventListener(
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
537
|
+
signal?.addEventListener(
|
|
538
|
+
"abort",
|
|
539
|
+
() => {
|
|
540
|
+
clearTimeout(timer);
|
|
541
|
+
reject(new Error("aborted"));
|
|
542
|
+
},
|
|
543
|
+
{ once: true },
|
|
544
|
+
);
|
|
425
545
|
});
|
|
426
546
|
};
|
|
427
547
|
|
|
@@ -438,7 +558,7 @@ describe('CommitEnrichmentService', () => {
|
|
|
438
558
|
// Allow any zombie work to settle — if abort didn't work, the 2s timer
|
|
439
559
|
// would still be running and would set enrichmentCompleted=true. Wait
|
|
440
560
|
// longer than the 2000ms mock delay to reliably catch the regression.
|
|
441
|
-
await new Promise(resolve => setTimeout(resolve, 2500));
|
|
561
|
+
await new Promise((resolve) => setTimeout(resolve, 2500));
|
|
442
562
|
|
|
443
563
|
// The job should have timed out and been counted as failed
|
|
444
564
|
expect(service._getFailedCount()).toBe(1);
|
|
@@ -450,7 +570,7 @@ describe('CommitEnrichmentService', () => {
|
|
|
450
570
|
gitService.writeNote = originalWriteNote;
|
|
451
571
|
});
|
|
452
572
|
|
|
453
|
-
test(
|
|
573
|
+
test("shutdown does not hang on timed-out jobs", async () => {
|
|
454
574
|
const commitHash = await createCommit();
|
|
455
575
|
|
|
456
576
|
const service = new CommitEnrichmentService({
|
|
@@ -463,13 +583,21 @@ describe('CommitEnrichmentService', () => {
|
|
|
463
583
|
// Make writeNote artificially slow so the job will always time out.
|
|
464
584
|
// The mock respects the abort signal so the subprocess is killed on timeout.
|
|
465
585
|
const originalWriteNote = gitService.writeNote.bind(gitService);
|
|
466
|
-
gitService.writeNote = async (
|
|
586
|
+
gitService.writeNote = async (
|
|
587
|
+
_hash: string,
|
|
588
|
+
_note: string,
|
|
589
|
+
signal?: AbortSignal,
|
|
590
|
+
) => {
|
|
467
591
|
await new Promise<void>((resolve, reject) => {
|
|
468
592
|
const timer = setTimeout(resolve, 5000);
|
|
469
|
-
signal?.addEventListener(
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
593
|
+
signal?.addEventListener(
|
|
594
|
+
"abort",
|
|
595
|
+
() => {
|
|
596
|
+
clearTimeout(timer);
|
|
597
|
+
reject(new Error("aborted"));
|
|
598
|
+
},
|
|
599
|
+
{ once: true },
|
|
600
|
+
);
|
|
473
601
|
});
|
|
474
602
|
};
|
|
475
603
|
|
|
@@ -492,7 +620,7 @@ describe('CommitEnrichmentService', () => {
|
|
|
492
620
|
gitService.writeNote = originalWriteNote;
|
|
493
621
|
}, 10000);
|
|
494
622
|
|
|
495
|
-
test(
|
|
623
|
+
test("abort signal is triggered on non-timeout errors before retry", async () => {
|
|
496
624
|
const commitHash = await createCommit();
|
|
497
625
|
|
|
498
626
|
const service = new CommitEnrichmentService({
|
|
@@ -508,7 +636,7 @@ describe('CommitEnrichmentService', () => {
|
|
|
508
636
|
// Set up a listener on the abort controller's signal to track abortion.
|
|
509
637
|
// We access the signal indirectly by throwing, which triggers the catch
|
|
510
638
|
// block in executeJob where controller.abort() is called.
|
|
511
|
-
throw new Error(
|
|
639
|
+
throw new Error("Simulated writeNote failure");
|
|
512
640
|
};
|
|
513
641
|
|
|
514
642
|
service.enqueue({
|
|
@@ -528,7 +656,7 @@ describe('CommitEnrichmentService', () => {
|
|
|
528
656
|
gitService.writeNote = originalWriteNote;
|
|
529
657
|
});
|
|
530
658
|
|
|
531
|
-
test(
|
|
659
|
+
test("enqueue is fire-and-forget and never throws even when called rapidly", async () => {
|
|
532
660
|
const service = new CommitEnrichmentService({
|
|
533
661
|
maxQueueSize: 3,
|
|
534
662
|
maxConcurrency: 1,
|
|
@@ -8,33 +8,33 @@
|
|
|
8
8
|
* - summary call count within expected range
|
|
9
9
|
* - severe pressure overriding cooldown
|
|
10
10
|
*/
|
|
11
|
-
import { describe, expect, mock, test } from
|
|
11
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
12
12
|
|
|
13
|
-
import { DEFAULT_CONFIG } from
|
|
14
|
-
import { estimatePromptTokens } from
|
|
15
|
-
import { ContextWindowManager } from
|
|
16
|
-
import type { Message, Provider } from
|
|
13
|
+
import { DEFAULT_CONFIG } from "../config/defaults.js";
|
|
14
|
+
import { estimatePromptTokens } from "../context/token-estimator.js";
|
|
15
|
+
import { ContextWindowManager } from "../context/window-manager.js";
|
|
16
|
+
import type { Message, Provider } from "../providers/types.js";
|
|
17
17
|
|
|
18
|
-
mock.module(
|
|
18
|
+
mock.module("../util/logger.js", () => ({
|
|
19
19
|
getLogger: () =>
|
|
20
20
|
new Proxy({} as Record<string, unknown>, { get: () => () => {} }),
|
|
21
21
|
}));
|
|
22
22
|
|
|
23
23
|
function makeSummaryProvider(counter: { calls: number }): Provider {
|
|
24
24
|
return {
|
|
25
|
-
name:
|
|
25
|
+
name: "mock",
|
|
26
26
|
async sendMessage() {
|
|
27
27
|
counter.calls += 1;
|
|
28
28
|
return {
|
|
29
29
|
content: [
|
|
30
30
|
{
|
|
31
|
-
type:
|
|
31
|
+
type: "text",
|
|
32
32
|
text: `## Goals\n- Preserve state\n## Constraints\n- Keep PRs small\n## Decisions\n- Call ${counter.calls}`,
|
|
33
33
|
},
|
|
34
34
|
],
|
|
35
|
-
model:
|
|
35
|
+
model: "mock-model",
|
|
36
36
|
usage: { inputTokens: 420, outputTokens: 85 },
|
|
37
|
-
stopReason:
|
|
37
|
+
stopReason: "end_turn",
|
|
38
38
|
};
|
|
39
39
|
},
|
|
40
40
|
};
|
|
@@ -44,19 +44,19 @@ function makeLongMessages(turns: number): Message[] {
|
|
|
44
44
|
const rows: Message[] = [];
|
|
45
45
|
for (let i = 0; i < turns; i++) {
|
|
46
46
|
rows.push({
|
|
47
|
-
role:
|
|
47
|
+
role: "user",
|
|
48
48
|
content: [
|
|
49
49
|
{
|
|
50
|
-
type:
|
|
50
|
+
type: "text",
|
|
51
51
|
text: `[U${i}] User message with enough content to estimate tokens. Topic ${i % 9}.`,
|
|
52
52
|
},
|
|
53
53
|
],
|
|
54
54
|
});
|
|
55
55
|
rows.push({
|
|
56
|
-
role:
|
|
56
|
+
role: "assistant",
|
|
57
57
|
content: [
|
|
58
58
|
{
|
|
59
|
-
type:
|
|
59
|
+
type: "text",
|
|
60
60
|
text: `[A${i}] Assistant response with relevant content. Result ${i % 7}.`,
|
|
61
61
|
},
|
|
62
62
|
],
|
|
@@ -76,19 +76,21 @@ function makeConfig() {
|
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
describe(
|
|
80
|
-
test(
|
|
79
|
+
describe("Compaction benchmark", () => {
|
|
80
|
+
test("compaction with mock provider completes under 500ms", async () => {
|
|
81
81
|
const counter = { calls: 0 };
|
|
82
82
|
const provider = makeSummaryProvider(counter);
|
|
83
83
|
const config = makeConfig();
|
|
84
|
-
const manager = new ContextWindowManager(provider,
|
|
84
|
+
const manager = new ContextWindowManager(provider, "system prompt", config);
|
|
85
85
|
|
|
86
86
|
// 90 turns = 180 messages, well above 60% of 6000 = 3600 threshold
|
|
87
87
|
const messages = makeLongMessages(90);
|
|
88
|
-
const before = estimatePromptTokens(messages,
|
|
89
|
-
providerName:
|
|
88
|
+
const before = estimatePromptTokens(messages, "system prompt", {
|
|
89
|
+
providerName: "mock",
|
|
90
90
|
});
|
|
91
|
-
expect(before).toBeGreaterThan(
|
|
91
|
+
expect(before).toBeGreaterThan(
|
|
92
|
+
config.maxInputTokens * config.compactThreshold,
|
|
93
|
+
);
|
|
92
94
|
|
|
93
95
|
const start = performance.now();
|
|
94
96
|
const result = await manager.maybeCompact(messages);
|
|
@@ -98,11 +100,11 @@ describe('Compaction benchmark', () => {
|
|
|
98
100
|
expect(elapsed).toBeLessThan(500);
|
|
99
101
|
});
|
|
100
102
|
|
|
101
|
-
test(
|
|
103
|
+
test("below-threshold check returns in under 50ms (no-op)", async () => {
|
|
102
104
|
const counter = { calls: 0 };
|
|
103
105
|
const provider = makeSummaryProvider(counter);
|
|
104
106
|
const config = makeConfig();
|
|
105
|
-
const manager = new ContextWindowManager(provider,
|
|
107
|
+
const manager = new ContextWindowManager(provider, "system prompt", config);
|
|
106
108
|
|
|
107
109
|
// 3 turns = 6 messages, well below threshold
|
|
108
110
|
const messages = makeLongMessages(3);
|
|
@@ -112,16 +114,16 @@ describe('Compaction benchmark', () => {
|
|
|
112
114
|
const elapsed = performance.now() - start;
|
|
113
115
|
|
|
114
116
|
expect(result.compacted).toBe(false);
|
|
115
|
-
expect(result.reason).toBe(
|
|
117
|
+
expect(result.reason).toBe("below compaction threshold");
|
|
116
118
|
expect(elapsed).toBeLessThan(50);
|
|
117
119
|
expect(counter.calls).toBe(0);
|
|
118
120
|
});
|
|
119
121
|
|
|
120
|
-
test(
|
|
122
|
+
test("token reduction ratio exceeds 30% after compaction", async () => {
|
|
121
123
|
const counter = { calls: 0 };
|
|
122
124
|
const provider = makeSummaryProvider(counter);
|
|
123
125
|
const config = makeConfig();
|
|
124
|
-
const manager = new ContextWindowManager(provider,
|
|
126
|
+
const manager = new ContextWindowManager(provider, "system prompt", config);
|
|
125
127
|
|
|
126
128
|
const messages = makeLongMessages(90);
|
|
127
129
|
const result = await manager.maybeCompact(messages);
|
|
@@ -133,11 +135,11 @@ describe('Compaction benchmark', () => {
|
|
|
133
135
|
expect(reductionRatio).toBeGreaterThan(0.3);
|
|
134
136
|
});
|
|
135
137
|
|
|
136
|
-
test(
|
|
138
|
+
test("summary calls fall within 2-6 range", async () => {
|
|
137
139
|
const counter = { calls: 0 };
|
|
138
140
|
const provider = makeSummaryProvider(counter);
|
|
139
141
|
const config = makeConfig();
|
|
140
|
-
const manager = new ContextWindowManager(provider,
|
|
142
|
+
const manager = new ContextWindowManager(provider, "system prompt", config);
|
|
141
143
|
|
|
142
144
|
const messages = makeLongMessages(90);
|
|
143
145
|
const result = await manager.maybeCompact(messages);
|
|
@@ -148,7 +150,7 @@ describe('Compaction benchmark', () => {
|
|
|
148
150
|
expect(result.summaryCalls).toBe(counter.calls);
|
|
149
151
|
});
|
|
150
152
|
|
|
151
|
-
test(
|
|
153
|
+
test("severe pressure triggers compaction even during cooldown", async () => {
|
|
152
154
|
const counter = { calls: 0 };
|
|
153
155
|
const provider = makeSummaryProvider(counter);
|
|
154
156
|
// Use a tighter maxInputTokens so 90 turns exceeds the 95% severe threshold
|
|
@@ -157,11 +159,11 @@ describe('Compaction benchmark', () => {
|
|
|
157
159
|
maxInputTokens: 4000,
|
|
158
160
|
targetInputTokens: 2000,
|
|
159
161
|
};
|
|
160
|
-
const manager = new ContextWindowManager(provider,
|
|
162
|
+
const manager = new ContextWindowManager(provider, "system prompt", config);
|
|
161
163
|
|
|
162
164
|
const messages = makeLongMessages(90);
|
|
163
|
-
const estimated = estimatePromptTokens(messages,
|
|
164
|
-
providerName:
|
|
165
|
+
const estimated = estimatePromptTokens(messages, "system prompt", {
|
|
166
|
+
providerName: "mock",
|
|
165
167
|
});
|
|
166
168
|
expect(estimated).toBeGreaterThan(config.maxInputTokens * 0.95);
|
|
167
169
|
|