@vellumai/assistant 0.4.56 → 0.5.0
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 +204 -185
- 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 +249 -15
- package/src/__tests__/ephemeral-permissions.test.ts +4 -5
- package/src/__tests__/event-bus.test.ts +3 -3
- package/src/__tests__/file-read-tool.test.ts +40 -0
- 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-file-read-tool.test.ts +87 -0
- package/src/__tests__/host-shell-tool.test.ts +6 -6
- package/src/__tests__/http-user-message-parity.test.ts +2 -2
- package/src/__tests__/identity-intro-cache.test.ts +209 -0
- 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 +2 -2
- package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +1 -1
- package/src/__tests__/non-member-access-request.test.ts +3 -3
- 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 +549 -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 +141 -275
- 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 +26 -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 +5 -1
- 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 +45 -4
- package/src/notifications/emit-signal.ts +5 -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 +172 -33
- package/src/prompts/templates/IDENTITY.md +8 -24
- 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 +51 -19
- 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 +16 -2
- 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 +61 -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 +93 -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/identity-intro-cache.ts +105 -0
- package/src/runtime/routes/identity-routes.ts +51 -0
- 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 +9 -9
- 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 +262 -78
- package/src/skills/catalog-install.ts +10 -0
- package/src/skills/managed-store.ts +2 -0
- package/src/skills/skill-memory.ts +222 -0
- package/src/subagent/manager.ts +1 -4
- package/src/telemetry/types.ts +10 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +7 -2
- package/src/telemetry/usage-telemetry-reporter.ts +53 -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 +15 -4
- package/src/tools/filesystem/write.ts +1 -1
- package/src/tools/host-filesystem/edit.ts +2 -1
- package/src/tools/host-filesystem/read.ts +18 -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/pricing.ts +4 -0
- 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
|
@@ -239,7 +239,6 @@ describe("ASK_GUARDIAN canonical notification path", () => {
|
|
|
239
239
|
|
|
240
240
|
const signalParams = emitCalls[0] as Record<string, unknown>;
|
|
241
241
|
expect(typeof signalParams.onConversationCreated).toBe("function");
|
|
242
|
-
expect(signalParams.skipVellumThread).toBeUndefined();
|
|
243
242
|
});
|
|
244
243
|
|
|
245
244
|
test("creates guardian action deliveries from notification pipeline delivery results", async () => {
|
|
@@ -72,7 +72,7 @@ function makeSignal(
|
|
|
72
72
|
signalId: `sig-${crypto.randomUUID()}`,
|
|
73
73
|
createdAt: Date.now(),
|
|
74
74
|
sourceChannel: "scheduler",
|
|
75
|
-
|
|
75
|
+
sourceContextId: "schedule-123",
|
|
76
76
|
sourceEventName: "schedule.complete",
|
|
77
77
|
contextPayload: { scheduleId: "schedule-123", name: "Drink water" },
|
|
78
78
|
attentionHints: {
|
|
@@ -118,7 +118,7 @@ describe("recurring schedule notification dedup", () => {
|
|
|
118
118
|
id: firstSignal.signalId,
|
|
119
119
|
sourceEventName: "schedule.complete",
|
|
120
120
|
sourceChannel: "scheduler",
|
|
121
|
-
|
|
121
|
+
sourceContextId: "schedule-123",
|
|
122
122
|
attentionHints: firstSignal.attentionHints,
|
|
123
123
|
payload: firstSignal.contextPayload,
|
|
124
124
|
// No dedupeKey — this is the bug scenario
|
|
@@ -132,7 +132,7 @@ describe("recurring schedule notification dedup", () => {
|
|
|
132
132
|
id: secondSignal.signalId,
|
|
133
133
|
sourceEventName: "schedule.complete",
|
|
134
134
|
sourceChannel: "scheduler",
|
|
135
|
-
|
|
135
|
+
sourceContextId: "schedule-123",
|
|
136
136
|
attentionHints: secondSignal.attentionHints,
|
|
137
137
|
payload: secondSignal.contextPayload,
|
|
138
138
|
});
|
|
@@ -160,7 +160,7 @@ describe("recurring schedule notification dedup", () => {
|
|
|
160
160
|
id: firstSignal.signalId,
|
|
161
161
|
sourceEventName: "schedule.complete",
|
|
162
162
|
sourceChannel: "scheduler",
|
|
163
|
-
|
|
163
|
+
sourceContextId: "schedule-123",
|
|
164
164
|
attentionHints: firstSignal.attentionHints,
|
|
165
165
|
payload: firstSignal.contextPayload,
|
|
166
166
|
dedupeKey: `schedule:complete:schedule-123:${Date.now() - 60_000}`,
|
|
@@ -173,7 +173,7 @@ describe("recurring schedule notification dedup", () => {
|
|
|
173
173
|
id: secondSignal.signalId,
|
|
174
174
|
sourceEventName: "schedule.complete",
|
|
175
175
|
sourceChannel: "scheduler",
|
|
176
|
-
|
|
176
|
+
sourceContextId: "schedule-123",
|
|
177
177
|
attentionHints: secondSignal.attentionHints,
|
|
178
178
|
payload: secondSignal.contextPayload,
|
|
179
179
|
dedupeKey: `schedule:complete:schedule-123:${Date.now()}`,
|
|
@@ -204,7 +204,7 @@ describe("recurring schedule notification dedup", () => {
|
|
|
204
204
|
id: firstSignal.signalId,
|
|
205
205
|
sourceEventName: "schedule.notify",
|
|
206
206
|
sourceChannel: "scheduler",
|
|
207
|
-
|
|
207
|
+
sourceContextId: "schedule-123",
|
|
208
208
|
attentionHints: firstSignal.attentionHints,
|
|
209
209
|
payload: firstSignal.contextPayload,
|
|
210
210
|
dedupeKey: `schedule:notify:schedule-123:${Date.now() - 60_000}`,
|
|
@@ -219,7 +219,7 @@ describe("recurring schedule notification dedup", () => {
|
|
|
219
219
|
id: secondSignal.signalId,
|
|
220
220
|
sourceEventName: "schedule.notify",
|
|
221
221
|
sourceChannel: "scheduler",
|
|
222
|
-
|
|
222
|
+
sourceContextId: "schedule-123",
|
|
223
223
|
attentionHints: secondSignal.attentionHints,
|
|
224
224
|
payload: secondSignal.contextPayload,
|
|
225
225
|
dedupeKey: `schedule:notify:schedule-123:${Date.now()}`,
|
|
@@ -83,9 +83,7 @@ async function createTestApp(providerKey = "github", clientId = "client-1") {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
beforeEach(() => {
|
|
86
|
-
|
|
87
|
-
initializeDb();
|
|
88
|
-
// Explicitly clear all OAuth tables to prevent cross-test state pollution.
|
|
86
|
+
// Clear OAuth tables between tests instead of full DB reset + migration.
|
|
89
87
|
// Delete in FK-dependency order: connections → apps → providers.
|
|
90
88
|
resetTestTables("oauth_connections", "oauth_apps", "oauth_providers");
|
|
91
89
|
mockDeleteSecureKeyAsync.mockClear();
|
|
@@ -484,17 +484,22 @@ describe("OAuth2 gateway transport", () => {
|
|
|
484
484
|
};
|
|
485
485
|
|
|
486
486
|
let capturedAuthUrl = "";
|
|
487
|
+
let urlReady!: () => void;
|
|
488
|
+
const urlReadyPromise = new Promise<void>((r) => {
|
|
489
|
+
urlReady = r;
|
|
490
|
+
});
|
|
487
491
|
const flowPromise = startOAuth2Flow(
|
|
488
492
|
BASE_OAUTH_CONFIG,
|
|
489
493
|
{
|
|
490
494
|
openUrl: (url) => {
|
|
491
495
|
capturedAuthUrl = url;
|
|
496
|
+
urlReady();
|
|
492
497
|
},
|
|
493
498
|
},
|
|
494
499
|
{ callbackTransport: "loopback" },
|
|
495
500
|
);
|
|
496
501
|
|
|
497
|
-
await
|
|
502
|
+
await urlReadyPromise;
|
|
498
503
|
|
|
499
504
|
const authUrl = new URL(capturedAuthUrl);
|
|
500
505
|
const redirectUri = authUrl.searchParams.get("redirect_uri")!;
|
|
@@ -9,14 +9,19 @@ const user = readFileSync(join(templatesDir, "USER.md"), "utf-8");
|
|
|
9
9
|
|
|
10
10
|
describe("onboarding template contracts", () => {
|
|
11
11
|
describe("BOOTSTRAP.md", () => {
|
|
12
|
-
test("
|
|
12
|
+
test("preserves comment line format instruction", () => {
|
|
13
|
+
expect(bootstrap).toMatch(/^_ Lines starting with _/);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("contains identity discovery prompts", () => {
|
|
13
17
|
const lower = bootstrap.toLowerCase();
|
|
14
|
-
expect(lower).toContain("
|
|
18
|
+
expect(lower).toContain("your name");
|
|
19
|
+
expect(lower).toContain("personality");
|
|
20
|
+
expect(lower).toContain("avatar");
|
|
15
21
|
});
|
|
16
22
|
|
|
17
23
|
test("infers personality organically instead of asking directly", () => {
|
|
18
24
|
const lower = bootstrap.toLowerCase();
|
|
19
|
-
// Personality step must instruct organic discovery via conversation
|
|
20
25
|
expect(lower).toContain("personality");
|
|
21
26
|
expect(lower).toContain("emerge");
|
|
22
27
|
expect(lower).toContain("vibe");
|
|
@@ -28,28 +33,17 @@ describe("onboarding template contracts", () => {
|
|
|
28
33
|
expect(lower).toContain("change it later");
|
|
29
34
|
});
|
|
30
35
|
|
|
31
|
-
test("
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
expect(
|
|
35
|
-
|
|
36
|
-
expect(
|
|
37
|
-
// The conversation sequence must include identity/naming
|
|
38
|
-
expect(lower).toContain("who am i");
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test("asks user name AFTER assistant identity is established", () => {
|
|
42
|
-
// Step 1 is the assistant's name, step 4 is asking the user's name
|
|
43
|
-
const assistantNameIdx = bootstrap.indexOf("Your name:");
|
|
44
|
-
const userNameIdx = bootstrap.indexOf("who am I talking to?");
|
|
45
|
-
expect(assistantNameIdx).toBeGreaterThan(-1);
|
|
46
|
-
expect(userNameIdx).toBeGreaterThan(-1);
|
|
47
|
-
expect(assistantNameIdx).toBeLessThan(userNameIdx);
|
|
36
|
+
test("asks about user after assistant identity", () => {
|
|
37
|
+
const nameIdx = bootstrap.indexOf("Your name");
|
|
38
|
+
const theirNameIdx = bootstrap.indexOf("Their name");
|
|
39
|
+
expect(nameIdx).toBeGreaterThan(-1);
|
|
40
|
+
expect(theirNameIdx).toBeGreaterThan(-1);
|
|
41
|
+
expect(nameIdx).toBeLessThan(theirNameIdx);
|
|
48
42
|
});
|
|
49
43
|
|
|
50
44
|
test("gathers user context: work role, hobbies, daily tools", () => {
|
|
51
45
|
const lower = bootstrap.toLowerCase();
|
|
52
|
-
expect(lower).toContain("work");
|
|
46
|
+
expect(lower).toContain("work role");
|
|
53
47
|
expect(lower).toContain("hobbies");
|
|
54
48
|
expect(lower).toContain("tools");
|
|
55
49
|
});
|
|
@@ -57,51 +51,32 @@ describe("onboarding template contracts", () => {
|
|
|
57
51
|
test("shows exactly 2 suggestions via ui_show card with relay_prompt actions", () => {
|
|
58
52
|
expect(bootstrap).toContain("ui_show");
|
|
59
53
|
expect(bootstrap).toContain("exactly 2");
|
|
60
|
-
// Must use card surface with relay_prompt action buttons
|
|
61
|
-
expect(bootstrap).toContain('surface_type: "card"');
|
|
62
54
|
expect(bootstrap).toContain("relay_prompt");
|
|
63
55
|
});
|
|
64
56
|
|
|
65
|
-
test("contains
|
|
57
|
+
test("contains wrapping-up criteria with required conditions", () => {
|
|
66
58
|
const lower = bootstrap.toLowerCase();
|
|
67
|
-
expect(lower).toContain("
|
|
68
|
-
expect(lower).toContain("
|
|
69
|
-
// Assistant name is hard-required
|
|
70
|
-
expect(lower).toContain("you have a name");
|
|
71
|
-
expect(lower).toContain("hard-required");
|
|
59
|
+
expect(lower).toContain("wrapping up");
|
|
60
|
+
expect(lower).toContain("done with onboarding");
|
|
72
61
|
expect(lower).toContain("vibe");
|
|
73
|
-
|
|
74
|
-
expect(lower).toContain("resolved");
|
|
75
|
-
expect(lower).toContain("work role");
|
|
76
|
-
expect(lower).toContain("2 suggestions from step 6");
|
|
62
|
+
expect(lower).toContain("two suggestions");
|
|
77
63
|
});
|
|
78
64
|
|
|
79
65
|
test("contains refusal policy", () => {
|
|
80
66
|
const lower = bootstrap.toLowerCase();
|
|
81
|
-
// Assistant name is hard-required, user details are best-effort
|
|
82
67
|
expect(lower).toContain("hard-required");
|
|
83
68
|
expect(lower).toContain("best-effort");
|
|
84
|
-
// Refusal is a valid resolution
|
|
85
69
|
expect(lower).toContain("declined");
|
|
86
|
-
expect(lower).toContain("
|
|
70
|
+
expect(lower).toContain("not interrogation");
|
|
87
71
|
});
|
|
88
72
|
|
|
89
73
|
test("defines resolved as provided, inferred, or declined", () => {
|
|
90
74
|
const lower = bootstrap.toLowerCase();
|
|
91
|
-
// The template must define what "resolved" means
|
|
92
75
|
expect(lower).toContain("resolved");
|
|
93
76
|
expect(lower).toContain("inferred");
|
|
94
77
|
expect(lower).toContain("declined");
|
|
95
78
|
});
|
|
96
79
|
|
|
97
|
-
// em-dash and technical jargon instructions are now hardcoded in the system
|
|
98
|
-
// prompt builder (buildSystemPrompt) rather than in the BOOTSTRAP.md template.
|
|
99
|
-
|
|
100
|
-
test("preserves comment line format instruction", () => {
|
|
101
|
-
// The template must start with the comment format explanation
|
|
102
|
-
expect(bootstrap).toMatch(/^_ Lines starting with _/);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
80
|
test("instructs saving to IDENTITY.md, USER.md, and SOUL.md via file_edit", () => {
|
|
106
81
|
expect(bootstrap).toContain("IDENTITY.md");
|
|
107
82
|
expect(bootstrap).toContain("USER.md");
|
|
@@ -118,18 +93,13 @@ describe("onboarding template contracts", () => {
|
|
|
118
93
|
expect(identity).toContain("**Emoji:**");
|
|
119
94
|
});
|
|
120
95
|
|
|
121
|
-
test("contains
|
|
122
|
-
|
|
123
|
-
expect(lower).toContain("change their emoji");
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
test("contains the style tendency field", () => {
|
|
127
|
-
expect(identity).toContain("**Style tendency:**");
|
|
96
|
+
test("contains parsed field format guidance", () => {
|
|
97
|
+
expect(identity).toContain("parsed by the app");
|
|
128
98
|
});
|
|
129
99
|
});
|
|
130
100
|
|
|
131
101
|
describe("USER.md", () => {
|
|
132
|
-
test("contains
|
|
102
|
+
test("contains profile fields", () => {
|
|
133
103
|
expect(user).toContain("Preferred name/reference:");
|
|
134
104
|
expect(user).toContain("Goals:");
|
|
135
105
|
expect(user).toContain("Locale:");
|
|
@@ -137,11 +107,5 @@ describe("onboarding template contracts", () => {
|
|
|
137
107
|
expect(user).toContain("Hobbies/fun:");
|
|
138
108
|
expect(user).toContain("Daily tools:");
|
|
139
109
|
});
|
|
140
|
-
|
|
141
|
-
test("documents resolved-field status conventions", () => {
|
|
142
|
-
const lower = user.toLowerCase();
|
|
143
|
-
expect(lower).toContain("declined_by_user");
|
|
144
|
-
expect(lower).toContain("resolved");
|
|
145
|
-
});
|
|
146
110
|
});
|
|
147
111
|
});
|
|
@@ -49,6 +49,11 @@ mock.module("../util/retry.js", () => {
|
|
|
49
49
|
return status === 429 || status >= 500;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
const RETRYABLE_NETWORK_MESSAGE_PATTERNS = [
|
|
53
|
+
/socket.*closed unexpectedly/i,
|
|
54
|
+
/socket hang up/i,
|
|
55
|
+
];
|
|
56
|
+
|
|
52
57
|
function isRetryableNetworkError(error: unknown): boolean {
|
|
53
58
|
if (!(error instanceof Error)) return false;
|
|
54
59
|
const retryableCodes = new Set([
|
|
@@ -63,6 +68,18 @@ mock.module("../util/retry.js", () => {
|
|
|
63
68
|
const causeCode = (error.cause as NodeJS.ErrnoException).code;
|
|
64
69
|
if (causeCode && retryableCodes.has(causeCode)) return true;
|
|
65
70
|
}
|
|
71
|
+
if (
|
|
72
|
+
RETRYABLE_NETWORK_MESSAGE_PATTERNS.some((p) => p.test(error.message))
|
|
73
|
+
) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
const cause = error.cause;
|
|
77
|
+
if (
|
|
78
|
+
cause instanceof Error &&
|
|
79
|
+
RETRYABLE_NETWORK_MESSAGE_PATTERNS.some((p) => p.test(cause.message))
|
|
80
|
+
) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
66
83
|
return false;
|
|
67
84
|
}
|
|
68
85
|
|
|
@@ -396,6 +413,34 @@ describe("RetryProvider — network error retries", () => {
|
|
|
396
413
|
expect(result.content[0]).toMatchObject({ type: "text", text: "ok" });
|
|
397
414
|
});
|
|
398
415
|
|
|
416
|
+
test("retries on Bun 'socket connection was closed unexpectedly' (ProviderError wrapping)", async () => {
|
|
417
|
+
const inner = makeFlaky(
|
|
418
|
+
1,
|
|
419
|
+
new ProviderError(
|
|
420
|
+
"Anthropic request failed: The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()",
|
|
421
|
+
"anthropic",
|
|
422
|
+
),
|
|
423
|
+
);
|
|
424
|
+
const provider = new RetryProvider(inner);
|
|
425
|
+
|
|
426
|
+
const result = await provider.sendMessage(MESSAGES);
|
|
427
|
+
expect(inner.calls).toBe(2);
|
|
428
|
+
expect(result.stopReason).toBe("end_turn");
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
test("retries on 'socket connection was closed unexpectedly' in error cause", async () => {
|
|
432
|
+
const cause = new Error(
|
|
433
|
+
"The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()",
|
|
434
|
+
);
|
|
435
|
+
const outer = new Error("fetch failed", { cause });
|
|
436
|
+
const inner = makeFlaky(1, outer);
|
|
437
|
+
const provider = new RetryProvider(inner);
|
|
438
|
+
|
|
439
|
+
const result = await provider.sendMessage(MESSAGES);
|
|
440
|
+
expect(inner.calls).toBe(2);
|
|
441
|
+
expect(result.content[0]).toMatchObject({ type: "text", text: "ok" });
|
|
442
|
+
});
|
|
443
|
+
|
|
399
444
|
test("does not retry on non-retryable errors", async () => {
|
|
400
445
|
const inner = makeFailing(new Error("unexpected error"));
|
|
401
446
|
const provider = new RetryProvider(inner);
|
|
@@ -419,6 +464,115 @@ describe("RetryProvider — network error retries", () => {
|
|
|
419
464
|
});
|
|
420
465
|
});
|
|
421
466
|
|
|
467
|
+
// ---------------------------------------------------------------------------
|
|
468
|
+
// RetryProvider — streaming corruption retries
|
|
469
|
+
// ---------------------------------------------------------------------------
|
|
470
|
+
|
|
471
|
+
describe("RetryProvider — streaming corruption retries", () => {
|
|
472
|
+
test("retries on 'Unexpected event order' (message_start before message_stop)", async () => {
|
|
473
|
+
const inner = makeFlaky(
|
|
474
|
+
1,
|
|
475
|
+
new ProviderError(
|
|
476
|
+
'Anthropic request failed: Unexpected event order, got message_start before receiving "message_stop"',
|
|
477
|
+
"anthropic",
|
|
478
|
+
),
|
|
479
|
+
);
|
|
480
|
+
const provider = new RetryProvider(inner);
|
|
481
|
+
|
|
482
|
+
const result = await provider.sendMessage(MESSAGES);
|
|
483
|
+
expect(result.stopReason).toBe("end_turn");
|
|
484
|
+
expect(inner.calls).toBe(2);
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
test("retries on 'Unexpected event order' (event before message_start)", async () => {
|
|
488
|
+
const inner = makeFlaky(
|
|
489
|
+
1,
|
|
490
|
+
new ProviderError(
|
|
491
|
+
'Anthropic request failed: Unexpected event order, got content_block_start before "message_start"',
|
|
492
|
+
"anthropic",
|
|
493
|
+
),
|
|
494
|
+
);
|
|
495
|
+
const provider = new RetryProvider(inner);
|
|
496
|
+
|
|
497
|
+
const result = await provider.sendMessage(MESSAGES);
|
|
498
|
+
expect(inner.calls).toBe(2);
|
|
499
|
+
expect(result.model).toBe("test-model");
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
test("retries on 'stream ended without producing'", async () => {
|
|
503
|
+
const inner = makeFlaky(
|
|
504
|
+
1,
|
|
505
|
+
new ProviderError(
|
|
506
|
+
"Anthropic request failed: stream ended without producing a Message with role=assistant",
|
|
507
|
+
"anthropic",
|
|
508
|
+
),
|
|
509
|
+
);
|
|
510
|
+
const provider = new RetryProvider(inner);
|
|
511
|
+
|
|
512
|
+
const result = await provider.sendMessage(MESSAGES);
|
|
513
|
+
expect(inner.calls).toBe(2);
|
|
514
|
+
expect(result.content).toHaveLength(1);
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
test("retries on 'request ended without sending any chunks'", async () => {
|
|
518
|
+
const inner = makeFlaky(
|
|
519
|
+
1,
|
|
520
|
+
new ProviderError(
|
|
521
|
+
"Anthropic request failed: request ended without sending any chunks",
|
|
522
|
+
"anthropic",
|
|
523
|
+
),
|
|
524
|
+
);
|
|
525
|
+
const provider = new RetryProvider(inner);
|
|
526
|
+
|
|
527
|
+
await provider.sendMessage(MESSAGES);
|
|
528
|
+
expect(inner.calls).toBe(2);
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
test("throws after exhausting retries on persistent stream corruption", async () => {
|
|
532
|
+
const inner = makeFailing(
|
|
533
|
+
new ProviderError(
|
|
534
|
+
'Anthropic request failed: Unexpected event order, got message_start before receiving "message_stop"',
|
|
535
|
+
"anthropic",
|
|
536
|
+
),
|
|
537
|
+
);
|
|
538
|
+
const provider = new RetryProvider(inner);
|
|
539
|
+
|
|
540
|
+
await expect(provider.sendMessage(MESSAGES)).rejects.toThrow(
|
|
541
|
+
"Unexpected event order",
|
|
542
|
+
);
|
|
543
|
+
expect(inner.calls).toBe(DEFAULT_MAX_RETRIES + 1);
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
test("does not retry non-stream ProviderError without status code", async () => {
|
|
547
|
+
const inner = makeFailing(
|
|
548
|
+
new ProviderError("model not found", "anthropic"),
|
|
549
|
+
);
|
|
550
|
+
const provider = new RetryProvider(inner);
|
|
551
|
+
|
|
552
|
+
await expect(provider.sendMessage(MESSAGES)).rejects.toThrow(
|
|
553
|
+
"model not found",
|
|
554
|
+
);
|
|
555
|
+
expect(inner.calls).toBe(1);
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
test("does not treat stream pattern as retryable when ProviderError has a status code", async () => {
|
|
559
|
+
// A 400 error that happens to contain "Unexpected event order" should NOT be retried
|
|
560
|
+
const inner = makeFailing(
|
|
561
|
+
new ProviderError(
|
|
562
|
+
"Unexpected event order in request payload",
|
|
563
|
+
"anthropic",
|
|
564
|
+
400,
|
|
565
|
+
),
|
|
566
|
+
);
|
|
567
|
+
const provider = new RetryProvider(inner);
|
|
568
|
+
|
|
569
|
+
await expect(provider.sendMessage(MESSAGES)).rejects.toThrow(
|
|
570
|
+
"Unexpected event order",
|
|
571
|
+
);
|
|
572
|
+
expect(inner.calls).toBe(1);
|
|
573
|
+
});
|
|
574
|
+
});
|
|
575
|
+
|
|
422
576
|
// ---------------------------------------------------------------------------
|
|
423
577
|
// RetryProvider — streaming + options passthrough
|
|
424
578
|
// ---------------------------------------------------------------------------
|
|
@@ -52,9 +52,9 @@ function makeProvidersConfig(provider: string, model: string): ProvidersConfig {
|
|
|
52
52
|
"image-generation": {
|
|
53
53
|
mode: "your-own",
|
|
54
54
|
provider: "gemini",
|
|
55
|
-
model: "gemini-
|
|
55
|
+
model: "gemini-3.1-flash-image-preview",
|
|
56
56
|
},
|
|
57
|
-
"web-search": { mode: "your-own", provider: "
|
|
57
|
+
"web-search": { mode: "your-own", provider: "inference-provider-native" },
|
|
58
58
|
},
|
|
59
59
|
};
|
|
60
60
|
}
|
|
@@ -69,9 +69,9 @@ function makeProvidersConfig(provider: string, model: string): ProvidersConfig {
|
|
|
69
69
|
"image-generation": {
|
|
70
70
|
mode: "your-own",
|
|
71
71
|
provider: "gemini",
|
|
72
|
-
model: "gemini-
|
|
72
|
+
model: "gemini-3.1-flash-image-preview",
|
|
73
73
|
},
|
|
74
|
-
"web-search": { mode: "your-own", provider: "
|
|
74
|
+
"web-search": { mode: "your-own", provider: "inference-provider-native" },
|
|
75
75
|
},
|
|
76
76
|
};
|
|
77
77
|
}
|
|
@@ -206,7 +206,7 @@ describe("managed proxy integration — credential precedence", () => {
|
|
|
206
206
|
expect(baseURL).toContain("/v1/runtime-proxy/anthropic");
|
|
207
207
|
});
|
|
208
208
|
|
|
209
|
-
test("managed gemini uses
|
|
209
|
+
test("managed gemini uses gemini proxy path", async () => {
|
|
210
210
|
enableManagedProxy();
|
|
211
211
|
mockProviderKeys = {};
|
|
212
212
|
await initializeProviders(makeProvidersConfig("anthropic", "test-model"));
|
|
@@ -216,8 +216,7 @@ describe("managed proxy integration — credential precedence", () => {
|
|
|
216
216
|
| { baseUrl?: string }
|
|
217
217
|
| undefined;
|
|
218
218
|
expect(httpOptions).toBeDefined();
|
|
219
|
-
expect(httpOptions!.baseUrl).toContain("/v1/runtime-proxy/
|
|
220
|
-
expect(httpOptions!.baseUrl).not.toContain("/v1/runtime-proxy/gemini");
|
|
219
|
+
expect(httpOptions!.baseUrl).toContain("/v1/runtime-proxy/gemini");
|
|
221
220
|
});
|
|
222
221
|
});
|
|
223
222
|
|
|
@@ -309,8 +308,8 @@ describe("managed proxy integration — ollama exclusion", () => {
|
|
|
309
308
|
});
|
|
310
309
|
|
|
311
310
|
describe("managed proxy integration — constants integrity", () => {
|
|
312
|
-
test("anthropic
|
|
313
|
-
for (const provider of ["anthropic", "gemini"
|
|
311
|
+
test("anthropic and gemini have metadata with managed=true and a proxyPath", () => {
|
|
312
|
+
for (const provider of ["anthropic", "gemini"]) {
|
|
314
313
|
const meta = MANAGED_PROVIDER_META[provider];
|
|
315
314
|
expect(meta).toBeDefined();
|
|
316
315
|
expect(meta.managed).toBe(true);
|
|
@@ -325,9 +324,9 @@ describe("managed proxy integration — constants integrity", () => {
|
|
|
325
324
|
);
|
|
326
325
|
});
|
|
327
326
|
|
|
328
|
-
test("gemini routes through
|
|
327
|
+
test("gemini routes through gemini proxy path", () => {
|
|
329
328
|
expect(MANAGED_PROVIDER_META.gemini.proxyPath).toBe(
|
|
330
|
-
"/v1/runtime-proxy/
|
|
329
|
+
"/v1/runtime-proxy/gemini",
|
|
331
330
|
);
|
|
332
331
|
});
|
|
333
332
|
|
|
@@ -25,9 +25,12 @@ describe("provider registry (ollama)", () => {
|
|
|
25
25
|
"image-generation": {
|
|
26
26
|
mode: "your-own",
|
|
27
27
|
provider: "gemini",
|
|
28
|
-
model: "gemini-
|
|
28
|
+
model: "gemini-3.1-flash-image-preview",
|
|
29
|
+
},
|
|
30
|
+
"web-search": {
|
|
31
|
+
mode: "your-own",
|
|
32
|
+
provider: "inference-provider-native",
|
|
29
33
|
},
|
|
30
|
-
"web-search": { mode: "your-own", provider: "anthropic-native" },
|
|
31
34
|
},
|
|
32
35
|
});
|
|
33
36
|
|
|
@@ -36,9 +36,9 @@ import { QdrantManager } from "../memory/qdrant-manager.js";
|
|
|
36
36
|
|
|
37
37
|
/** Short timeouts so tests complete fast but with enough headroom for CI. */
|
|
38
38
|
const FAST_TIMEOUTS = {
|
|
39
|
-
readyzPollIntervalMs:
|
|
40
|
-
readyzTimeoutMs:
|
|
41
|
-
shutdownGraceMs:
|
|
39
|
+
readyzPollIntervalMs: 5,
|
|
40
|
+
readyzTimeoutMs: 100,
|
|
41
|
+
shutdownGraceMs: 50,
|
|
42
42
|
} as const;
|
|
43
43
|
|
|
44
44
|
function placeFakeBinary(script: string): string {
|
|
@@ -248,7 +248,7 @@ describe("QdrantManager", () => {
|
|
|
248
248
|
const startPromise = mgr.start();
|
|
249
249
|
|
|
250
250
|
// Wait for spawn to happen
|
|
251
|
-
await Bun.sleep(
|
|
251
|
+
await Bun.sleep(50);
|
|
252
252
|
|
|
253
253
|
// PID file should be written
|
|
254
254
|
expect(existsSync(pidPath)).toBe(true);
|
|
@@ -277,7 +277,7 @@ describe("QdrantManager", () => {
|
|
|
277
277
|
});
|
|
278
278
|
|
|
279
279
|
const startPromise = mgr.start();
|
|
280
|
-
await Bun.sleep(
|
|
280
|
+
await Bun.sleep(50);
|
|
281
281
|
|
|
282
282
|
expect(existsSync(pidPath)).toBe(true);
|
|
283
283
|
|
|
@@ -285,8 +285,8 @@ describe("QdrantManager", () => {
|
|
|
285
285
|
await mgr.stop();
|
|
286
286
|
const stopElapsed = Date.now() - stopStart;
|
|
287
287
|
|
|
288
|
-
// Grace period is
|
|
289
|
-
expect(stopElapsed).toBeGreaterThanOrEqual(
|
|
288
|
+
// Grace period is 50ms with FAST_TIMEOUTS — should wait at least that long
|
|
289
|
+
expect(stopElapsed).toBeGreaterThanOrEqual(30);
|
|
290
290
|
expect(existsSync(pidPath)).toBe(false);
|
|
291
291
|
|
|
292
292
|
await expect(startPromise).rejects.toThrow("did not become ready");
|