@vellumai/assistant 0.4.17 → 0.4.19
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/docs/runbook-trusted-contacts.md +5 -3
- 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 +7 -2
- 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-routes-guardian-reply.test.ts +8 -0
- 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 +2 -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 +13 -5
- 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 +7 -2
- 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 +5 -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 +13 -6
- 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 +190 -124
- 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 +9 -2
- 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 +8 -3
- 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__/sms-messaging-provider.test.ts +4 -0
- 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 +311 -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 +155 -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/cli/mcp.ts +81 -28
- 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/guardian-verify-setup/SKILL.md +6 -11
- 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/phone-calls/SKILL.md +1 -2
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +34 -32
- package/src/config/bundled-skills/sms-setup/SKILL.md +8 -16
- package/src/config/bundled-skills/telegram-setup/SKILL.md +3 -3
- package/src/config/bundled-skills/trusted-contacts/SKILL.md +13 -25
- package/src/config/bundled-skills/twilio-setup/SKILL.md +13 -23
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/env.ts +3 -4
- package/src/config/system-prompt.ts +32 -0
- package/src/mcp/client.ts +2 -7
- 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 +120 -54
- 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
- package/src/tools/terminal/safe-env.ts +7 -0
|
@@ -5,31 +5,31 @@
|
|
|
5
5
|
* decision-model call is unavailable.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { beforeEach, describe, expect, mock, test } from
|
|
8
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
9
9
|
|
|
10
|
-
mock.module(
|
|
11
|
-
getDeliverableChannels: () => [
|
|
10
|
+
mock.module("../channels/config.js", () => ({
|
|
11
|
+
getDeliverableChannels: () => ["vellum", "telegram", "sms"],
|
|
12
12
|
}));
|
|
13
13
|
|
|
14
|
-
mock.module(
|
|
14
|
+
mock.module("../config/loader.js", () => ({
|
|
15
15
|
getConfig: () => ({
|
|
16
16
|
ui: {},
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
notifications: {
|
|
19
|
-
decisionModelIntent:
|
|
19
|
+
decisionModelIntent: "latency-optimized",
|
|
20
20
|
},
|
|
21
21
|
}),
|
|
22
22
|
}));
|
|
23
23
|
|
|
24
|
-
mock.module(
|
|
24
|
+
mock.module("../notifications/decisions-store.js", () => ({
|
|
25
25
|
createDecision: () => {},
|
|
26
26
|
}));
|
|
27
27
|
|
|
28
|
-
mock.module(
|
|
28
|
+
mock.module("../notifications/preference-summary.js", () => ({
|
|
29
29
|
getPreferenceSummary: () => undefined,
|
|
30
30
|
}));
|
|
31
31
|
|
|
32
|
-
mock.module(
|
|
32
|
+
mock.module("../notifications/thread-candidates.js", () => ({
|
|
33
33
|
buildThreadCandidates: () => undefined,
|
|
34
34
|
serializeCandidatesForPrompt: () => undefined,
|
|
35
35
|
}));
|
|
@@ -37,41 +37,43 @@ mock.module('../notifications/thread-candidates.js', () => ({
|
|
|
37
37
|
let configuredProvider: { sendMessage: () => Promise<unknown> } | null = null;
|
|
38
38
|
let extractedToolUse: unknown = null;
|
|
39
39
|
|
|
40
|
-
mock.module(
|
|
40
|
+
mock.module("../providers/provider-send-message.js", () => ({
|
|
41
41
|
getConfiguredProvider: () => configuredProvider,
|
|
42
42
|
createTimeout: () => ({
|
|
43
43
|
signal: new AbortController().signal,
|
|
44
44
|
cleanup: () => {},
|
|
45
45
|
}),
|
|
46
46
|
extractToolUse: () => extractedToolUse,
|
|
47
|
-
userMessage: (text: string) => ({ role:
|
|
47
|
+
userMessage: (text: string) => ({ role: "user", content: text }),
|
|
48
48
|
}));
|
|
49
49
|
|
|
50
|
-
mock.module(
|
|
50
|
+
mock.module("../util/logger.js", () => ({
|
|
51
51
|
getLogger: () =>
|
|
52
52
|
new Proxy({} as Record<string, unknown>, {
|
|
53
53
|
get: () => () => {},
|
|
54
54
|
}),
|
|
55
55
|
}));
|
|
56
56
|
|
|
57
|
-
import { evaluateSignal } from
|
|
58
|
-
import type { NotificationSignal } from
|
|
59
|
-
import type { NotificationChannel } from
|
|
57
|
+
import { evaluateSignal } from "../notifications/decision-engine.js";
|
|
58
|
+
import type { NotificationSignal } from "../notifications/signal.js";
|
|
59
|
+
import type { NotificationChannel } from "../notifications/types.js";
|
|
60
60
|
|
|
61
|
-
function makeSignal(
|
|
61
|
+
function makeSignal(
|
|
62
|
+
overrides?: Partial<NotificationSignal>,
|
|
63
|
+
): NotificationSignal {
|
|
62
64
|
return {
|
|
63
|
-
signalId:
|
|
64
|
-
assistantId:
|
|
65
|
+
signalId: "sig-fallback-guardian-1",
|
|
66
|
+
assistantId: "self",
|
|
65
67
|
createdAt: Date.now(),
|
|
66
|
-
sourceChannel:
|
|
67
|
-
sourceSessionId:
|
|
68
|
-
sourceEventName:
|
|
68
|
+
sourceChannel: "voice",
|
|
69
|
+
sourceSessionId: "call-session-1",
|
|
70
|
+
sourceEventName: "guardian.question",
|
|
69
71
|
contextPayload: {
|
|
70
|
-
questionText:
|
|
72
|
+
questionText: "What is the gate code?",
|
|
71
73
|
},
|
|
72
74
|
attentionHints: {
|
|
73
75
|
requiresAction: true,
|
|
74
|
-
urgency:
|
|
76
|
+
urgency: "high",
|
|
75
77
|
isAsyncBackground: false,
|
|
76
78
|
visibleInSourceNow: false,
|
|
77
79
|
},
|
|
@@ -79,223 +81,245 @@ function makeSignal(overrides?: Partial<NotificationSignal>): NotificationSignal
|
|
|
79
81
|
};
|
|
80
82
|
}
|
|
81
83
|
|
|
82
|
-
describe(
|
|
84
|
+
describe("notification decision fallback copy", () => {
|
|
83
85
|
beforeEach(() => {
|
|
84
86
|
configuredProvider = null;
|
|
85
87
|
extractedToolUse = null;
|
|
86
88
|
});
|
|
87
89
|
|
|
88
|
-
test(
|
|
90
|
+
test("uses human-friendly template copy for guardian.question", async () => {
|
|
89
91
|
const signal = makeSignal();
|
|
90
|
-
const decision = await evaluateSignal(signal, [
|
|
92
|
+
const decision = await evaluateSignal(signal, [
|
|
93
|
+
"vellum",
|
|
94
|
+
] as NotificationChannel[]);
|
|
91
95
|
|
|
92
96
|
expect(decision.fallbackUsed).toBe(true);
|
|
93
|
-
expect(decision.renderedCopy.vellum?.title).toBe(
|
|
94
|
-
expect(decision.renderedCopy.vellum?.body).toBe(
|
|
95
|
-
expect(decision.renderedCopy.vellum?.title).not.toBe(
|
|
96
|
-
expect(decision.renderedCopy.vellum?.body).not.toContain(
|
|
97
|
+
expect(decision.renderedCopy.vellum?.title).toBe("Guardian Question");
|
|
98
|
+
expect(decision.renderedCopy.vellum?.body).toBe("What is the gate code?");
|
|
99
|
+
expect(decision.renderedCopy.vellum?.title).not.toBe("guardian.question");
|
|
100
|
+
expect(decision.renderedCopy.vellum?.body).not.toContain(
|
|
101
|
+
"Action required: guardian.question",
|
|
102
|
+
);
|
|
97
103
|
});
|
|
98
104
|
|
|
99
|
-
test(
|
|
105
|
+
test("enforces free-text answer instructions for guardian.question when requestCode exists", async () => {
|
|
100
106
|
const signal = makeSignal({
|
|
101
107
|
contextPayload: {
|
|
102
|
-
requestId:
|
|
103
|
-
questionText:
|
|
104
|
-
requestCode:
|
|
105
|
-
requestKind:
|
|
106
|
-
callSessionId:
|
|
108
|
+
requestId: "req-pending-1",
|
|
109
|
+
questionText: "What is the gate code?",
|
|
110
|
+
requestCode: "A1B2C3",
|
|
111
|
+
requestKind: "pending_question",
|
|
112
|
+
callSessionId: "call-1",
|
|
107
113
|
activeGuardianRequestCount: 1,
|
|
108
114
|
},
|
|
109
115
|
});
|
|
110
|
-
const decision = await evaluateSignal(signal, [
|
|
116
|
+
const decision = await evaluateSignal(signal, [
|
|
117
|
+
"vellum",
|
|
118
|
+
] as NotificationChannel[]);
|
|
111
119
|
|
|
112
120
|
expect(decision.fallbackUsed).toBe(true);
|
|
113
|
-
expect(decision.renderedCopy.vellum?.body).toContain(
|
|
114
|
-
expect(decision.renderedCopy.vellum?.body).toContain(
|
|
115
|
-
expect(decision.renderedCopy.vellum?.body).not.toContain(
|
|
116
|
-
expect(decision.renderedCopy.vellum?.body).not.toContain(
|
|
121
|
+
expect(decision.renderedCopy.vellum?.body).toContain("A1B2C3");
|
|
122
|
+
expect(decision.renderedCopy.vellum?.body).toContain("<your answer>");
|
|
123
|
+
expect(decision.renderedCopy.vellum?.body).not.toContain("approve");
|
|
124
|
+
expect(decision.renderedCopy.vellum?.body).not.toContain("reject");
|
|
117
125
|
});
|
|
118
126
|
|
|
119
|
-
test(
|
|
127
|
+
test("enforcement appends free-text answer instructions when LLM copy only mentions request code", async () => {
|
|
120
128
|
configuredProvider = {
|
|
121
129
|
sendMessage: async () => ({ content: [] }),
|
|
122
130
|
};
|
|
123
131
|
extractedToolUse = {
|
|
124
|
-
name:
|
|
132
|
+
name: "record_notification_decision",
|
|
125
133
|
input: {
|
|
126
134
|
shouldNotify: true,
|
|
127
|
-
selectedChannels: [
|
|
128
|
-
reasoningSummary:
|
|
135
|
+
selectedChannels: ["vellum"],
|
|
136
|
+
reasoningSummary: "LLM decision",
|
|
129
137
|
renderedCopy: {
|
|
130
138
|
vellum: {
|
|
131
|
-
title:
|
|
132
|
-
body:
|
|
139
|
+
title: "Guardian Question",
|
|
140
|
+
body: "Use reference code A1B2C3 for this request.",
|
|
133
141
|
},
|
|
134
142
|
},
|
|
135
|
-
dedupeKey:
|
|
143
|
+
dedupeKey: "guardian-question-test",
|
|
136
144
|
confidence: 0.9,
|
|
137
145
|
},
|
|
138
146
|
};
|
|
139
147
|
|
|
140
148
|
const signal = makeSignal({
|
|
141
149
|
contextPayload: {
|
|
142
|
-
requestId:
|
|
143
|
-
questionText:
|
|
144
|
-
requestCode:
|
|
145
|
-
requestKind:
|
|
146
|
-
callSessionId:
|
|
150
|
+
requestId: "req-pending-1",
|
|
151
|
+
questionText: "What is the gate code?",
|
|
152
|
+
requestCode: "A1B2C3",
|
|
153
|
+
requestKind: "pending_question",
|
|
154
|
+
callSessionId: "call-1",
|
|
147
155
|
activeGuardianRequestCount: 1,
|
|
148
156
|
},
|
|
149
157
|
});
|
|
150
158
|
|
|
151
|
-
const decision = await evaluateSignal(signal, [
|
|
159
|
+
const decision = await evaluateSignal(signal, [
|
|
160
|
+
"vellum",
|
|
161
|
+
] as NotificationChannel[]);
|
|
152
162
|
|
|
153
163
|
expect(decision.fallbackUsed).toBe(false);
|
|
154
|
-
expect(decision.renderedCopy.vellum?.body).toContain(
|
|
155
|
-
|
|
164
|
+
expect(decision.renderedCopy.vellum?.body).toContain(
|
|
165
|
+
'"A1B2C3 <your answer>"',
|
|
166
|
+
);
|
|
167
|
+
expect(decision.renderedCopy.vellum?.body).not.toContain(
|
|
168
|
+
'"A1B2C3 approve"',
|
|
169
|
+
);
|
|
156
170
|
expect(decision.renderedCopy.vellum?.body).not.toContain('"A1B2C3 reject"');
|
|
157
171
|
});
|
|
158
172
|
|
|
159
|
-
test(
|
|
173
|
+
test("enforcement appends answer instructions when LLM copy incorrectly uses approve/reject wording", async () => {
|
|
160
174
|
configuredProvider = {
|
|
161
175
|
sendMessage: async () => ({ content: [] }),
|
|
162
176
|
};
|
|
163
177
|
extractedToolUse = {
|
|
164
|
-
name:
|
|
178
|
+
name: "record_notification_decision",
|
|
165
179
|
input: {
|
|
166
180
|
shouldNotify: true,
|
|
167
|
-
selectedChannels: [
|
|
168
|
-
reasoningSummary:
|
|
181
|
+
selectedChannels: ["vellum"],
|
|
182
|
+
reasoningSummary: "LLM decision",
|
|
169
183
|
renderedCopy: {
|
|
170
184
|
vellum: {
|
|
171
|
-
title:
|
|
185
|
+
title: "Guardian Question",
|
|
172
186
|
body: 'Reference code: A1B2C3. Reply "A1B2C3 approve" or "A1B2C3 reject".',
|
|
173
187
|
},
|
|
174
188
|
},
|
|
175
|
-
dedupeKey:
|
|
189
|
+
dedupeKey: "guardian-question-wrong-instructions-test",
|
|
176
190
|
confidence: 0.9,
|
|
177
191
|
},
|
|
178
192
|
};
|
|
179
193
|
|
|
180
194
|
const signal = makeSignal({
|
|
181
195
|
contextPayload: {
|
|
182
|
-
requestId:
|
|
183
|
-
questionText:
|
|
184
|
-
requestCode:
|
|
185
|
-
requestKind:
|
|
186
|
-
callSessionId:
|
|
196
|
+
requestId: "req-pending-approve-phrasing",
|
|
197
|
+
questionText: "What is the gate code?",
|
|
198
|
+
requestCode: "A1B2C3",
|
|
199
|
+
requestKind: "pending_question",
|
|
200
|
+
callSessionId: "call-1",
|
|
187
201
|
activeGuardianRequestCount: 1,
|
|
188
202
|
},
|
|
189
203
|
});
|
|
190
204
|
|
|
191
|
-
const decision = await evaluateSignal(signal, [
|
|
205
|
+
const decision = await evaluateSignal(signal, [
|
|
206
|
+
"vellum",
|
|
207
|
+
] as NotificationChannel[]);
|
|
192
208
|
|
|
193
209
|
expect(decision.fallbackUsed).toBe(false);
|
|
194
|
-
expect(decision.renderedCopy.vellum?.body).toContain(
|
|
210
|
+
expect(decision.renderedCopy.vellum?.body).toContain(
|
|
211
|
+
'"A1B2C3 <your answer>"',
|
|
212
|
+
);
|
|
195
213
|
});
|
|
196
214
|
|
|
197
|
-
test(
|
|
215
|
+
test("enforcement appends explicit approve/reject instructions for tool-approval guardian questions", async () => {
|
|
198
216
|
configuredProvider = {
|
|
199
217
|
sendMessage: async () => ({ content: [] }),
|
|
200
218
|
};
|
|
201
219
|
extractedToolUse = {
|
|
202
|
-
name:
|
|
220
|
+
name: "record_notification_decision",
|
|
203
221
|
input: {
|
|
204
222
|
shouldNotify: true,
|
|
205
|
-
selectedChannels: [
|
|
206
|
-
reasoningSummary:
|
|
223
|
+
selectedChannels: ["vellum"],
|
|
224
|
+
reasoningSummary: "LLM decision",
|
|
207
225
|
renderedCopy: {
|
|
208
226
|
vellum: {
|
|
209
|
-
title:
|
|
210
|
-
body:
|
|
227
|
+
title: "Guardian Question",
|
|
228
|
+
body: "Use reference code A1B2C3 for this request.",
|
|
211
229
|
},
|
|
212
230
|
},
|
|
213
|
-
dedupeKey:
|
|
231
|
+
dedupeKey: "guardian-question-tool-approval-test",
|
|
214
232
|
confidence: 0.9,
|
|
215
233
|
},
|
|
216
234
|
};
|
|
217
235
|
|
|
218
236
|
const signal = makeSignal({
|
|
219
237
|
contextPayload: {
|
|
220
|
-
requestId:
|
|
221
|
-
questionText:
|
|
222
|
-
requestCode:
|
|
223
|
-
requestKind:
|
|
224
|
-
toolName:
|
|
238
|
+
requestId: "req-grant-1",
|
|
239
|
+
questionText: "Allow running host_bash?",
|
|
240
|
+
requestCode: "A1B2C3",
|
|
241
|
+
requestKind: "tool_grant_request",
|
|
242
|
+
toolName: "host_bash",
|
|
225
243
|
},
|
|
226
244
|
});
|
|
227
245
|
|
|
228
|
-
const decision = await evaluateSignal(signal, [
|
|
246
|
+
const decision = await evaluateSignal(signal, [
|
|
247
|
+
"vellum",
|
|
248
|
+
] as NotificationChannel[]);
|
|
229
249
|
|
|
230
250
|
expect(decision.fallbackUsed).toBe(false);
|
|
231
251
|
expect(decision.renderedCopy.vellum?.body).toContain('"A1B2C3 approve"');
|
|
232
252
|
expect(decision.renderedCopy.vellum?.body).toContain('"A1B2C3 reject"');
|
|
233
253
|
});
|
|
234
254
|
|
|
235
|
-
test(
|
|
255
|
+
test("approval-mode enforcement removes conflicting answer-mode phrasing", async () => {
|
|
236
256
|
configuredProvider = {
|
|
237
257
|
sendMessage: async () => ({ content: [] }),
|
|
238
258
|
};
|
|
239
259
|
extractedToolUse = {
|
|
240
|
-
name:
|
|
260
|
+
name: "record_notification_decision",
|
|
241
261
|
input: {
|
|
242
262
|
shouldNotify: true,
|
|
243
|
-
selectedChannels: [
|
|
244
|
-
reasoningSummary:
|
|
263
|
+
selectedChannels: ["vellum"],
|
|
264
|
+
reasoningSummary: "LLM decision",
|
|
245
265
|
renderedCopy: {
|
|
246
266
|
vellum: {
|
|
247
|
-
title:
|
|
267
|
+
title: "Guardian Question",
|
|
248
268
|
body: 'Reference code: A1B2C3. Reply "A1B2C3 <your answer>".',
|
|
249
269
|
},
|
|
250
270
|
},
|
|
251
|
-
dedupeKey:
|
|
271
|
+
dedupeKey: "guardian-question-approval-removes-answer-test",
|
|
252
272
|
confidence: 0.9,
|
|
253
273
|
},
|
|
254
274
|
};
|
|
255
275
|
|
|
256
276
|
const signal = makeSignal({
|
|
257
277
|
contextPayload: {
|
|
258
|
-
requestId:
|
|
259
|
-
questionText:
|
|
260
|
-
requestCode:
|
|
261
|
-
requestKind:
|
|
262
|
-
toolName:
|
|
278
|
+
requestId: "req-grant-2",
|
|
279
|
+
questionText: "Allow running host_bash?",
|
|
280
|
+
requestCode: "A1B2C3",
|
|
281
|
+
requestKind: "tool_grant_request",
|
|
282
|
+
toolName: "host_bash",
|
|
263
283
|
},
|
|
264
284
|
});
|
|
265
285
|
|
|
266
|
-
const decision = await evaluateSignal(signal, [
|
|
286
|
+
const decision = await evaluateSignal(signal, [
|
|
287
|
+
"vellum",
|
|
288
|
+
] as NotificationChannel[]);
|
|
267
289
|
|
|
268
290
|
expect(decision.fallbackUsed).toBe(false);
|
|
269
291
|
expect(decision.renderedCopy.vellum?.body).toContain('"A1B2C3 approve"');
|
|
270
292
|
expect(decision.renderedCopy.vellum?.body).toContain('"A1B2C3 reject"');
|
|
271
|
-
expect(decision.renderedCopy.vellum?.body).not.toContain(
|
|
293
|
+
expect(decision.renderedCopy.vellum?.body).not.toContain("<your answer>");
|
|
272
294
|
});
|
|
273
295
|
});
|
|
274
296
|
|
|
275
297
|
// ── Access-request instruction enforcement ──────────────────────────────
|
|
276
298
|
|
|
277
|
-
describe(
|
|
299
|
+
describe("access-request instruction enforcement", () => {
|
|
278
300
|
beforeEach(() => {
|
|
279
301
|
configuredProvider = null;
|
|
280
302
|
extractedToolUse = null;
|
|
281
303
|
});
|
|
282
304
|
|
|
283
|
-
function makeAccessRequestSignal(
|
|
305
|
+
function makeAccessRequestSignal(
|
|
306
|
+
overrides?: Partial<NotificationSignal>,
|
|
307
|
+
): NotificationSignal {
|
|
284
308
|
return {
|
|
285
|
-
signalId:
|
|
286
|
-
assistantId:
|
|
309
|
+
signalId: "sig-access-req-1",
|
|
310
|
+
assistantId: "self",
|
|
287
311
|
createdAt: Date.now(),
|
|
288
|
-
sourceChannel:
|
|
289
|
-
sourceSessionId:
|
|
290
|
-
sourceEventName:
|
|
312
|
+
sourceChannel: "telegram",
|
|
313
|
+
sourceSessionId: "tg-session-1",
|
|
314
|
+
sourceEventName: "ingress.access_request",
|
|
291
315
|
contextPayload: {
|
|
292
|
-
senderIdentifier:
|
|
293
|
-
requestCode:
|
|
294
|
-
sourceChannel:
|
|
316
|
+
senderIdentifier: "Alice",
|
|
317
|
+
requestCode: "A1B2C3",
|
|
318
|
+
sourceChannel: "telegram",
|
|
295
319
|
},
|
|
296
320
|
attentionHints: {
|
|
297
321
|
requiresAction: true,
|
|
298
|
-
urgency:
|
|
322
|
+
urgency: "high",
|
|
299
323
|
isAsyncBackground: false,
|
|
300
324
|
visibleInSourceNow: false,
|
|
301
325
|
},
|
|
@@ -303,203 +327,224 @@ describe('access-request instruction enforcement', () => {
|
|
|
303
327
|
};
|
|
304
328
|
}
|
|
305
329
|
|
|
306
|
-
test(
|
|
330
|
+
test("fallback copy includes access-request contract elements", async () => {
|
|
307
331
|
const signal = makeAccessRequestSignal();
|
|
308
|
-
const decision = await evaluateSignal(signal, [
|
|
332
|
+
const decision = await evaluateSignal(signal, [
|
|
333
|
+
"vellum",
|
|
334
|
+
] as NotificationChannel[]);
|
|
309
335
|
|
|
310
336
|
expect(decision.fallbackUsed).toBe(true);
|
|
311
|
-
expect(decision.renderedCopy.vellum?.body).toContain(
|
|
312
|
-
expect(decision.renderedCopy.vellum?.body).toContain(
|
|
313
|
-
expect(decision.renderedCopy.vellum?.body).toContain(
|
|
314
|
-
expect(decision.renderedCopy.vellum?.body).toContain(
|
|
337
|
+
expect(decision.renderedCopy.vellum?.body).toContain("A1B2C3");
|
|
338
|
+
expect(decision.renderedCopy.vellum?.body).toContain("approve");
|
|
339
|
+
expect(decision.renderedCopy.vellum?.body).toContain("reject");
|
|
340
|
+
expect(decision.renderedCopy.vellum?.body).toContain("open invite flow");
|
|
315
341
|
});
|
|
316
342
|
|
|
317
|
-
test(
|
|
343
|
+
test("enforcement appends contract when LLM copy is missing request code", async () => {
|
|
318
344
|
configuredProvider = {
|
|
319
345
|
sendMessage: async () => ({ content: [] }),
|
|
320
346
|
};
|
|
321
347
|
extractedToolUse = {
|
|
322
|
-
name:
|
|
348
|
+
name: "record_notification_decision",
|
|
323
349
|
input: {
|
|
324
350
|
shouldNotify: true,
|
|
325
|
-
selectedChannels: [
|
|
326
|
-
reasoningSummary:
|
|
351
|
+
selectedChannels: ["vellum"],
|
|
352
|
+
reasoningSummary: "LLM decision",
|
|
327
353
|
renderedCopy: {
|
|
328
354
|
vellum: {
|
|
329
|
-
title:
|
|
330
|
-
body:
|
|
355
|
+
title: "Access Request",
|
|
356
|
+
body: "Someone wants access to your assistant.",
|
|
331
357
|
},
|
|
332
358
|
},
|
|
333
|
-
dedupeKey:
|
|
359
|
+
dedupeKey: "access-req-missing-code",
|
|
334
360
|
confidence: 0.9,
|
|
335
361
|
},
|
|
336
362
|
};
|
|
337
363
|
|
|
338
364
|
const signal = makeAccessRequestSignal();
|
|
339
|
-
const decision = await evaluateSignal(signal, [
|
|
365
|
+
const decision = await evaluateSignal(signal, [
|
|
366
|
+
"vellum",
|
|
367
|
+
] as NotificationChannel[]);
|
|
340
368
|
|
|
341
369
|
expect(decision.fallbackUsed).toBe(false);
|
|
342
|
-
expect(decision.renderedCopy.vellum?.body).toContain(
|
|
343
|
-
expect(decision.renderedCopy.vellum?.body).toContain(
|
|
344
|
-
expect(decision.renderedCopy.vellum?.body).toContain(
|
|
345
|
-
expect(decision.renderedCopy.vellum?.body).toContain(
|
|
370
|
+
expect(decision.renderedCopy.vellum?.body).toContain("A1B2C3");
|
|
371
|
+
expect(decision.renderedCopy.vellum?.body).toContain("approve");
|
|
372
|
+
expect(decision.renderedCopy.vellum?.body).toContain("reject");
|
|
373
|
+
expect(decision.renderedCopy.vellum?.body).toContain("open invite flow");
|
|
346
374
|
});
|
|
347
375
|
|
|
348
|
-
test(
|
|
376
|
+
test("enforcement appends contract when LLM copy has code but missing invite flow", async () => {
|
|
349
377
|
configuredProvider = {
|
|
350
378
|
sendMessage: async () => ({ content: [] }),
|
|
351
379
|
};
|
|
352
380
|
extractedToolUse = {
|
|
353
|
-
name:
|
|
381
|
+
name: "record_notification_decision",
|
|
354
382
|
input: {
|
|
355
383
|
shouldNotify: true,
|
|
356
|
-
selectedChannels: [
|
|
357
|
-
reasoningSummary:
|
|
384
|
+
selectedChannels: ["vellum"],
|
|
385
|
+
reasoningSummary: "LLM decision",
|
|
358
386
|
renderedCopy: {
|
|
359
387
|
vellum: {
|
|
360
|
-
title:
|
|
388
|
+
title: "Access Request",
|
|
361
389
|
body: 'Alice wants access. Reply "A1B2C3 approve" or "A1B2C3 reject".',
|
|
362
390
|
},
|
|
363
391
|
},
|
|
364
|
-
dedupeKey:
|
|
392
|
+
dedupeKey: "access-req-missing-invite",
|
|
365
393
|
confidence: 0.9,
|
|
366
394
|
},
|
|
367
395
|
};
|
|
368
396
|
|
|
369
397
|
const signal = makeAccessRequestSignal();
|
|
370
|
-
const decision = await evaluateSignal(signal, [
|
|
398
|
+
const decision = await evaluateSignal(signal, [
|
|
399
|
+
"vellum",
|
|
400
|
+
] as NotificationChannel[]);
|
|
371
401
|
|
|
372
402
|
expect(decision.fallbackUsed).toBe(false);
|
|
373
|
-
expect(decision.renderedCopy.vellum?.body).toContain(
|
|
403
|
+
expect(decision.renderedCopy.vellum?.body).toContain("open invite flow");
|
|
374
404
|
});
|
|
375
405
|
|
|
376
|
-
test(
|
|
377
|
-
const fullBody =
|
|
406
|
+
test("enforcement does not duplicate when LLM copy already has all required elements", async () => {
|
|
407
|
+
const fullBody =
|
|
408
|
+
'Alice wants access.\nReply "A1B2C3 approve" to grant access or "A1B2C3 reject" to deny.\nReply "open invite flow" to start Trusted Contacts invite flow.';
|
|
378
409
|
configuredProvider = {
|
|
379
410
|
sendMessage: async () => ({ content: [] }),
|
|
380
411
|
};
|
|
381
412
|
extractedToolUse = {
|
|
382
|
-
name:
|
|
413
|
+
name: "record_notification_decision",
|
|
383
414
|
input: {
|
|
384
415
|
shouldNotify: true,
|
|
385
|
-
selectedChannels: [
|
|
386
|
-
reasoningSummary:
|
|
416
|
+
selectedChannels: ["vellum"],
|
|
417
|
+
reasoningSummary: "LLM decision",
|
|
387
418
|
renderedCopy: {
|
|
388
419
|
vellum: {
|
|
389
|
-
title:
|
|
420
|
+
title: "Access Request",
|
|
390
421
|
body: fullBody,
|
|
391
422
|
},
|
|
392
423
|
},
|
|
393
|
-
dedupeKey:
|
|
424
|
+
dedupeKey: "access-req-already-valid",
|
|
394
425
|
confidence: 0.9,
|
|
395
426
|
},
|
|
396
427
|
};
|
|
397
428
|
|
|
398
429
|
const signal = makeAccessRequestSignal();
|
|
399
|
-
const decision = await evaluateSignal(signal, [
|
|
430
|
+
const decision = await evaluateSignal(signal, [
|
|
431
|
+
"vellum",
|
|
432
|
+
] as NotificationChannel[]);
|
|
400
433
|
|
|
401
434
|
expect(decision.fallbackUsed).toBe(false);
|
|
402
435
|
// Body should remain unchanged when all required elements are present
|
|
403
436
|
expect(decision.renderedCopy.vellum?.body).toBe(fullBody);
|
|
404
437
|
});
|
|
405
438
|
|
|
406
|
-
test(
|
|
439
|
+
test("enforcement also applies to deliveryText and threadSeedMessage", async () => {
|
|
407
440
|
configuredProvider = {
|
|
408
441
|
sendMessage: async () => ({ content: [] }),
|
|
409
442
|
};
|
|
410
443
|
extractedToolUse = {
|
|
411
|
-
name:
|
|
444
|
+
name: "record_notification_decision",
|
|
412
445
|
input: {
|
|
413
446
|
shouldNotify: true,
|
|
414
|
-
selectedChannels: [
|
|
415
|
-
reasoningSummary:
|
|
447
|
+
selectedChannels: ["telegram"],
|
|
448
|
+
reasoningSummary: "LLM decision",
|
|
416
449
|
renderedCopy: {
|
|
417
450
|
telegram: {
|
|
418
|
-
title:
|
|
419
|
-
body:
|
|
420
|
-
deliveryText:
|
|
421
|
-
threadSeedMessage:
|
|
451
|
+
title: "Access Request",
|
|
452
|
+
body: "Someone wants access.",
|
|
453
|
+
deliveryText: "Someone wants access.",
|
|
454
|
+
threadSeedMessage: "Someone wants access.",
|
|
422
455
|
},
|
|
423
456
|
},
|
|
424
|
-
dedupeKey:
|
|
457
|
+
dedupeKey: "access-req-multi-field",
|
|
425
458
|
confidence: 0.9,
|
|
426
459
|
},
|
|
427
460
|
};
|
|
428
461
|
|
|
429
462
|
const signal = makeAccessRequestSignal();
|
|
430
|
-
const decision = await evaluateSignal(signal, [
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
expect(decision.renderedCopy.telegram?.
|
|
435
|
-
expect(decision.renderedCopy.telegram?.
|
|
463
|
+
const decision = await evaluateSignal(signal, [
|
|
464
|
+
"telegram",
|
|
465
|
+
] as NotificationChannel[]);
|
|
466
|
+
|
|
467
|
+
expect(decision.renderedCopy.telegram?.deliveryText).toContain("A1B2C3");
|
|
468
|
+
expect(decision.renderedCopy.telegram?.deliveryText).toContain(
|
|
469
|
+
"open invite flow",
|
|
470
|
+
);
|
|
471
|
+
expect(decision.renderedCopy.telegram?.threadSeedMessage).toContain(
|
|
472
|
+
"A1B2C3",
|
|
473
|
+
);
|
|
474
|
+
expect(decision.renderedCopy.telegram?.threadSeedMessage).toContain(
|
|
475
|
+
"open invite flow",
|
|
476
|
+
);
|
|
436
477
|
});
|
|
437
478
|
|
|
438
|
-
test(
|
|
479
|
+
test("enforcement appends contract when LLM copy contains conflicting instructions", async () => {
|
|
439
480
|
configuredProvider = {
|
|
440
481
|
sendMessage: async () => ({ content: [] }),
|
|
441
482
|
};
|
|
442
483
|
extractedToolUse = {
|
|
443
|
-
name:
|
|
484
|
+
name: "record_notification_decision",
|
|
444
485
|
input: {
|
|
445
486
|
shouldNotify: true,
|
|
446
|
-
selectedChannels: [
|
|
447
|
-
reasoningSummary:
|
|
487
|
+
selectedChannels: ["vellum"],
|
|
488
|
+
reasoningSummary: "LLM decision",
|
|
448
489
|
renderedCopy: {
|
|
449
490
|
vellum: {
|
|
450
|
-
title:
|
|
491
|
+
title: "Access Request",
|
|
451
492
|
body: 'Alice wants access. Just reply "yes" or "no" to decide.',
|
|
452
493
|
},
|
|
453
494
|
},
|
|
454
|
-
dedupeKey:
|
|
495
|
+
dedupeKey: "access-req-conflicting",
|
|
455
496
|
confidence: 0.9,
|
|
456
497
|
},
|
|
457
498
|
};
|
|
458
499
|
|
|
459
500
|
const signal = makeAccessRequestSignal();
|
|
460
|
-
const decision = await evaluateSignal(signal, [
|
|
501
|
+
const decision = await evaluateSignal(signal, [
|
|
502
|
+
"vellum",
|
|
503
|
+
] as NotificationChannel[]);
|
|
461
504
|
|
|
462
505
|
// Must contain the proper contract instructions despite conflicting LLM copy
|
|
463
|
-
expect(decision.renderedCopy.vellum?.body).toContain(
|
|
464
|
-
expect(decision.renderedCopy.vellum?.body).toContain(
|
|
465
|
-
expect(decision.renderedCopy.vellum?.body).toContain(
|
|
506
|
+
expect(decision.renderedCopy.vellum?.body).toContain("A1B2C3 approve");
|
|
507
|
+
expect(decision.renderedCopy.vellum?.body).toContain("A1B2C3 reject");
|
|
508
|
+
expect(decision.renderedCopy.vellum?.body).toContain("open invite flow");
|
|
466
509
|
});
|
|
467
510
|
|
|
468
|
-
test(
|
|
511
|
+
test("enforcement appends invite directive when requestCode is absent", async () => {
|
|
469
512
|
configuredProvider = {
|
|
470
513
|
sendMessage: async () => ({ content: [] }),
|
|
471
514
|
};
|
|
472
515
|
extractedToolUse = {
|
|
473
|
-
name:
|
|
516
|
+
name: "record_notification_decision",
|
|
474
517
|
input: {
|
|
475
518
|
shouldNotify: true,
|
|
476
|
-
selectedChannels: [
|
|
477
|
-
reasoningSummary:
|
|
519
|
+
selectedChannels: ["vellum"],
|
|
520
|
+
reasoningSummary: "LLM decision",
|
|
478
521
|
renderedCopy: {
|
|
479
522
|
vellum: {
|
|
480
|
-
title:
|
|
481
|
-
body:
|
|
523
|
+
title: "Access Request",
|
|
524
|
+
body: "Someone wants access to your assistant.",
|
|
482
525
|
},
|
|
483
526
|
},
|
|
484
|
-
dedupeKey:
|
|
527
|
+
dedupeKey: "access-req-no-code-invite",
|
|
485
528
|
confidence: 0.9,
|
|
486
529
|
},
|
|
487
530
|
};
|
|
488
531
|
|
|
489
532
|
const signal = makeAccessRequestSignal({
|
|
490
533
|
contextPayload: {
|
|
491
|
-
senderIdentifier:
|
|
492
|
-
sourceChannel:
|
|
534
|
+
senderIdentifier: "Alice",
|
|
535
|
+
sourceChannel: "telegram",
|
|
493
536
|
// No requestCode
|
|
494
537
|
},
|
|
495
538
|
});
|
|
496
|
-
const decision = await evaluateSignal(signal, [
|
|
539
|
+
const decision = await evaluateSignal(signal, [
|
|
540
|
+
"vellum",
|
|
541
|
+
] as NotificationChannel[]);
|
|
497
542
|
|
|
498
543
|
expect(decision.fallbackUsed).toBe(false);
|
|
499
544
|
// Invite directive should still be enforced even without requestCode
|
|
500
|
-
expect(decision.renderedCopy.vellum?.body).toContain(
|
|
545
|
+
expect(decision.renderedCopy.vellum?.body).toContain("open invite flow");
|
|
501
546
|
// Approve/reject should NOT be present since there is no requestCode
|
|
502
|
-
expect(decision.renderedCopy.vellum?.body).not.toContain(
|
|
503
|
-
expect(decision.renderedCopy.vellum?.body).not.toContain(
|
|
547
|
+
expect(decision.renderedCopy.vellum?.body).not.toContain("approve");
|
|
548
|
+
expect(decision.renderedCopy.vellum?.body).not.toContain("reject");
|
|
504
549
|
});
|
|
505
550
|
});
|