@vellumai/assistant 0.10.2 → 0.10.3-dev.202606252046.9075fd5
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/bun.lock +20 -0
- package/docs/workspace-tools.md +42 -33
- package/eslint-rules/cli-no-daemon-internals.js +6 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +91 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +31 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +44 -0
- package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +48 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +14 -0
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +17 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +28 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +41 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/openapi.yaml +196 -56
- package/package.json +4 -1
- package/scripts/test.sh +36 -15
- package/src/__tests__/actor-token-service.test.ts +36 -14
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +83 -31
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +2 -0
- package/src/__tests__/annotate-activity-metadata.test.ts +2 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -0
- package/src/__tests__/approval-cascade.test.ts +2 -0
- package/src/__tests__/assistant-attachments.test.ts +42 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -0
- package/src/__tests__/btw-routes.test.ts +2 -0
- package/src/__tests__/build-persisted-content.test.ts +2 -0
- package/src/__tests__/call-controller.test.ts +19 -0
- package/src/__tests__/channel-approval-routes.test.ts +21 -26
- package/src/__tests__/channel-delivery-store.test.ts +28 -0
- package/src/__tests__/channel-guardian.test.ts +164 -78
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +11 -19
- package/src/__tests__/channel-reply-delivery.test.ts +2 -0
- package/src/__tests__/code-search-tool.test.ts +585 -0
- package/src/__tests__/compaction-events.test.ts +2 -0
- package/src/__tests__/compaction-ledger-store.test.ts +128 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +2 -0
- package/src/__tests__/compactor-low-watermark-cut.test.ts +2 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +2 -0
- package/src/__tests__/compactor-summary-call-truncation.test.ts +2 -0
- package/src/__tests__/compactor-web-search-strip.test.ts +2 -0
- package/src/__tests__/config-loader-backfill.test.ts +123 -10
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +31 -29
- package/src/__tests__/consult-deadline.test.ts +60 -0
- package/src/__tests__/contact-store-interaction-info.test.ts +156 -0
- package/src/__tests__/contact-store-user-file.test.ts +7 -10
- package/src/__tests__/contacts-relay-reads.test.ts +19 -24
- package/src/__tests__/contacts-write.test.ts +0 -2
- package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop.test.ts +134 -0
- package/src/__tests__/conversation-analysis-routes.test.ts +2 -0
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +2 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +9 -11
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -0
- package/src/__tests__/conversation-fork-crud.test.ts +354 -24
- package/src/__tests__/conversation-history-web-search.test.ts +2 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +2 -0
- package/src/__tests__/conversation-load-history-stripped.test.ts +2 -0
- package/src/__tests__/conversation-pairing.test.ts +2 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
- package/src/__tests__/conversation-process-callsite.test.ts +2 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
- package/src/__tests__/conversation-queue.test.ts +91 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +14 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +14 -0
- package/src/__tests__/conversation-slash-queue.test.ts +2 -0
- package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
- package/src/__tests__/conversation-speed-override.test.ts +2 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +29 -0
- package/src/__tests__/conversation-title-service.test.ts +2 -0
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +47 -0
- package/src/__tests__/conversation-usage.test.ts +2 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -1
- package/src/__tests__/db-compaction-events-migration.test.ts +129 -0
- package/src/__tests__/db-migration-rollback.test.ts +205 -171
- package/src/__tests__/db-test-helpers.ts +5 -4
- package/src/__tests__/delete-propagation.test.ts +5 -3
- package/src/__tests__/deterministic-verification-control-plane.test.ts +4 -2
- package/src/__tests__/disk-pressure-guard.test.ts +41 -0
- package/src/__tests__/dm-backfill.test.ts +6 -4
- package/src/__tests__/dm-persistence.test.ts +2 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +7 -6
- package/src/__tests__/events-dev-bypass-actor.test.ts +7 -1
- package/src/__tests__/exploration-drift-hook.test.ts +3 -2
- package/src/__tests__/filing-service.test.ts +2 -0
- package/src/__tests__/guardian-binding-drift-heal.test.ts +104 -19
- package/src/__tests__/guardian-dispatch.test.ts +140 -1
- package/src/__tests__/guardian-expiry-notifier.test.ts +282 -0
- package/src/__tests__/guardian-outbound-http.test.ts +13 -0
- package/src/__tests__/guardian-routing-state.test.ts +6 -10
- package/src/__tests__/heartbeat-disk-pressure.test.ts +2 -0
- package/src/__tests__/heartbeat-service.test.ts +2 -0
- package/src/__tests__/helpers/channel-test-adapter.ts +46 -19
- package/src/__tests__/helpers/create-guardian-binding.ts +15 -23
- package/src/__tests__/helpers/mock-logger.ts +1 -0
- package/src/__tests__/helpers/seed-contact-channel.ts +96 -0
- package/src/__tests__/host-app-control-routes.test.ts +24 -30
- package/src/__tests__/host-bash-routes.test.ts +31 -41
- package/src/__tests__/host-browser-routes.test.ts +26 -32
- package/src/__tests__/host-cu-routes-targeted.test.ts +25 -33
- package/src/__tests__/host-file-routes-targeted.test.ts +40 -52
- package/src/__tests__/host-transfer-routes-targeted.test.ts +31 -43
- package/src/__tests__/http-user-message-parity.test.ts +290 -8
- package/src/__tests__/inbound-invite-redemption.test.ts +115 -10
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/invite-redemption-service.test.ts +471 -53
- package/src/__tests__/invite-routes-http.test.ts +34 -0
- package/src/__tests__/invite-service-ipc.test.ts +65 -2
- package/src/__tests__/llm-context-normalization.test.ts +105 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +71 -9
- package/src/__tests__/llm-usage-store.test.ts +25 -0
- package/src/__tests__/mcp-config-secret-boundary.test.ts +392 -0
- package/src/__tests__/mcp-health-check.test.ts +2 -1
- package/src/__tests__/media-stream-server-integration.test.ts +127 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +2 -0
- package/src/__tests__/messaging-send-tool.test.ts +2 -0
- package/src/__tests__/migration-import-from-url.test.ts +2 -2
- package/src/__tests__/mtime-cache.test.ts +275 -5
- package/src/__tests__/native-web-search.test.ts +2 -0
- package/src/__tests__/non-member-access-request.test.ts +191 -17
- package/src/__tests__/notification-broadcaster.test.ts +4 -0
- package/src/__tests__/notification-decision-recipient-context.test.ts +33 -32
- package/src/__tests__/notification-deep-link.test.ts +6 -0
- package/src/__tests__/notification-guardian-path.test.ts +19 -0
- package/src/__tests__/onboarding-persona-write.test.ts +52 -22
- package/src/__tests__/openai-provider.test.ts +22 -12
- package/src/__tests__/openai-responses-provider.test.ts +12 -2
- package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/pending-interactions-resolved-event.test.ts +7 -4
- package/src/__tests__/persist-onboarding-artifacts.test.ts +1 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
- package/src/__tests__/persona-resolver.test.ts +75 -45
- package/src/__tests__/plugin-bootstrap.test.ts +21 -83
- package/src/__tests__/plugin-disabled-state.test.ts +190 -0
- package/src/__tests__/plugin-pipeline.test.ts +96 -0
- package/src/__tests__/plugin-route-contribution.test.ts +6 -19
- package/src/__tests__/plugin-tool-contribution.test.ts +5 -20
- package/src/__tests__/plugin-types.test.ts +2 -4
- package/src/__tests__/process-message-background-slack.test.ts +2 -0
- package/src/__tests__/process-message-display-content.test.ts +2 -0
- package/src/__tests__/provider-error-scenarios.test.ts +5 -4
- package/src/__tests__/provider-usage-tracking.test.ts +39 -0
- package/src/__tests__/reaction-intercept-cold-cache-warm.test.ts +135 -0
- package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +158 -0
- package/src/__tests__/reaction-persistence.test.ts +51 -4
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +2 -0
- package/src/__tests__/registry.test.ts +4 -1
- package/src/__tests__/relay-server.test.ts +758 -32
- package/src/__tests__/runtime-attachment-metadata.test.ts +9 -12
- package/src/__tests__/secret-ingress-http.test.ts +14 -0
- package/src/__tests__/send-endpoint-busy.test.ts +30 -8
- package/src/__tests__/settings-routes.test.ts +32 -0
- package/src/__tests__/skills.test.ts +44 -0
- package/src/__tests__/slack-inbound-verification.test.ts +47 -2
- package/src/__tests__/sse-actor-principal-guardian-source.test.ts +79 -0
- package/src/__tests__/steer-on-enqueue-question.test.ts +181 -0
- package/src/__tests__/stt-hints.test.ts +49 -15
- package/src/__tests__/subagent-detail.test.ts +27 -0
- package/src/__tests__/subagent-disposal.test.ts +65 -0
- package/src/__tests__/subagent-fork-prompt-role.test.ts +195 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +6 -7
- package/src/__tests__/subagent-notify-parent.test.ts +2 -0
- package/src/__tests__/subagent-role-registry.test.ts +24 -6
- package/src/__tests__/subagent-spawn-and-await.test.ts +546 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +2 -0
- package/src/__tests__/subagent-tools.test.ts +398 -1
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- package/src/__tests__/thread-backfill.test.ts +3 -3
- package/src/__tests__/title-generate-hook.test.ts +2 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -0
- package/src/__tests__/tool-executor.test.ts +16 -11
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +2 -0
- package/src/__tests__/tool-start-timestamp.test.ts +2 -0
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +37 -51
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +12 -12
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +9 -7
- package/src/__tests__/trusted-contact-multichannel.test.ts +16 -7
- package/src/__tests__/trusted-contact-verification.test.ts +79 -54
- package/src/__tests__/twilio-routes.test.ts +96 -0
- package/src/__tests__/ui-file-upload-surface.test.ts +86 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -0
- package/src/__tests__/voice-guardian-cold-cache-warm.test.ts +137 -0
- package/src/__tests__/voice-invite-redemption.test.ts +216 -20
- package/src/__tests__/web-search-backend-failure.test.ts +2 -0
- package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +3 -3
- package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +208 -0
- package/src/__tests__/workspace-migration-112-remove-advisor-callsite-override.test.ts +170 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +196 -238
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +14 -35
- package/src/__tests__/workspace-tool-loader.test.ts +195 -2
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +35 -47
- package/src/agent/loop-exclusive-tool.test.ts +154 -0
- package/src/agent/loop-native-web-search.test.ts +200 -0
- package/src/agent/loop.ts +164 -1
- package/src/api/constants/sse-replay.ts +41 -0
- package/src/api/events/conversation-notice.ts +26 -0
- package/src/api/index.ts +19 -1
- package/src/api/responses/llm-request-log-entry.ts +29 -0
- package/src/api/responses/subagent-detail.ts +17 -0
- package/src/api/surfaces.ts +39 -3
- package/src/approvals/guardian-channel-delivery.ts +30 -0
- package/src/approvals/guardian-expiry-notifier.ts +148 -0
- package/src/approvals/guardian-request-resolvers.ts +17 -15
- package/src/calls/__tests__/relay-setup-router.test.ts +267 -17
- package/src/calls/call-domain.ts +3 -3
- package/src/calls/guardian-dispatch.ts +14 -9
- package/src/calls/inbound-trust-reader.ts +24 -2
- package/src/calls/media-stream-server.ts +21 -0
- package/src/calls/relay-access-wait.ts +6 -6
- package/src/calls/relay-server.ts +188 -51
- package/src/calls/relay-setup-router.ts +47 -17
- package/src/calls/relay-verification.ts +4 -4
- package/src/calls/stt-hints.ts +9 -12
- package/src/calls/twilio-routes.ts +14 -4
- package/src/channels/types.ts +10 -20
- package/src/cli/commands/__tests__/cache.test.ts +8 -1
- package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -0
- package/src/cli/commands/cache.ts +194 -181
- package/src/cli/commands/contacts.ts +10 -7
- package/src/cli/commands/db/__tests__/repair.test.ts +6 -5
- package/src/cli/commands/db/status.ts +37 -1
- package/src/cli/commands/mcp.ts +252 -218
- package/src/cli/commands/memory/__tests__/worker.test.ts +432 -0
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/worker.ts +242 -0
- package/src/cli/commands/plugins.ts +125 -13
- package/src/cli/lib/__tests__/install-from-github.test.ts +102 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +160 -1
- package/src/cli/lib/__tests__/publish-plugin.test.ts +404 -0
- package/src/cli/lib/list-installed-plugins.ts +179 -1
- package/src/cli/lib/publish-plugin.ts +633 -0
- package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +143 -0
- package/src/config/__tests__/sync-gated-profiles.test.ts +16 -10
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +27 -17
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +13 -3
- package/src/config/bundled-skills/subagent/SKILL.md +17 -2
- package/src/config/bundled-skills/subagent/TOOLS.json +5 -4
- package/src/config/call-site-defaults.ts +0 -6
- package/src/config/feature-flag-registry.json +5 -13
- package/src/config/llm-resolver.ts +0 -3
- package/src/config/loader.ts +38 -5
- package/src/config/prune-seeded-callsite-defaults.ts +110 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +0 -7
- package/src/config/schemas/heartbeat.ts +2 -5
- package/src/config/schemas/llm.ts +3 -12
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/memory-v3.ts +7 -0
- package/src/config/schemas/memory.ts +4 -0
- package/src/config/schemas/timeouts.ts +8 -0
- package/src/config/seed-inference-profiles.ts +30 -34
- package/src/config/skills.ts +27 -5
- package/src/config/sync-gated-profiles.ts +12 -16
- package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +128 -0
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +312 -0
- package/src/contacts/__tests__/member-write-relay.test.ts +226 -0
- package/src/contacts/contact-store.ts +27 -237
- package/src/contacts/contacts-write.ts +18 -55
- package/src/contacts/gateway-channel-read.ts +51 -0
- package/src/contacts/guardian-delivery-reader.ts +223 -0
- package/src/contacts/member-write-relay.ts +183 -0
- package/src/contacts/types.ts +3 -15
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +35 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +0 -44
- package/src/daemon/assistant-attachments.ts +27 -4
- package/src/daemon/conversation-agent-loop.ts +28 -0
- package/src/daemon/conversation-notices.ts +60 -0
- package/src/daemon/conversation-process.ts +35 -16
- package/src/daemon/conversation-surfaces.ts +111 -38
- package/src/daemon/conversation-tool-setup.ts +71 -30
- package/src/daemon/conversation.ts +23 -1
- package/src/daemon/disk-pressure-guard.ts +12 -2
- package/src/daemon/event-loop-watchdog.ts +28 -1
- package/src/daemon/external-plugins-bootstrap.ts +17 -41
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -2
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +25 -2
- package/src/daemon/handlers/__tests__/config-channels.test.ts +220 -0
- package/src/daemon/handlers/config-a2a.ts +6 -14
- package/src/daemon/handlers/config-channels.ts +67 -26
- package/src/daemon/handlers/conversations.ts +77 -0
- package/src/daemon/lifecycle.ts +16 -0
- package/src/daemon/mcp-reload-service.ts +10 -0
- package/src/daemon/memory-v2-startup.test.ts +72 -0
- package/src/daemon/memory-v2-startup.ts +87 -19
- package/src/daemon/message-types/conversations.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +12 -12
- package/src/daemon/server.ts +0 -4
- package/src/daemon/shutdown-handlers.ts +20 -0
- package/src/daemon/tool-setup-types.ts +9 -0
- package/src/heartbeat/heartbeat-service.ts +5 -0
- package/src/home/relationship-state-writer.ts +5 -0
- package/src/hooks/hook-loader.ts +341 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
- package/src/ipc/assistant-server.ts +2 -2
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +1 -0
- package/src/mcp/client.ts +15 -1
- package/src/mcp/mcp-auth-orchestrator.ts +6 -1
- package/src/mcp/mcp-header-store.ts +134 -0
- package/src/mcp/mcp-oauth-provider.ts +19 -8
- package/src/memory/__tests__/301-create-watchdog-events.test.ts +110 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +8 -0
- package/src/memory/__tests__/prompt-override.test.ts +192 -0
- package/src/memory/__tests__/watchdog-events-store.test.ts +161 -0
- package/src/memory/compaction-ledger-store.ts +107 -0
- package/src/memory/conversation-crud.ts +129 -61
- package/src/memory/db-connection.ts +22 -3
- package/src/memory/db-init.ts +37 -503
- package/src/memory/db-singleton.ts +6 -4
- package/src/memory/jobs-worker.ts +104 -0
- package/src/memory/llm-request-log-store.ts +26 -1
- package/src/memory/llm-usage-store.ts +48 -20
- package/src/memory/memory-retrospective-job.ts +14 -8
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +152 -56
- package/src/memory/migrations/300-add-processing-started-at.ts +30 -0
- package/src/memory/migrations/301-create-watchdog-events.ts +45 -0
- package/src/memory/migrations/302-create-compaction-events.ts +107 -0
- package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +297 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
- package/src/memory/migrations/run-migrations.ts +106 -10
- package/src/memory/migrations/validate-migration-state.ts +105 -67
- package/src/memory/prompt-override.ts +129 -0
- package/src/memory/schema/contacts.ts +6 -2
- package/src/memory/schema/conversations.ts +37 -0
- package/src/memory/schema/infrastructure.ts +20 -0
- package/src/memory/steps.ts +1294 -0
- package/src/memory/v2/__tests__/cli-command-store.test.ts +25 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +80 -0
- package/src/memory/v2/cli-command-store.ts +75 -38
- package/src/memory/v2/prompts/consolidation.ts +13 -82
- package/src/memory/v2/prompts/router.ts +21 -93
- package/src/memory/v2/skill-store.ts +68 -31
- package/src/memory/watchdog-events-store.ts +87 -0
- package/src/memory/worker-control.ts +204 -0
- package/src/memory/worker-process.ts +89 -0
- package/src/messaging/channel-binding-metadata.ts +31 -0
- package/src/messaging/provider-types.ts +8 -0
- package/src/messaging/providers/slack/binding-metadata.ts +60 -0
- package/src/notifications/__tests__/broadcaster.test.ts +8 -8
- package/src/notifications/__tests__/connected-channels.test.ts +86 -0
- package/src/notifications/__tests__/decision-engine.test.ts +78 -9
- package/src/notifications/__tests__/destination-resolver.test.ts +151 -0
- package/src/notifications/broadcaster.ts +8 -1
- package/src/notifications/decision-engine.ts +15 -7
- package/src/notifications/destination-resolver.ts +53 -25
- package/src/notifications/emit-signal.ts +34 -15
- package/src/onboarding/checkin-event.test.ts +222 -0
- package/src/onboarding/checkin-event.ts +321 -0
- package/src/onboarding/schedule-checkin.ts +190 -0
- package/src/permissions/question-prompter.test.ts +1 -1
- package/src/permissions/question-prompter.ts +7 -4
- package/src/plugin-api/index.ts +12 -12
- package/src/plugin-api/types.ts +12 -14
- package/src/plugin-api/vision-support.test.ts +28 -4
- package/src/plugin-api/vision-support.ts +66 -31
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/empty-response/hooks/stop.ts +2 -2
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +4 -3
- package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/history-repair/hooks/stop.ts +2 -2
- package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +47 -7
- package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +12 -13
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +14 -22
- package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +42 -11
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/image-recovery/hooks/stop.ts +2 -2
- package/src/plugins/defaults/index.ts +0 -35
- package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +33 -3
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +48 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +4 -8
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +43 -15
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +11 -2
- package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +146 -0
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +77 -13
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +12 -11
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +2 -2
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -2
- package/src/plugins/defaults/title-generate/hooks/stop.ts +2 -2
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +2 -2
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +2 -2
- package/src/plugins/disabled-state.ts +31 -0
- package/src/plugins/external-plugin-loader.ts +2 -2
- package/src/plugins/mtime-cache.ts +186 -330
- package/src/plugins/pipeline.ts +111 -13
- package/src/plugins/registry.ts +59 -16
- package/src/plugins/surface-import.ts +121 -0
- package/src/plugins/types.ts +7 -9
- package/src/prompts/persona-resolver.ts +43 -11
- package/src/providers/anthropic/client.ts +5 -0
- package/src/providers/call-site-routing.ts +45 -0
- package/src/providers/model-catalog.ts +16 -0
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +321 -0
- package/src/providers/openai/api-error-normalization.ts +270 -0
- package/src/providers/openai/chat-completions-provider.ts +37 -83
- package/src/providers/openai/responses-provider.ts +50 -46
- package/src/providers/openrouter/client.ts +5 -0
- package/src/providers/provider-send-message.ts +10 -0
- package/src/providers/ratelimit.ts +10 -0
- package/src/providers/registry.ts +1 -1
- package/src/providers/retry.ts +10 -0
- package/src/providers/types.ts +22 -0
- package/src/providers/usage-tracking.ts +10 -0
- package/src/runtime/__tests__/channel-verification-service.test.ts +133 -0
- package/src/runtime/__tests__/guardian-vellum-migration.test.ts +184 -0
- package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +66 -0
- package/src/runtime/__tests__/local-principal-trust.test.ts +162 -0
- package/src/runtime/__tests__/member-verdict-cache.test.ts +119 -0
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +402 -123
- package/src/runtime/access-request-helper.ts +18 -39
- package/src/runtime/actor-trust-resolver.ts +46 -19
- package/src/runtime/anchored-guardian.test.ts +109 -0
- package/src/runtime/anchored-guardian.ts +86 -0
- package/src/runtime/assistant-event-hub.ts +1 -1
- package/src/runtime/assistant-stream-state.ts +9 -2
- package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +99 -0
- package/src/runtime/auth/require-bound-guardian.ts +21 -11
- package/src/runtime/channel-verification-service.ts +56 -31
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
- package/src/runtime/guardian-vellum-migration.ts +69 -8
- package/src/runtime/invite-redemption-service.ts +213 -187
- package/src/runtime/local-actor-identity.test.ts +108 -0
- package/src/runtime/local-actor-identity.ts +85 -13
- package/src/runtime/local-principal-trust.ts +52 -0
- package/src/runtime/member-verdict-cache.ts +0 -0
- package/src/runtime/pending-interactions.ts +11 -1
- package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +56 -5
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/contact-routes.test.ts +305 -0
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +92 -0
- package/src/runtime/routes/__tests__/http-adapter-actor-header.test.ts +129 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +188 -0
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +13 -5
- package/src/runtime/routes/channel-verification-routes.ts +3 -3
- package/src/runtime/routes/contact-routes.ts +48 -57
- package/src/runtime/routes/conversation-cli-routes.ts +4 -5
- package/src/runtime/routes/conversation-list-routes.ts +4 -7
- package/src/runtime/routes/conversation-query-routes.ts +72 -0
- package/src/runtime/routes/conversation-routes.ts +84 -85
- package/src/runtime/routes/events-routes.ts +2 -2
- package/src/runtime/routes/global-search-routes.ts +3 -1
- package/src/runtime/routes/guardian-action-routes.ts +4 -5
- package/src/runtime/routes/host-app-control-routes.ts +5 -4
- package/src/runtime/routes/host-bash-routes.ts +5 -4
- package/src/runtime/routes/host-browser-routes.ts +9 -11
- package/src/runtime/routes/host-cu-routes.ts +5 -4
- package/src/runtime/routes/host-file-routes.ts +5 -4
- package/src/runtime/routes/host-transfer-routes.ts +6 -6
- package/src/runtime/routes/http-adapter.ts +16 -1
- package/src/runtime/routes/identity-routes.ts +3 -2
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +97 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +61 -59
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +13 -5
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +21 -8
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -3
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +19 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/llm-context-normalization.ts +83 -0
- package/src/runtime/routes/mcp-auth-routes.ts +171 -19
- package/src/runtime/routes/migration-rollback-routes.ts +4 -3
- package/src/runtime/routes/migration-routes.ts +4 -1
- package/src/runtime/routes/onboarding-checkin-routes.ts +86 -0
- package/src/runtime/routes/settings-routes.ts +8 -3
- package/src/runtime/routes/subagents-routes.ts +5 -0
- package/src/runtime/routes/surface-action-routes.ts +42 -56
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +13 -58
- package/src/runtime/tool-grant-request-helper.ts +3 -3
- package/src/runtime/trust-verdict-consumer.ts +111 -40
- package/src/runtime/verification-outbound-actions.ts +18 -18
- package/src/signals/user-message.ts +16 -0
- package/src/subagent/__tests__/consult-prompt.test.ts +35 -0
- package/src/{plugins/defaults/advisor/__tests__/transcript.test.ts → subagent/__tests__/consult-transcript.test.ts} +47 -10
- package/src/{plugins/defaults/advisor/steering.ts → subagent/consult-prompt.ts} +22 -32
- package/src/{plugins/defaults/advisor/transcript.ts → subagent/consult-transcript.ts} +18 -8
- package/src/subagent/index.ts +1 -1
- package/src/subagent/manager.ts +254 -33
- package/src/subagent/types.ts +11 -4
- package/src/telemetry/types.ts +34 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
- package/src/telemetry/usage-telemetry-reporter.ts +87 -3
- package/src/tools/ask-question/ask-question-tool.test.ts +29 -0
- package/src/tools/ask-question/ask-question-tool.ts +13 -0
- package/src/tools/executor.ts +4 -4
- package/src/tools/filesystem/search.ts +543 -0
- package/src/tools/registry.ts +38 -0
- package/src/tools/shared/filesystem/path-policy.ts +12 -5
- package/src/tools/subagent/consult-deadline.ts +49 -0
- package/src/tools/subagent/spawn.ts +234 -5
- package/src/tools/tool-approval-handler.ts +1 -1
- package/src/tools/tool-defaults.ts +9 -2
- package/src/tools/tool-manifest.ts +3 -0
- package/src/tools/types.ts +131 -25
- package/src/tools/workspace-tools/loader.ts +348 -244
- package/src/util/errors.ts +26 -1
- package/src/util/logger.ts +9 -0
- package/src/util/platform.ts +19 -0
- package/src/util/telemetry-db-path.ts +24 -0
- package/src/workflows/library.test.ts +140 -0
- package/src/workflows/library.ts +82 -28
- package/src/workspace/migrations/017-seed-persona-dirs.ts +3 -34
- package/src/workspace/migrations/019-scope-journal-to-guardian.ts +3 -24
- package/src/workspace/migrations/031-drop-user-md.ts +232 -148
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +14 -66
- package/src/workspace/migrations/111-prune-seeded-callsite-defaults.ts +134 -0
- package/src/workspace/migrations/112-remove-advisor-callsite-override.ts +64 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +0 -70
- package/src/daemon/workspace-tools-watcher.ts +0 -328
- package/src/memory/migrations/registry.ts +0 -573
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +0 -56
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +0 -43
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +0 -137
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -153
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +0 -138
- package/src/plugins/defaults/advisor/advisor-gate.ts +0 -29
- package/src/plugins/defaults/advisor/advisor-state-store.ts +0 -94
- package/src/plugins/defaults/advisor/config.ts +0 -21
- package/src/plugins/defaults/advisor/consult.ts +0 -93
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +0 -34
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +0 -30
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +0 -19
- package/src/plugins/defaults/advisor/package.json +0 -14
- package/src/plugins/defaults/advisor/tools/advisor.ts +0 -65
- package/src/providers/openai/__tests__/api-error-detail.test.ts +0 -120
|
@@ -7,12 +7,8 @@ import type { AdmissionPolicy, SourceMetadata } from "@vellumai/gateway-client";
|
|
|
7
7
|
|
|
8
8
|
import { isInviteCodeRedemptionEnabled } from "../../../channels/config.js";
|
|
9
9
|
import type { ChannelId } from "../../../channels/types.js";
|
|
10
|
-
import {
|
|
10
|
+
import { getGuardianDelivery } from "../../../contacts/guardian-delivery-reader.js";
|
|
11
11
|
import { channelStatusToMemberStatus } from "../../../contacts/member-status.js";
|
|
12
|
-
import type {
|
|
13
|
-
ContactChannel,
|
|
14
|
-
ContactWithChannels,
|
|
15
|
-
} from "../../../contacts/types.js";
|
|
16
12
|
import { deleteInbound, recordInbound } from "../../../memory/delivery-crud.js";
|
|
17
13
|
import { markProcessed } from "../../../memory/delivery-status.js";
|
|
18
14
|
import {
|
|
@@ -25,6 +21,7 @@ import { getLogger } from "../../../util/logger.js";
|
|
|
25
21
|
import { truncate } from "../../../util/truncate.js";
|
|
26
22
|
import { hashVoiceCode } from "../../../util/voice-code.js";
|
|
27
23
|
import { notifyGuardianOfAccessRequest } from "../../access-request-helper.js";
|
|
24
|
+
import { resolveAnchoredGuardian } from "../../anchored-guardian.js";
|
|
28
25
|
import { getInviteAdapterRegistry } from "../../channel-invite-transport.js";
|
|
29
26
|
import {
|
|
30
27
|
createOutboundSession,
|
|
@@ -38,7 +35,8 @@ import {
|
|
|
38
35
|
redeemInviteByCode,
|
|
39
36
|
} from "../../invite-redemption-service.js";
|
|
40
37
|
import { getInviteRedemptionReply } from "../../invite-redemption-templates.js";
|
|
41
|
-
import {
|
|
38
|
+
import type { VerdictMember } from "../../trust-verdict-consumer.js";
|
|
39
|
+
import { verdictMemberFromVerdict } from "../../trust-verdict-consumer.js";
|
|
42
40
|
|
|
43
41
|
const log = getLogger("runtime-http");
|
|
44
42
|
|
|
@@ -46,28 +44,20 @@ const log = getLogger("runtime-http");
|
|
|
46
44
|
* Resolve the guardian's display name for use in requester-facing messages.
|
|
47
45
|
*
|
|
48
46
|
* Uses the assistant's anchored vellum principal to validate the guardian
|
|
49
|
-
*
|
|
50
|
-
* This prevents stale or cross-assistant
|
|
47
|
+
* binding, matching the same strategy used by `notifyGuardianOfAccessRequest`.
|
|
48
|
+
* This prevents stale or cross-assistant bindings from leaking a wrong name.
|
|
49
|
+
* Cosmetic copy, not an admission decision, so a null gateway list degrades
|
|
50
|
+
* gracefully to the default reference.
|
|
51
51
|
*/
|
|
52
|
-
function resolveGuardianLabel(sourceChannel: ChannelId): string {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
// matches the assistant's anchor.
|
|
62
|
-
const sourceGuardian = findGuardianForChannel(sourceChannel);
|
|
63
|
-
if (
|
|
64
|
-
sourceGuardian &&
|
|
65
|
-
sourceGuardian.contact.principalId === anchoredPrincipalId
|
|
66
|
-
) {
|
|
67
|
-
return resolveGuardianName(sourceGuardian.contact.displayName);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return resolveGuardianName(vellumGuardian.contact.displayName);
|
|
52
|
+
async function resolveGuardianLabel(sourceChannel: ChannelId): Promise<string> {
|
|
53
|
+
// Cosmetic copy, not an admission decision: no local-store fallback, and a
|
|
54
|
+
// missing anchor principal degrades to the default reference.
|
|
55
|
+
const anchored = resolveAnchoredGuardian({
|
|
56
|
+
guardians: await getGuardianDelivery(),
|
|
57
|
+
sourceChannel,
|
|
58
|
+
requireAnchorPrincipal: true,
|
|
59
|
+
});
|
|
60
|
+
return resolveGuardianName(anchored?.displayName);
|
|
71
61
|
}
|
|
72
62
|
|
|
73
63
|
// ---------------------------------------------------------------------------
|
|
@@ -102,14 +92,8 @@ export interface AclEnforcementParams {
|
|
|
102
92
|
effectiveAdmissionPolicy?: AdmissionPolicy;
|
|
103
93
|
}
|
|
104
94
|
|
|
105
|
-
/** Resolved contact + channel pair from ACL enforcement. */
|
|
106
|
-
export type ResolvedMember = {
|
|
107
|
-
contact: ContactWithChannels;
|
|
108
|
-
channel: ContactChannel;
|
|
109
|
-
};
|
|
110
|
-
|
|
111
95
|
export interface AclResult {
|
|
112
|
-
resolvedMember:
|
|
96
|
+
resolvedMember: VerdictMember | null;
|
|
113
97
|
/** When set, the caller must return this response immediately. */
|
|
114
98
|
earlyResponse?: Record<string, unknown>;
|
|
115
99
|
/**
|
|
@@ -173,9 +157,27 @@ export async function enforceIngressAcl(
|
|
|
173
157
|
};
|
|
174
158
|
}
|
|
175
159
|
|
|
160
|
+
// Gateway attempted resolution but failed (DB error) → fail-closed deny,
|
|
161
|
+
// distinct from an absent verdict and from a real stranger. TEXT does not
|
|
162
|
+
// fall back to local ACL reads; the sender can retry.
|
|
163
|
+
if (verdict.resolutionFailed === true) {
|
|
164
|
+
log.warn(
|
|
165
|
+
{ sourceChannel, externalUserId: canonicalSenderId },
|
|
166
|
+
"Ingress ACL: gateway trust resolution failed, denying fail-closed",
|
|
167
|
+
);
|
|
168
|
+
return {
|
|
169
|
+
resolvedMember: null,
|
|
170
|
+
earlyResponse: {
|
|
171
|
+
accepted: true,
|
|
172
|
+
denied: true,
|
|
173
|
+
reason: "not_a_member",
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
176
178
|
// Member resolved from the gateway verdict (ACL + identity only); null for a
|
|
177
179
|
// stranger verdict, which falls through to the non-member intercepts.
|
|
178
|
-
const resolvedMember:
|
|
180
|
+
const resolvedMember: VerdictMember | null = verdictMemberFromVerdict(verdict);
|
|
179
181
|
|
|
180
182
|
// A verdict carrying member identity but no resolvable member
|
|
181
183
|
// (malformed/unknown ACL) fails closed, not treated as a stranger.
|
|
@@ -329,7 +331,7 @@ export async function enforceIngressAcl(
|
|
|
329
331
|
if (slackVerifyResult.initiated) {
|
|
330
332
|
// Still notify the guardian about the access attempt
|
|
331
333
|
try {
|
|
332
|
-
notifyGuardianOfAccessRequest({
|
|
334
|
+
await notifyGuardianOfAccessRequest({
|
|
333
335
|
canonicalAssistantId,
|
|
334
336
|
sourceChannel,
|
|
335
337
|
conversationExternalId,
|
|
@@ -369,7 +371,7 @@ export async function enforceIngressAcl(
|
|
|
369
371
|
try {
|
|
370
372
|
await deliverChannelReply(dmCallbackUrl, {
|
|
371
373
|
chatId: senderUserId,
|
|
372
|
-
text: `I don't recognize you yet! I've let ${resolveGuardianLabel(sourceChannel)} know you're trying to reach me. They'll need to share a 6-digit verification code with you — ask them directly if you know them. Once you have the code, reply here with it.`,
|
|
374
|
+
text: `I don't recognize you yet! I've let ${await resolveGuardianLabel(sourceChannel)} know you're trying to reach me. They'll need to share a 6-digit verification code with you — ask them directly if you know them. Once you have the code, reply here with it.`,
|
|
373
375
|
assistantId,
|
|
374
376
|
});
|
|
375
377
|
} catch (err) {
|
|
@@ -404,7 +406,7 @@ export async function enforceIngressAcl(
|
|
|
404
406
|
|
|
405
407
|
if (emailVerifyResult.initiated) {
|
|
406
408
|
try {
|
|
407
|
-
notifyGuardianOfAccessRequest({
|
|
409
|
+
await notifyGuardianOfAccessRequest({
|
|
408
410
|
canonicalAssistantId,
|
|
409
411
|
sourceChannel,
|
|
410
412
|
conversationExternalId,
|
|
@@ -443,7 +445,7 @@ export async function enforceIngressAcl(
|
|
|
443
445
|
// deduplication, canonical request creation, and notification emission.
|
|
444
446
|
let guardianNotified = false;
|
|
445
447
|
try {
|
|
446
|
-
const accessResult = notifyGuardianOfAccessRequest({
|
|
448
|
+
const accessResult = await notifyGuardianOfAccessRequest({
|
|
447
449
|
canonicalAssistantId,
|
|
448
450
|
sourceChannel,
|
|
449
451
|
conversationExternalId,
|
|
@@ -467,7 +469,7 @@ export async function enforceIngressAcl(
|
|
|
467
469
|
}
|
|
468
470
|
|
|
469
471
|
const replyText = guardianNotified
|
|
470
|
-
? `Hmm looks like you don't have access to talk to me. I'll let ${resolveGuardianLabel(sourceChannel)} know you tried talking to me and get back to you.`
|
|
472
|
+
? `Hmm looks like you don't have access to talk to me. I'll let ${await resolveGuardianLabel(sourceChannel)} know you tried talking to me and get back to you.`
|
|
471
473
|
: "Sorry, you haven't been approved to message this assistant.";
|
|
472
474
|
let replyDelivered = false;
|
|
473
475
|
if (replyCallbackUrl) {
|
|
@@ -507,8 +509,8 @@ export async function enforceIngressAcl(
|
|
|
507
509
|
}
|
|
508
510
|
|
|
509
511
|
if (resolvedMember) {
|
|
510
|
-
if (resolvedMember.
|
|
511
|
-
const isBlockedMember = resolvedMember.
|
|
512
|
+
if (resolvedMember.status !== "active") {
|
|
513
|
+
const isBlockedMember = resolvedMember.status === "blocked";
|
|
512
514
|
// Bootstrap commands must pass through for re-verifiable states
|
|
513
515
|
// (pending/revoked), but never for blocked members.
|
|
514
516
|
let denyInactiveMember = true;
|
|
@@ -529,7 +531,7 @@ export async function enforceIngressAcl(
|
|
|
529
531
|
log.info(
|
|
530
532
|
{
|
|
531
533
|
sourceChannel,
|
|
532
|
-
channelId: resolvedMember.
|
|
534
|
+
channelId: resolvedMember.channelId,
|
|
533
535
|
hasValidBootstrapSession: false,
|
|
534
536
|
},
|
|
535
537
|
"Ingress ACL: inactive member bootstrap bypass denied",
|
|
@@ -614,11 +616,11 @@ export async function enforceIngressAcl(
|
|
|
614
616
|
if (!isBlockedMember && denyInactiveMember) {
|
|
615
617
|
if (
|
|
616
618
|
(effectiveAdmissionPolicy === "strangers" &&
|
|
617
|
-
resolvedMember.
|
|
619
|
+
resolvedMember.status !== "revoked") ||
|
|
618
620
|
((effectiveAdmissionPolicy === "any_contact" ||
|
|
619
621
|
effectiveAdmissionPolicy === "guardian_only") &&
|
|
620
|
-
(resolvedMember.
|
|
621
|
-
resolvedMember.
|
|
622
|
+
(resolvedMember.status === "pending" ||
|
|
623
|
+
resolvedMember.status === "unverified"))
|
|
622
624
|
) {
|
|
623
625
|
denyInactiveMember = false;
|
|
624
626
|
}
|
|
@@ -628,8 +630,8 @@ export async function enforceIngressAcl(
|
|
|
628
630
|
log.info(
|
|
629
631
|
{
|
|
630
632
|
sourceChannel,
|
|
631
|
-
channelId: resolvedMember.
|
|
632
|
-
status: resolvedMember.
|
|
633
|
+
channelId: resolvedMember.channelId,
|
|
634
|
+
status: resolvedMember.status,
|
|
633
635
|
},
|
|
634
636
|
"Ingress ACL: member not active, denying",
|
|
635
637
|
);
|
|
@@ -639,7 +641,7 @@ export async function enforceIngressAcl(
|
|
|
639
641
|
// the guardian made an explicit decision to block them.
|
|
640
642
|
if (
|
|
641
643
|
sourceChannel === "slack" &&
|
|
642
|
-
resolvedMember.
|
|
644
|
+
resolvedMember.status !== "blocked" &&
|
|
643
645
|
(canonicalSenderId ?? rawSenderId)
|
|
644
646
|
) {
|
|
645
647
|
const slackVerifyResult = initiateSlackVerificationChallenge({
|
|
@@ -649,7 +651,7 @@ export async function enforceIngressAcl(
|
|
|
649
651
|
|
|
650
652
|
if (slackVerifyResult.initiated) {
|
|
651
653
|
try {
|
|
652
|
-
notifyGuardianOfAccessRequest({
|
|
654
|
+
await notifyGuardianOfAccessRequest({
|
|
653
655
|
canonicalAssistantId,
|
|
654
656
|
sourceChannel,
|
|
655
657
|
conversationExternalId,
|
|
@@ -657,7 +659,7 @@ export async function enforceIngressAcl(
|
|
|
657
659
|
actorDisplayName,
|
|
658
660
|
actorUsername,
|
|
659
661
|
previousMemberStatus: channelStatusToMemberStatus(
|
|
660
|
-
resolvedMember.
|
|
662
|
+
resolvedMember.status,
|
|
661
663
|
),
|
|
662
664
|
messagePreview: truncate(
|
|
663
665
|
trimmedContent,
|
|
@@ -688,7 +690,7 @@ export async function enforceIngressAcl(
|
|
|
688
690
|
try {
|
|
689
691
|
await deliverChannelReply(dmCallbackUrl, {
|
|
690
692
|
chatId: senderUserId,
|
|
691
|
-
text: `I don't recognize you yet! I've let ${resolveGuardianLabel(sourceChannel)} know you're trying to reach me. They'll need to share a 6-digit verification code with you — ask them directly if you know them. Once you have the code, reply here with it.`,
|
|
693
|
+
text: `I don't recognize you yet! I've let ${await resolveGuardianLabel(sourceChannel)} know you're trying to reach me. They'll need to share a 6-digit verification code with you — ask them directly if you know them. Once you have the code, reply here with it.`,
|
|
692
694
|
assistantId,
|
|
693
695
|
});
|
|
694
696
|
} catch (err) {
|
|
@@ -715,9 +717,9 @@ export async function enforceIngressAcl(
|
|
|
715
717
|
// re-approve. Blocked members are intentionally excluded — the
|
|
716
718
|
// guardian already made an explicit decision to block them.
|
|
717
719
|
let guardianNotified = false;
|
|
718
|
-
if (resolvedMember.
|
|
720
|
+
if (resolvedMember.status !== "blocked") {
|
|
719
721
|
try {
|
|
720
|
-
const accessResult = notifyGuardianOfAccessRequest({
|
|
722
|
+
const accessResult = await notifyGuardianOfAccessRequest({
|
|
721
723
|
canonicalAssistantId,
|
|
722
724
|
sourceChannel,
|
|
723
725
|
conversationExternalId,
|
|
@@ -725,7 +727,7 @@ export async function enforceIngressAcl(
|
|
|
725
727
|
actorDisplayName,
|
|
726
728
|
actorUsername,
|
|
727
729
|
previousMemberStatus: channelStatusToMemberStatus(
|
|
728
|
-
resolvedMember.
|
|
730
|
+
resolvedMember.status,
|
|
729
731
|
),
|
|
730
732
|
messagePreview: truncate(
|
|
731
733
|
trimmedContent,
|
|
@@ -745,7 +747,7 @@ export async function enforceIngressAcl(
|
|
|
745
747
|
}
|
|
746
748
|
|
|
747
749
|
const inactiveReplyText = guardianNotified
|
|
748
|
-
? `Hmm looks like you don't have access to talk to me. I'll let ${resolveGuardianLabel(sourceChannel)} know you tried talking to me and get back to you.`
|
|
750
|
+
? `Hmm looks like you don't have access to talk to me. I'll let ${await resolveGuardianLabel(sourceChannel)} know you tried talking to me and get back to you.`
|
|
749
751
|
: "Sorry, you haven't been approved to message this assistant.";
|
|
750
752
|
let inactiveReplyDelivered = false;
|
|
751
753
|
if (replyCallbackUrl) {
|
|
@@ -779,16 +781,16 @@ export async function enforceIngressAcl(
|
|
|
779
781
|
earlyResponse: {
|
|
780
782
|
accepted: true,
|
|
781
783
|
denied: true,
|
|
782
|
-
reason: `member_${channelStatusToMemberStatus(resolvedMember.
|
|
784
|
+
reason: `member_${channelStatusToMemberStatus(resolvedMember.status)}`,
|
|
783
785
|
...(!inactiveReplyDelivered && { replyText: inactiveReplyText }),
|
|
784
786
|
},
|
|
785
787
|
};
|
|
786
788
|
}
|
|
787
789
|
}
|
|
788
790
|
|
|
789
|
-
if (resolvedMember.
|
|
791
|
+
if (resolvedMember.policy === "deny") {
|
|
790
792
|
log.info(
|
|
791
|
-
{ sourceChannel, channelId: resolvedMember.
|
|
793
|
+
{ sourceChannel, channelId: resolvedMember.channelId },
|
|
792
794
|
"Ingress ACL: member policy deny",
|
|
793
795
|
);
|
|
794
796
|
const denyReplyText =
|
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
* focused on orchestration.
|
|
9
9
|
*/
|
|
10
10
|
import type { ChannelId, InterfaceId } from "../../../channels/types.js";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
getGuardianDelivery,
|
|
13
|
+
guardianForChannel,
|
|
14
|
+
} from "../../../contacts/guardian-delivery-reader.js";
|
|
12
15
|
import type { ServerMessage } from "../../../daemon/message-protocol.js";
|
|
13
16
|
import type { TrustContext } from "../../../daemon/trust-context.js";
|
|
14
17
|
import {
|
|
@@ -960,10 +963,15 @@ function startTrustedContactApprovalNotifier(params: {
|
|
|
960
963
|
|
|
961
964
|
if (info && !globalNotifiedApprovalRequestIds.has(info.requestId)) {
|
|
962
965
|
globalNotifiedApprovalRequestIds.set(info.requestId, conversationId);
|
|
963
|
-
|
|
964
|
-
const
|
|
965
|
-
|
|
966
|
-
);
|
|
966
|
+
// Gateway-resolved guardian display name (display-only).
|
|
967
|
+
const guardians = await getGuardianDelivery({
|
|
968
|
+
channelTypes: [sourceChannel],
|
|
969
|
+
});
|
|
970
|
+
const displayName = guardians
|
|
971
|
+
? (guardianForChannel(guardians, sourceChannel)?.displayName ??
|
|
972
|
+
undefined)
|
|
973
|
+
: undefined;
|
|
974
|
+
const guardianName = resolveGuardianName(displayName);
|
|
967
975
|
const waitingText = `Waiting for ${guardianName}'s approval...`;
|
|
968
976
|
try {
|
|
969
977
|
await deliverChannelReply(replyCallbackUrl, {
|
|
@@ -13,8 +13,8 @@ import { storePayload } from "../../../memory/delivery-crud.js";
|
|
|
13
13
|
import { emitNotificationSignal } from "../../../notifications/emit-signal.js";
|
|
14
14
|
import { getLogger } from "../../../util/logger.js";
|
|
15
15
|
import { getGuardianBinding } from "../../channel-verification-service.js";
|
|
16
|
+
import type { VerdictMember } from "../../trust-verdict-consumer.js";
|
|
16
17
|
import { GUARDIAN_APPROVAL_TTL_MS } from "../channel-route-shared.js";
|
|
17
|
-
import type { ResolvedMember } from "./acl-enforcement.js";
|
|
18
18
|
|
|
19
19
|
const log = getLogger("runtime-http");
|
|
20
20
|
|
|
@@ -23,7 +23,7 @@ const log = getLogger("runtime-http");
|
|
|
23
23
|
// ---------------------------------------------------------------------------
|
|
24
24
|
|
|
25
25
|
export interface EscalationInterceptParams {
|
|
26
|
-
resolvedMember:
|
|
26
|
+
resolvedMember: VerdictMember | null;
|
|
27
27
|
canonicalAssistantId: string;
|
|
28
28
|
sourceChannel: ChannelId;
|
|
29
29
|
sourceInterface: InterfaceId;
|
|
@@ -49,9 +49,9 @@ export interface EscalationInterceptParams {
|
|
|
49
49
|
* Returns a Response if the escalation was handled (the pipeline should
|
|
50
50
|
* short-circuit), or null to continue the pipeline.
|
|
51
51
|
*/
|
|
52
|
-
export function handleEscalationIntercept(
|
|
52
|
+
export async function handleEscalationIntercept(
|
|
53
53
|
params: EscalationInterceptParams,
|
|
54
|
-
): Record<string, unknown> | null {
|
|
54
|
+
): Promise<Record<string, unknown> | null> {
|
|
55
55
|
const {
|
|
56
56
|
resolvedMember,
|
|
57
57
|
canonicalAssistantId,
|
|
@@ -72,15 +72,15 @@ export function handleEscalationIntercept(
|
|
|
72
72
|
rawSenderId,
|
|
73
73
|
} = params;
|
|
74
74
|
|
|
75
|
-
if (resolvedMember?.
|
|
75
|
+
if (resolvedMember?.policy !== "escalate") {
|
|
76
76
|
return null;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
const binding = getGuardianBinding(canonicalAssistantId, sourceChannel);
|
|
79
|
+
const binding = await getGuardianBinding(canonicalAssistantId, sourceChannel);
|
|
80
80
|
if (!binding) {
|
|
81
81
|
// Fail-closed: can't escalate without a guardian to route to
|
|
82
82
|
log.info(
|
|
83
|
-
{ sourceChannel, channelId: resolvedMember.
|
|
83
|
+
{ sourceChannel, channelId: resolvedMember.channelId },
|
|
84
84
|
"Ingress ACL: escalate policy but no guardian binding, denying",
|
|
85
85
|
);
|
|
86
86
|
return {
|
|
@@ -4,7 +4,9 @@ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
4
4
|
// Mocks — must be set up before importing the module under test
|
|
5
5
|
// ---------------------------------------------------------------------------
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
// Gateway guardian-delivery list: empty = unbound, one entry = bound,
|
|
8
|
+
// null = gateway unreachable.
|
|
9
|
+
let mockGuardianList: Array<Record<string, unknown>> | null = [];
|
|
8
10
|
let mockActiveSession: Record<string, unknown> | null = null;
|
|
9
11
|
let mockSessionResult = {
|
|
10
12
|
sessionId: "sess-1",
|
|
@@ -20,8 +22,14 @@ let deliverChannelReplyCalls: unknown[][] = [];
|
|
|
20
22
|
let emitNotificationSignalCalls: unknown[] = [];
|
|
21
23
|
let messageIdCounter = 0;
|
|
22
24
|
|
|
23
|
-
mock.module("../../../contacts/
|
|
24
|
-
|
|
25
|
+
mock.module("../../../contacts/guardian-delivery-reader.js", () => ({
|
|
26
|
+
// Existence guard reads fresh (uncached) — only this variant is stubbed.
|
|
27
|
+
getGuardianDeliveryFresh: () => Promise.resolve(mockGuardianList),
|
|
28
|
+
guardianForChannel: (
|
|
29
|
+
list: Array<{ channelType: string; status: string }>,
|
|
30
|
+
channelType: string,
|
|
31
|
+
) =>
|
|
32
|
+
list.find((g) => g.channelType === channelType && g.status === "active"),
|
|
25
33
|
}));
|
|
26
34
|
|
|
27
35
|
mock.module("../../channel-verification-service.js", () => ({
|
|
@@ -96,7 +104,7 @@ function makeParams(
|
|
|
96
104
|
|
|
97
105
|
describe("handleGuardianActivationIntercept", () => {
|
|
98
106
|
beforeEach(() => {
|
|
99
|
-
|
|
107
|
+
mockGuardianList = [];
|
|
100
108
|
mockActiveSession = null;
|
|
101
109
|
mockSessionResult = {
|
|
102
110
|
sessionId: "sess-1",
|
|
@@ -155,10 +163,15 @@ describe("handleGuardianActivationIntercept", () => {
|
|
|
155
163
|
});
|
|
156
164
|
|
|
157
165
|
test("bare /start with existing guardian returns null", async () => {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
166
|
+
mockGuardianList = [{ channelType: "telegram", status: "active" }];
|
|
167
|
+
|
|
168
|
+
const result = await handleGuardianActivationIntercept(makeParams());
|
|
169
|
+
expect(result).toBeNull();
|
|
170
|
+
expect(createOutboundSessionCalls).toHaveLength(0);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("null guardian list (gateway unreachable) does NOT auto-start", async () => {
|
|
174
|
+
mockGuardianList = null;
|
|
162
175
|
|
|
163
176
|
const result = await handleGuardianActivationIntercept(makeParams());
|
|
164
177
|
expect(result).toBeNull();
|
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
* the guardian binding, and sends a success reply.
|
|
10
10
|
*/
|
|
11
11
|
import type { ChannelId } from "../../../channels/types.js";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
getGuardianDeliveryFresh,
|
|
14
|
+
guardianForChannel,
|
|
15
|
+
} from "../../../contacts/guardian-delivery-reader.js";
|
|
13
16
|
import { emitNotificationSignal } from "../../../notifications/emit-signal.js";
|
|
14
17
|
import { getLogger } from "../../../util/logger.js";
|
|
15
18
|
import {
|
|
@@ -88,8 +91,16 @@ export async function handleGuardianActivationIntercept(
|
|
|
88
91
|
// Only proceed for Telegram (can be extended later)
|
|
89
92
|
if (sourceChannel !== "telegram") return null;
|
|
90
93
|
|
|
91
|
-
// If a guardian already exists for this channel, continue to normal flow
|
|
92
|
-
|
|
94
|
+
// If a guardian already exists for this channel, continue to normal flow.
|
|
95
|
+
// Null-list (gateway unreachable) is treated as guardian-present so a
|
|
96
|
+
// transient miss does NOT spuriously auto-start verification.
|
|
97
|
+
// Read fresh: gateway-side binding writes don't invalidate the daemon cache.
|
|
98
|
+
const guardianList = await getGuardianDeliveryFresh({
|
|
99
|
+
channelTypes: [sourceChannel],
|
|
100
|
+
});
|
|
101
|
+
if (guardianList === null || guardianForChannel(guardianList, sourceChannel)) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
93
104
|
|
|
94
105
|
// Can't bind a session without sender identity
|
|
95
106
|
if (!rawSenderId) return null;
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
import type { SourceMetadata } from "@vellumai/gateway-client";
|
|
19
19
|
|
|
20
20
|
import type { ChannelId, InterfaceId } from "../../../channels/types.js";
|
|
21
|
+
import { getGuardianDeliveryFresh } from "../../../contacts/guardian-delivery-reader.js";
|
|
21
22
|
import { getDiskPressureStatus } from "../../../daemon/disk-pressure-guard.js";
|
|
22
23
|
import { classifyDiskPressureTurnPolicy } from "../../../daemon/disk-pressure-policy.js";
|
|
23
24
|
import { addMessage } from "../../../memory/conversation-crud.js";
|
|
@@ -35,6 +36,7 @@ import {
|
|
|
35
36
|
import { getLogger } from "../../../util/logger.js";
|
|
36
37
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../../assistant-scope.js";
|
|
37
38
|
import type { ApprovalConversationGenerator } from "../../http-types.js";
|
|
39
|
+
import { setMemberVerdict } from "../../member-verdict-cache.js";
|
|
38
40
|
import { resolveTrustContext } from "../../trust-context-resolver.js";
|
|
39
41
|
import { handleGuardianReplyIntercept } from "./guardian-reply-intercept.js";
|
|
40
42
|
|
|
@@ -126,6 +128,23 @@ export async function handleSlackReactionIntercept(
|
|
|
126
128
|
approvalConversationGenerator,
|
|
127
129
|
} = params;
|
|
128
130
|
|
|
131
|
+
// Warm the channel-specific guardian-delivery cache before the SYNC trust
|
|
132
|
+
// resolve below. The sync resolver reads the IO-free cache snapshot; on a
|
|
133
|
+
// cold process only `vellum` is warmed at startup, so a Slack guardian
|
|
134
|
+
// reaction would otherwise misclassify as `unknown` and drop. Read fresh:
|
|
135
|
+
// gateway-side binding writes don't invalidate the daemon cache, so a stale
|
|
136
|
+
// empty snapshot would otherwise survive the TTL. This await runs in the
|
|
137
|
+
// already-async intercept, off the sync resolver's hot path.
|
|
138
|
+
await getGuardianDeliveryFresh({ channelTypes: [sourceChannel] });
|
|
139
|
+
|
|
140
|
+
// Reactions carry the gateway-stamped verdict but skip getInboundTrustVerdict,
|
|
141
|
+
// which warms the member-verdict cache the sync resolver reads. Seed it from
|
|
142
|
+
// the stamped verdict so an active non-guardian contact's reaction resolves
|
|
143
|
+
// instead of failing closed to `unknown` on a cold cache.
|
|
144
|
+
if (sourceMetadata?.trustVerdict) {
|
|
145
|
+
setMemberVerdict(sourceChannel, rawSenderId, sourceMetadata.trustVerdict);
|
|
146
|
+
}
|
|
147
|
+
|
|
129
148
|
// Classify the reactor. No timezone enrichment — reactions never drive an
|
|
130
149
|
// agent turn, so only the trust class / guardian principal matter.
|
|
131
150
|
const trustCtx = resolveTrustContext({
|
|
@@ -102,6 +102,7 @@ import { ROUTES as OAUTH_COMMANDS_ROUTES } from "./oauth-commands-routes.js";
|
|
|
102
102
|
import { ROUTES as OAUTH_CONNECT_ROUTES } from "./oauth-connect-routes.js";
|
|
103
103
|
import { ROUTES as OAUTH_LIFECYCLE_ROUTES } from "./oauth-lifecycle-routes.js";
|
|
104
104
|
import { ROUTES as OAUTH_PROVIDERS_ROUTES } from "./oauth-providers.js";
|
|
105
|
+
import { ROUTES as ONBOARDING_CHECKIN_ROUTES } from "./onboarding-checkin-routes.js";
|
|
105
106
|
import { ROUTES as PLATFORM_ROUTES } from "./platform-routes.js";
|
|
106
107
|
import { ROUTES as PLAYGROUND_ROUTES } from "./playground/index.js";
|
|
107
108
|
import { ROUTES as PLUGINS_ROUTES } from "./plugins-routes.js";
|
|
@@ -234,6 +235,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
234
235
|
...OAUTH_LIFECYCLE_ROUTES,
|
|
235
236
|
...OAUTH_COMMANDS_ROUTES,
|
|
236
237
|
...OAUTH_PROVIDERS_ROUTES,
|
|
238
|
+
...ONBOARDING_CHECKIN_ROUTES,
|
|
237
239
|
...PLATFORM_ROUTES,
|
|
238
240
|
...PLAYGROUND_ROUTES,
|
|
239
241
|
...PLUGINS_ROUTES,
|
|
@@ -40,10 +40,31 @@ export interface LlmContextSection {
|
|
|
40
40
|
language?: string;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Structured error extracted from a rejected call's response payload.
|
|
45
|
+
* Mirrors the on-disk `responsePayload.error` shape produced by
|
|
46
|
+
* `buildProviderErrorResponsePayload`. Present only when the call failed
|
|
47
|
+
* before returning a response — consumers branch on its presence to
|
|
48
|
+
* render the call as failed.
|
|
49
|
+
*/
|
|
50
|
+
export interface LlmContextError {
|
|
51
|
+
name?: string;
|
|
52
|
+
message?: string;
|
|
53
|
+
code?: string;
|
|
54
|
+
provider?: string;
|
|
55
|
+
statusCode?: number;
|
|
56
|
+
retryAfterMs?: number;
|
|
57
|
+
apiErrorCode?: string;
|
|
58
|
+
apiErrorType?: string;
|
|
59
|
+
apiErrorParam?: string;
|
|
60
|
+
requestId?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
43
63
|
export interface LlmContextNormalizationResult {
|
|
44
64
|
summary?: LlmContextSummary;
|
|
45
65
|
requestSections?: LlmContextSection[];
|
|
46
66
|
responseSections?: LlmContextSection[];
|
|
67
|
+
error?: LlmContextError;
|
|
47
68
|
}
|
|
48
69
|
|
|
49
70
|
interface NormalizedPayloadCandidate {
|
|
@@ -55,6 +76,68 @@ interface NormalizedPayloadCandidate {
|
|
|
55
76
|
|
|
56
77
|
export function normalizeLlmContextPayloads(
|
|
57
78
|
input: LlmContextNormalizationInput,
|
|
79
|
+
): LlmContextNormalizationResult {
|
|
80
|
+
const base = normalizeSuccessPayloads(input);
|
|
81
|
+
const error = normalizeProviderErrorPayload(input.responsePayload);
|
|
82
|
+
if (!error) {
|
|
83
|
+
return base;
|
|
84
|
+
}
|
|
85
|
+
// A rejected call has no response sections to render — only the request
|
|
86
|
+
// side normalized. Attach the structured error so the inspector can show
|
|
87
|
+
// a failure banner and treat the cost as $0.00 instead of silently
|
|
88
|
+
// falling back to "section rendering unavailable".
|
|
89
|
+
return { ...base, error };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Detect a provider/transport error stored in the response payload.
|
|
94
|
+
*
|
|
95
|
+
* Error rows are written as `{ error: { name, message, code?, provider?,
|
|
96
|
+
* statusCode?, retryAfterMs? } }` by `buildProviderErrorResponsePayload`.
|
|
97
|
+
* Successful provider responses never carry a top-level `error` object, so
|
|
98
|
+
* the presence of one (with at least one identifying field) is a reliable
|
|
99
|
+
* signal that the call failed.
|
|
100
|
+
*/
|
|
101
|
+
function normalizeProviderErrorPayload(
|
|
102
|
+
responsePayload: unknown,
|
|
103
|
+
): LlmContextError | null {
|
|
104
|
+
const error = asRecord(asRecord(responsePayload)?.error);
|
|
105
|
+
if (!error) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const name = asString(error.name);
|
|
110
|
+
const message = asString(error.message);
|
|
111
|
+
const code = asString(error.code);
|
|
112
|
+
// Require at least one identifying field so an unrelated `error` key on a
|
|
113
|
+
// success payload isn't misread as a provider failure.
|
|
114
|
+
if (name === undefined && message === undefined && code === undefined) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const normalized: LlmContextError = {};
|
|
119
|
+
if (name !== undefined) normalized.name = name;
|
|
120
|
+
if (message !== undefined) normalized.message = message;
|
|
121
|
+
if (code !== undefined) normalized.code = code;
|
|
122
|
+
const provider = asString(error.provider);
|
|
123
|
+
if (provider !== undefined) normalized.provider = provider;
|
|
124
|
+
const statusCode = asNumber(error.statusCode);
|
|
125
|
+
if (statusCode !== undefined) normalized.statusCode = statusCode;
|
|
126
|
+
const retryAfterMs = asNumber(error.retryAfterMs);
|
|
127
|
+
if (retryAfterMs !== undefined) normalized.retryAfterMs = retryAfterMs;
|
|
128
|
+
const apiErrorCode = asString(error.apiErrorCode);
|
|
129
|
+
if (apiErrorCode !== undefined) normalized.apiErrorCode = apiErrorCode;
|
|
130
|
+
const apiErrorType = asString(error.apiErrorType);
|
|
131
|
+
if (apiErrorType !== undefined) normalized.apiErrorType = apiErrorType;
|
|
132
|
+
const apiErrorParam = asString(error.apiErrorParam);
|
|
133
|
+
if (apiErrorParam !== undefined) normalized.apiErrorParam = apiErrorParam;
|
|
134
|
+
const requestId = asString(error.requestId);
|
|
135
|
+
if (requestId !== undefined) normalized.requestId = requestId;
|
|
136
|
+
return normalized;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function normalizeSuccessPayloads(
|
|
140
|
+
input: LlmContextNormalizationInput,
|
|
58
141
|
): LlmContextNormalizationResult {
|
|
59
142
|
const requestCandidates = [
|
|
60
143
|
normalizeOpenAiRequestPayload(input.requestPayload),
|