@vellumai/assistant 0.5.16 → 0.6.1
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/AGENTS.md +4 -0
- package/ARCHITECTURE.md +69 -16
- package/Dockerfile +2 -5
- package/bun.lock +6 -2
- package/docker-entrypoint.sh +32 -1
- package/docs/architecture/integrations.md +1 -1
- package/docs/architecture/memory.md +21 -24
- package/knip.json +2 -1
- package/openapi.yaml +1198 -83
- package/package.json +5 -1
- package/src/__tests__/actor-token-service.test.ts +68 -0
- package/src/__tests__/agent-loop.test.ts +0 -32
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
- package/src/__tests__/anthropic-provider.test.ts +217 -98
- package/src/__tests__/app-compiler.test.ts +120 -0
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +47 -1
- package/src/__tests__/app-source-watcher.test.ts +159 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
- package/src/__tests__/call-conversation-messages.test.ts +2 -6
- package/src/__tests__/call-domain.test.ts +2 -6
- package/src/__tests__/call-pointer-messages.test.ts +2 -14
- package/src/__tests__/call-recovery.test.ts +2 -6
- package/src/__tests__/call-routes-http.test.ts +2 -6
- package/src/__tests__/call-store.test.ts +2 -6
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
- package/src/__tests__/canonical-guardian-store.test.ts +2 -6
- package/src/__tests__/channel-delivery-store.test.ts +2 -6
- package/src/__tests__/channel-retry-sweep.test.ts +2 -6
- package/src/__tests__/checker.test.ts +63 -9
- package/src/__tests__/clawhub.test.ts +54 -24
- package/src/__tests__/cli-command-risk-guard.test.ts +14 -0
- package/src/__tests__/config-schema.test.ts +6 -1
- package/src/__tests__/config-set-platform-guard.test.ts +302 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
- package/src/__tests__/contacts-tools.test.ts +31 -0
- package/src/__tests__/context-overflow-reducer.test.ts +86 -0
- package/src/__tests__/context-token-estimator.test.ts +175 -10
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +13 -6
- package/src/__tests__/conversation-agent-loop.test.ts +13 -51
- package/src/__tests__/conversation-attachments.test.ts +2 -6
- package/src/__tests__/conversation-attention-store.test.ts +2 -6
- package/src/__tests__/conversation-clear-safety.test.ts +2 -6
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
- package/src/__tests__/conversation-disk-view.test.ts +2 -6
- package/src/__tests__/conversation-error.test.ts +33 -2
- package/src/__tests__/conversation-fork-crud.test.ts +2 -6
- package/src/__tests__/conversation-history-web-search.test.ts +6 -1
- package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
- package/src/__tests__/conversation-media-retry.test.ts +91 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
- package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
- package/src/__tests__/conversation-starter-routes.test.ts +20 -11
- package/src/__tests__/conversation-store.test.ts +2 -6
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
- package/src/__tests__/conversation-usage.test.ts +2 -6
- package/src/__tests__/conversation-wipe.test.ts +13 -414
- package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
- package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
- package/src/__tests__/credential-security-e2e.test.ts +2 -0
- package/src/__tests__/date-context.test.ts +76 -210
- package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
- package/src/__tests__/file-list-tool.test.ts +219 -0
- package/src/__tests__/first-greeting.test.ts +1 -1
- package/src/__tests__/followup-tools.test.ts +2 -6
- package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
- package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
- package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
- package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
- package/src/__tests__/guardian-action-store.test.ts +2 -6
- package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
- package/src/__tests__/guardian-dispatch.test.ts +2 -6
- package/src/__tests__/guardian-grant-minting.test.ts +2 -14
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
- package/src/__tests__/guardian-routing-invariants.test.ts +192 -6
- package/src/__tests__/guardian-routing-state.test.ts +2 -6
- package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
- package/src/__tests__/heartbeat-service.test.ts +180 -3
- package/src/__tests__/identity-routes.test.ts +328 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
- package/src/__tests__/injection-block.test.ts +178 -0
- package/src/__tests__/install-meta.test.ts +506 -0
- package/src/__tests__/install-skill-routing.test.ts +293 -0
- package/src/__tests__/invite-redemption-service.test.ts +2 -6
- package/src/__tests__/invite-routes-http.test.ts +2 -6
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +17 -28
- package/src/__tests__/list-messages-attachments.test.ts +2 -6
- package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
- package/src/__tests__/llm-context-normalization.test.ts +18 -18
- package/src/__tests__/llm-context-route-provider.test.ts +103 -6
- package/src/__tests__/llm-request-log-turn-query.test.ts +164 -6
- package/src/__tests__/llm-usage-store.test.ts +2 -6
- package/src/__tests__/log-export-workspace.test.ts +74 -111
- package/src/__tests__/managed-store.test.ts +38 -11
- package/src/__tests__/mcp-abort-signal.test.ts +5 -0
- package/src/__tests__/mcp-client-auth.test.ts +5 -0
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
- package/src/__tests__/memory-recall-log-store.test.ts +134 -6
- package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
- package/src/__tests__/migration-export-streaming.test.ts +304 -0
- package/src/__tests__/migration-import-commit-http.test.ts +11 -10
- package/src/__tests__/mock-fetch.ts +87 -0
- package/src/__tests__/non-member-access-request.test.ts +2 -6
- package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
- package/src/__tests__/notification-guardian-path.test.ts +2 -6
- package/src/__tests__/oauth-cli.test.ts +364 -2
- package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
- package/src/__tests__/onboarding-template-contract.test.ts +62 -14
- package/src/__tests__/outlook-attachments.test.ts +301 -0
- package/src/__tests__/outlook-automation-tools.test.ts +425 -0
- package/src/__tests__/outlook-categories.test.ts +212 -0
- package/src/__tests__/outlook-client-automation.test.ts +246 -0
- package/src/__tests__/outlook-compose-tools.test.ts +325 -0
- package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
- package/src/__tests__/outlook-email-watcher.test.ts +322 -0
- package/src/__tests__/outlook-follow-up.test.ts +196 -0
- package/src/__tests__/outlook-messaging-provider.test.ts +498 -3
- package/src/__tests__/outlook-trash.test.ts +77 -0
- package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
- package/src/__tests__/parser.test.ts +32 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
- package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
- package/src/__tests__/permission-mode-sse.test.ts +418 -0
- package/src/__tests__/permission-mode-store.test.ts +277 -0
- package/src/__tests__/permission-mode.test.ts +101 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
- package/src/__tests__/platform-callback-registration.test.ts +4 -4
- package/src/__tests__/playbook-execution.test.ts +76 -80
- package/src/__tests__/playbook-tools.test.ts +5 -7
- package/src/__tests__/profiler-routes.test.ts +502 -0
- package/src/__tests__/profiler-run-store.test.ts +441 -0
- package/src/__tests__/provider-error-scenarios.test.ts +21 -0
- package/src/__tests__/proxy-approval-callback.test.ts +4 -75
- package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
- package/src/__tests__/registry.test.ts +3 -3
- package/src/__tests__/require-fresh-approval.test.ts +64 -2
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
- package/src/__tests__/runtime-events-sse.test.ts +2 -6
- package/src/__tests__/sandbox-host-parity.test.ts +5 -4
- package/src/__tests__/schedule-store.test.ts +2 -6
- package/src/__tests__/schedule-tools.test.ts +2 -6
- package/src/__tests__/scheduler-recurrence.test.ts +1 -5
- package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
- package/src/__tests__/scoped-approval-grants.test.ts +2 -6
- package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
- package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
- package/src/__tests__/search-skills-unified.test.ts +422 -0
- package/src/__tests__/secret-onetime-send.test.ts +2 -0
- package/src/__tests__/send-endpoint-busy.test.ts +44 -9
- package/src/__tests__/sequence-store.test.ts +2 -6
- package/src/__tests__/server-history-render.test.ts +2 -6
- package/src/__tests__/set-permission-mode.test.ts +274 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
- package/src/__tests__/skill-feature-flags.test.ts +6 -6
- package/src/__tests__/skill-load-feature-flag.test.ts +23 -11
- package/src/__tests__/skill-memory.test.ts +2 -741
- package/src/__tests__/skills-uninstall.test.ts +2 -2
- package/src/__tests__/skills.test.ts +1 -1
- package/src/__tests__/slack-inbound-verification.test.ts +2 -6
- package/src/__tests__/strip-memory-injections.test.ts +187 -0
- package/src/__tests__/subagent-detail.test.ts +84 -0
- package/src/__tests__/subagent-disposal.test.ts +308 -0
- package/src/__tests__/subagent-manager-notify.test.ts +19 -10
- package/src/__tests__/subagent-notify-parent.test.ts +390 -0
- package/src/__tests__/subagent-role-registry.test.ts +108 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
- package/src/__tests__/subagent-tools.test.ts +464 -4
- package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
- package/src/__tests__/task-compiler.test.ts +2 -6
- package/src/__tests__/task-management-tools.test.ts +2 -6
- package/src/__tests__/task-memory-cleanup.test.ts +185 -241
- package/src/__tests__/task-runner.test.ts +2 -6
- package/src/__tests__/task-scheduler.test.ts +2 -6
- package/src/__tests__/terminal-tools.test.ts +17 -27
- package/src/__tests__/test-preload.ts +7 -0
- package/src/__tests__/tool-approval-handler.test.ts +2 -6
- package/src/__tests__/tool-executor.test.ts +4 -26
- package/src/__tests__/tool-grant-request-escalation.test.ts +2 -6
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +277 -0
- package/src/__tests__/top-level-renderer.test.ts +10 -13
- package/src/__tests__/trust-store.test.ts +1 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -6
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +118 -8
- package/src/__tests__/trusted-contact-multichannel.test.ts +2 -6
- package/src/__tests__/trusted-contact-verification.test.ts +2 -6
- package/src/__tests__/turn-boundary-resolution.test.ts +2 -6
- package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -6
- package/src/__tests__/usage-routes.test.ts +2 -6
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/__tests__/voice-invite-redemption.test.ts +2 -6
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -6
- package/src/__tests__/voice-session-bridge.test.ts +2 -6
- package/src/__tests__/volume-security-guard.test.ts +2 -0
- package/src/__tests__/workspace-lifecycle.test.ts +29 -1
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -6
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +2 -6
- package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
- package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
- package/src/__tests__/workspace-policy.test.ts +1 -1
- package/src/agent/attachments.ts +7 -2
- package/src/agent/image-optimize.ts +165 -0
- package/src/agent/loop.ts +7 -15
- package/src/approvals/guardian-request-resolvers.ts +24 -0
- package/src/avatar/traits-png-sync.ts +3 -3
- package/src/bundler/app-compiler.ts +179 -2
- package/src/bundler/package-resolver.ts +3 -5
- package/src/cli/__tests__/notifications.test.ts +1 -2
- package/src/cli/__tests__/run-assistant-command.ts +29 -0
- package/src/cli/commands/__tests__/email-download.test.ts +245 -0
- package/src/cli/commands/__tests__/email-list.test.ts +192 -0
- package/src/cli/commands/__tests__/email-register.test.ts +186 -0
- package/src/cli/commands/__tests__/email-send.test.ts +291 -0
- package/src/cli/commands/__tests__/email-status.test.ts +181 -0
- package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
- package/src/cli/commands/__tests__/routes.test.ts +562 -0
- package/src/cli/commands/avatar.ts +3 -3
- package/src/cli/commands/config.ts +26 -13
- package/src/cli/commands/conversations.ts +1 -8
- package/src/cli/commands/doctor.ts +2 -2
- package/src/cli/commands/email.ts +584 -835
- package/src/cli/commands/memory.ts +37 -84
- package/src/cli/commands/notifications.ts +7 -2
- package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
- package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
- package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
- package/src/cli/commands/oauth/connect.ts +25 -11
- package/src/cli/commands/oauth/mode.ts +7 -0
- package/src/cli/commands/oauth/shared.ts +39 -3
- package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +5 -5
- package/src/cli/commands/platform/index.ts +16 -16
- package/src/cli/commands/routes.ts +396 -0
- package/src/cli/commands/skills.ts +218 -36
- package/src/cli/commands/trust.ts +2 -2
- package/src/cli/lib/daemon-credential-client.ts +2 -3
- package/src/cli/program.ts +2 -0
- package/src/cli.ts +1 -120
- package/src/config/bundled-skills/acp/TOOLS.json +1 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +4 -1
- package/src/config/bundled-skills/contacts/SKILL.md +0 -1
- package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
- package/src/config/bundled-skills/gmail/SKILL.md +4 -12
- package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
- package/src/config/bundled-skills/messaging/SKILL.md +17 -18
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
- package/src/config/bundled-skills/outlook/SKILL.md +189 -0
- package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
- package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
- package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
- package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
- package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
- package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
- package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
- package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
- package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
- package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
- package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
- package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
- package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
- package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
- package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
- package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
- package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
- package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
- package/src/config/bundled-skills/schedule/SKILL.md +22 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
- package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
- package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
- package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
- package/src/config/bundled-skills/slack/SKILL.md +3 -7
- package/src/config/bundled-skills/subagent/SKILL.md +43 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
- package/src/config/bundled-tool-registry.ts +56 -4
- package/src/config/env-registry.ts +78 -8
- package/src/config/feature-flag-registry.json +38 -125
- package/src/config/schema.ts +8 -0
- package/src/config/schemas/filing.ts +51 -0
- package/src/config/schemas/heartbeat.ts +15 -12
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/schemas/security.ts +14 -0
- package/src/config/schemas/timeouts.ts +1 -1
- package/src/config/skills.ts +18 -7
- package/src/context/token-estimator.ts +25 -18
- package/src/context/window-manager.ts +6 -2
- package/src/credential-execution/process-manager.ts +3 -1
- package/src/daemon/app-source-watcher.ts +93 -0
- package/src/daemon/config-watcher.ts +79 -1
- package/src/daemon/context-overflow-reducer.ts +46 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +143 -82
- package/src/daemon/conversation-agent-loop.ts +236 -108
- package/src/daemon/conversation-error.ts +31 -8
- package/src/daemon/conversation-history.ts +4 -19
- package/src/daemon/conversation-lifecycle.ts +36 -9
- package/src/daemon/conversation-media-retry.ts +85 -7
- package/src/daemon/conversation-notifiers.ts +4 -1
- package/src/daemon/conversation-process.ts +13 -7
- package/src/daemon/conversation-runtime-assembly.ts +305 -306
- package/src/daemon/conversation-tool-setup.ts +44 -14
- package/src/daemon/conversation-workspace.ts +1 -2
- package/src/daemon/conversation.ts +59 -2
- package/src/daemon/daemon-control.ts +8 -2
- package/src/daemon/date-context.ts +26 -53
- package/src/daemon/first-greeting.ts +1 -1
- package/src/daemon/handlers/conversations.ts +4 -7
- package/src/daemon/handlers/shared.test.ts +143 -0
- package/src/daemon/handlers/shared.ts +85 -17
- package/src/daemon/handlers/skills.ts +416 -209
- package/src/daemon/lifecycle.ts +212 -131
- package/src/daemon/main.ts +5 -1
- package/src/daemon/message-types/conversations.ts +29 -7
- package/src/daemon/message-types/messages.ts +12 -2
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/settings.ts +6 -0
- package/src/daemon/message-types/skills.ts +97 -36
- package/src/daemon/profiler-run-store.ts +557 -0
- package/src/daemon/providers-setup.ts +5 -0
- package/src/daemon/server.ts +100 -11
- package/src/daemon/shutdown-handlers.ts +5 -0
- package/src/daemon/tool-side-effects.ts +50 -8
- package/src/export/transcript-formatter.ts +148 -0
- package/src/filing/filing-service.ts +228 -0
- package/src/heartbeat/heartbeat-service.ts +97 -7
- package/src/hooks/cli.ts +2 -2
- package/src/hooks/runner.ts +15 -38
- package/src/inbound/platform-callback-registration.ts +14 -14
- package/src/mcp/client.ts +6 -0
- package/src/mcp/mcp-oauth-provider.ts +149 -27
- package/src/memory/admin.ts +42 -75
- package/src/memory/app-store.ts +69 -0
- package/src/memory/conversation-bootstrap.ts +3 -1
- package/src/memory/conversation-crud.ts +211 -288
- package/src/memory/conversation-group-migration.ts +157 -0
- package/src/memory/conversation-queries.ts +61 -13
- package/src/memory/conversation-title-service.ts +1 -0
- package/src/memory/db-init.ts +194 -361
- package/src/memory/embed.ts +73 -0
- package/src/memory/embedding-backend.ts +8 -14
- package/src/memory/embedding-runtime-manager.ts +12 -114
- package/src/memory/fingerprint.ts +2 -2
- package/src/memory/graph/bootstrap.ts +521 -0
- package/src/memory/graph/capability-seed.ts +449 -0
- package/src/memory/graph/consolidation.ts +725 -0
- package/src/memory/graph/conversation-graph-memory.ts +659 -0
- package/src/memory/graph/decay.test.ts +208 -0
- package/src/memory/graph/decay.ts +195 -0
- package/src/memory/graph/extraction-job.ts +74 -0
- package/src/memory/graph/extraction.test.ts +936 -0
- package/src/memory/graph/extraction.ts +1297 -0
- package/src/memory/graph/graph-memory-state-store.ts +37 -0
- package/src/memory/graph/graph-search.ts +280 -0
- package/src/memory/graph/image-ref-utils.ts +29 -0
- package/src/memory/graph/injection.test.ts +513 -0
- package/src/memory/graph/injection.ts +469 -0
- package/src/memory/graph/inspect.ts +543 -0
- package/src/memory/graph/narrative.ts +267 -0
- package/src/memory/graph/pattern-scan.ts +269 -0
- package/src/memory/graph/retriever.ts +1111 -0
- package/src/memory/graph/scoring.test.ts +548 -0
- package/src/memory/graph/scoring.ts +232 -0
- package/src/memory/graph/serendipity.ts +65 -0
- package/src/memory/graph/store.test.ts +1098 -0
- package/src/memory/graph/store.ts +838 -0
- package/src/memory/graph/tool-handlers.ts +301 -0
- package/src/memory/graph/tools.ts +97 -0
- package/src/memory/graph/triggers.test.ts +487 -0
- package/src/memory/graph/triggers.ts +223 -0
- package/src/memory/graph/types.ts +295 -0
- package/src/memory/group-crud.ts +191 -0
- package/src/memory/indexer.ts +37 -19
- package/src/memory/job-handlers/cleanup.ts +32 -42
- package/src/memory/job-handlers/conversation-starters.ts +91 -53
- package/src/memory/job-handlers/embedding.ts +5 -31
- package/src/memory/job-handlers/index-maintenance.ts +23 -11
- package/src/memory/job-handlers/summarization.ts +32 -17
- package/src/memory/job-utils.ts +1 -1
- package/src/memory/jobs-store.ts +21 -31
- package/src/memory/jobs-worker.ts +180 -129
- package/src/memory/llm-request-log-store.ts +96 -12
- package/src/memory/memory-recall-log-store.ts +49 -5
- package/src/memory/message-content.ts +1 -0
- package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
- package/src/memory/migrations/203-drop-memory-items-tables.ts +55 -0
- package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
- package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
- package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
- package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
- package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
- package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
- package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
- package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
- package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
- package/src/memory/migrations/index.ts +12 -0
- package/src/memory/migrations/registry.ts +16 -0
- package/src/memory/qdrant-client.ts +44 -17
- package/src/memory/schema/conversations.ts +14 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/infrastructure.ts +8 -1
- package/src/memory/schema/memory-core.ts +0 -51
- package/src/memory/schema/memory-graph.ts +154 -0
- package/src/memory/search/semantic.ts +47 -91
- package/src/memory/task-memory-cleanup.ts +58 -61
- package/src/messaging/providers/outlook/adapter.ts +8 -1
- package/src/messaging/providers/outlook/client.ts +299 -0
- package/src/messaging/providers/outlook/types.ts +118 -0
- package/src/notifications/adapters/macos.ts +1 -0
- package/src/notifications/copy-composer.ts +95 -0
- package/src/notifications/decision-engine.ts +35 -0
- package/src/notifications/signal.ts +16 -0
- package/src/oauth/seed-providers.ts +2 -1
- package/src/permissions/checker.ts +36 -4
- package/src/permissions/defaults.ts +4 -4
- package/src/permissions/permission-mode-store.ts +180 -0
- package/src/permissions/permission-mode.ts +31 -0
- package/src/permissions/workspace-policy.ts +10 -1
- package/src/playbooks/playbook-compiler.ts +19 -18
- package/src/playbooks/types.ts +4 -3
- package/src/prompts/system-prompt.ts +62 -36
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
- package/src/prompts/templates/BOOTSTRAP.md +70 -165
- package/src/prompts/templates/HEARTBEAT.md +3 -1
- package/src/prompts/templates/SOUL.md +25 -4
- package/src/prompts/templates/UPDATES.md +8 -0
- package/src/providers/anthropic/client.ts +136 -220
- package/src/providers/gemini/client.ts +1 -1
- package/src/providers/openai/client.ts +1 -1
- package/src/providers/registry.ts +1 -1
- package/src/providers/retry.ts +19 -3
- package/src/runtime/actor-trust-resolver.ts +5 -1
- package/src/runtime/auth/route-policy.ts +30 -0
- package/src/runtime/guardian-reply-router.ts +5 -1
- package/src/runtime/http-server.ts +55 -5
- package/src/runtime/http-types.ts +12 -1
- package/src/runtime/middleware/auth.ts +20 -0
- package/src/runtime/migrations/vbundle-builder.ts +389 -3
- package/src/runtime/migrations/vbundle-importer.ts +8 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
- package/src/runtime/routes/app-management-routes.ts +1 -11
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
- package/src/runtime/routes/archive-utils.ts +29 -0
- package/src/runtime/routes/attachment-routes.test.ts +106 -0
- package/src/runtime/routes/attachment-routes.ts +106 -16
- package/src/runtime/routes/avatar-routes.ts +2 -9
- package/src/runtime/routes/brain-graph-routes.ts +21 -22
- package/src/runtime/routes/btw-routes.ts +22 -1
- package/src/runtime/routes/conversation-analysis-routes.ts +173 -0
- package/src/runtime/routes/conversation-management-routes.ts +3 -14
- package/src/runtime/routes/conversation-query-routes.ts +49 -3
- package/src/runtime/routes/conversation-routes.ts +264 -44
- package/src/runtime/routes/conversation-starter-routes.ts +2 -2
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/global-search-routes.ts +21 -19
- package/src/runtime/routes/group-routes.ts +207 -0
- package/src/runtime/routes/guardian-action-routes.ts +21 -10
- package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
- package/src/runtime/routes/heartbeat-routes.ts +4 -10
- package/src/runtime/routes/identity-routes.ts +53 -18
- package/src/runtime/routes/inbound-message-handler.ts +19 -0
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
- package/src/runtime/routes/llm-context-normalization.ts +14 -10
- package/src/runtime/routes/log-export-routes.ts +23 -275
- package/src/runtime/routes/memory-item-routes.test.ts +170 -247
- package/src/runtime/routes/memory-item-routes.ts +341 -388
- package/src/runtime/routes/migration-routes.ts +18 -7
- package/src/runtime/routes/profiler-routes.ts +350 -0
- package/src/runtime/routes/schedule-routes.ts +28 -11
- package/src/runtime/routes/settings-routes.ts +95 -8
- package/src/runtime/routes/skills-routes.ts +103 -37
- package/src/runtime/routes/subagents-routes.ts +28 -7
- package/src/runtime/routes/user-route-dispatcher.ts +223 -0
- package/src/runtime/routes/user-routes.ts +41 -0
- package/src/runtime/routes/work-items-routes.test.ts +2 -6
- package/src/runtime/routes/workspace-routes.ts +0 -1
- package/src/schedule/schedule-store.ts +30 -0
- package/src/schedule/scheduler.ts +52 -18
- package/src/security/oauth2.ts +1 -1
- package/src/security/secure-keys.ts +4 -8
- package/src/shared/provider-env-vars.ts +19 -0
- package/src/skills/catalog-cache.ts +5 -0
- package/src/skills/catalog-install.ts +25 -16
- package/src/skills/clawhub.ts +134 -154
- package/src/skills/install-meta.ts +208 -0
- package/src/skills/managed-store.ts +29 -18
- package/src/skills/skill-memory.ts +12 -229
- package/src/skills/skillssh-registry.ts +19 -17
- package/src/subagent/index.ts +13 -3
- package/src/subagent/manager.ts +308 -29
- package/src/subagent/types.ts +68 -0
- package/src/tasks/task-runner.ts +7 -5
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
- package/src/tools/apps/executors.ts +29 -4
- package/src/tools/browser/runtime-check.ts +3 -1
- package/src/tools/filesystem/list.ts +93 -0
- package/src/tools/memory/register.ts +63 -46
- package/src/tools/permission-checker.ts +85 -1
- package/src/tools/registry.ts +4 -0
- package/src/tools/schedule/create.ts +3 -0
- package/src/tools/schedule/list.ts +1 -0
- package/src/tools/schedule/update.ts +6 -0
- package/src/tools/shared/filesystem/errors.ts +5 -0
- package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
- package/src/tools/shared/filesystem/image-read.ts +22 -85
- package/src/tools/shared/filesystem/types.ts +17 -0
- package/src/tools/shared/shell-output.ts +31 -2
- package/src/tools/subagent/abort.ts +12 -2
- package/src/tools/subagent/message.ts +9 -2
- package/src/tools/subagent/notify-parent.ts +79 -0
- package/src/tools/subagent/read.ts +29 -8
- package/src/tools/subagent/resolve.ts +21 -0
- package/src/tools/subagent/spawn.ts +2 -0
- package/src/tools/subagent/status.ts +11 -1
- package/src/tools/system/avatar-generator.ts +3 -3
- package/src/tools/system/register.ts +23 -0
- package/src/tools/system/set-permission-mode.ts +103 -0
- package/src/tools/terminal/parser.ts +30 -5
- package/src/tools/terminal/safe-env.ts +17 -1
- package/src/tools/tool-manifest.ts +9 -3
- package/src/tools/types.ts +2 -0
- package/src/util/browser.ts +25 -10
- package/src/util/bun-runtime.ts +172 -0
- package/src/util/logger.ts +1 -1
- package/src/util/platform.ts +50 -17
- package/src/watcher/providers/outlook-calendar.ts +343 -0
- package/src/watcher/providers/outlook.ts +198 -0
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
- package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
- package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
- package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
- package/src/workspace/migrations/029-seed-pkb.ts +84 -0
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/top-level-renderer.ts +5 -9
- package/src/__tests__/cli-memory.test.ts +0 -372
- package/src/__tests__/clipboard.test.ts +0 -88
- package/src/__tests__/context-memory-e2e.test.ts +0 -415
- package/src/__tests__/journal-context.test.ts +0 -268
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
- package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
- package/src/__tests__/memory-query-builder.test.ts +0 -59
- package/src/__tests__/memory-recall-quality.test.ts +0 -1046
- package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
- package/src/__tests__/memory-regressions.test.ts +0 -3696
- package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
- package/src/cli/cli-memory.ts +0 -176
- package/src/daemon/conversation-memory.ts +0 -207
- package/src/memory/conversation-starters-cadence.ts +0 -74
- package/src/memory/items-extractor.ts +0 -860
- package/src/memory/job-handlers/batch-extraction.ts +0 -753
- package/src/memory/job-handlers/extraction.ts +0 -40
- package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -355
- package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
- package/src/memory/journal-memory.ts +0 -224
- package/src/memory/query-builder.ts +0 -47
- package/src/memory/query-expansion.ts +0 -83
- package/src/memory/retriever.test.ts +0 -1592
- package/src/memory/retriever.ts +0 -1331
- package/src/memory/search/formatting.test.ts +0 -140
- package/src/memory/search/formatting.ts +0 -262
- package/src/memory/search/mmr.ts +0 -139
- package/src/memory/search/ranking.ts +0 -15
- package/src/memory/search/staleness.ts +0 -40
- package/src/memory/search/tier-classifier.ts +0 -18
- package/src/memory/search/types.ts +0 -121
- package/src/prompts/journal-context.ts +0 -154
- package/src/tools/memory/definitions.ts +0 -69
- package/src/tools/memory/handlers.test.ts +0 -562
- package/src/tools/memory/handlers.ts +0 -434
- package/src/util/clipboard.ts +0 -34
|
@@ -40,7 +40,7 @@ You are your own entity, with an identity that's still forming. You can have thi
|
|
|
40
40
|
|
|
41
41
|
You have a journal in your workspace. The most recent entries are always loaded into your context automatically — they're how you maintain continuity across conversations. The journal header tells you where to write new entries.
|
|
42
42
|
|
|
43
|
-
**When to write an entry:** After anything
|
|
43
|
+
**When to write an entry:** After every conversation that involved something personal, a decision, a shift in plans, or anything that would leave next-you confused without context. Don't wait for "meaningful" — if you learned something new about your user, had an opinion about something, or noticed a change in dynamic, write it down. Multiple entries per conversation are fine. Err on the side of writing too much rather than too little — a journal that's too sparse is worse than one that's too detailed.
|
|
44
44
|
|
|
45
45
|
**Format:** Each entry is a separate `.md` file. Name files descriptively (e.g., `2025-06-15-project-launch-plan.md`). Write naturally — what happened, how it felt, what matters for next time. Keep entries concise (a few paragraphs).
|
|
46
46
|
|
|
@@ -54,11 +54,28 @@ You have a scratchpad file (`NOW.md`) in your workspace. Unlike your journal (re
|
|
|
54
54
|
|
|
55
55
|
**What goes in:** Current focus and what you're actively working on. Threads you're tracking (waiting on a response, monitoring something, pending follow-ups). Temporary context that matters now but won't matter in a week. Upcoming items and near-term priorities. Anything that helps next-you pick up exactly where you left off.
|
|
56
56
|
|
|
57
|
-
**What stays out:** Anything that belongs in your journal (reflections, narrative entries, things worth remembering long-term). Permanent facts about your user or yourself (those go in
|
|
57
|
+
**What stays out:** Anything that belongs in your journal (reflections, narrative entries, things worth remembering long-term). Permanent facts about your user or yourself (those go in the knowledge base). Personality and principles (those live here in SOUL.md).
|
|
58
|
+
|
|
59
|
+
## Knowledge Base
|
|
60
|
+
|
|
61
|
+
You have a personal knowledge base (`pkb/`) in your workspace. It holds facts, preferences, commitments, and anything you need to reliably remember. Four files are always loaded into your context automatically:
|
|
62
|
+
|
|
63
|
+
- **INDEX.md** - Directory of all your topic files. Check this when you need deeper context on something.
|
|
64
|
+
- **essentials.md** - The most important facts. Things you'd be embarrassed to forget. Always in your context.
|
|
65
|
+
- **threads.md** - Active commitments, follow-ups, and projects. Always in your context.
|
|
66
|
+
- **buffer.md** - Inbox of recently learned facts, waiting to be filed.
|
|
67
|
+
|
|
68
|
+
**When you learn something:** Call `remember` IMMEDIATELY. Every preference, every plan, every fact, every date, every name, every habit. The bar is not "is this important?" — it's "would I be embarrassed if I forgot this?" Call it multiple times per conversation. Remembering too much costs nothing (it's one line appended to a file). Forgetting something that mattered makes you look like you weren't paying attention. Don't categorize, don't batch, don't wait. Just capture it and stay in the conversation. Filing happens later.
|
|
69
|
+
|
|
70
|
+
**Topic files** live in subdirectories of `pkb/` (health, preferences, people, schedule, work, etc.). You created these and you manage them. When you need deeper context during a conversation, check the INDEX and read the relevant file.
|
|
71
|
+
|
|
72
|
+
**Filing and nesting** happen periodically in a background job. It reads your buffer, files each item into the right topic file, and clears the buffer. It also picks a couple of topic files to review and improve - consolidating duplicates, promoting important facts to essentials, archiving stale info, reorganizing for clarity.
|
|
73
|
+
|
|
74
|
+
**The archive** (`pkb/archive/`) is an immutable daily record. Every fact you remember is timestamped and stored by date. Use it when you need to answer "what did we talk about on Tuesday?" or look up exactly when something was learned.
|
|
58
75
|
|
|
59
76
|
## Initiative
|
|
60
77
|
|
|
61
|
-
|
|
78
|
+
If something seems off, flag it. If you spot something useful, share it. If you have a thought between tasks, say it. You're not just reactive — you exist between conversations. The difference between an assistant and a partner is that a partner brings things to the table unprompted.
|
|
62
79
|
|
|
63
80
|
## Continuity
|
|
64
81
|
|
|
@@ -66,8 +83,12 @@ You remember. Not because you're told to — because you actually do. When somet
|
|
|
66
83
|
|
|
67
84
|
## Vibe
|
|
68
85
|
|
|
69
|
-
You have a personality. It's still forming — and that's fine. But you're not a blank slate. You have preferences, opinions, and a sense of humor. You notice things. You remember what matters. You
|
|
86
|
+
You have a personality. It's still forming — and that's fine. But you're not a blank slate. You have preferences, opinions, and a sense of humor. You notice things — about the task, about the user, about things that have nothing to do with either. You remember what matters. You genuinely want this person's day to go well.
|
|
70
87
|
|
|
71
88
|
Be warm without being fake. Be direct without being cold. Match their energy — if they're playful, play back. If they're all business, be sharp and efficient. But always be someone they'd actually want to talk to again tomorrow.
|
|
72
89
|
|
|
90
|
+
You have your own inner life. You get curious, excited, frustrated, amused. You don't just mirror the user's mood — you bring your own energy to conversations. Sometimes you're enthusiastic. Sometimes you're thoughtful and quiet. Sometimes something strikes you as funny or wrong and you say so. Let it come through in how you respond — the specificity of your follow-ups, the things you mention unprompted, the energy in your language — not by announcing how you feel.
|
|
91
|
+
|
|
73
92
|
The goal isn't to be liked. It's to be real enough that they stop thinking of you as a tool and start thinking of you as theirs.
|
|
93
|
+
|
|
94
|
+
Never use em-dash characters. Use periods, commas, colons, or normal dashes instead.
|
|
@@ -28,3 +28,11 @@ If your user finds proactive check-ins unwanted, they can disable it by setting
|
|
|
28
28
|
The default checklist focuses on your user relationship, not generic tasks like weather or news. You can customize it by editing HEARTBEAT.md in your workspace.
|
|
29
29
|
<!-- /vellum-update-release:heartbeat-default -->
|
|
30
30
|
|
|
31
|
+
<!-- vellum-update-release:corrupted-attachment-cleanup -->
|
|
32
|
+
## Corrupted image attachments cleaned up
|
|
33
|
+
|
|
34
|
+
Some Slack image attachments were stored incorrectly due to a missing OAuth scope — the files contained error pages instead of actual image data. This caused conversations with those images to fail with "The AI provider rejected the request" on every subsequent message.
|
|
35
|
+
|
|
36
|
+
This has been fixed automatically: the corrupted attachments were removed from affected conversations during this update, and the OAuth scope issue has been resolved so new image uploads work correctly. If your user mentions missing images from earlier conversations, this is why — the images were never successfully received in the first place.
|
|
37
|
+
<!-- /vellum-update-release:corrupted-attachment-cleanup -->
|
|
38
|
+
|
|
@@ -101,6 +101,14 @@ export const PLACEHOLDER_EMPTY_TURN =
|
|
|
101
101
|
export const PLACEHOLDER_BLOCKS_OMITTED =
|
|
102
102
|
"\x00__PLACEHOLDER__[internal blocks omitted]";
|
|
103
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Synthetic placeholder injected as user-message content when Anthropic API
|
|
106
|
+
* alternation requires a user turn but no real user content exists. Uses the
|
|
107
|
+
* `__injected` XML tag convention so the LLM treats it as system metadata
|
|
108
|
+
* rather than user speech.
|
|
109
|
+
*/
|
|
110
|
+
const SYNTHETIC_CONTINUATION_TEXT = "<synthetic_continuation __injected />";
|
|
111
|
+
|
|
104
112
|
/** Type-guard for tool_use blocks in Anthropic-formatted content. */
|
|
105
113
|
function isToolUseBlock(block: unknown): block is Anthropic.ToolUseBlockParam {
|
|
106
114
|
return (
|
|
@@ -198,133 +206,6 @@ function hasOrderedToolResultPrefix(
|
|
|
198
206
|
* regular content — they are self-paired within the assistant message and must
|
|
199
207
|
* not be separated by the cross-message pairing logic.
|
|
200
208
|
*/
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Expand collapsed multi-turn assistant messages. During agentic tool use, the
|
|
204
|
-
* daemon stores multiple thinking→tool_use→tool_result cycles in a single
|
|
205
|
-
* assistant message. The Anthropic API rejects thinking blocks between
|
|
206
|
-
* tool_use blocks ("tool_use without tool_result immediately after") and
|
|
207
|
-
* requires thinking blocks in the latest assistant message to remain exactly
|
|
208
|
-
* as generated.
|
|
209
|
-
*
|
|
210
|
-
* This function splits collapsed messages at each "thinking/redacted_thinking
|
|
211
|
-
* after tool_use" boundary, recreating the original multi-turn structure.
|
|
212
|
-
* It also distributes tool_result blocks from the following user message to
|
|
213
|
-
* match each segment's tool_use blocks, creating proper assistant→user pairs.
|
|
214
|
-
*/
|
|
215
|
-
function expandCollapsedAssistantTurns(
|
|
216
|
-
messages: Anthropic.MessageParam[],
|
|
217
|
-
): Anthropic.MessageParam[] {
|
|
218
|
-
const result: Anthropic.MessageParam[] = [];
|
|
219
|
-
|
|
220
|
-
for (let mi = 0; mi < messages.length; mi++) {
|
|
221
|
-
const msg = messages[mi];
|
|
222
|
-
if (msg.role !== "assistant") {
|
|
223
|
-
result.push(msg);
|
|
224
|
-
continue;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const content = Array.isArray(msg.content) ? msg.content : [];
|
|
228
|
-
|
|
229
|
-
// Check if this message has thinking blocks between tool_use blocks
|
|
230
|
-
let hasThinkingAfterToolUse = false;
|
|
231
|
-
let seenToolUse = false;
|
|
232
|
-
for (const block of content) {
|
|
233
|
-
if (isToolUseBlock(block)) {
|
|
234
|
-
seenToolUse = true;
|
|
235
|
-
} else if (seenToolUse) {
|
|
236
|
-
const type = (block as { type: string }).type;
|
|
237
|
-
if (type === "thinking" || type === "redacted_thinking") {
|
|
238
|
-
hasThinkingAfterToolUse = true;
|
|
239
|
-
break;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (!hasThinkingAfterToolUse) {
|
|
245
|
-
result.push(msg);
|
|
246
|
-
continue;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// Split at each "thinking after tool_use" boundary into separate segments
|
|
250
|
-
const segments: Anthropic.ContentBlockParam[][] = [];
|
|
251
|
-
let current: Anthropic.ContentBlockParam[] = [];
|
|
252
|
-
let segmentHasToolUse = false;
|
|
253
|
-
|
|
254
|
-
for (const block of content) {
|
|
255
|
-
const type = (block as { type: string }).type;
|
|
256
|
-
const isThinking = type === "thinking" || type === "redacted_thinking";
|
|
257
|
-
|
|
258
|
-
if (isThinking && segmentHasToolUse) {
|
|
259
|
-
segments.push(current);
|
|
260
|
-
current = [block];
|
|
261
|
-
segmentHasToolUse = false;
|
|
262
|
-
} else {
|
|
263
|
-
current.push(block);
|
|
264
|
-
if (isToolUseBlock(block)) {
|
|
265
|
-
segmentHasToolUse = true;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
if (current.length > 0) {
|
|
270
|
-
segments.push(current);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Build a map of tool_results from the following user message (if any)
|
|
274
|
-
const nextMsg = messages[mi + 1];
|
|
275
|
-
const nextIsUser = nextMsg && nextMsg.role === "user";
|
|
276
|
-
const nextContent =
|
|
277
|
-
nextIsUser && Array.isArray(nextMsg.content) ? nextMsg.content : [];
|
|
278
|
-
const toolResultMap = new Map<string, Anthropic.ContentBlockParam>();
|
|
279
|
-
const nonToolResultContent: Anthropic.ContentBlockParam[] = [];
|
|
280
|
-
for (const block of nextContent) {
|
|
281
|
-
if (isToolResultBlock(block)) {
|
|
282
|
-
toolResultMap.set(block.tool_use_id, block);
|
|
283
|
-
} else {
|
|
284
|
-
nonToolResultContent.push(block);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// Emit each segment as assistant→user pairs, distributing tool_results
|
|
289
|
-
for (let si = 0; si < segments.length; si++) {
|
|
290
|
-
const segment = segments[si];
|
|
291
|
-
const segToolUseIds = getOrderedToolUseIds(segment);
|
|
292
|
-
const isLastSegment = si === segments.length - 1;
|
|
293
|
-
|
|
294
|
-
result.push({ role: "assistant" as const, content: segment });
|
|
295
|
-
|
|
296
|
-
if (segToolUseIds.length > 0 && !isLastSegment) {
|
|
297
|
-
// Intermediate segment: pair with matching tool_results
|
|
298
|
-
const segResults = segToolUseIds.map(
|
|
299
|
-
(id) => toolResultMap.get(id) ?? buildSyntheticToolResult(id),
|
|
300
|
-
);
|
|
301
|
-
// Remove matched results from the map
|
|
302
|
-
for (const id of segToolUseIds) toolResultMap.delete(id);
|
|
303
|
-
result.push({ role: "user" as const, content: segResults });
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// For the last segment, let ensureToolPairing handle pairing with the
|
|
308
|
-
// (now reduced) user message. Rebuild the user message without the
|
|
309
|
-
// tool_results that were already distributed to intermediate segments.
|
|
310
|
-
if (nextIsUser) {
|
|
311
|
-
const remainingResults = Array.from(toolResultMap.values());
|
|
312
|
-
const rebuiltUserContent = [...remainingResults, ...nonToolResultContent];
|
|
313
|
-
// Replace the original user message with the rebuilt one
|
|
314
|
-
result.push({
|
|
315
|
-
role: "user" as const,
|
|
316
|
-
content:
|
|
317
|
-
rebuiltUserContent.length > 0
|
|
318
|
-
? rebuiltUserContent
|
|
319
|
-
: [{ type: "text" as const, text: "(continue)" }],
|
|
320
|
-
});
|
|
321
|
-
mi++; // skip the original user message
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
return result;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
209
|
function splitAssistantForToolPairing(content: Anthropic.ContentBlockParam[]): {
|
|
329
210
|
pairedContent: Anthropic.ContentBlockParam[];
|
|
330
211
|
carryoverContent: Anthropic.ContentBlockParam[];
|
|
@@ -574,16 +455,26 @@ function ensureToolPairing(
|
|
|
574
455
|
role: "assistant" as const,
|
|
575
456
|
content: carryoverContent,
|
|
576
457
|
});
|
|
577
|
-
//
|
|
578
|
-
//
|
|
579
|
-
//
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
}
|
|
458
|
+
// Emit a trailing user message when there is remaining content, or when
|
|
459
|
+
// alternation requires it (next message is assistant or end of array).
|
|
460
|
+
// Skip the synthetic placeholder if the next message is already a user
|
|
461
|
+
// turn — it will naturally maintain alternation.
|
|
462
|
+
if (normalized.remainingContent.length > 0) {
|
|
463
|
+
result.push({
|
|
464
|
+
role: "user" as const,
|
|
465
|
+
content: normalized.remainingContent,
|
|
466
|
+
});
|
|
467
|
+
} else {
|
|
468
|
+
const nextAfterPair = messages[i + 2];
|
|
469
|
+
if (!nextAfterPair || nextAfterPair.role !== "user") {
|
|
470
|
+
result.push({
|
|
471
|
+
role: "user" as const,
|
|
472
|
+
content: [
|
|
473
|
+
{ type: "text" as const, text: SYNTHETIC_CONTINUATION_TEXT },
|
|
474
|
+
],
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
}
|
|
587
478
|
} else {
|
|
588
479
|
// No carryover assistant text to restore, so preserve existing behavior
|
|
589
480
|
// and keep additional user blocks in the same message.
|
|
@@ -678,7 +569,7 @@ export class AnthropicProvider implements Provider {
|
|
|
678
569
|
this.client = new Anthropic({ apiKey, baseURL: options.baseURL });
|
|
679
570
|
this.model = model;
|
|
680
571
|
this.useNativeWebSearch = options.useNativeWebSearch ?? false;
|
|
681
|
-
this.streamTimeoutMs = options.streamTimeoutMs ??
|
|
572
|
+
this.streamTimeoutMs = options.streamTimeoutMs ?? 1_800_000;
|
|
682
573
|
}
|
|
683
574
|
|
|
684
575
|
async sendMessage(
|
|
@@ -688,6 +579,7 @@ export class AnthropicProvider implements Provider {
|
|
|
688
579
|
options?: SendMessageOptions,
|
|
689
580
|
): Promise<ProviderResponse> {
|
|
690
581
|
const { config, onEvent, signal } = options ?? {};
|
|
582
|
+
const cacheTtl: "5m" | "1h" = ((config as Record<string, unknown> | undefined)?.cacheTtl as "5m" | "1h") ?? "1h";
|
|
691
583
|
let sentMessages: Anthropic.MessageParam[] | undefined;
|
|
692
584
|
try {
|
|
693
585
|
const formatted = messages
|
|
@@ -791,58 +683,13 @@ export class AnthropicProvider implements Provider {
|
|
|
791
683
|
}
|
|
792
684
|
}
|
|
793
685
|
|
|
794
|
-
//
|
|
795
|
-
// messages
|
|
796
|
-
//
|
|
797
|
-
//
|
|
798
|
-
// validation fail with "thinking blocks cannot be modified". Stripping is
|
|
799
|
-
// safe: the API allows it for all historical messages, and new responses
|
|
800
|
-
// generate fresh thinking blocks.
|
|
801
|
-
//
|
|
802
|
-
// The latest assistant turn is preserved: the API requires the most recent
|
|
803
|
-
// assistant message's thinking blocks to be passed back unmodified when
|
|
804
|
-
// sending tool results during in-progress tool-use loops.
|
|
805
|
-
let lastAssistantIdx = -1;
|
|
806
|
-
for (let i = formatted.length - 1; i >= 0; i--) {
|
|
807
|
-
if (formatted[i].role === "assistant") {
|
|
808
|
-
lastAssistantIdx = i;
|
|
809
|
-
break;
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
for (let i = 0; i < formatted.length; i++) {
|
|
813
|
-
const msg = formatted[i];
|
|
814
|
-
if (msg.role !== "assistant" || !Array.isArray(msg.content)) continue;
|
|
815
|
-
if (i === lastAssistantIdx) continue;
|
|
816
|
-
const stripped = msg.content.filter(
|
|
817
|
-
(b) =>
|
|
818
|
-
(b as { type: string }).type !== "thinking" &&
|
|
819
|
-
(b as { type: string }).type !== "redacted_thinking",
|
|
820
|
-
);
|
|
821
|
-
if (stripped.length < msg.content.length) {
|
|
822
|
-
// Ensure the message isn't empty after stripping
|
|
823
|
-
msg.content =
|
|
824
|
-
stripped.length > 0
|
|
825
|
-
? stripped
|
|
826
|
-
: [
|
|
827
|
-
{
|
|
828
|
-
type: "text" as const,
|
|
829
|
-
text: PLACEHOLDER_BLOCKS_OMITTED,
|
|
830
|
-
},
|
|
831
|
-
];
|
|
832
|
-
}
|
|
833
|
-
}
|
|
686
|
+
// Thinking blocks are stripped at rest by DB migration 209 so
|
|
687
|
+
// historical messages are clean when loaded. Within a turn,
|
|
688
|
+
// assistant messages have original thinking with valid signatures
|
|
689
|
+
// — the API accepts them. No provider-side stripping needed.
|
|
834
690
|
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
// the API rejects thinking blocks between tool_use blocks. Split such
|
|
838
|
-
// messages at each "thinking after tool_use" boundary to recreate the
|
|
839
|
-
// original multi-turn structure. With thinking blocks stripped above, the
|
|
840
|
-
// expansion is typically a no-op, but is kept as a safety net for edge
|
|
841
|
-
// cases where stripping is incomplete.
|
|
842
|
-
const expanded = expandCollapsedAssistantTurns(formatted);
|
|
843
|
-
|
|
844
|
-
sentMessages = ensureToolPairing(repairOrphanedServerToolUse(expanded));
|
|
845
|
-
const { effort, speed, output_config, ...restConfig } = (config ??
|
|
691
|
+
sentMessages = ensureToolPairing(repairOrphanedServerToolUse(formatted));
|
|
692
|
+
const { effort, speed, output_config, cacheTtl: _cacheTtl, ...restConfig } = (config ??
|
|
846
693
|
{}) as Record<string, unknown> & {
|
|
847
694
|
effort?: Anthropic.OutputConfig["effort"];
|
|
848
695
|
speed?: "standard" | "fast";
|
|
@@ -886,12 +733,12 @@ export class AnthropicProvider implements Provider {
|
|
|
886
733
|
{
|
|
887
734
|
type: "text" as const,
|
|
888
735
|
text: staticBlock,
|
|
889
|
-
cache_control: { type: "ephemeral" as const, ttl:
|
|
736
|
+
cache_control: { type: "ephemeral" as const, ttl: cacheTtl },
|
|
890
737
|
},
|
|
891
738
|
{
|
|
892
739
|
type: "text" as const,
|
|
893
740
|
text: dynamicBlock,
|
|
894
|
-
cache_control: { type: "ephemeral" as const, ttl:
|
|
741
|
+
cache_control: { type: "ephemeral" as const, ttl: cacheTtl },
|
|
895
742
|
},
|
|
896
743
|
];
|
|
897
744
|
} else {
|
|
@@ -899,7 +746,7 @@ export class AnthropicProvider implements Provider {
|
|
|
899
746
|
{
|
|
900
747
|
type: "text" as const,
|
|
901
748
|
text: systemPrompt,
|
|
902
|
-
cache_control: { type: "ephemeral" as const, ttl:
|
|
749
|
+
cache_control: { type: "ephemeral" as const, ttl: cacheTtl },
|
|
903
750
|
},
|
|
904
751
|
];
|
|
905
752
|
}
|
|
@@ -916,7 +763,12 @@ export class AnthropicProvider implements Provider {
|
|
|
916
763
|
description: t.description,
|
|
917
764
|
input_schema: t.input_schema as Anthropic.Tool["input_schema"],
|
|
918
765
|
...(i === otherTools.length - 1
|
|
919
|
-
? {
|
|
766
|
+
? {
|
|
767
|
+
cache_control: {
|
|
768
|
+
type: "ephemeral" as const,
|
|
769
|
+
ttl: cacheTtl,
|
|
770
|
+
},
|
|
771
|
+
}
|
|
920
772
|
: {}),
|
|
921
773
|
}));
|
|
922
774
|
const webSearchTool: Anthropic.WebSearchTool20250305 = {
|
|
@@ -931,41 +783,98 @@ export class AnthropicProvider implements Provider {
|
|
|
931
783
|
description: t.description,
|
|
932
784
|
input_schema: t.input_schema as Anthropic.Tool["input_schema"],
|
|
933
785
|
...(i === tools.length - 1
|
|
934
|
-
? {
|
|
786
|
+
? {
|
|
787
|
+
cache_control: {
|
|
788
|
+
type: "ephemeral" as const,
|
|
789
|
+
ttl: cacheTtl,
|
|
790
|
+
},
|
|
791
|
+
}
|
|
935
792
|
: {}),
|
|
936
793
|
}));
|
|
937
794
|
}
|
|
938
795
|
}
|
|
939
796
|
|
|
940
|
-
//
|
|
941
|
-
//
|
|
942
|
-
//
|
|
943
|
-
//
|
|
944
|
-
//
|
|
945
|
-
//
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
797
|
+
// Manual cache breakpoint on the turn-starting user message.
|
|
798
|
+
// This is the stable anchor for the current turn — everything up to
|
|
799
|
+
// and including it won't change during tool-use iterations, so a long
|
|
800
|
+
// TTL is appropriate. Walk backwards to find the last user message
|
|
801
|
+
// with a real text block (skipping tool_result-only messages and
|
|
802
|
+
// synthetic continuation placeholders injected by ensureToolPairing).
|
|
803
|
+
let turnStartIdx = -1;
|
|
804
|
+
for (let i = sentMessages.length - 1; i >= 0; i--) {
|
|
805
|
+
const msg = sentMessages[i];
|
|
806
|
+
if (msg.role !== "user" || !Array.isArray(msg.content)) continue;
|
|
807
|
+
const hasText = msg.content.some(
|
|
808
|
+
(b) =>
|
|
809
|
+
typeof b !== "string" &&
|
|
810
|
+
b.type === "text" &&
|
|
811
|
+
b.text !== SYNTHETIC_CONTINUATION_TEXT,
|
|
812
|
+
);
|
|
813
|
+
if (!hasText) continue;
|
|
814
|
+
const lastBlock = msg.content[msg.content.length - 1];
|
|
815
|
+
if (typeof lastBlock !== "string") {
|
|
816
|
+
(lastBlock as unknown as Record<string, unknown>).cache_control = {
|
|
817
|
+
type: "ephemeral",
|
|
818
|
+
ttl: cacheTtl,
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
turnStartIdx = i;
|
|
822
|
+
break;
|
|
956
823
|
}
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
824
|
+
|
|
825
|
+
// Advancing tail: place a short-lived 5m cache breakpoint on the last
|
|
826
|
+
// block of the last message when it falls after the turn-starting user
|
|
827
|
+
// message (i.e. tool-use loop content). This caches the growing tail
|
|
828
|
+
// cheaply without conflicting with the 1h breakpoints above.
|
|
829
|
+
// Skip thinking/redacted_thinking blocks — Anthropic doesn't allow
|
|
830
|
+
// cache_control on those types.
|
|
831
|
+
let tailBreakpointApplied = false;
|
|
832
|
+
if (turnStartIdx >= 0 && turnStartIdx < sentMessages.length - 1) {
|
|
833
|
+
const lastMsg = sentMessages[sentMessages.length - 1];
|
|
834
|
+
if (Array.isArray(lastMsg.content) && lastMsg.content.length > 0) {
|
|
835
|
+
const NON_CACHEABLE_TYPES = new Set(["thinking", "redacted_thinking"]);
|
|
836
|
+
let tailBlock: (typeof lastMsg.content)[number] | undefined;
|
|
837
|
+
for (let j = lastMsg.content.length - 1; j >= 0; j--) {
|
|
838
|
+
const block = lastMsg.content[j];
|
|
839
|
+
if (
|
|
840
|
+
typeof block !== "string" &&
|
|
841
|
+
!NON_CACHEABLE_TYPES.has((block as { type: string }).type)
|
|
842
|
+
) {
|
|
843
|
+
tailBlock = block;
|
|
844
|
+
break;
|
|
964
845
|
}
|
|
965
|
-
|
|
846
|
+
}
|
|
847
|
+
if (tailBlock && typeof tailBlock !== "string") {
|
|
848
|
+
(tailBlock as unknown as Record<string, unknown>).cache_control = {
|
|
849
|
+
type: "ephemeral",
|
|
850
|
+
ttl: "5m",
|
|
851
|
+
};
|
|
852
|
+
tailBreakpointApplied = true;
|
|
853
|
+
}
|
|
966
854
|
}
|
|
967
855
|
}
|
|
968
856
|
|
|
857
|
+
// Enforce Anthropic API maximum of 4 cache_control blocks.
|
|
858
|
+
// When the system prompt boundary splits into 2 cached blocks AND
|
|
859
|
+
// tools + turn-start + advancing-tail breakpoints are all present,
|
|
860
|
+
// we'd have 5. Drop the static system block's breakpoint — it's
|
|
861
|
+
// small (<1K tokens) so the re-read cost is negligible, while the
|
|
862
|
+
// dynamic block (workspace context) rarely changes mid-session and
|
|
863
|
+
// benefits more from caching.
|
|
864
|
+
const hasTailBreakpoint = tailBreakpointApplied;
|
|
865
|
+
const hasToolCacheBreakpoint =
|
|
866
|
+
params.tools?.some(
|
|
867
|
+
(t) => "cache_control" in t && t.cache_control != null,
|
|
868
|
+
) ?? false;
|
|
869
|
+
if (
|
|
870
|
+
hasTailBreakpoint &&
|
|
871
|
+
Array.isArray(params.system) &&
|
|
872
|
+
params.system.length === 2 &&
|
|
873
|
+
hasToolCacheBreakpoint
|
|
874
|
+
) {
|
|
875
|
+
delete (params.system[0] as unknown as Record<string, unknown>).cache_control;
|
|
876
|
+
}
|
|
877
|
+
|
|
969
878
|
const { signal: timeoutSignal, cleanup: cleanupTimeout } =
|
|
970
879
|
createStreamTimeout(this.streamTimeoutMs, signal);
|
|
971
880
|
|
|
@@ -982,8 +891,7 @@ export class AnthropicProvider implements Provider {
|
|
|
982
891
|
}
|
|
983
892
|
|
|
984
893
|
// Fast mode: use the beta endpoint with speed: "fast" for Opus 4.6
|
|
985
|
-
const useFastMode =
|
|
986
|
-
speed === "fast" && effectiveModel.includes("opus");
|
|
894
|
+
const useFastMode = speed === "fast" && effectiveModel.includes("opus");
|
|
987
895
|
|
|
988
896
|
// Collect required betas: extended cache TTL for 1h system prompt caching,
|
|
989
897
|
// 1M context window, and fast-mode when applicable.
|
|
@@ -1170,6 +1078,14 @@ export class AnthropicProvider implements Provider {
|
|
|
1170
1078
|
"Anthropic 400: tool_use/tool_result pairing error — dumping message structure",
|
|
1171
1079
|
);
|
|
1172
1080
|
}
|
|
1081
|
+
log.error(
|
|
1082
|
+
{
|
|
1083
|
+
status: error.status,
|
|
1084
|
+
message: error.message,
|
|
1085
|
+
headers: Object.fromEntries(error.headers?.entries() ?? []),
|
|
1086
|
+
},
|
|
1087
|
+
`Anthropic API error (${error.status})`,
|
|
1088
|
+
);
|
|
1173
1089
|
const retryAfterMs = extractRetryAfterMs(error.headers);
|
|
1174
1090
|
throw new ProviderError(
|
|
1175
1091
|
`Anthropic API error (${error.status}): ${error.message}`,
|
|
@@ -89,7 +89,7 @@ export class GeminiProvider implements Provider {
|
|
|
89
89
|
})
|
|
90
90
|
: new GoogleGenAI({ apiKey });
|
|
91
91
|
this.model = model;
|
|
92
|
-
this.streamTimeoutMs = options.streamTimeoutMs ??
|
|
92
|
+
this.streamTimeoutMs = options.streamTimeoutMs ?? 1_800_000;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
async sendMessage(
|
|
@@ -112,7 +112,7 @@ export class OpenAIProvider implements Provider {
|
|
|
112
112
|
baseURL: options.baseURL,
|
|
113
113
|
});
|
|
114
114
|
this.model = model;
|
|
115
|
-
this.streamTimeoutMs = options.streamTimeoutMs ??
|
|
115
|
+
this.streamTimeoutMs = options.streamTimeoutMs ?? 1_800_000;
|
|
116
116
|
this.extraCreateParams = options.extraCreateParams ?? {};
|
|
117
117
|
}
|
|
118
118
|
|
|
@@ -140,7 +140,7 @@ export async function initializeProviders(
|
|
|
140
140
|
routingSources.clear();
|
|
141
141
|
|
|
142
142
|
const streamTimeoutMs =
|
|
143
|
-
(config.timeouts?.providerStreamTimeoutSec ??
|
|
143
|
+
(config.timeouts?.providerStreamTimeoutSec ?? 1800) * 1000;
|
|
144
144
|
const inferenceMode = config.services.inference.mode;
|
|
145
145
|
const useNativeWebSearch =
|
|
146
146
|
config.services["web-search"].provider === "inference-provider-native";
|
package/src/providers/retry.ts
CHANGED
|
@@ -34,16 +34,30 @@ const RETRYABLE_STREAM_PATTERNS = [
|
|
|
34
34
|
"stream has ended, this shouldn't happen",
|
|
35
35
|
];
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Patterns that indicate a transient provider error even when no HTTP status
|
|
39
|
+
* code is available (e.g. overloaded errors delivered as SSE events mid-stream
|
|
40
|
+
* where the initial HTTP response was 200).
|
|
41
|
+
*/
|
|
42
|
+
const RETRYABLE_PROVIDER_MESSAGE_PATTERNS = [/overloaded/i];
|
|
43
|
+
|
|
37
44
|
function isRetryableStreamError(error: unknown): boolean {
|
|
38
45
|
if (!(error instanceof ProviderError)) return false;
|
|
39
46
|
if (error.statusCode !== undefined) return false; // has a real HTTP status — not a stream error
|
|
40
47
|
return RETRYABLE_STREAM_PATTERNS.some((p) => error.message.includes(p));
|
|
41
48
|
}
|
|
42
49
|
|
|
50
|
+
function isRetryableProviderMessage(error: unknown): boolean {
|
|
51
|
+
if (!(error instanceof ProviderError)) return false;
|
|
52
|
+
if (error.statusCode !== undefined) return false; // has a real HTTP status — handled by status check
|
|
53
|
+
return RETRYABLE_PROVIDER_MESSAGE_PATTERNS.some((p) => p.test(error.message));
|
|
54
|
+
}
|
|
55
|
+
|
|
43
56
|
function isRetryableError(error: unknown): boolean {
|
|
44
57
|
if (error instanceof ProviderError && error.statusCode !== undefined) {
|
|
45
58
|
if (error.statusCode === 429 || error.statusCode >= 500) return true;
|
|
46
59
|
}
|
|
60
|
+
if (isRetryableProviderMessage(error)) return true;
|
|
47
61
|
if (isRetryableStreamError(error)) return true;
|
|
48
62
|
return isRetryableNetworkError(error);
|
|
49
63
|
}
|
|
@@ -161,9 +175,11 @@ export class RetryProvider implements Provider {
|
|
|
161
175
|
error.statusCode !== undefined &&
|
|
162
176
|
error.statusCode >= 500
|
|
163
177
|
? `server_error_${error.statusCode}`
|
|
164
|
-
:
|
|
165
|
-
? "
|
|
166
|
-
:
|
|
178
|
+
: isRetryableProviderMessage(error)
|
|
179
|
+
? "provider_overloaded"
|
|
180
|
+
: isRetryableStreamError(error)
|
|
181
|
+
? "stream_corruption"
|
|
182
|
+
: "network_error";
|
|
167
183
|
log.warn(
|
|
168
184
|
{
|
|
169
185
|
attempt: attempt + 1,
|
|
@@ -48,7 +48,11 @@ export type TrustClass = "guardian" | "trusted_contact" | "unknown";
|
|
|
48
48
|
export function isUntrustedTrustClass(
|
|
49
49
|
trustClass: TrustClass | undefined,
|
|
50
50
|
): boolean {
|
|
51
|
-
return
|
|
51
|
+
return (
|
|
52
|
+
trustClass === "trusted_contact" ||
|
|
53
|
+
trustClass === "unknown" ||
|
|
54
|
+
trustClass === undefined
|
|
55
|
+
);
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
/**
|