@vellumai/assistant 0.4.56 → 0.4.57
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/ARCHITECTURE.md +10 -10
- package/Dockerfile +3 -0
- package/README.md +11 -11
- package/docs/architecture/integrations.md +2 -2
- package/docs/architecture/memory.md +3 -4
- package/docs/credential-execution-service.md +13 -20
- package/node_modules/@vellumai/ces-contracts/src/error.ts +5 -4
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +7 -7
- package/src/__tests__/anthropic-provider.test.ts +172 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +15 -1
- package/src/__tests__/approval-cascade.test.ts +2 -2
- package/src/__tests__/approval-routes-http.test.ts +3 -4
- package/src/__tests__/asset-materialize-tool.test.ts +5 -5
- package/src/__tests__/asset-search-tool.test.ts +1 -1
- package/src/__tests__/assistant-attachments.test.ts +5 -5
- package/src/__tests__/assistant-events-sse-hardening.test.ts +1 -1
- package/src/__tests__/assistant-feature-flags-integration.test.ts +50 -38
- package/src/__tests__/attachments-store.test.ts +2 -2
- package/src/__tests__/avatar-e2e.test.ts +5 -3
- package/src/__tests__/browser-skill-endstate.test.ts +0 -1
- package/src/__tests__/call-routes-http.test.ts +2 -2
- package/src/__tests__/callback-handoff-copy.test.ts +1 -1
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +158 -0
- package/src/__tests__/channel-readiness-routes.test.ts +0 -1
- package/src/__tests__/channel-readiness-service.test.ts +0 -1
- package/src/__tests__/checker.test.ts +31 -32
- package/src/__tests__/chrome-cdp.test.ts +47 -18
- package/src/__tests__/claude-code-skill-regression.test.ts +2 -2
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +9 -18
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -2
- package/src/__tests__/conversation-agent-loop.test.ts +11 -4
- package/src/__tests__/conversation-attachments.test.ts +1 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -2
- package/src/__tests__/conversation-error.test.ts +33 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -1
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +1 -1
- package/src/__tests__/conversation-pre-run-repair.test.ts +4 -4
- package/src/__tests__/conversation-provider-retry-repair.test.ts +4 -4
- package/src/__tests__/conversation-queue.test.ts +23 -14
- package/src/__tests__/conversation-routes-slash-commands.test.ts +3 -3
- package/src/__tests__/conversation-runtime-assembly.test.ts +185 -173
- package/src/__tests__/conversation-seed-composer.test.ts +1 -1
- package/src/__tests__/conversation-slash-queue.test.ts +4 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +4 -4
- package/src/__tests__/conversation-starter-routes.test.ts +291 -0
- package/src/__tests__/conversation-wipe.test.ts +438 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -3
- package/src/__tests__/conversation-workspace-injection.test.ts +4 -5
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -5
- package/src/__tests__/credential-security-e2e.test.ts +20 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +227 -0
- package/src/__tests__/credentials-cli.test.ts +3 -0
- package/src/__tests__/date-context.test.ts +59 -377
- package/src/__tests__/drop-capability-card-state-migration.test.ts +169 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +11 -45
- package/src/__tests__/emit-signal-routing-intent.test.ts +3 -3
- package/src/__tests__/encrypted-store.test.ts +237 -15
- package/src/__tests__/ephemeral-permissions.test.ts +4 -5
- package/src/__tests__/event-bus.test.ts +3 -3
- package/src/__tests__/gateway-only-enforcement.test.ts +2 -2
- package/src/__tests__/gateway-only-guard.test.ts +1 -0
- package/src/__tests__/gemini-image-service.test.ts +4 -4
- package/src/__tests__/gemini-provider.test.ts +6 -9
- package/src/__tests__/guardian-binding-drift-heal.test.ts +128 -0
- package/src/__tests__/guardian-dispatch.test.ts +0 -1
- package/src/__tests__/host-shell-tool.test.ts +6 -6
- package/src/__tests__/http-user-message-parity.test.ts +2 -2
- package/src/__tests__/intent-routing.test.ts +51 -99
- package/src/__tests__/invite-routes-http.test.ts +5 -0
- package/src/__tests__/list-messages-attachments.test.ts +1 -1
- package/src/__tests__/managed-proxy-context.test.ts +2 -5
- package/src/__tests__/managed-skill-lifecycle.test.ts +8 -8
- package/src/__tests__/media-generate-image.test.ts +32 -15
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +1 -1
- package/src/__tests__/memory-lifecycle-e2e.test.ts +24 -18
- package/src/__tests__/memory-recall-quality.test.ts +4 -3
- package/src/__tests__/memory-regressions.test.ts +86 -90
- package/src/__tests__/migration-cross-version-compatibility.test.ts +32 -32
- package/src/__tests__/migration-export-http.test.ts +26 -27
- package/src/__tests__/migration-import-commit-http.test.ts +165 -37
- package/src/__tests__/migration-import-preflight-http.test.ts +81 -20
- package/src/__tests__/migration-validate-http.test.ts +16 -16
- package/src/__tests__/model-intents.test.ts +1 -1
- package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +1 -1
- package/src/__tests__/notification-broadcaster.test.ts +1 -1
- package/src/__tests__/notification-decision-fallback.test.ts +2 -2
- package/src/__tests__/notification-decision-identity.test.ts +8 -9
- package/src/__tests__/notification-decision-strategy.test.ts +1 -1
- package/src/__tests__/notification-deep-link.test.ts +1 -1
- package/src/__tests__/notification-guardian-path.test.ts +0 -1
- package/src/__tests__/notification-schedule-dedup.test.ts +7 -7
- package/src/__tests__/oauth-store.test.ts +1 -3
- package/src/__tests__/oauth2-gateway-transport.test.ts +6 -1
- package/src/__tests__/onboarding-template-contract.test.ts +23 -59
- package/src/__tests__/provider-error-scenarios.test.ts +154 -0
- package/src/__tests__/provider-fail-open-selection.test.ts +2 -2
- package/src/__tests__/provider-managed-proxy-integration.test.ts +8 -9
- package/src/__tests__/provider-registry-ollama.test.ts +5 -2
- package/src/__tests__/qdrant-manager.test.ts +7 -7
- package/src/__tests__/ratelimit.test.ts +0 -74
- package/src/__tests__/recording-handler.test.ts +0 -1
- package/src/__tests__/require-fresh-approval.test.ts +1 -1
- package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
- package/src/__tests__/runtime-events-sse-parity.test.ts +1 -1
- package/src/__tests__/runtime-events-sse.test.ts +1 -1
- package/src/__tests__/scheduler-recurrence.test.ts +46 -2
- package/src/__tests__/schema-transforms.test.ts +114 -54
- package/src/__tests__/secret-onetime-send.test.ts +20 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -2
- package/src/__tests__/secret-scanner-executor.test.ts +1 -2
- package/src/__tests__/send-endpoint-busy.test.ts +63 -4
- package/src/__tests__/send-notification-tool.test.ts +2 -2
- package/src/__tests__/shell-credential-ref.test.ts +0 -1
- package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -2
- package/src/__tests__/skill-memory.test.ts +547 -0
- package/src/__tests__/skill-script-runner-sandbox.test.ts +1 -2
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +37 -0
- package/src/__tests__/slack-channel-config.test.ts +109 -94
- package/src/__tests__/swarm-conversation-integration.test.ts +2 -2
- package/src/__tests__/swarm-recursion.test.ts +2 -2
- package/src/__tests__/swarm-tool.test.ts +2 -2
- package/src/__tests__/system-prompt.test.ts +19 -66
- package/src/__tests__/telegram-config.test.ts +121 -0
- package/src/__tests__/terminal-tools.test.ts +1 -1
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -2
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +1 -1
- package/src/__tests__/trace-emitter.test.ts +8 -1
- package/src/__tests__/trust-store.test.ts +7 -8
- package/src/__tests__/twilio-routes.test.ts +1 -18
- package/src/__tests__/user-reference.test.ts +82 -2
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +196 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -1
- package/src/approvals/guardian-request-resolvers.ts +3 -3
- package/src/avatar/ascii-renderer.ts +2 -2
- package/src/avatar/png-renderer.ts +2 -2
- package/src/avatar/resvg-lazy.ts +21 -0
- package/src/calls/guardian-dispatch.ts +1 -1
- package/src/calls/relay-access-wait.ts +2 -2
- package/src/calls/twilio-rest.ts +0 -248
- package/src/cli/AGENTS.md +5 -8
- package/src/cli/__tests__/notifications.test.ts +5 -5
- package/src/cli/commands/avatar.ts +64 -2
- package/src/cli/commands/conversations.ts +131 -1
- package/src/cli/commands/credentials.ts +2 -0
- package/src/cli/commands/notifications.ts +3 -3
- package/src/cli.ts +10 -0
- package/src/config/bundled-skills/acp/SKILL.md +5 -5
- package/src/config/bundled-skills/acp/TOOLS.json +6 -6
- package/src/config/bundled-skills/app-builder/SKILL.md +42 -42
- package/src/config/bundled-skills/app-builder/TOOLS.json +10 -10
- package/src/config/bundled-skills/browser/SKILL.md +15 -15
- package/src/config/bundled-skills/browser/TOOLS.json +14 -14
- package/src/config/bundled-skills/chatgpt-import/SKILL.md +2 -2
- package/src/config/bundled-skills/chatgpt-import/TOOLS.json +1 -1
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
- package/src/config/bundled-skills/claude-code/SKILL.md +5 -5
- package/src/config/bundled-skills/computer-use/SKILL.md +2 -2
- package/src/config/bundled-skills/computer-use/TOOLS.json +15 -15
- package/src/config/bundled-skills/contacts/SKILL.md +3 -3
- package/src/config/bundled-skills/contacts/TOOLS.json +4 -4
- package/src/config/bundled-skills/document/SKILL.md +4 -4
- package/src/config/bundled-skills/document/TOOLS.json +2 -2
- package/src/config/bundled-skills/followups/TOOLS.json +3 -3
- package/src/config/bundled-skills/gmail/SKILL.md +32 -32
- package/src/config/bundled-skills/gmail/TOOLS.json +16 -16
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
- package/src/config/bundled-skills/google-calendar/SKILL.md +1 -1
- package/src/config/bundled-skills/google-calendar/TOOLS.json +5 -5
- package/src/config/bundled-skills/google-calendar/types.ts +1 -1
- package/src/config/bundled-skills/heartbeat/SKILL.md +43 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +3 -3
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -3
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +16 -12
- package/src/config/bundled-skills/media-processing/SKILL.md +40 -40
- package/src/config/bundled-skills/media-processing/TOOLS.json +8 -8
- package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +2 -2
- package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +1 -1
- package/src/config/bundled-skills/media-processing/services/gemini-map.ts +5 -5
- package/src/config/bundled-skills/media-processing/services/gemini-video.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/reduce.ts +3 -3
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
- package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +29 -25
- package/src/config/bundled-skills/messaging/TOOLS.json +11 -11
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
- package/src/config/bundled-skills/notifications/SKILL.md +3 -3
- package/src/config/bundled-skills/notifications/TOOLS.json +2 -2
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +3 -3
- package/src/config/bundled-skills/orchestration/SKILL.md +1 -1
- package/src/config/bundled-skills/orchestration/TOOLS.json +1 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +18 -14
- package/src/config/bundled-skills/phone-calls/TOOLS.json +3 -3
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +1 -1
- package/src/config/bundled-skills/playbooks/TOOLS.json +4 -4
- package/src/config/bundled-skills/schedule/SKILL.md +26 -26
- package/src/config/bundled-skills/schedule/TOOLS.json +5 -5
- package/src/config/bundled-skills/screen-watch/SKILL.md +3 -3
- package/src/config/bundled-skills/screen-watch/TOOLS.json +1 -1
- package/src/config/bundled-skills/sequences/SKILL.md +2 -2
- package/src/config/bundled-skills/sequences/TOOLS.json +10 -10
- package/src/config/bundled-skills/sequences/tools/sequence-analytics.ts +2 -2
- package/src/config/bundled-skills/sequences/tools/sequence-enroll.ts +2 -2
- package/src/config/bundled-skills/sequences/tools/sequence-enrollment-list.ts +1 -1
- package/src/config/bundled-skills/sequences/tools/sequence-get.ts +1 -1
- package/src/config/bundled-skills/sequences/tools/sequence-import.ts +3 -3
- package/src/config/bundled-skills/sequences/tools/sequence-list.ts +1 -1
- package/src/config/bundled-skills/sequences/tools/sequence-update.ts +1 -1
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +5 -5
- package/src/config/bundled-skills/skills-catalog/SKILL.md +84 -0
- package/src/config/bundled-skills/slack/SKILL.md +2 -2
- package/src/config/bundled-skills/slack/TOOLS.json +8 -8
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +3 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +5 -5
- package/src/config/bundled-skills/tasks/SKILL.md +1 -1
- package/src/config/bundled-skills/tasks/TOOLS.json +9 -9
- package/src/config/bundled-skills/transcribe/SKILL.md +5 -5
- package/src/config/bundled-skills/transcribe/TOOLS.json +1 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +10 -10
- package/src/config/bundled-skills/watcher/SKILL.md +4 -4
- package/src/config/bundled-skills/watcher/TOOLS.json +5 -5
- package/src/config/feature-flag-registry.json +33 -17
- package/src/config/schemas/sandbox.ts +1 -1
- package/src/config/schemas/services.ts +13 -3
- package/src/config/schemas/timeouts.ts +0 -10
- package/src/contacts/contact-store.ts +63 -0
- package/src/contacts/contacts-write.ts +1 -1
- package/src/daemon/assistant-attachments.ts +2 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -2
- package/src/daemon/conversation-agent-loop.ts +7 -30
- package/src/daemon/conversation-error.ts +24 -0
- package/src/daemon/conversation-memory.ts +8 -7
- package/src/daemon/conversation-runtime-assembly.ts +139 -274
- package/src/daemon/conversation-slash.ts +7 -26
- package/src/daemon/conversation-surfaces.ts +14 -0
- package/src/daemon/conversation-tool-setup.ts +9 -8
- package/src/daemon/conversation.ts +2 -0
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/date-context.ts +10 -83
- package/src/daemon/handlers/config-channels.ts +12 -2
- package/src/daemon/handlers/config-slack-channel.ts +7 -1
- package/src/daemon/handlers/config-telegram.ts +6 -1
- package/src/daemon/handlers/conversations.ts +2 -2
- package/src/daemon/handlers/skills.ts +4 -0
- package/src/daemon/lifecycle.ts +28 -4
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +1 -5
- package/src/daemon/shutdown-handlers.ts +9 -3
- package/src/daemon/tool-side-effects.ts +40 -0
- package/src/daemon/trace-emitter.ts +25 -2
- package/src/events/domain-events.ts +1 -1
- package/src/events/tool-permission-telemetry-listener.ts +46 -0
- package/src/inbound/platform-callback-registration.ts +0 -18
- package/src/media/app-icon-generator.ts +15 -8
- package/src/media/avatar-router.ts +15 -8
- package/src/media/gemini-image-service.ts +125 -21
- package/src/memory/attachments-store.ts +3 -3
- package/src/memory/channel-verification-sessions.ts +6 -6
- package/src/memory/conversation-crud.ts +196 -1
- package/src/memory/{thread-starters-cadence.ts → conversation-starters-cadence.ts} +9 -42
- package/src/memory/conversation-title-service.ts +2 -3
- package/src/memory/db-init.ts +25 -1
- package/src/memory/invite-store.ts +4 -4
- package/src/memory/items-extractor.ts +4 -4
- package/src/memory/job-handlers/{thread-starters.ts → conversation-starters.ts} +123 -38
- package/src/memory/jobs-store.ts +3 -2
- package/src/memory/jobs-worker.ts +7 -5
- package/src/memory/lifecycle-events-store.ts +63 -0
- package/src/memory/migrations/172-rename-created-by-session-id.ts +27 -0
- package/src/memory/migrations/173-rename-source-session-id.ts +16 -0
- package/src/memory/migrations/174-rename-thread-starters-table.ts +52 -0
- package/src/memory/migrations/175-create-lifecycle-events.ts +15 -0
- package/src/memory/migrations/176-drop-capability-card-state.ts +36 -0
- package/src/memory/migrations/177-create-trace-events-table.ts +40 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +13 -0
- package/src/memory/retriever.test.ts +223 -96
- package/src/memory/retriever.ts +115 -138
- package/src/memory/schema/calls.ts +1 -1
- package/src/memory/schema/contacts.ts +1 -1
- package/src/memory/schema/infrastructure.ts +29 -0
- package/src/memory/schema/memory-core.ts +7 -17
- package/src/memory/schema/notifications.ts +1 -1
- package/src/memory/search/formatting.ts +23 -6
- package/src/memory/search/lexical.ts +2 -0
- package/src/memory/search/semantic.ts +2 -0
- package/src/memory/search/staleness.ts +1 -0
- package/src/memory/search/types.ts +4 -0
- package/src/memory/task-memory-cleanup.ts +96 -6
- package/src/memory/trace-event-store.ts +148 -0
- package/src/notifications/README.md +1 -1
- package/src/notifications/decision-engine.ts +2 -2
- package/src/notifications/emit-signal.ts +4 -4
- package/src/notifications/events-store.ts +4 -4
- package/src/notifications/signal.ts +1 -1
- package/src/oauth/manual-token-connection.ts +49 -25
- package/src/permissions/checker.ts +6 -5
- package/src/permissions/defaults.ts +4 -4
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +9 -90
- package/src/prompts/cache-boundary.ts +8 -0
- package/src/prompts/system-prompt.ts +105 -634
- package/src/prompts/templates/BOOTSTRAP.md +166 -33
- package/src/prompts/templates/IDENTITY.md +8 -23
- package/src/prompts/templates/SOUL.md +20 -41
- package/src/prompts/templates/USER.md +3 -19
- package/src/prompts/user-reference.ts +14 -16
- package/src/providers/anthropic/client.ts +46 -2
- package/src/providers/gemini/client.ts +6 -9
- package/src/providers/managed-proxy/constants.ts +1 -7
- package/src/providers/managed-proxy/context.ts +0 -1
- package/src/providers/model-intents.ts +5 -5
- package/src/providers/openai/client.ts +10 -1
- package/src/providers/openrouter/client.ts +1 -0
- package/src/providers/ratelimit.ts +0 -35
- package/src/providers/registry.ts +3 -5
- package/src/providers/retry.ts +18 -1
- package/src/runtime/access-request-helper.ts +1 -1
- package/src/runtime/auth/route-policy.ts +7 -0
- package/src/runtime/channel-verification-service.ts +1 -1
- package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
- package/src/runtime/guardian-vellum-migration.ts +63 -1
- package/src/runtime/http-server.ts +8 -4
- package/src/runtime/migrations/vbundle-builder.ts +212 -32
- package/src/runtime/migrations/vbundle-import-analyzer.ts +74 -8
- package/src/runtime/migrations/vbundle-importer.ts +66 -1
- package/src/runtime/migrations/vbundle-validator.ts +17 -3
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +4 -4
- package/src/runtime/routes/attachment-routes.ts +2 -2
- package/src/runtime/routes/btw-routes.ts +9 -0
- package/src/runtime/routes/channel-verification-routes.ts +19 -2
- package/src/runtime/routes/conversation-management-routes.ts +55 -1
- package/src/runtime/routes/conversation-query-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +49 -5
- package/src/runtime/routes/conversation-starter-routes.ts +207 -0
- package/src/runtime/routes/guardian-bootstrap-routes.ts +13 -9
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -1
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +1 -1
- package/src/runtime/routes/migration-routes.ts +25 -13
- package/src/runtime/routes/secret-routes.ts +18 -0
- package/src/runtime/routes/settings-routes.ts +8 -8
- package/src/runtime/routes/telemetry-routes.ts +53 -0
- package/src/runtime/routes/trace-event-routes.ts +62 -0
- package/src/runtime/tool-grant-request-helper.ts +1 -1
- package/src/runtime/verification-outbound-actions.ts +47 -31
- package/src/security/encrypted-store.ts +263 -78
- package/src/skills/catalog-install.ts +10 -0
- package/src/skills/managed-store.ts +2 -0
- package/src/skills/skill-memory.ts +220 -0
- package/src/subagent/manager.ts +1 -4
- package/src/telemetry/types.ts +10 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +1 -1
- package/src/telemetry/usage-telemetry-reporter.ts +51 -4
- package/src/tools/AGENTS.md +11 -11
- package/src/tools/acp/spawn.ts +1 -1
- package/src/tools/apps/executors.ts +8 -8
- package/src/tools/apps/registry.ts +1 -1
- package/src/tools/assets/materialize.ts +6 -6
- package/src/tools/assets/search.ts +10 -10
- package/src/tools/browser/__tests__/auth-cache.test.ts +2 -2
- package/src/tools/browser/__tests__/auth-detector.test.ts +4 -4
- package/src/tools/browser/auth-detector.ts +6 -6
- package/src/tools/browser/browser-execution.ts +13 -13
- package/src/tools/browser/browser-manager.ts +3 -3
- package/src/tools/browser/chrome-cdp.ts +5 -5
- package/src/tools/browser/jit-auth.ts +2 -2
- package/src/tools/browser/network-recorder.test.ts +2 -2
- package/src/tools/browser/network-recorder.ts +3 -3
- package/src/tools/browser/runtime-check.ts +3 -3
- package/src/tools/claude-code/claude-code.ts +2 -2
- package/src/tools/computer-use/definitions.ts +18 -18
- package/src/tools/credential-execution/make-authenticated-request.ts +4 -4
- package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -3
- package/src/tools/credential-execution/run-authenticated-command.ts +4 -4
- package/src/tools/credentials/broker-types.ts +5 -5
- package/src/tools/credentials/broker.ts +15 -15
- package/src/tools/credentials/metadata-store.ts +2 -2
- package/src/tools/credentials/resolve.ts +1 -1
- package/src/tools/credentials/selection.ts +1 -1
- package/src/tools/credentials/tool-policy.ts +1 -1
- package/src/tools/credentials/vault.ts +115 -25
- package/src/tools/execution-target.ts +2 -2
- package/src/tools/executor.ts +7 -7
- package/src/tools/filesystem/edit.ts +2 -2
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +1 -1
- package/src/tools/host-filesystem/edit.ts +2 -1
- package/src/tools/host-filesystem/read.ts +2 -1
- package/src/tools/host-filesystem/write.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +9 -8
- package/src/tools/mcp/mcp-tool-factory.ts +7 -6
- package/src/tools/memory/definitions.ts +6 -5
- package/src/tools/memory/handlers.test.ts +1 -1
- package/src/tools/network/__tests__/web-search.test.ts +3 -3
- package/src/tools/network/domain-normalize.ts +2 -2
- package/src/tools/network/script-proxy/session-manager.ts +10 -10
- package/src/tools/network/web-fetch.ts +1 -1
- package/src/tools/network/web-search.ts +3 -3
- package/src/tools/permission-checker.ts +8 -8
- package/src/tools/registry.ts +7 -7
- package/src/tools/schedule/list.ts +2 -2
- package/src/tools/schema-transforms.ts +31 -21
- package/src/tools/secret-detection-handler.ts +1 -1
- package/src/tools/sensitive-output-placeholders.ts +1 -1
- package/src/tools/shared/filesystem/edit-engine.ts +1 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +3 -3
- package/src/tools/shared/filesystem/image-read.ts +25 -5
- package/src/tools/shared/filesystem/path-policy.ts +2 -2
- package/src/tools/shared/shell-output.ts +1 -1
- package/src/tools/side-effects.ts +1 -1
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/load.ts +3 -3
- package/src/tools/skills/sandbox-runner.ts +3 -3
- package/src/tools/subagent/read.ts +1 -1
- package/src/tools/subagent/spawn.ts +2 -2
- package/src/tools/swarm/delegate.ts +3 -3
- package/src/tools/system/request-permission.ts +5 -4
- package/src/tools/terminal/backends/native.ts +4 -4
- package/src/tools/terminal/parser.ts +6 -6
- package/src/tools/terminal/sandbox-diagnostics.ts +1 -1
- package/src/tools/terminal/shell.ts +16 -16
- package/src/tools/tool-approval-handler.ts +21 -12
- package/src/tools/tool-manifest.ts +4 -4
- package/src/tools/types.ts +3 -3
- package/src/tools/ui-surface/definitions.ts +9 -37
- package/src/tools/watcher/list.ts +1 -1
- package/src/util/logger.ts +7 -2
- package/src/util/retry.ts +29 -1
- package/src/workspace/migrations/007-web-search-provider-rename.ts +37 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/__tests__/cli-help-reference-sync.test.ts +0 -26
- package/src/__tests__/onboarding-starter-tasks.test.ts +0 -190
- package/src/cli/reference.ts +0 -38
- package/src/memory/job-handlers/capability-cards.ts +0 -420
- package/src/runtime/routes/thread-starter-routes.ts +0 -294
|
@@ -69,11 +69,10 @@ mock.module("../config/loader.js", () => ({
|
|
|
69
69
|
nonInteractiveLatestTurnCompression: "truncate",
|
|
70
70
|
},
|
|
71
71
|
},
|
|
72
|
-
rateLimit: { maxRequestsPerMinute: 0
|
|
72
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
73
73
|
timeouts: { permissionTimeoutSec: 1 },
|
|
74
74
|
skills: { entries: {}, allowBundled: true },
|
|
75
75
|
permissions: { mode: "workspace" },
|
|
76
|
-
sandbox: { enabled: false },
|
|
77
76
|
daemon: {
|
|
78
77
|
startupSocketWaitMs: 5000,
|
|
79
78
|
stopTimeoutMs: 5000,
|
|
@@ -90,9 +89,9 @@ mock.module("../config/loader.js", () => ({
|
|
|
90
89
|
"image-generation": {
|
|
91
90
|
mode: "your-own",
|
|
92
91
|
provider: "gemini",
|
|
93
|
-
model: "gemini-
|
|
92
|
+
model: "gemini-3.1-flash-image-preview",
|
|
94
93
|
},
|
|
95
|
-
"web-search": { mode: "your-own", provider: "
|
|
94
|
+
"web-search": { mode: "your-own", provider: "inference-provider-native" },
|
|
96
95
|
},
|
|
97
96
|
}),
|
|
98
97
|
loadRawConfig: () => ({}),
|
|
@@ -178,7 +177,7 @@ mock.module("../memory/retriever.js", () => ({
|
|
|
178
177
|
injectedTokens: 0,
|
|
179
178
|
latencyMs: 0,
|
|
180
179
|
}),
|
|
181
|
-
|
|
180
|
+
injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
|
|
182
181
|
}));
|
|
183
182
|
|
|
184
183
|
mock.module("../context/window-manager.js", () => ({
|
|
@@ -1735,12 +1734,23 @@ describe("Regression: cancel semantics and error channel split", () => {
|
|
|
1735
1734
|
});
|
|
1736
1735
|
|
|
1737
1736
|
test("commitTurnChanges never resolving within budget -> turn still completes and drains queue", async () => {
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1737
|
+
// Replace setTimeout with a zero-delay version so the 4000ms
|
|
1738
|
+
// raceWithTimeout fires instantly instead of waiting real time.
|
|
1739
|
+
const origSetTimeout = globalThis.setTimeout;
|
|
1740
|
+
globalThis.setTimeout = ((
|
|
1741
|
+
fn: TimerHandler,
|
|
1742
|
+
_ms?: number,
|
|
1743
|
+
...args: unknown[]
|
|
1744
|
+
) => {
|
|
1745
|
+
return origSetTimeout(fn, 0, ...args);
|
|
1746
|
+
}) as typeof setTimeout;
|
|
1742
1747
|
|
|
1743
1748
|
try {
|
|
1749
|
+
const conversation = makeConversation();
|
|
1750
|
+
await conversation.loadFromDb();
|
|
1751
|
+
|
|
1752
|
+
turnCommitHangForever = true;
|
|
1753
|
+
|
|
1744
1754
|
const events1: ServerMessage[] = [];
|
|
1745
1755
|
const events2: ServerMessage[] = [];
|
|
1746
1756
|
|
|
@@ -1761,10 +1771,8 @@ describe("Regression: cancel semantics and error channel split", () => {
|
|
|
1761
1771
|
|
|
1762
1772
|
// The turn should still complete (timeout fires) and drain the queue
|
|
1763
1773
|
// even though commitTurnChanges never resolves.
|
|
1764
|
-
//
|
|
1765
|
-
//
|
|
1766
|
-
// We wait for the second run to be registered, which proves the
|
|
1767
|
-
// turn completed and the queue drained despite the hanging commit.
|
|
1774
|
+
// With the zero-delay setTimeout wrapper the 4000ms budget fires
|
|
1775
|
+
// instantly, so we only need a short wait for the second run.
|
|
1768
1776
|
await waitForPendingRun(2, 10_000);
|
|
1769
1777
|
|
|
1770
1778
|
// First message should have completed
|
|
@@ -1781,9 +1789,10 @@ describe("Regression: cancel semantics and error channel split", () => {
|
|
|
1781
1789
|
// Complete the second run so the test can clean up
|
|
1782
1790
|
turnCommitHangForever = false;
|
|
1783
1791
|
resolveRun(1);
|
|
1784
|
-
await new Promise((r) =>
|
|
1792
|
+
await new Promise((r) => origSetTimeout(r, 10));
|
|
1785
1793
|
} finally {
|
|
1786
1794
|
turnCommitHangForever = false;
|
|
1795
|
+
globalThis.setTimeout = origSetTimeout;
|
|
1787
1796
|
}
|
|
1788
1797
|
}, 15_000);
|
|
1789
1798
|
});
|
|
@@ -29,7 +29,7 @@ mock.module("../config/loader.js", () => ({
|
|
|
29
29
|
model: "claude-opus-4-6",
|
|
30
30
|
provider: "anthropic",
|
|
31
31
|
memory: { enabled: false },
|
|
32
|
-
rateLimit: { maxRequestsPerMinute: 0
|
|
32
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
33
33
|
secretDetection: { enabled: false },
|
|
34
34
|
contextWindow: { maxInputTokens: 200000 },
|
|
35
35
|
services: {
|
|
@@ -41,9 +41,9 @@ mock.module("../config/loader.js", () => ({
|
|
|
41
41
|
"image-generation": {
|
|
42
42
|
mode: "your-own",
|
|
43
43
|
provider: "gemini",
|
|
44
|
-
model: "gemini-
|
|
44
|
+
model: "gemini-3.1-flash-image-preview",
|
|
45
45
|
},
|
|
46
|
-
"web-search": { mode: "your-own", provider: "
|
|
46
|
+
"web-search": { mode: "your-own", provider: "inference-provider-native" },
|
|
47
47
|
},
|
|
48
48
|
}),
|
|
49
49
|
}));
|
|
@@ -7,20 +7,19 @@ import type {
|
|
|
7
7
|
} from "../daemon/conversation-runtime-assembly.js";
|
|
8
8
|
import {
|
|
9
9
|
applyRuntimeInjections,
|
|
10
|
-
|
|
10
|
+
buildTurnContextBlock,
|
|
11
11
|
injectChannelCapabilityContext,
|
|
12
|
-
|
|
12
|
+
injectChannelCommandContext,
|
|
13
13
|
injectInboundActorContext,
|
|
14
14
|
injectTemporalContext,
|
|
15
|
+
injectTurnContext,
|
|
15
16
|
isGroupChatType,
|
|
16
17
|
resolveChannelCapabilities,
|
|
17
|
-
sanitizePttActivationKey,
|
|
18
18
|
stripChannelCapabilityContext,
|
|
19
19
|
stripChannelTurnContext,
|
|
20
20
|
stripInboundActorContext,
|
|
21
21
|
stripTemporalContext,
|
|
22
22
|
} from "../daemon/conversation-runtime-assembly.js";
|
|
23
|
-
import { buildChannelAwarenessSection } from "../prompts/system-prompt.js";
|
|
24
23
|
import type { Message } from "../providers/types.js";
|
|
25
24
|
|
|
26
25
|
// ---------------------------------------------------------------------------
|
|
@@ -135,7 +134,7 @@ describe("resolveChannelCapabilities", () => {
|
|
|
135
134
|
});
|
|
136
135
|
|
|
137
136
|
test("propagates chatType when provided", () => {
|
|
138
|
-
const caps = resolveChannelCapabilities("telegram", null,
|
|
137
|
+
const caps = resolveChannelCapabilities("telegram", null, "group");
|
|
139
138
|
expect(caps.chatType).toBe("group");
|
|
140
139
|
});
|
|
141
140
|
|
|
@@ -155,7 +154,7 @@ describe("injectChannelCapabilityContext", () => {
|
|
|
155
154
|
content: [{ type: "text", text: "Hello" }],
|
|
156
155
|
};
|
|
157
156
|
|
|
158
|
-
test("
|
|
157
|
+
test("skips injection entirely for desktop happy path (all capabilities true)", () => {
|
|
159
158
|
const caps: ChannelCapabilities = {
|
|
160
159
|
channel: "vellum",
|
|
161
160
|
dashboardCapable: true,
|
|
@@ -165,26 +164,9 @@ describe("injectChannelCapabilityContext", () => {
|
|
|
165
164
|
|
|
166
165
|
const result = injectChannelCapabilityContext(baseUserMessage, caps);
|
|
167
166
|
|
|
168
|
-
//
|
|
169
|
-
expect(result
|
|
170
|
-
|
|
171
|
-
expect(injected.type).toBe("text");
|
|
172
|
-
expect((injected as { type: "text"; text: string }).text).toContain(
|
|
173
|
-
"<channel_capabilities>",
|
|
174
|
-
);
|
|
175
|
-
expect((injected as { type: "text"; text: string }).text).toContain(
|
|
176
|
-
"dashboard_capable: true",
|
|
177
|
-
);
|
|
178
|
-
expect((injected as { type: "text"; text: string }).text).toContain(
|
|
179
|
-
"supports_dynamic_ui: true",
|
|
180
|
-
);
|
|
181
|
-
expect((injected as { type: "text"; text: string }).text).toContain(
|
|
182
|
-
"</channel_capabilities>",
|
|
183
|
-
);
|
|
184
|
-
// Should NOT contain constraint rules for dashboard
|
|
185
|
-
expect((injected as { type: "text"; text: string }).text).not.toContain(
|
|
186
|
-
"CHANNEL CONSTRAINTS",
|
|
187
|
-
);
|
|
167
|
+
// Message returned unchanged — no injection at all
|
|
168
|
+
expect(result).toBe(baseUserMessage);
|
|
169
|
+
expect(result.content.length).toBe(1);
|
|
188
170
|
});
|
|
189
171
|
|
|
190
172
|
test("injects constraint rules for non-dashboard channel", () => {
|
|
@@ -293,6 +275,50 @@ describe("injectChannelCapabilityContext", () => {
|
|
|
293
275
|
expect(text).toContain("GROUP CHAT ETIQUETTE");
|
|
294
276
|
expect(text).toContain("emoji reactions");
|
|
295
277
|
});
|
|
278
|
+
|
|
279
|
+
test("still injects for group chats even when all capabilities are true", () => {
|
|
280
|
+
const caps: ChannelCapabilities = {
|
|
281
|
+
channel: "slack",
|
|
282
|
+
dashboardCapable: true,
|
|
283
|
+
supportsDynamicUi: true,
|
|
284
|
+
supportsVoiceInput: true,
|
|
285
|
+
chatType: "channel",
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const result = injectChannelCapabilityContext(baseUserMessage, caps);
|
|
289
|
+
// Not the happy path because chatType is a group type
|
|
290
|
+
expect(result).not.toBe(baseUserMessage);
|
|
291
|
+
const text = (result.content[0] as { type: "text"; text: string }).text;
|
|
292
|
+
expect(text).toContain("GROUP CHAT ETIQUETTE");
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test("injects WhatsApp formatting constraint for whatsapp channel", () => {
|
|
296
|
+
const caps: ChannelCapabilities = {
|
|
297
|
+
channel: "whatsapp",
|
|
298
|
+
dashboardCapable: false,
|
|
299
|
+
supportsDynamicUi: false,
|
|
300
|
+
supportsVoiceInput: false,
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const result = injectChannelCapabilityContext(baseUserMessage, caps);
|
|
304
|
+
const text = (result.content[0] as { type: "text"; text: string }).text;
|
|
305
|
+
expect(text).toContain("Do NOT use markdown tables");
|
|
306
|
+
expect(text).toContain("bullet lists");
|
|
307
|
+
expect(text).toContain("CAPS for emphasis");
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
test("does NOT inject WhatsApp formatting for non-whatsapp channels", () => {
|
|
311
|
+
const caps: ChannelCapabilities = {
|
|
312
|
+
channel: "telegram",
|
|
313
|
+
dashboardCapable: false,
|
|
314
|
+
supportsDynamicUi: false,
|
|
315
|
+
supportsVoiceInput: false,
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
const result = injectChannelCapabilityContext(baseUserMessage, caps);
|
|
319
|
+
const text = (result.content[0] as { type: "text"; text: string }).text;
|
|
320
|
+
expect(text).not.toContain("Do NOT use markdown tables");
|
|
321
|
+
});
|
|
296
322
|
});
|
|
297
323
|
|
|
298
324
|
// ---------------------------------------------------------------------------
|
|
@@ -449,60 +475,12 @@ describe("applyRuntimeInjections with channelCapabilities", () => {
|
|
|
449
475
|
});
|
|
450
476
|
});
|
|
451
477
|
|
|
452
|
-
// ---------------------------------------------------------------------------
|
|
453
|
-
// buildChannelAwarenessSection
|
|
454
|
-
// ---------------------------------------------------------------------------
|
|
455
|
-
|
|
456
|
-
describe("buildChannelAwarenessSection", () => {
|
|
457
|
-
test("includes channel awareness heading", () => {
|
|
458
|
-
const section = buildChannelAwarenessSection();
|
|
459
|
-
expect(section).toContain("## Channel Awareness & Trust Gating");
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
test("includes channel-specific rules", () => {
|
|
463
|
-
const section = buildChannelAwarenessSection();
|
|
464
|
-
expect(section).toContain("dashboard_capable");
|
|
465
|
-
expect(section).toContain("supports_dynamic_ui");
|
|
466
|
-
expect(section).toContain("supports_voice_input");
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
test("includes trust gating rules for permission asks", () => {
|
|
470
|
-
const section = buildChannelAwarenessSection();
|
|
471
|
-
expect(section).toContain("firstConversationComplete");
|
|
472
|
-
expect(section).toContain("Permission ask trust gating");
|
|
473
|
-
expect(section).toContain(
|
|
474
|
-
"Do NOT proactively ask for elevated permissions",
|
|
475
|
-
);
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
test("gates microphone permissions on voice capability", () => {
|
|
479
|
-
const section = buildChannelAwarenessSection();
|
|
480
|
-
expect(section).toContain("Do not ask for microphone permissions");
|
|
481
|
-
});
|
|
482
|
-
|
|
483
|
-
test("gates computer-control on dashboard channel", () => {
|
|
484
|
-
const section = buildChannelAwarenessSection();
|
|
485
|
-
expect(section).toContain("computer-control permissions on non-dashboard");
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
test("does NOT include group chat etiquette (gated per-turn instead)", () => {
|
|
489
|
-
const section = buildChannelAwarenessSection();
|
|
490
|
-
expect(section).not.toContain("Group chat etiquette");
|
|
491
|
-
expect(section).not.toContain("Stay silent when");
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
test("does NOT include Discord references (not a supported channel)", () => {
|
|
495
|
-
const section = buildChannelAwarenessSection();
|
|
496
|
-
expect(section).not.toContain("Discord");
|
|
497
|
-
});
|
|
498
|
-
});
|
|
499
|
-
|
|
500
478
|
// ---------------------------------------------------------------------------
|
|
501
479
|
// Trust-gating behavior: channel constraints for permission asks
|
|
502
480
|
// ---------------------------------------------------------------------------
|
|
503
481
|
|
|
504
482
|
describe("trust-gating via channel capabilities", () => {
|
|
505
|
-
test("vellum channel with macos interface
|
|
483
|
+
test("vellum channel with macos interface skips injection (happy path)", () => {
|
|
506
484
|
const caps = resolveChannelCapabilities("vellum", "macos");
|
|
507
485
|
const message: Message = {
|
|
508
486
|
role: "user",
|
|
@@ -510,10 +488,9 @@ describe("trust-gating via channel capabilities", () => {
|
|
|
510
488
|
};
|
|
511
489
|
|
|
512
490
|
const result = injectChannelCapabilityContext(message, caps);
|
|
513
|
-
const injected = (result.content[0] as { type: "text"; text: string }).text;
|
|
514
491
|
|
|
515
|
-
|
|
516
|
-
expect(
|
|
492
|
+
// Happy path: message returned unchanged
|
|
493
|
+
expect(result).toBe(message);
|
|
517
494
|
});
|
|
518
495
|
|
|
519
496
|
test("non-dashboard channel adds constraint rules preventing UI references", () => {
|
|
@@ -554,6 +531,50 @@ describe("trust-gating via channel capabilities", () => {
|
|
|
554
531
|
});
|
|
555
532
|
});
|
|
556
533
|
|
|
534
|
+
// ---------------------------------------------------------------------------
|
|
535
|
+
// injectChannelCommandContext
|
|
536
|
+
// ---------------------------------------------------------------------------
|
|
537
|
+
|
|
538
|
+
describe("injectChannelCommandContext", () => {
|
|
539
|
+
const baseUserMessage: Message = {
|
|
540
|
+
role: "user",
|
|
541
|
+
content: [{ type: "text", text: "Hello" }],
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
test("injects start command instructions when type is start", () => {
|
|
545
|
+
const result = injectChannelCommandContext(baseUserMessage, {
|
|
546
|
+
type: "start",
|
|
547
|
+
});
|
|
548
|
+
const text = (result.content[0] as { type: "text"; text: string }).text;
|
|
549
|
+
expect(text).toContain("command_type: start");
|
|
550
|
+
expect(text).toContain("warm, brief greeting");
|
|
551
|
+
expect(text).toContain("Treat /start as a hello");
|
|
552
|
+
expect(text).toContain("Do NOT reset conversation");
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
test("includes language code and payload when provided", () => {
|
|
556
|
+
const result = injectChannelCommandContext(baseUserMessage, {
|
|
557
|
+
type: "start",
|
|
558
|
+
payload: "ref123",
|
|
559
|
+
languageCode: "es",
|
|
560
|
+
});
|
|
561
|
+
const text = (result.content[0] as { type: "text"; text: string }).text;
|
|
562
|
+
expect(text).toContain("payload: ref123");
|
|
563
|
+
expect(text).toContain("language_code: es");
|
|
564
|
+
expect(text).toContain("warm, brief greeting");
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
test("does NOT inject start instructions for non-start commands", () => {
|
|
568
|
+
const result = injectChannelCommandContext(baseUserMessage, {
|
|
569
|
+
type: "help",
|
|
570
|
+
});
|
|
571
|
+
const text = (result.content[0] as { type: "text"; text: string }).text;
|
|
572
|
+
expect(text).toContain("command_type: help");
|
|
573
|
+
expect(text).not.toContain("warm, brief greeting");
|
|
574
|
+
expect(text).not.toContain("Treat /start as a hello");
|
|
575
|
+
});
|
|
576
|
+
});
|
|
577
|
+
|
|
557
578
|
// ---------------------------------------------------------------------------
|
|
558
579
|
// injectTemporalContext
|
|
559
580
|
// ---------------------------------------------------------------------------
|
|
@@ -768,9 +789,13 @@ describe("injectInboundActorContext", () => {
|
|
|
768
789
|
expect(text).toContain("trust_class: guardian");
|
|
769
790
|
expect(text).toContain("source_channel: phone");
|
|
770
791
|
expect(text).toContain("canonical_actor_identity: guardian-user-1");
|
|
792
|
+
// Display names differ from canonical, so they should appear
|
|
793
|
+
expect(text).toContain("actor_identifier: +15550001111");
|
|
771
794
|
expect(text).toContain("actor_display_name: Guardian Name");
|
|
772
795
|
expect(text).toContain("actor_sender_display_name: Guardian Name");
|
|
773
796
|
expect(text).toContain("actor_member_display_name: Guardian Name");
|
|
797
|
+
// guardian_identity matches canonical, so it should be omitted
|
|
798
|
+
expect(text).not.toContain("guardian_identity:");
|
|
774
799
|
expect(text).toContain("</inbound_actor_context>");
|
|
775
800
|
});
|
|
776
801
|
|
|
@@ -798,6 +823,30 @@ describe("injectInboundActorContext", () => {
|
|
|
798
823
|
);
|
|
799
824
|
});
|
|
800
825
|
|
|
826
|
+
test("omits name_preference_note when member name matches canonical and is suppressed", () => {
|
|
827
|
+
const ctx: InboundActorContext = {
|
|
828
|
+
sourceChannel: "telegram",
|
|
829
|
+
canonicalActorIdentity: "Jeff",
|
|
830
|
+
actorIdentifier: "@jeff_handle",
|
|
831
|
+
actorDisplayName: "Jeff",
|
|
832
|
+
actorSenderDisplayName: "Jeffrey",
|
|
833
|
+
actorMemberDisplayName: "Jeff",
|
|
834
|
+
trustClass: "trusted_contact",
|
|
835
|
+
guardianIdentity: "guardian-user-1",
|
|
836
|
+
memberStatus: "active",
|
|
837
|
+
memberPolicy: "allow",
|
|
838
|
+
};
|
|
839
|
+
|
|
840
|
+
const result = injectInboundActorContext(baseUserMessage, ctx);
|
|
841
|
+
const text = (result.content[0] as { type: "text"; text: string }).text;
|
|
842
|
+
// actor_member_display_name matches canonical → omitted by differs() guard
|
|
843
|
+
expect(text).not.toContain("actor_member_display_name:");
|
|
844
|
+
// actor_sender_display_name differs from canonical → emitted
|
|
845
|
+
expect(text).toContain("actor_sender_display_name: Jeffrey");
|
|
846
|
+
// name_preference_note must NOT appear since actor_member_display_name was omitted
|
|
847
|
+
expect(text).not.toContain("name_preference_note:");
|
|
848
|
+
});
|
|
849
|
+
|
|
801
850
|
test("sanitizes inline actor context values to prevent line injection", () => {
|
|
802
851
|
const ctx: InboundActorContext = {
|
|
803
852
|
sourceChannel: "telegram",
|
|
@@ -903,6 +952,38 @@ describe("injectInboundActorContext", () => {
|
|
|
903
952
|
expect(text).not.toContain("non-guardian account");
|
|
904
953
|
});
|
|
905
954
|
|
|
955
|
+
test("omits redundant fields when they match canonical_actor_identity", () => {
|
|
956
|
+
const uuid = "vellum-principal-b77e94f5-67c0-4599-8baa-871b925b3da8";
|
|
957
|
+
const ctx: InboundActorContext = {
|
|
958
|
+
sourceChannel: "vellum",
|
|
959
|
+
canonicalActorIdentity: uuid,
|
|
960
|
+
actorIdentifier: uuid,
|
|
961
|
+
actorDisplayName: uuid,
|
|
962
|
+
actorSenderDisplayName: undefined,
|
|
963
|
+
actorMemberDisplayName: uuid,
|
|
964
|
+
trustClass: "guardian",
|
|
965
|
+
guardianIdentity: uuid,
|
|
966
|
+
memberStatus: "active",
|
|
967
|
+
memberPolicy: "allow",
|
|
968
|
+
contactNotes: "guardian",
|
|
969
|
+
};
|
|
970
|
+
|
|
971
|
+
const result = injectInboundActorContext(baseUserMessage, ctx);
|
|
972
|
+
const text = (result.content[0] as { type: "text"; text: string }).text;
|
|
973
|
+
// Only essential fields should remain
|
|
974
|
+
expect(text).toContain("source_channel: vellum");
|
|
975
|
+
expect(text).toContain(`canonical_actor_identity: ${uuid}`);
|
|
976
|
+
expect(text).toContain("trust_class: guardian");
|
|
977
|
+
// Redundant fields should be omitted
|
|
978
|
+
expect(text).not.toContain("actor_identifier:");
|
|
979
|
+
expect(text).not.toContain("actor_display_name:");
|
|
980
|
+
expect(text).not.toContain("actor_sender_display_name:");
|
|
981
|
+
expect(text).not.toContain("actor_member_display_name:");
|
|
982
|
+
expect(text).not.toContain("guardian_identity:");
|
|
983
|
+
// contact_notes: "guardian" matches trust_class, should be omitted
|
|
984
|
+
expect(text).not.toContain("contact_notes:");
|
|
985
|
+
});
|
|
986
|
+
|
|
906
987
|
test("omits member_status and member_policy when not provided", () => {
|
|
907
988
|
const ctx: InboundActorContext = {
|
|
908
989
|
sourceChannel: "phone",
|
|
@@ -969,46 +1050,44 @@ describe("applyRuntimeInjections with inboundActorContext", () => {
|
|
|
969
1050
|
});
|
|
970
1051
|
|
|
971
1052
|
// ---------------------------------------------------------------------------
|
|
972
|
-
//
|
|
1053
|
+
// buildTurnContextBlock (channel-only)
|
|
973
1054
|
// ---------------------------------------------------------------------------
|
|
974
1055
|
|
|
975
|
-
describe("
|
|
976
|
-
test("
|
|
977
|
-
const block =
|
|
1056
|
+
describe("buildTurnContextBlock (channel-only)", () => {
|
|
1057
|
+
test("collapses to single field when all channels match", () => {
|
|
1058
|
+
const block = buildTurnContextBlock({
|
|
978
1059
|
turnContext: {
|
|
979
1060
|
userMessageChannel: "telegram",
|
|
980
1061
|
assistantMessageChannel: "telegram",
|
|
981
1062
|
},
|
|
982
1063
|
conversationOriginChannel: "telegram",
|
|
983
|
-
});
|
|
1064
|
+
}, undefined);
|
|
984
1065
|
expect(block).toBe(
|
|
985
|
-
"<
|
|
986
|
-
"
|
|
987
|
-
"
|
|
988
|
-
"conversation_origin_channel: telegram\n" +
|
|
989
|
-
"</channel_turn_context>",
|
|
1066
|
+
"<turn_context>\n" +
|
|
1067
|
+
"channel: telegram\n" +
|
|
1068
|
+
"</turn_context>",
|
|
990
1069
|
);
|
|
991
1070
|
});
|
|
992
1071
|
|
|
993
1072
|
test('uses "unknown" when conversationOriginChannel is null', () => {
|
|
994
|
-
const block =
|
|
1073
|
+
const block = buildTurnContextBlock({
|
|
995
1074
|
turnContext: {
|
|
996
1075
|
userMessageChannel: "vellum",
|
|
997
1076
|
assistantMessageChannel: "vellum",
|
|
998
1077
|
},
|
|
999
1078
|
conversationOriginChannel: null,
|
|
1000
|
-
});
|
|
1079
|
+
}, undefined);
|
|
1001
1080
|
expect(block).toContain("conversation_origin_channel: unknown");
|
|
1002
1081
|
});
|
|
1003
1082
|
|
|
1004
1083
|
test("handles mixed channels", () => {
|
|
1005
|
-
const block =
|
|
1084
|
+
const block = buildTurnContextBlock({
|
|
1006
1085
|
turnContext: {
|
|
1007
1086
|
userMessageChannel: "telegram",
|
|
1008
1087
|
assistantMessageChannel: "vellum",
|
|
1009
1088
|
},
|
|
1010
1089
|
conversationOriginChannel: "vellum",
|
|
1011
|
-
});
|
|
1090
|
+
}, undefined);
|
|
1012
1091
|
expect(block).toContain("user_message_channel: telegram");
|
|
1013
1092
|
expect(block).toContain("assistant_message_channel: vellum");
|
|
1014
1093
|
expect(block).toContain("conversation_origin_channel: vellum");
|
|
@@ -1016,10 +1095,10 @@ describe("buildChannelTurnContextBlock", () => {
|
|
|
1016
1095
|
});
|
|
1017
1096
|
|
|
1018
1097
|
// ---------------------------------------------------------------------------
|
|
1019
|
-
//
|
|
1098
|
+
// injectTurnContext (channel-only)
|
|
1020
1099
|
// ---------------------------------------------------------------------------
|
|
1021
1100
|
|
|
1022
|
-
describe("
|
|
1101
|
+
describe("injectTurnContext (channel-only)", () => {
|
|
1023
1102
|
const baseUserMessage: Message = {
|
|
1024
1103
|
role: "user",
|
|
1025
1104
|
content: [{ type: "text", text: "Hello from telegram" }],
|
|
@@ -1033,14 +1112,14 @@ describe("injectChannelTurnContext", () => {
|
|
|
1033
1112
|
},
|
|
1034
1113
|
conversationOriginChannel: "telegram",
|
|
1035
1114
|
};
|
|
1036
|
-
const result =
|
|
1115
|
+
const result = injectTurnContext(baseUserMessage, params, undefined);
|
|
1037
1116
|
expect(result.content.length).toBe(2);
|
|
1038
1117
|
const injected = result.content[0];
|
|
1039
1118
|
expect(injected.type).toBe("text");
|
|
1040
1119
|
const text = (injected as { type: "text"; text: string }).text;
|
|
1041
|
-
expect(text).toContain("<
|
|
1042
|
-
expect(text).toContain("
|
|
1043
|
-
expect(text).toContain("</
|
|
1120
|
+
expect(text).toContain("<turn_context>");
|
|
1121
|
+
expect(text).toContain("channel: telegram");
|
|
1122
|
+
expect(text).toContain("</turn_context>");
|
|
1044
1123
|
});
|
|
1045
1124
|
|
|
1046
1125
|
test("preserves original message content", () => {
|
|
@@ -1051,7 +1130,7 @@ describe("injectChannelTurnContext", () => {
|
|
|
1051
1130
|
},
|
|
1052
1131
|
conversationOriginChannel: "vellum",
|
|
1053
1132
|
};
|
|
1054
|
-
const result =
|
|
1133
|
+
const result = injectTurnContext(baseUserMessage, params, undefined);
|
|
1055
1134
|
const lastBlock = result.content[result.content.length - 1];
|
|
1056
1135
|
expect((lastBlock as { type: "text"; text: string }).text).toBe(
|
|
1057
1136
|
"Hello from telegram",
|
|
@@ -1071,7 +1150,7 @@ describe("stripChannelTurnContext", () => {
|
|
|
1071
1150
|
content: [
|
|
1072
1151
|
{
|
|
1073
1152
|
type: "text",
|
|
1074
|
-
text: "<
|
|
1153
|
+
text: "<turn_context>\nuser_message_channel: telegram\n</turn_context>",
|
|
1075
1154
|
},
|
|
1076
1155
|
{ type: "text", text: "Hello" },
|
|
1077
1156
|
],
|
|
@@ -1099,7 +1178,7 @@ describe("stripChannelTurnContext", () => {
|
|
|
1099
1178
|
content: [
|
|
1100
1179
|
{
|
|
1101
1180
|
type: "text",
|
|
1102
|
-
text: "<
|
|
1181
|
+
text: "<turn_context>\nuser_message_channel: macos\n</turn_context>",
|
|
1103
1182
|
},
|
|
1104
1183
|
],
|
|
1105
1184
|
},
|
|
@@ -1152,7 +1231,7 @@ describe("applyRuntimeInjections with channelTurnContext", () => {
|
|
|
1152
1231
|
expect(result[0].content.length).toBe(2);
|
|
1153
1232
|
const injected = result[0].content[0];
|
|
1154
1233
|
expect((injected as { type: "text"; text: string }).text).toContain(
|
|
1155
|
-
"<
|
|
1234
|
+
"<turn_context>",
|
|
1156
1235
|
);
|
|
1157
1236
|
});
|
|
1158
1237
|
|
|
@@ -1173,73 +1252,6 @@ describe("applyRuntimeInjections with channelTurnContext", () => {
|
|
|
1173
1252
|
});
|
|
1174
1253
|
});
|
|
1175
1254
|
|
|
1176
|
-
// ---------------------------------------------------------------------------
|
|
1177
|
-
// sanitizePttActivationKey
|
|
1178
|
-
// ---------------------------------------------------------------------------
|
|
1179
|
-
|
|
1180
|
-
describe("sanitizePttActivationKey", () => {
|
|
1181
|
-
test("returns undefined for null/undefined input", () => {
|
|
1182
|
-
expect(sanitizePttActivationKey(null)).toBeUndefined();
|
|
1183
|
-
expect(sanitizePttActivationKey(undefined)).toBeUndefined();
|
|
1184
|
-
});
|
|
1185
|
-
|
|
1186
|
-
test("passes through valid JSON PTTActivator payloads", () => {
|
|
1187
|
-
const modifierOnly = JSON.stringify({
|
|
1188
|
-
kind: "modifierOnly",
|
|
1189
|
-
modifierFlags: 8388608,
|
|
1190
|
-
});
|
|
1191
|
-
expect(sanitizePttActivationKey(modifierOnly)).toBe(modifierOnly);
|
|
1192
|
-
const keyPayload = JSON.stringify({ kind: "key", keyCode: 49 });
|
|
1193
|
-
expect(sanitizePttActivationKey(keyPayload)).toBe(keyPayload);
|
|
1194
|
-
const nonePayload = JSON.stringify({ kind: "none" });
|
|
1195
|
-
expect(sanitizePttActivationKey(nonePayload)).toBe(nonePayload);
|
|
1196
|
-
});
|
|
1197
|
-
|
|
1198
|
-
test("returns undefined for invalid keys", () => {
|
|
1199
|
-
expect(
|
|
1200
|
-
sanitizePttActivationKey("malicious\nprompt injection"),
|
|
1201
|
-
).toBeUndefined();
|
|
1202
|
-
expect(sanitizePttActivationKey("arbitrary_value")).toBeUndefined();
|
|
1203
|
-
expect(sanitizePttActivationKey("")).toBeUndefined();
|
|
1204
|
-
});
|
|
1205
|
-
});
|
|
1206
|
-
|
|
1207
|
-
// ---------------------------------------------------------------------------
|
|
1208
|
-
// resolveChannelCapabilities sanitizes pttActivationKey
|
|
1209
|
-
// ---------------------------------------------------------------------------
|
|
1210
|
-
|
|
1211
|
-
describe("resolveChannelCapabilities with PTT metadata", () => {
|
|
1212
|
-
test("sanitizes valid JSON PTTActivator pttActivationKey", () => {
|
|
1213
|
-
const key = JSON.stringify({
|
|
1214
|
-
kind: "modifierOnly",
|
|
1215
|
-
modifierFlags: 8388608,
|
|
1216
|
-
});
|
|
1217
|
-
const caps = resolveChannelCapabilities("macos", "macos", {
|
|
1218
|
-
pttActivationKey: key,
|
|
1219
|
-
});
|
|
1220
|
-
expect(caps.pttActivationKey).toBe(key);
|
|
1221
|
-
});
|
|
1222
|
-
|
|
1223
|
-
test("sanitizes invalid pttActivationKey to undefined", () => {
|
|
1224
|
-
const caps = resolveChannelCapabilities("macos", "macos", {
|
|
1225
|
-
pttActivationKey: "evil\nprompt",
|
|
1226
|
-
});
|
|
1227
|
-
expect(caps.pttActivationKey).toBeUndefined();
|
|
1228
|
-
});
|
|
1229
|
-
|
|
1230
|
-
test("passes through microphonePermissionGranted", () => {
|
|
1231
|
-
const key = JSON.stringify({
|
|
1232
|
-
kind: "modifierOnly",
|
|
1233
|
-
modifierFlags: 8388608,
|
|
1234
|
-
});
|
|
1235
|
-
const caps = resolveChannelCapabilities("macos", "macos", {
|
|
1236
|
-
pttActivationKey: key,
|
|
1237
|
-
microphonePermissionGranted: true,
|
|
1238
|
-
});
|
|
1239
|
-
expect(caps.microphonePermissionGranted).toBe(true);
|
|
1240
|
-
});
|
|
1241
|
-
});
|
|
1242
|
-
|
|
1243
1255
|
// ---------------------------------------------------------------------------
|
|
1244
1256
|
// applyRuntimeInjections — injection mode
|
|
1245
1257
|
// ---------------------------------------------------------------------------
|
|
@@ -1299,8 +1311,8 @@ describe("applyRuntimeInjections — injection mode", () => {
|
|
|
1299
1311
|
expect(allText).toContain("<channel_command_context>");
|
|
1300
1312
|
expect(allText).toContain("<active_workspace>");
|
|
1301
1313
|
expect(allText).toContain("<channel_capabilities>");
|
|
1302
|
-
expect(allText).toContain("<
|
|
1303
|
-
expect(allText).toContain("<
|
|
1314
|
+
expect(allText).toContain("<turn_context>");
|
|
1315
|
+
expect(allText).toContain("<turn_context>");
|
|
1304
1316
|
expect(allText).toContain("<inbound_actor_context>");
|
|
1305
1317
|
expect(allText).toContain("<non_interactive_context>");
|
|
1306
1318
|
});
|
|
@@ -1349,8 +1361,8 @@ describe("applyRuntimeInjections — injection mode", () => {
|
|
|
1349
1361
|
.join("\n");
|
|
1350
1362
|
|
|
1351
1363
|
// Kept in minimal mode
|
|
1352
|
-
expect(allText).toContain("<
|
|
1353
|
-
expect(allText).toContain("<
|
|
1364
|
+
expect(allText).toContain("<turn_context>");
|
|
1365
|
+
expect(allText).toContain("<turn_context>");
|
|
1354
1366
|
expect(allText).toContain("<inbound_actor_context>");
|
|
1355
1367
|
expect(allText).toContain("<non_interactive_context>");
|
|
1356
1368
|
expect(allText).toContain("<channel_capabilities>");
|