@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
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verifies the agent loop's provider-native web-search gate (used by the
|
|
3
|
+
* tool-less advisor consult): when `enableNativeWebSearch` is set, the loop
|
|
4
|
+
* appends a `web_search`-named SERVER tool to the outbound request and forces
|
|
5
|
+
* `tool_choice: auto` — but ONLY when the provider/model the call ACTUALLY
|
|
6
|
+
* routes to reports native-search support. A non-native target gets nothing,
|
|
7
|
+
* and the consult stays tool-less (no client `web_search` tool surfaced).
|
|
8
|
+
*
|
|
9
|
+
* The gate prefers the routing-aware `supportsNativeWebSearchFor(options)` (the
|
|
10
|
+
* routed (provider, model)'s capability) over the construction-time
|
|
11
|
+
* `supportsNativeWebSearch` snapshot. The advisor's `advisorProfile` can route
|
|
12
|
+
* `subagentSpawn` to a provider/model whose native-search support DIFFERS from
|
|
13
|
+
* the default, so the routed capability — not the default's — must drive the
|
|
14
|
+
* decision in both directions. Drives the REAL loop, mocking only the provider
|
|
15
|
+
* boundary.
|
|
16
|
+
*/
|
|
17
|
+
import { describe, expect, test } from "bun:test";
|
|
18
|
+
|
|
19
|
+
import { createMockProvider } from "../__tests__/helpers/mock-provider.js";
|
|
20
|
+
import type {
|
|
21
|
+
Provider,
|
|
22
|
+
ProviderResponse,
|
|
23
|
+
SendMessageOptions,
|
|
24
|
+
} from "../providers/types.js";
|
|
25
|
+
import { AgentLoop } from "./loop.js";
|
|
26
|
+
|
|
27
|
+
const endTurn = (text: string): ProviderResponse => ({
|
|
28
|
+
content: [{ type: "text", text }],
|
|
29
|
+
model: "mock-model",
|
|
30
|
+
usage: { inputTokens: 1, outputTokens: 1 },
|
|
31
|
+
stopReason: "end_turn",
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const baseRun = {
|
|
35
|
+
requestId: "req-web",
|
|
36
|
+
onEvent: () => {},
|
|
37
|
+
callSite: "subagentSpawn" as const,
|
|
38
|
+
trust: { sourceChannel: "vellum" as const, trustClass: "unknown" as const },
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const userMessages = [
|
|
42
|
+
{
|
|
43
|
+
role: "user" as const,
|
|
44
|
+
content: [{ type: "text" as const, text: "advise" }],
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
/** Build a tool-less loop (mirrors the advisor consult) with the given flag. */
|
|
49
|
+
function buildAdvisorLoop(
|
|
50
|
+
provider: Provider,
|
|
51
|
+
enableNativeWebSearch: boolean,
|
|
52
|
+
): AgentLoop {
|
|
53
|
+
return new AgentLoop({
|
|
54
|
+
provider,
|
|
55
|
+
systemPrompt: "advisor system",
|
|
56
|
+
conversationId: "advisor-1",
|
|
57
|
+
// Tool-less for client tools — exactly the advisor role's empty allowlist.
|
|
58
|
+
config: { enableNativeWebSearch },
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
describe("AgentLoop — provider-native web search gate", () => {
|
|
63
|
+
test("attaches the native web_search server tool when the provider supports it", async () => {
|
|
64
|
+
const { provider, calls } = createMockProvider([endTurn("guidance")]);
|
|
65
|
+
(
|
|
66
|
+
provider as { supportsNativeWebSearch?: boolean }
|
|
67
|
+
).supportsNativeWebSearch = true;
|
|
68
|
+
|
|
69
|
+
const loop = buildAdvisorLoop(provider, true);
|
|
70
|
+
await loop.run({ ...baseRun, messages: userMessages });
|
|
71
|
+
|
|
72
|
+
expect(calls).toHaveLength(1);
|
|
73
|
+
const sent = calls[0];
|
|
74
|
+
// The native web_search SERVER tool is the only tool surfaced.
|
|
75
|
+
expect(sent.tools?.map((t) => t.name)).toEqual(["web_search"]);
|
|
76
|
+
// tool_choice is forced to auto so the model may invoke the search.
|
|
77
|
+
expect(sent.options?.config?.tool_choice).toEqual({ type: "auto" });
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("attaches nothing on a non-native provider (consult stays tool-less)", async () => {
|
|
81
|
+
const { provider, calls } = createMockProvider([endTurn("guidance")]);
|
|
82
|
+
// supportsNativeWebSearch is absent (falsy) — a non-native provider.
|
|
83
|
+
|
|
84
|
+
const loop = buildAdvisorLoop(provider, true);
|
|
85
|
+
await loop.run({ ...baseRun, messages: userMessages });
|
|
86
|
+
|
|
87
|
+
expect(calls).toHaveLength(1);
|
|
88
|
+
const sent = calls[0];
|
|
89
|
+
// No web_search tool surfaced — no client tool the one-shot consult can't run.
|
|
90
|
+
expect(sent.tools).toBeUndefined();
|
|
91
|
+
// No tool_choice forced when nothing is attached.
|
|
92
|
+
expect(sent.options?.config?.tool_choice).toBeUndefined();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("attaches nothing when the flag is off, even on a native provider", async () => {
|
|
96
|
+
const { provider, calls } = createMockProvider([endTurn("guidance")]);
|
|
97
|
+
(
|
|
98
|
+
provider as { supportsNativeWebSearch?: boolean }
|
|
99
|
+
).supportsNativeWebSearch = true;
|
|
100
|
+
|
|
101
|
+
const loop = buildAdvisorLoop(provider, false);
|
|
102
|
+
await loop.run({ ...baseRun, messages: userMessages });
|
|
103
|
+
|
|
104
|
+
expect(calls).toHaveLength(1);
|
|
105
|
+
const sent = calls[0];
|
|
106
|
+
expect(sent.tools).toBeUndefined();
|
|
107
|
+
expect(sent.options?.config?.tool_choice).toBeUndefined();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("does not duplicate web_search when a tool of that name is already present", async () => {
|
|
111
|
+
const { provider, calls } = createMockProvider([endTurn("guidance")]);
|
|
112
|
+
(
|
|
113
|
+
provider as { supportsNativeWebSearch?: boolean }
|
|
114
|
+
).supportsNativeWebSearch = true;
|
|
115
|
+
|
|
116
|
+
// A loop that already exposes a `web_search` client tool (e.g. researcher
|
|
117
|
+
// role). The gate must not append a second `web_search` entry.
|
|
118
|
+
const loop = new AgentLoop({
|
|
119
|
+
provider,
|
|
120
|
+
systemPrompt: "sys",
|
|
121
|
+
conversationId: "advisor-dup",
|
|
122
|
+
config: { enableNativeWebSearch: true },
|
|
123
|
+
tools: [
|
|
124
|
+
{
|
|
125
|
+
name: "web_search",
|
|
126
|
+
description: "",
|
|
127
|
+
input_schema: { type: "object" },
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
});
|
|
131
|
+
await loop.run({ ...baseRun, messages: userMessages });
|
|
132
|
+
|
|
133
|
+
const sent = calls[0];
|
|
134
|
+
expect(sent.tools?.filter((t) => t.name === "web_search")).toHaveLength(1);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// ── Routed capability drives the decision (not the static default) ────────
|
|
138
|
+
|
|
139
|
+
test("false positive: static flag is native but the ROUTED target is not — attaches nothing", async () => {
|
|
140
|
+
const { provider, calls } = createMockProvider([endTurn("guidance")]);
|
|
141
|
+
// The construction-time default supports native search…
|
|
142
|
+
(
|
|
143
|
+
provider as { supportsNativeWebSearch?: boolean }
|
|
144
|
+
).supportsNativeWebSearch = true;
|
|
145
|
+
// …but the advisorProfile routes `subagentSpawn` to a provider/model that
|
|
146
|
+
// does NOT. The routing-aware probe wins, so no unexecutable client tool is
|
|
147
|
+
// surfaced to the otherwise tool-less advisor.
|
|
148
|
+
(
|
|
149
|
+
provider as {
|
|
150
|
+
supportsNativeWebSearchFor?: (o?: SendMessageOptions) => boolean;
|
|
151
|
+
}
|
|
152
|
+
).supportsNativeWebSearchFor = () => false;
|
|
153
|
+
|
|
154
|
+
const loop = buildAdvisorLoop(provider, true);
|
|
155
|
+
await loop.run({ ...baseRun, messages: userMessages });
|
|
156
|
+
|
|
157
|
+
const sent = calls[0];
|
|
158
|
+
expect(sent.tools).toBeUndefined();
|
|
159
|
+
expect(sent.options?.config?.tool_choice).toBeUndefined();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test("false negative: static flag is non-native but the ROUTED target is — attaches the tool", async () => {
|
|
163
|
+
const { provider, calls } = createMockProvider([endTurn("guidance")]);
|
|
164
|
+
// The construction-time default lacks native search (flag absent/falsy)…
|
|
165
|
+
// …but the advisorProfile routes to a provider/model that has it.
|
|
166
|
+
(
|
|
167
|
+
provider as {
|
|
168
|
+
supportsNativeWebSearchFor?: (o?: SendMessageOptions) => boolean;
|
|
169
|
+
}
|
|
170
|
+
).supportsNativeWebSearchFor = () => true;
|
|
171
|
+
|
|
172
|
+
const loop = buildAdvisorLoop(provider, true);
|
|
173
|
+
await loop.run({ ...baseRun, messages: userMessages });
|
|
174
|
+
|
|
175
|
+
const sent = calls[0];
|
|
176
|
+
expect(sent.tools?.map((t) => t.name)).toEqual(["web_search"]);
|
|
177
|
+
expect(sent.options?.config?.tool_choice).toEqual({ type: "auto" });
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("the routing probe receives the loop's callSite", async () => {
|
|
181
|
+
const { provider, calls } = createMockProvider([endTurn("guidance")]);
|
|
182
|
+
const probeOptions: (SendMessageOptions | undefined)[] = [];
|
|
183
|
+
(
|
|
184
|
+
provider as {
|
|
185
|
+
supportsNativeWebSearchFor?: (o?: SendMessageOptions) => boolean;
|
|
186
|
+
}
|
|
187
|
+
).supportsNativeWebSearchFor = (o) => {
|
|
188
|
+
probeOptions.push(o);
|
|
189
|
+
return true;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const loop = buildAdvisorLoop(provider, true);
|
|
193
|
+
await loop.run({ ...baseRun, messages: userMessages });
|
|
194
|
+
|
|
195
|
+
expect(calls).toHaveLength(1);
|
|
196
|
+
// The probe is resolved against the same callSite the dispatch uses
|
|
197
|
+
// (`subagentSpawn` per `baseRun`), so the routed (provider, model) matches.
|
|
198
|
+
expect(probeOptions[0]?.config?.callSite).toBe("subagentSpawn");
|
|
199
|
+
});
|
|
200
|
+
});
|
package/src/agent/loop.ts
CHANGED
|
@@ -95,6 +95,74 @@ export interface AgentLoopConfig {
|
|
|
95
95
|
minTurnIntervalMs?: number;
|
|
96
96
|
/** Override the default prompt cache TTL sent to the provider (e.g. "5m" for short-lived subagents). */
|
|
97
97
|
cacheTtl?: "5m" | "1h";
|
|
98
|
+
/**
|
|
99
|
+
* Give every LLM call provider-native (server-side) web search, gated on the
|
|
100
|
+
* native-search capability of the (provider, model) the call routes to —
|
|
101
|
+
* {@link Provider.supportsNativeWebSearchFor} when the provider exposes it,
|
|
102
|
+
* else the static {@link Provider.supportsNativeWebSearch} flag.
|
|
103
|
+
* When both are true, the loop appends a `web_search`-named tool to the
|
|
104
|
+
* outbound request — which Anthropic/OpenAI substitute for their server-side
|
|
105
|
+
* search tool, running the search inline and returning results without a
|
|
106
|
+
* client tool round-trip — and forces `tool_choice: auto` so the model may
|
|
107
|
+
* call it. Non-native providers get nothing.
|
|
108
|
+
*
|
|
109
|
+
* This is a SERVER tool the provider runs itself, distinct from the client
|
|
110
|
+
* tool list (`tools` / `resolveTools`): it is never executed by
|
|
111
|
+
* {@link AgentLoopConstructorOptions.toolExecutor} and does not require any
|
|
112
|
+
* client-tool allowlist entry. Used by the tool-less advisor consult to
|
|
113
|
+
* ground its guidance with live web access while staying one-shot for client
|
|
114
|
+
* tools. Defaults to false — existing behavior.
|
|
115
|
+
*/
|
|
116
|
+
enableNativeWebSearch?: boolean;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* The `web_search`-named tool the loop appends when
|
|
121
|
+
* {@link AgentLoopConfig.enableNativeWebSearch} is set on a native provider.
|
|
122
|
+
* Anthropic/OpenAI intercept a tool with this name and substitute their own
|
|
123
|
+
* server-side web search (run inline, no client execution), so the exact
|
|
124
|
+
* `input_schema` is informational — the provider supplies the real schema.
|
|
125
|
+
*/
|
|
126
|
+
const NATIVE_WEB_SEARCH_TOOL: ToolDefinition = {
|
|
127
|
+
name: "web_search",
|
|
128
|
+
description:
|
|
129
|
+
"Search the web for current information to ground your response.",
|
|
130
|
+
input_schema: {
|
|
131
|
+
type: "object",
|
|
132
|
+
properties: {
|
|
133
|
+
query: { type: "string", description: "The search query." },
|
|
134
|
+
},
|
|
135
|
+
required: ["query"],
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Build the minimal `SendMessageOptions` a routing-aware provider needs to
|
|
141
|
+
* report the native web-search capability of the (provider, model) THIS turn
|
|
142
|
+
* routes to. Mirrors the call-site fields the loop plumbs onto the actual send
|
|
143
|
+
* (`callSite` + `overrideProfile`/`forceOverrideProfile` + per-conversation
|
|
144
|
+
* `selectionSeed`) so the capability probe and the dispatch resolve the same
|
|
145
|
+
* arm. Returns `undefined` when there is no `callSite` (the legacy
|
|
146
|
+
* default-provider path); `selectionSeed` is omitted for standalone loops with
|
|
147
|
+
* no conversation id, matching the dispatch path's own guard.
|
|
148
|
+
*/
|
|
149
|
+
function buildNativeWebSearchProbeOptions(
|
|
150
|
+
callSite: LLMCallSite | undefined,
|
|
151
|
+
overrideProfile: string | undefined,
|
|
152
|
+
forceOverrideProfile: boolean,
|
|
153
|
+
conversationId: string | undefined,
|
|
154
|
+
): SendMessageOptions | undefined {
|
|
155
|
+
if (!callSite) return undefined;
|
|
156
|
+
return {
|
|
157
|
+
config: {
|
|
158
|
+
callSite,
|
|
159
|
+
...(overrideProfile ? { overrideProfile } : {}),
|
|
160
|
+
...(overrideProfile && forceOverrideProfile
|
|
161
|
+
? { forceOverrideProfile: true }
|
|
162
|
+
: {}),
|
|
163
|
+
...(conversationId ? { selectionSeed: conversationId } : {}),
|
|
164
|
+
},
|
|
165
|
+
};
|
|
98
166
|
}
|
|
99
167
|
|
|
100
168
|
export interface CheckpointInfo {
|
|
@@ -625,6 +693,20 @@ export type LoopToolExecutor = (
|
|
|
625
693
|
activityMetadata?: ToolActivityMetadata;
|
|
626
694
|
}>;
|
|
627
695
|
|
|
696
|
+
/**
|
|
697
|
+
* The benign result returned for a sibling tool call that was deferred because
|
|
698
|
+
* an exclusive tool ran in the same turn. Phrased so the model treats it as a
|
|
699
|
+
* "not run yet" signal — read the exclusive tool's output, then re-issue this
|
|
700
|
+
* call if it is still the right next step.
|
|
701
|
+
*/
|
|
702
|
+
function deferredForExclusiveMessage(exclusiveToolName: string): string {
|
|
703
|
+
return (
|
|
704
|
+
`(not run: \`${exclusiveToolName}\` was called this turn and runs first, on its own, ` +
|
|
705
|
+
`so the rest of your tool calls were held back. Read its output, then call this tool ` +
|
|
706
|
+
`again if it is still the right next step.)`
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
|
|
628
710
|
export interface AgentLoopConstructorOptions {
|
|
629
711
|
/** LLM provider the loop issues every call through. */
|
|
630
712
|
provider: Provider;
|
|
@@ -634,6 +716,14 @@ export interface AgentLoopConstructorOptions {
|
|
|
634
716
|
tools?: ToolDefinition[];
|
|
635
717
|
toolExecutor?: LoopToolExecutor;
|
|
636
718
|
resolveTools?: (history: Message[]) => ToolDefinition[];
|
|
719
|
+
/**
|
|
720
|
+
* Decide whether a tool runs exclusively in its turn (see
|
|
721
|
+
* {@link ToolDefinition.exclusive}). When it returns true for a tool present
|
|
722
|
+
* in a multi-call turn, the loop runs only that tool and defers the siblings
|
|
723
|
+
* un-run. Injected by the conversation wiring, which can read the tool
|
|
724
|
+
* registry; lightweight loops that omit it never defer.
|
|
725
|
+
*/
|
|
726
|
+
isExclusiveTool?: (toolName: string) => boolean;
|
|
637
727
|
/**
|
|
638
728
|
* Conversation this loop drives. Scopes the loop-held compaction circuit
|
|
639
729
|
* breaker and is the source of truth the loop's pipeline contexts and
|
|
@@ -659,6 +749,7 @@ export class AgentLoop {
|
|
|
659
749
|
private tools: ToolDefinition[];
|
|
660
750
|
private resolveTools: ((history: Message[]) => ToolDefinition[]) | null;
|
|
661
751
|
private toolExecutor: LoopToolExecutor | null;
|
|
752
|
+
private isExclusiveTool: ((toolName: string) => boolean) | null;
|
|
662
753
|
|
|
663
754
|
/**
|
|
664
755
|
* Conversation this loop drives. Source of truth for the `conversationId`
|
|
@@ -688,6 +779,7 @@ export class AgentLoop {
|
|
|
688
779
|
tools,
|
|
689
780
|
toolExecutor,
|
|
690
781
|
resolveTools,
|
|
782
|
+
isExclusiveTool,
|
|
691
783
|
conversationId,
|
|
692
784
|
resolveConversationDir,
|
|
693
785
|
} = options;
|
|
@@ -697,6 +789,7 @@ export class AgentLoop {
|
|
|
697
789
|
this.tools = tools ?? [];
|
|
698
790
|
this.resolveTools = resolveTools ?? null;
|
|
699
791
|
this.toolExecutor = toolExecutor ?? null;
|
|
792
|
+
this.isExclusiveTool = isExclusiveTool ?? null;
|
|
700
793
|
this.conversationId = conversationId;
|
|
701
794
|
this.resolveConversationDir = resolveConversationDir ?? null;
|
|
702
795
|
this.compactionCircuit = new CompactionCircuit(this.conversationId);
|
|
@@ -1215,10 +1308,44 @@ export class AgentLoop {
|
|
|
1215
1308
|
|
|
1216
1309
|
// Resolve tools for this turn: use the dynamic resolver if provided,
|
|
1217
1310
|
// otherwise fall back to the static tool list.
|
|
1218
|
-
const
|
|
1311
|
+
const resolvedTools = this.resolveTools
|
|
1219
1312
|
? this.resolveTools(history)
|
|
1220
1313
|
: this.tools;
|
|
1221
1314
|
|
|
1315
|
+
// Provider-native web search: append a `web_search`-named tool that the
|
|
1316
|
+
// provider substitutes for its server-side search (run inline, no client
|
|
1317
|
+
// execution), gated STRICTLY on the capability of the provider/model
|
|
1318
|
+
// this call ACTUALLY routes to so a non-native provider never sees an
|
|
1319
|
+
// unexecutable client tool. The advisor consult's `advisorProfile` can
|
|
1320
|
+
// route `subagentSpawn` to a provider/model whose native-search support
|
|
1321
|
+
// differs from the construction-time default, so the gate resolves the
|
|
1322
|
+
// routed target (callSite + overrideProfile) via
|
|
1323
|
+
// `supportsNativeWebSearchFor` rather than the static
|
|
1324
|
+
// `this.provider.supportsNativeWebSearch` snapshot; providers without
|
|
1325
|
+
// the routing-aware probe fall back to the static flag. This is a SERVER
|
|
1326
|
+
// tool — it bypasses the client allowlist and the tool executor — so the
|
|
1327
|
+
// tool-less advisor consult can ground its guidance with live web access
|
|
1328
|
+
// while staying one-shot for client tools. Skip when a `web_search` tool
|
|
1329
|
+
// is already present so we never duplicate the name.
|
|
1330
|
+
const supportsRoutedNativeWebSearch = this.provider
|
|
1331
|
+
.supportsNativeWebSearchFor
|
|
1332
|
+
? this.provider.supportsNativeWebSearchFor(
|
|
1333
|
+
buildNativeWebSearchProbeOptions(
|
|
1334
|
+
callSite,
|
|
1335
|
+
resolveEffectiveOverrideProfile(),
|
|
1336
|
+
forceOverrideProfile,
|
|
1337
|
+
this.conversationId,
|
|
1338
|
+
),
|
|
1339
|
+
)
|
|
1340
|
+
: this.provider.supportsNativeWebSearch === true;
|
|
1341
|
+
const attachNativeWebSearch =
|
|
1342
|
+
this.config.enableNativeWebSearch === true &&
|
|
1343
|
+
supportsRoutedNativeWebSearch &&
|
|
1344
|
+
!resolvedTools.some((t) => t.name === NATIVE_WEB_SEARCH_TOOL.name);
|
|
1345
|
+
const currentTools = attachNativeWebSearch
|
|
1346
|
+
? [...resolvedTools, NATIVE_WEB_SEARCH_TOOL]
|
|
1347
|
+
: resolvedTools;
|
|
1348
|
+
|
|
1222
1349
|
// Field precedence (highest wins):
|
|
1223
1350
|
// 1. Per-run explicit (`runModel`)
|
|
1224
1351
|
// 2. Call-site resolved values (filled by
|
|
@@ -1261,6 +1388,11 @@ export class AgentLoop {
|
|
|
1261
1388
|
|
|
1262
1389
|
if (this.config.toolChoice) {
|
|
1263
1390
|
providerConfig.tool_choice = this.config.toolChoice;
|
|
1391
|
+
} else if (attachNativeWebSearch) {
|
|
1392
|
+
// The native web-search tool is the only tool on this turn (the
|
|
1393
|
+
// advisor consult is otherwise tool-less). Let the model decide
|
|
1394
|
+
// whether to search rather than forcing it.
|
|
1395
|
+
providerConfig.tool_choice = { type: "auto" };
|
|
1264
1396
|
}
|
|
1265
1397
|
|
|
1266
1398
|
if (this.config.cacheTtl) {
|
|
@@ -1883,8 +2015,39 @@ export class AgentLoop {
|
|
|
1883
2015
|
"Tool execution start",
|
|
1884
2016
|
);
|
|
1885
2017
|
|
|
2018
|
+
// When an exclusive tool (e.g. the advisor) is among this turn's calls,
|
|
2019
|
+
// it must run alone: the model should incorporate its output before
|
|
2020
|
+
// acting on anything else. Run only the first exclusive call and defer
|
|
2021
|
+
// the siblings with a benign, un-run result so the model re-issues them
|
|
2022
|
+
// next turn if still needed. Every tool_use still gets a matching
|
|
2023
|
+
// tool_result, so history stays well-formed.
|
|
2024
|
+
const exclusiveBlock = this.isExclusiveTool
|
|
2025
|
+
? toolUseBlocks.find((block) => this.isExclusiveTool!(block.name))
|
|
2026
|
+
: undefined;
|
|
2027
|
+
const deferSiblings =
|
|
2028
|
+
exclusiveBlock !== undefined && toolUseBlocks.length > 1;
|
|
2029
|
+
if (deferSiblings) {
|
|
2030
|
+
rlog.info(
|
|
2031
|
+
{
|
|
2032
|
+
turn: toolUseTurns,
|
|
2033
|
+
exclusiveTool: exclusiveBlock!.name,
|
|
2034
|
+
deferred: toolUseBlocks
|
|
2035
|
+
.filter((block) => block !== exclusiveBlock)
|
|
2036
|
+
.map((block) => block.name),
|
|
2037
|
+
},
|
|
2038
|
+
"Exclusive tool present — running it alone and deferring sibling tool calls this turn",
|
|
2039
|
+
);
|
|
2040
|
+
}
|
|
2041
|
+
|
|
1886
2042
|
const toolExecutionPromise = Promise.all(
|
|
1887
2043
|
toolUseBlocks.map(async (toolUse) => {
|
|
2044
|
+
if (deferSiblings && toolUse !== exclusiveBlock) {
|
|
2045
|
+
const result: Awaited<ReturnType<LoopToolExecutor>> = {
|
|
2046
|
+
content: deferredForExclusiveMessage(exclusiveBlock!.name),
|
|
2047
|
+
isError: false,
|
|
2048
|
+
};
|
|
2049
|
+
return { toolUse, result };
|
|
2050
|
+
}
|
|
1888
2051
|
const result = await this.toolExecutor!(
|
|
1889
2052
|
toolUse.name,
|
|
1890
2053
|
toolUse.input,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maximum number of events the daemon's per-process SSE replay ring
|
|
3
|
+
* retains for `Last-Event-ID` resume. This is the ring's count bound; the
|
|
4
|
+
* ring is also bounded by total bytes and entry age (whichever limit is
|
|
5
|
+
* hit first wins), so the live ring can hold *fewer* than this many
|
|
6
|
+
* events, never more. The daemon-side definition and eviction live in
|
|
7
|
+
* `assistant/src/runtime/assistant-stream-state.ts`.
|
|
8
|
+
*
|
|
9
|
+
* Exposed on the API surface so the web client's SSE consumer can size
|
|
10
|
+
* its seq-gap tolerance against the same number the daemon buffers
|
|
11
|
+
* against, instead of hard-coding a duplicate.
|
|
12
|
+
*
|
|
13
|
+
* A live seq gap smaller than this is benign: the global per-assistant
|
|
14
|
+
* `seq` counter is stamped before fanout, but the hub deliberately
|
|
15
|
+
* withholds some events from a given subscriber — self-echo-suppressed
|
|
16
|
+
* `sync_changed` (a client's own mutation echo) and capability-targeted
|
|
17
|
+
* host-proxy events — so a subscriber legitimately sees its cursor skip a
|
|
18
|
+
* few seqs it was never going to receive. Such a hole is not data loss
|
|
19
|
+
* and must not trigger a destructive authoritative snapshot heal. Only a
|
|
20
|
+
* gap that meets or exceeds this count proves the live suffix fell
|
|
21
|
+
* outside the ring entirely and is genuinely non-contiguous.
|
|
22
|
+
*/
|
|
23
|
+
export const SSE_REPLAY_RING_COUNT_LIMIT = 200;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Maximum age (in milliseconds) an event may reach in the daemon's SSE
|
|
27
|
+
* replay ring before it is evicted, regardless of count or byte usage.
|
|
28
|
+
* The daemon-side definition and eviction live in
|
|
29
|
+
* `assistant/src/runtime/assistant-stream-state.ts`.
|
|
30
|
+
*
|
|
31
|
+
* Exposed alongside the count bound so the web client can reason about
|
|
32
|
+
* the age dimension of the ring too. A small live seq gap is only safe to
|
|
33
|
+
* treat as benign when the client has been continuously receiving events
|
|
34
|
+
* — live delivery is concurrent with stamping, so a connected subscriber
|
|
35
|
+
* never relies on the ring. Once the connection has been quiet for longer
|
|
36
|
+
* than this window (a disconnect/resume), events the client missed may
|
|
37
|
+
* have aged out of the ring and become unrecoverable by replay, so even a
|
|
38
|
+
* small seq gap must trigger an authoritative reconcile rather than be
|
|
39
|
+
* waved through.
|
|
40
|
+
*/
|
|
41
|
+
export const SSE_REPLAY_RING_AGE_LIMIT_MS = 30_000;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `conversation_notice` SSE event.
|
|
3
|
+
*
|
|
4
|
+
* Non-terminal, conversation-scoped notice for actionable runtime conditions
|
|
5
|
+
* that should not mark the turn as failed. The client may render CTA UI from
|
|
6
|
+
* this event while preserving the current assistant response.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
|
|
11
|
+
import { ConversationErrorCodeSchema } from "./conversation-error.js";
|
|
12
|
+
|
|
13
|
+
export const ConversationNoticeSourceSchema = z.enum(["memory_v3"]);
|
|
14
|
+
|
|
15
|
+
export const ConversationNoticeEventSchema = z.object({
|
|
16
|
+
type: z.literal("conversation_notice"),
|
|
17
|
+
conversationId: z.string(),
|
|
18
|
+
source: ConversationNoticeSourceSchema,
|
|
19
|
+
code: ConversationErrorCodeSchema,
|
|
20
|
+
userMessage: z.string(),
|
|
21
|
+
errorCategory: z.string().optional(),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export type ConversationNoticeEvent = z.infer<
|
|
25
|
+
typeof ConversationNoticeEventSchema
|
|
26
|
+
>;
|
package/src/api/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { ConfirmationRequestEventSchema } from "./events/confirmation-request.js
|
|
|
11
11
|
import { ContactRequestEventSchema } from "./events/contact-request.js";
|
|
12
12
|
import { ConversationErrorEventSchema } from "./events/conversation-error.js";
|
|
13
13
|
import { ConversationListInvalidatedEventSchema } from "./events/conversation-list-invalidated.js";
|
|
14
|
+
import { ConversationNoticeEventSchema } from "./events/conversation-notice.js";
|
|
14
15
|
import { ConversationTitleUpdatedEventSchema } from "./events/conversation-title-updated.js";
|
|
15
16
|
import { DiskPressureStatusChangedEventSchema } from "./events/disk-pressure-status-changed.js";
|
|
16
17
|
import { DocumentCommentCreatedEventSchema } from "./events/document-comment-created.js";
|
|
@@ -61,6 +62,10 @@ export {
|
|
|
61
62
|
CALL_SITE_COMPACTION_AGENT,
|
|
62
63
|
CALL_SITE_SYNTHETIC_AGENT_ERROR_MESSAGE,
|
|
63
64
|
} from "./constants/call-sites.js";
|
|
65
|
+
export {
|
|
66
|
+
SSE_REPLAY_RING_AGE_LIMIT_MS,
|
|
67
|
+
SSE_REPLAY_RING_COUNT_LIMIT,
|
|
68
|
+
} from "./constants/sse-replay.js";
|
|
64
69
|
export { DEFAULT_TOOL_EXECUTION_TIMEOUT_SEC } from "./constants/tool-execution.js";
|
|
65
70
|
export {
|
|
66
71
|
type AssistantActivityAnchor,
|
|
@@ -134,6 +139,11 @@ export {
|
|
|
134
139
|
type ConversationListInvalidatedReason,
|
|
135
140
|
ConversationListInvalidatedReasonSchema,
|
|
136
141
|
} from "./events/conversation-list-invalidated.js";
|
|
142
|
+
export {
|
|
143
|
+
type ConversationNoticeEvent,
|
|
144
|
+
ConversationNoticeEventSchema,
|
|
145
|
+
ConversationNoticeSourceSchema,
|
|
146
|
+
} from "./events/conversation-notice.js";
|
|
137
147
|
export {
|
|
138
148
|
type ConversationTitleUpdatedEvent,
|
|
139
149
|
ConversationTitleUpdatedEventSchema,
|
|
@@ -430,6 +440,8 @@ export {
|
|
|
430
440
|
LlmContextResponseSchema,
|
|
431
441
|
} from "./responses/llm-context-response.js";
|
|
432
442
|
export {
|
|
443
|
+
type LLMCallError,
|
|
444
|
+
LLMCallErrorSchema,
|
|
433
445
|
type LLMCallSummary,
|
|
434
446
|
LLMCallSummarySchema,
|
|
435
447
|
type LLMContextSection,
|
|
@@ -471,7 +483,12 @@ export {
|
|
|
471
483
|
type WorkflowLeaf,
|
|
472
484
|
WorkflowLeafSchema,
|
|
473
485
|
} from "./responses/workflow-journal.js";
|
|
474
|
-
export {
|
|
486
|
+
export {
|
|
487
|
+
type CardSurfaceData,
|
|
488
|
+
CardSurfaceDataSchema,
|
|
489
|
+
type FileUploadSurfaceData,
|
|
490
|
+
FileUploadSurfaceDataSchema,
|
|
491
|
+
} from "./surfaces.js";
|
|
475
492
|
|
|
476
493
|
/**
|
|
477
494
|
* Canonical SSE event schema for the assistant runtime.
|
|
@@ -498,6 +515,7 @@ export const AssistantEventSchema = z.discriminatedUnion("type", [
|
|
|
498
515
|
ContactRequestEventSchema,
|
|
499
516
|
ConversationErrorEventSchema,
|
|
500
517
|
ConversationListInvalidatedEventSchema,
|
|
518
|
+
ConversationNoticeEventSchema,
|
|
501
519
|
ConversationTitleUpdatedEventSchema,
|
|
502
520
|
DiskPressureStatusChangedEventSchema,
|
|
503
521
|
DocumentCommentCreatedEventSchema,
|
|
@@ -67,6 +67,34 @@ export const LLMContextSectionSchema = z.object({
|
|
|
67
67
|
|
|
68
68
|
export type LLMContextSection = z.infer<typeof LLMContextSectionSchema>;
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Structured provider/transport error recorded when an LLM call was
|
|
72
|
+
* rejected before producing a response. Mirrors the on-disk
|
|
73
|
+
* `responsePayload.error` shape written by
|
|
74
|
+
* `buildProviderErrorResponsePayload` — the inspector branches on the
|
|
75
|
+
* presence of this field to render a failed call distinctly (failure
|
|
76
|
+
* banner in the Response tab, $0.00 cost in the rail, etc.) instead of
|
|
77
|
+
* the generic "section rendering unavailable" fallback.
|
|
78
|
+
*
|
|
79
|
+
* Every field is optional because the serializer degrades a plain
|
|
80
|
+
* `Error` down to just `{ name, message }`; only the wrapper object is
|
|
81
|
+
* guaranteed.
|
|
82
|
+
*/
|
|
83
|
+
export const LLMCallErrorSchema = z.object({
|
|
84
|
+
name: z.string().nullish(),
|
|
85
|
+
message: z.string().nullish(),
|
|
86
|
+
code: z.string().nullish(),
|
|
87
|
+
provider: z.string().nullish(),
|
|
88
|
+
statusCode: z.number().nullish(),
|
|
89
|
+
retryAfterMs: z.number().nullish(),
|
|
90
|
+
apiErrorCode: z.string().nullish(),
|
|
91
|
+
apiErrorType: z.string().nullish(),
|
|
92
|
+
apiErrorParam: z.string().nullish(),
|
|
93
|
+
requestId: z.string().nullish(),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
export type LLMCallError = z.infer<typeof LLMCallErrorSchema>;
|
|
97
|
+
|
|
70
98
|
/**
|
|
71
99
|
* One LLM request log row.
|
|
72
100
|
*
|
|
@@ -88,6 +116,7 @@ export const LLMRequestLogEntrySchema = z.object({
|
|
|
88
116
|
responseSections: z.array(LLMContextSectionSchema).nullish(),
|
|
89
117
|
agentLoopExitReason: z.string().nullish(),
|
|
90
118
|
callSite: z.string().nullish(),
|
|
119
|
+
error: LLMCallErrorSchema.nullish(),
|
|
91
120
|
});
|
|
92
121
|
|
|
93
122
|
export type LLMRequestLogEntry = z.infer<typeof LLMRequestLogEntrySchema>;
|
|
@@ -31,6 +31,23 @@ export const SubagentDetailEventSchema = z.object({
|
|
|
31
31
|
toolName: z.string().optional(),
|
|
32
32
|
isError: z.boolean().optional(),
|
|
33
33
|
messageId: z.string().optional(),
|
|
34
|
+
/**
|
|
35
|
+
* Tool-call id — the `tool_use.id` on a tool-call event and the referencing
|
|
36
|
+
* `tool_use_id` on its tool-result event, in the daemon's canonical
|
|
37
|
+
* content-block format. That format is provider-agnostic: every provider
|
|
38
|
+
* (Anthropic, OpenAI, Gemini, …) normalizes its native tool calls into these
|
|
39
|
+
* `tool_use`/`tool_result` blocks (see `providers/types.ts`), so this id is
|
|
40
|
+
* present regardless of which model produced the call. Lets the web client
|
|
41
|
+
* pair a result with its call and key the nested tool-detail view, so tool
|
|
42
|
+
* pills on reloaded/history subagents are clickable (not just live ones).
|
|
43
|
+
*/
|
|
44
|
+
toolUseId: z.string().optional(),
|
|
45
|
+
/**
|
|
46
|
+
* Raw tool input object on tool-call events. (`content` also carries a
|
|
47
|
+
* JSON-stringified copy for back-compat / label derivation.) Surfaced in the
|
|
48
|
+
* tool-detail view's input section.
|
|
49
|
+
*/
|
|
50
|
+
input: z.record(z.string(), z.unknown()).optional(),
|
|
34
51
|
});
|
|
35
52
|
|
|
36
53
|
export type SubagentDetailEvent = z.infer<typeof SubagentDetailEventSchema>;
|
package/src/api/surfaces.ts
CHANGED
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
* normalizer *supports* — anything the model sends outside these fields is
|
|
12
12
|
* dropped (and logged) there, which is how we learn the shapes to recover.
|
|
13
13
|
*
|
|
14
|
-
* Card
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* Card and file_upload use canonical schemas; the remaining types are
|
|
15
|
+
* hand-written interfaces in `daemon/message-types/surfaces.ts` pending
|
|
16
|
+
* migration.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { z } from "zod";
|
|
@@ -31,3 +31,39 @@ export const CardSurfaceDataSchema = z.object({
|
|
|
31
31
|
templateData: z.record(z.string(), z.unknown()).optional(),
|
|
32
32
|
});
|
|
33
33
|
export type CardSurfaceData = z.infer<typeof CardSurfaceDataSchema>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Accepted MIME-type / extension patterns for a `file_upload` surface.
|
|
37
|
+
*
|
|
38
|
+
* The renderer consumes this as a `string[]` — it calls `.join`/`.some`/
|
|
39
|
+
* `.length` on the value — but the model may emit a single comma-joined string
|
|
40
|
+
* ("image/*, application/pdf") or a bare string. Coercing every shape to a
|
|
41
|
+
* clean `string[]` keeps that array invariant intact: a string is split on
|
|
42
|
+
* commas; array entries are stringified and trimmed; blanks and any non-array
|
|
43
|
+
* value collapse to `undefined` (no restriction).
|
|
44
|
+
*/
|
|
45
|
+
const FileUploadAcceptedTypesSchema = z.preprocess((value) => {
|
|
46
|
+
const items =
|
|
47
|
+
typeof value === "string"
|
|
48
|
+
? value.split(",")
|
|
49
|
+
: Array.isArray(value)
|
|
50
|
+
? value
|
|
51
|
+
: [];
|
|
52
|
+
const cleaned = items
|
|
53
|
+
.map((item) =>
|
|
54
|
+
typeof item === "string" || typeof item === "number"
|
|
55
|
+
? String(item).trim()
|
|
56
|
+
: "",
|
|
57
|
+
)
|
|
58
|
+
.filter((item) => item.length > 0);
|
|
59
|
+
return cleaned.length > 0 ? cleaned : undefined;
|
|
60
|
+
}, z.array(z.string()).optional());
|
|
61
|
+
|
|
62
|
+
export const FileUploadSurfaceDataSchema = z.object({
|
|
63
|
+
prompt: z.coerce.string().optional(),
|
|
64
|
+
acceptedTypes: FileUploadAcceptedTypesSchema,
|
|
65
|
+
/** A non-positive or non-numeric value is dropped rather than rejecting the surface. */
|
|
66
|
+
maxFiles: z.coerce.number().int().positive().optional().catch(undefined),
|
|
67
|
+
maxSizeBytes: z.coerce.number().positive().optional().catch(undefined),
|
|
68
|
+
});
|
|
69
|
+
export type FileUploadSurfaceData = z.infer<typeof FileUploadSurfaceDataSchema>;
|