@vellumai/assistant 0.12.2 → 0.12.3-staging.2
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/.env.example +2 -0
- package/AGENTS.md +1 -1
- package/ARCHITECTURE.md +43 -29
- package/Dockerfile +63 -0
- package/docs/architecture/integrations.md +7 -0
- package/docs/architecture/memory.md +17 -2
- package/docs/architecture/security.md +10 -5
- package/docs/architecture/turn-actor.md +2 -0
- package/docs/credential-execution-service.md +1 -1
- package/docs/desktop-browser-cli.md +50 -5
- package/docs/live-voice-task-outcomes.md +100 -0
- package/docs/mode-sessions.md +183 -0
- package/docs/voice-input-diagnostics.md +58 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +4 -1
- package/node_modules/@vellumai/environments/src/shell.test.ts +21 -0
- package/node_modules/@vellumai/environments/src/shell.ts +24 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +4 -1
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +20 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +8 -2
- package/node_modules/@vellumai/gateway-client/src/index.ts +4 -1
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +38 -7
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +5 -6
- package/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
- package/node_modules/@vellumai/service-contracts/src/secret-detection.ts +4 -1
- package/node_modules/@vellumai/slack-text/src/index.ts +4 -8
- package/openapi.yaml +716 -16
- package/package.json +1 -1
- package/scripts/smoke-desktop-browser-cli.ts +11 -26
- package/scripts/smoke-desktop-window-theme.ts +2 -3
- package/scripts/sync-llm-catalog.ts +3 -0
- package/scripts/voice-ttft-spike.ts +2 -2
- package/src/__tests__/agent-loop-output-hooks.test.ts +150 -1
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +87 -19
- package/src/__tests__/agent-loop.test.ts +259 -0
- package/src/__tests__/agent-wake-delegation-prompt.test.ts +64 -2
- package/src/__tests__/annotate-activity-metadata.test.ts +3 -1
- package/src/__tests__/annotate-risk-options.test.ts +2 -0
- package/src/__tests__/answered-question-persistence.test.ts +2 -0
- package/src/__tests__/anthropic-gateway-shared.test.ts +22 -0
- package/src/__tests__/anthropic-provider.test.ts +55 -0
- package/src/__tests__/approval-interception-trust-gates.test.ts +40 -0
- package/src/__tests__/approval-message-composer.test.ts +1 -118
- package/src/__tests__/approval-routes-http.test.ts +3 -0
- package/src/__tests__/assistant-event-hub-connection-history.test.ts +111 -0
- package/src/__tests__/assistant-event-hub-targeted.test.ts +50 -0
- package/src/__tests__/assistant-event-targeting.test.ts +186 -0
- package/src/__tests__/assistant-stream-state.test.ts +29 -9
- package/src/__tests__/attachments-store.test.ts +22 -3
- package/src/__tests__/browser-fill-credential.test.ts +11 -22
- package/src/__tests__/btw-routes.test.ts +4 -1
- package/src/__tests__/build-persisted-content.test.ts +1 -1
- package/src/__tests__/call-conversation-messages.test.ts +5 -0
- package/src/__tests__/call-recovery.test.ts +7 -2
- package/src/__tests__/call-routes-http.test.ts +12 -0
- package/src/__tests__/call-store.test.ts +7 -2
- package/src/__tests__/channel-approval.test.ts +9 -14
- package/src/__tests__/channel-reply-delivery.test.ts +56 -0
- package/src/__tests__/channel-retry-sweep.test.ts +9 -0
- package/src/__tests__/chat-credential-redaction.test.ts +23 -0
- package/src/__tests__/chat-reveal-guard-priming.test.ts +2 -0
- package/src/__tests__/checker.test.ts +11 -3
- package/src/__tests__/client-os-metadata-persistence.test.ts +35 -1
- package/src/__tests__/computer-use-screenshot-attachments.test.ts +411 -0
- package/src/__tests__/computer-use-screenshot-selection.test.ts +50 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +0 -25
- package/src/__tests__/computer-use-tools.test.ts +1 -233
- package/src/__tests__/config-model-image-provider.test.ts +39 -0
- package/src/__tests__/config-schema.test.ts +16 -0
- package/src/__tests__/config-watcher.test.ts +24 -0
- package/src/__tests__/context-search-agent-runner.test.ts +260 -1
- package/src/__tests__/context-search-fanout.test.ts +1 -1
- package/src/__tests__/context-search-types.test.ts +7 -7
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +8 -2
- package/src/__tests__/conversation-agent-loop-fatal-cleanup.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 +797 -16
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +53 -1
- package/src/__tests__/conversation-attachments.test.ts +106 -0
- package/src/__tests__/conversation-evictor.test.ts +62 -37
- package/src/__tests__/conversation-fork-crud.test.ts +191 -0
- package/src/__tests__/conversation-history-boundary-repair-failure.test.ts +174 -0
- package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +2 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
- package/src/__tests__/conversation-rendered-delegation-state.test.ts +276 -0
- package/src/__tests__/conversation-retry-route.test.ts +22 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +2 -0
- package/src/__tests__/conversation-routes-enabled-plugins.test.ts +2 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +10 -0
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +2 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +29 -0
- package/src/__tests__/conversation-surface-snapshots.test.ts +543 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +151 -0
- package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +63 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +23 -0
- package/src/__tests__/conversation-undo.test.ts +61 -0
- package/src/__tests__/credential-execution-client.test.ts +72 -55
- package/src/__tests__/credential-prompt-route.test.ts +35 -3
- package/src/__tests__/cu-unified-flow.test.ts +82 -1
- package/src/__tests__/db-conversation-tool-surface.test.ts +221 -0
- package/src/__tests__/db-mode-sessions-migration.test.ts +175 -0
- package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -0
- package/src/__tests__/dm-backfill.test.ts +91 -1
- package/src/__tests__/document-create-route.test.ts +88 -0
- package/src/__tests__/document-sync-tags.test.ts +27 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +142 -2
- package/src/__tests__/events-tail-route.test.ts +58 -0
- package/src/__tests__/evict-conversations-for-reload.test.ts +57 -8
- package/src/__tests__/guardian-action-sweep.test.ts +16 -0
- package/src/__tests__/guardian-dispatch.test.ts +13 -0
- package/src/__tests__/headless-browser-interactions.test.ts +64 -20
- package/src/__tests__/heartbeat-service.test.ts +2 -59
- package/src/__tests__/helpers/mock-conversation.ts +21 -0
- package/src/__tests__/helpers/native-web-search-harness.ts +2 -0
- package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +5 -0
- package/src/__tests__/history-repair.test.ts +53 -0
- package/src/__tests__/host-cu-proxy.test.ts +46 -2
- package/src/__tests__/http-user-message-parity.test.ts +2 -0
- package/src/__tests__/image-credentials.test.ts +49 -0
- package/src/__tests__/image-service-dispatcher.test.ts +45 -0
- package/src/__tests__/inference-profile-session-handler.test.ts +26 -0
- package/src/__tests__/interrupt-on-send.test.ts +57 -43
- package/src/__tests__/interrupt-turn-note.test.ts +5 -6
- package/src/__tests__/list-messages-tool-merge.test.ts +85 -1
- package/src/__tests__/llm-catalog-parity.test.ts +27 -4
- package/src/__tests__/media-generate-image.test.ts +49 -1
- package/src/__tests__/message-provenance-contact-id.test.ts +69 -0
- package/src/__tests__/migration-export-to-gcs.test.ts +142 -0
- package/src/__tests__/mode-session-history.test.ts +245 -0
- package/src/__tests__/mode-session-recovery.test.ts +222 -0
- package/src/__tests__/mode-session-wire.test.ts +141 -0
- package/src/__tests__/notification-guardian-path.test.ts +7 -0
- package/src/__tests__/notification-telegram-adapter.test.ts +2 -2
- package/src/__tests__/notification-vellum-adapter.test.ts +34 -0
- package/src/__tests__/oauth-apps-routes.test.ts +1 -0
- package/src/__tests__/oauth-commands-routes.test.ts +114 -101
- package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +13 -0
- package/src/__tests__/oauth-providers-routes.test.ts +3 -0
- package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/persist-media-references.test.ts +50 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
- package/src/__tests__/plugin-api-provider.test.ts +5 -0
- package/src/__tests__/plugin-api-resolve-credential.test.ts +97 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +9 -1
- package/src/__tests__/process-message-display-content.test.ts +56 -0
- package/src/__tests__/reaction-persistence.test.ts +5 -0
- package/src/__tests__/request-secret-standalone-channel-gate.test.ts +25 -9
- package/src/__tests__/run-conversation-turn-persistence.test.ts +138 -1
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +1 -1
- package/src/__tests__/schedule-routes.test.ts +20 -0
- package/src/__tests__/scheduler-result-notification.test.ts +23 -4
- package/src/__tests__/script-proxy-certs.test.ts +1 -1
- package/src/__tests__/scripted-turn-metadata-persistence.test.ts +73 -0
- package/src/__tests__/secret-ingress-http.test.ts +2 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +86 -31
- package/src/__tests__/secret-routes-scrub.test.ts +22 -27
- package/src/__tests__/secret-scanner.test.ts +20 -0
- package/src/__tests__/secure-keys-managed-failover.test.ts +8 -0
- package/src/__tests__/secure-keys.test.ts +50 -4
- package/src/__tests__/send-endpoint-busy.test.ts +163 -1
- package/src/__tests__/server-history-render.test.ts +91 -1
- package/src/__tests__/server-tool-pairing.test.ts +107 -0
- package/src/__tests__/settings-routes.test.ts +50 -1
- package/src/__tests__/skill-script-runner-sandbox.test.ts +32 -0
- package/src/__tests__/skills.test.ts +5 -4
- package/src/__tests__/subagent-fork-notifications.test.ts +1 -2
- package/src/__tests__/subagent-manager-notify.test.ts +1 -2
- package/src/__tests__/subagent-notify-parent.test.ts +146 -39
- package/src/__tests__/subagent-notify-stale-rebuild.test.ts +2 -4
- package/src/__tests__/subagent-tool-gate-mode.test.ts +226 -0
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +2 -0
- package/src/__tests__/terminal-tools.test.ts +8 -0
- package/src/__tests__/thread-backfill.test.ts +53 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
- package/src/__tests__/tool-result-follow-up.test.ts +99 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +66 -1
- package/src/__tests__/tool-start-timestamp.test.ts +2 -0
- package/src/__tests__/twilio-routes.test.ts +1 -0
- package/src/__tests__/unicode.test.ts +36 -0
- package/src/__tests__/web-search-backend-failure.test.ts +1 -1
- package/src/activation/progress-store.test.ts +12 -7
- package/src/agent/loop.ts +136 -53
- package/src/agent/tool-result-follow-up.ts +70 -0
- package/src/api/attachment-provenance.test.ts +68 -0
- package/src/api/computer-use-tool.test.ts +55 -0
- package/src/api/computer-use-tool.ts +27 -0
- package/src/api/events/assistant-outbound-attachment.ts +3 -0
- package/src/api/events/assistant-turn-start.ts +3 -0
- package/src/api/events/desktop-activity-changed.ts +10 -0
- package/src/api/events/generation-handoff.ts +2 -0
- package/src/api/events/message-complete.ts +2 -0
- package/src/api/events/notification-conversation-created.ts +14 -16
- package/src/api/events/notification-intent.ts +4 -2
- package/src/api/events/question-request.ts +1 -0
- package/src/api/events/tool-result.ts +85 -0
- package/src/api/events/tool-use-start.ts +3 -0
- package/src/api/events/user-message-echo.ts +3 -0
- package/src/api/index.ts +42 -6
- package/src/api/mode-session.test.ts +131 -0
- package/src/api/mode-session.ts +148 -0
- package/src/api/remember-tool.ts +15 -0
- package/src/api/responses/conversation-message.ts +9 -0
- package/src/approvals/AGENTS.md +4 -2
- package/src/approvals/approval-primitive.ts +5 -2
- package/src/approvals/scoped-approval-grants.ts +6 -2
- package/src/browser/__tests__/operations.test.ts +29 -0
- package/src/browser/operation-meta.ts +2 -1
- package/src/browser/operations.ts +29 -0
- package/src/browser/virtual-desktop-target.ts +2 -9
- package/src/bundler/bundle-signer.ts +1 -3
- package/src/calls/__tests__/barge-in-guard.test.ts +3 -0
- package/src/calls/__tests__/call-controller.test.ts +94 -0
- package/src/calls/__tests__/call-funnel-telemetry.test.ts +204 -0
- package/src/calls/__tests__/voice-control-protocol.test.ts +15 -0
- package/src/calls/__tests__/voice-escalation-judge.test.ts +209 -0
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +168 -0
- package/src/{live-voice/__tests__/live-voice-metrics.test.ts → calls/__tests__/voice-metrics.test.ts} +32 -38
- package/src/calls/__tests__/voice-session-bridge.test.ts +576 -122
- package/src/calls/barge-in-guard.ts +5 -0
- package/src/calls/call-controller.ts +196 -1
- package/src/calls/call-domain.ts +4 -0
- package/src/calls/call-store.ts +87 -2
- package/src/calls/guardian-action-sweep.ts +22 -8
- package/src/calls/media-stream-audio-transcode.ts +1 -3
- package/src/calls/typesafe-noul.ts +156 -0
- package/src/calls/voice-control-protocol.ts +6 -0
- package/src/calls/voice-escalation-judge.ts +145 -0
- package/src/calls/voice-escalation-target.ts +10 -0
- package/src/calls/voice-leg-coordinator.ts +121 -3
- package/src/{live-voice/live-voice-metrics.ts → calls/voice-metrics.ts} +94 -87
- package/src/calls/voice-session-bridge.ts +256 -112
- package/src/calls/voice-triage-escalate.ts +7 -0
- package/src/cli/__tests__/roadmap-cli.test.ts +4 -41
- package/src/cli/commands/__tests__/cli-test-harness.ts +21 -3
- package/src/cli/commands/__tests__/notifications.test.ts +35 -10
- package/src/cli/commands/__tests__/plugins.test.ts +20 -1
- package/src/cli/commands/__tests__/schedules.test.ts +14 -0
- package/src/cli/commands/bash.help.ts +4 -3
- package/src/cli/commands/browser.help.ts +9 -6
- package/src/cli/commands/browser.ts +0 -1
- package/src/cli/commands/clients.help.ts +46 -0
- package/src/cli/commands/clients.ts +116 -0
- package/src/cli/commands/credentials.help.ts +9 -4
- package/src/cli/commands/notifications.help.ts +16 -2
- package/src/cli/commands/notifications.ts +30 -8
- package/src/cli/commands/oauth/index.help.ts +10 -0
- package/src/cli/commands/oauth/providers.ts +12 -0
- package/src/cli/commands/oauth/request.test.ts +179 -2
- package/src/cli/commands/oauth/request.ts +41 -10
- package/src/cli/commands/plugins.ts +12 -5
- package/src/cli/commands/roadmap.help.ts +18 -21
- package/src/cli/commands/roadmap.ts +4 -21
- package/src/cli/commands/schedules.ts +2 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +41 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +5 -2
- package/src/cli/lib/__tests__/local-plugin-upgrade.test.ts +133 -51
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +8 -2
- package/src/cli/lib/bundled-marketplace.json +906 -1
- package/src/cli/lib/bundled-plugin-packages.json +783 -1
- package/src/cli/lib/inspect-plugin.ts +11 -4
- package/src/cli/lib/plugin-marketplace.ts +8 -0
- package/src/cli/lib/publish-plugin.ts +1 -2
- package/src/cli/lib/upgrade-plugin.ts +19 -11
- package/src/cli/output.ts +18 -4
- package/src/config/__tests__/default-provider.test.ts +6 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +14 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +1 -1
- package/src/config/bundled-skills/computer-use/TOOLS.json +89 -1
- package/src/config/bundled-skills/image-studio/SKILL.md +17 -2
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +7 -11
- package/src/config/bundled-skills/schedule/SKILL.md +6 -6
- package/src/config/bundled-skills/sequences/TOOLS.json +1 -5
- package/src/config/call-site-defaults.ts +9 -0
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/loader.ts +9 -17
- package/src/config/platform-identity.ts +8 -8
- package/src/config/profile-text-generation.test.ts +51 -0
- package/src/config/profile-text-generation.ts +51 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +12 -0
- package/src/config/schemas/__tests__/tools.test.ts +31 -0
- package/src/config/schemas/call-site-catalog.ts +14 -0
- package/src/config/schemas/channels.ts +0 -18
- package/src/config/schemas/llm.ts +9 -2
- package/src/config/schemas/mcp.ts +6 -1
- package/src/config/schemas/memory-retrospective.ts +11 -2
- package/src/config/schemas/memory-v3.ts +12 -0
- package/src/config/schemas/services.ts +15 -6
- package/src/config/schemas/tools.ts +10 -0
- package/src/config/session-groups-gate.ts +21 -0
- package/src/config/skills.ts +2 -6
- package/src/conversations/__tests__/message-consolidation.test.ts +149 -0
- package/src/conversations/message-consolidation.ts +102 -5
- package/src/credential-execution/ces-connect.test.ts +69 -0
- package/src/credential-execution/ces-connect.ts +115 -0
- package/src/credential-execution/ces-runtime.ts +55 -93
- package/src/credential-execution/executable-discovery.ts +34 -142
- package/src/credential-execution/process-manager.test.ts +20 -24
- package/src/credential-execution/process-manager.ts +24 -19
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +10 -1
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +43 -0
- package/src/daemon/__tests__/inference-profile-notification.test.ts +2 -0
- package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +36 -0
- package/src/daemon/assistant-attachments.ts +20 -12
- package/src/daemon/browser-mode-session.test.ts +402 -0
- package/src/daemon/browser-mode-session.ts +186 -0
- package/src/daemon/chat-credential-redaction.ts +16 -1
- package/src/daemon/computer-use-mode-session.test.ts +210 -0
- package/src/daemon/computer-use-mode-session.ts +105 -0
- package/src/daemon/config-watcher.ts +10 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +132 -13
- package/src/daemon/conversation-agent-loop.ts +231 -25
- package/src/daemon/conversation-attachments.ts +93 -12
- package/src/daemon/conversation-evictor.ts +5 -24
- package/src/daemon/conversation-history.ts +10 -0
- package/src/daemon/conversation-interrupt-repair.ts +4 -43
- package/src/daemon/conversation-interrupt.ts +7 -11
- package/src/daemon/conversation-messaging.ts +58 -5
- package/src/daemon/conversation-mode-session.test.ts +1667 -0
- package/src/daemon/conversation-mode-session.ts +1008 -0
- package/src/daemon/conversation-process.ts +5 -0
- package/src/daemon/conversation-runtime-assembly.ts +10 -20
- package/src/daemon/conversation-skill-tools.ts +1 -0
- package/src/daemon/conversation-store.ts +4 -4
- package/src/daemon/conversation-surface-snapshots.ts +135 -0
- package/src/daemon/conversation-surfaces.ts +113 -5
- package/src/daemon/conversation-tool-setup.ts +79 -6
- package/src/daemon/conversation-turn-finalize.ts +26 -13
- package/src/daemon/conversation.ts +215 -58
- package/src/daemon/daemon-control.ts +3 -7
- package/src/daemon/handlers/config-model.ts +7 -5
- package/src/daemon/handlers/shared.ts +55 -16
- package/src/daemon/host-cu-proxy.ts +51 -22
- package/src/daemon/lifecycle.ts +30 -13
- package/src/daemon/mcp-reload-service.ts +1 -1
- package/src/daemon/message-protocol.ts +0 -1
- package/src/daemon/message-provenance.ts +28 -4
- package/src/daemon/message-types/shared.ts +1 -0
- package/src/daemon/mode-session-startup-recovery.ts +23 -0
- package/src/daemon/mode-session-tracking.ts +54 -0
- package/src/daemon/orphan-reaper.ts +4 -3
- package/src/daemon/persist-media-references.ts +20 -3
- package/src/daemon/process-message.ts +13 -4
- package/src/daemon/tool-setup-types.ts +6 -0
- package/src/daemon/virtual-desktop-context.ts +21 -0
- package/src/daemon/wake-conversation-ops.ts +50 -15
- package/src/desktop/__tests__/fake-desktop.ts +1 -0
- package/src/desktop/desktop-accessibility-script.ts +88 -0
- package/src/desktop/desktop-accessibility.test.ts +160 -0
- package/src/desktop/desktop-accessibility.ts +169 -0
- package/src/desktop/desktop-automation-lease.test.ts +302 -64
- package/src/desktop/desktop-automation-lease.ts +129 -54
- package/src/desktop/desktop-browser-endpoint.ts +4 -0
- package/src/desktop/desktop-computer-use-routing.test.ts +330 -0
- package/src/desktop/desktop-computer-use.test.ts +449 -0
- package/src/desktop/desktop-computer-use.ts +502 -0
- package/src/desktop/desktop-dependencies.ts +47 -293
- package/src/desktop/desktop-display.ts +2 -2
- package/src/desktop/desktop-help.ts +15 -0
- package/src/desktop/desktop-panel-config.test.ts +13 -3
- package/src/desktop/desktop-panel-config.ts +109 -15
- package/src/desktop/desktop-session-manager.test.ts +69 -21
- package/src/desktop/desktop-session-manager.ts +76 -22
- package/src/desktop/desktop-stream-bridge.test.ts +76 -132
- package/src/desktop/desktop-stream-bridge.ts +9 -11
- package/src/desktop/desktop-wallpaper-renderer.ts +132 -0
- package/src/desktop/desktop-wallpaper-worker.ts +17 -0
- package/src/desktop/desktop-wallpaper.test.ts +48 -7
- package/src/desktop/desktop-wallpaper.ts +34 -102
- package/src/desktop/desktop-window-theme.ts +5 -25
- package/src/desktop/virtual-desktop-feature.ts +53 -3
- package/src/desktop/virtual-desktop-platform.test.ts +5 -9
- package/src/documents/document-store.ts +23 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +22 -26
- package/src/heartbeat/heartbeat-service.ts +5 -33
- package/src/ipc/__tests__/browser-ipc.test.ts +215 -6
- package/src/ipc/assistant-server.ts +2 -0
- package/src/ipc/routes/__tests__/channel-reply-ipc-routes.test.ts +111 -0
- package/src/ipc/routes/channel-reply-ipc-routes.ts +73 -0
- package/src/live-voice/__tests__/continuation-judge.test.ts +151 -0
- package/src/live-voice/__tests__/live-activity-reporter.test.ts +154 -15
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +743 -31
- package/src/live-voice/__tests__/live-voice-look-follow-up.test.ts +191 -10
- package/src/live-voice/__tests__/live-voice-session-groups-gate.test.ts +87 -0
- package/src/live-voice/__tests__/live-voice-session-manager.test.ts +5 -1
- package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +41 -0
- package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +1017 -9
- package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +224 -1
- package/src/live-voice/__tests__/live-voice-tts-session.test.ts +101 -0
- package/src/live-voice/__tests__/live-voice-tts.test.ts +44 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +939 -75
- package/src/live-voice/__tests__/microphone-idle-gate.test.ts +87 -0
- package/src/live-voice/__tests__/session-controls.test.ts +23 -0
- package/src/live-voice/__tests__/task-outcomes.test.ts +117 -0
- package/src/live-voice/__tests__/voice-input-diagnostics.test.ts +117 -0
- package/src/live-voice/camera-mode-session.test.ts +265 -0
- package/src/live-voice/camera-mode-session.ts +277 -0
- package/src/live-voice/continuation-judge.ts +123 -0
- package/src/live-voice/live-activity-reporter.ts +115 -6
- package/src/live-voice/live-voice-manager.ts +36 -1
- package/src/live-voice/live-voice-photo.ts +88 -7
- package/src/live-voice/live-voice-session-manager.ts +15 -0
- package/src/live-voice/live-voice-session.ts +1443 -747
- package/src/live-voice/live-voice-tts.ts +22 -4
- package/src/live-voice/microphone-idle-gate.ts +56 -0
- package/src/live-voice/protocol.ts +171 -4
- package/src/live-voice/session-controls.ts +11 -3
- package/src/live-voice/task-outcomes.ts +131 -0
- package/src/live-voice/voice-input-diagnostics.ts +117 -0
- package/src/mcp/__tests__/manager-tool-caps.test.ts +156 -0
- package/src/mcp/__tests__/startup.test.ts +30 -7
- package/src/mcp/__tests__/tool-caps.test.ts +134 -0
- package/src/mcp/manager.ts +171 -101
- package/src/mcp/startup.ts +33 -13
- package/src/mcp/tool-caps.ts +184 -0
- package/src/media/__tests__/image-models.test.ts +58 -0
- package/src/media/image-credentials.ts +5 -0
- package/src/media/image-models.ts +52 -0
- package/src/media/image-service.ts +8 -0
- package/src/media/openrouter-image-service.test.ts +133 -0
- package/src/media/openrouter-image-service.ts +153 -0
- package/src/media/types.ts +1 -1
- package/src/messaging/provider-message-metadata.ts +3 -3
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +65 -11
- package/src/messaging/providers/discord/send.ts +4 -17
- package/src/messaging/providers/discord/transport.ts +2 -2
- package/src/messaging/providers/outlook/client.ts +16 -22
- package/src/messaging/providers/telegram-bot/send.test.ts +236 -1
- package/src/messaging/providers/telegram-bot/send.ts +10 -7
- package/src/messaging/providers/telegram-bot/transport.ts +3 -10
- package/src/messaging/providers/whatsapp/send.test.ts +169 -4
- package/src/monitoring/__tests__/control.test.ts +17 -5
- package/src/monitoring/control.ts +15 -5
- package/src/notifications/AGENTS.md +1 -1
- package/src/notifications/README.md +83 -99
- package/src/notifications/__tests__/broadcaster.test.ts +1 -1
- package/src/notifications/__tests__/channel-allowlist.test.ts +39 -0
- package/src/notifications/__tests__/copy-composer.test.ts +71 -1
- package/src/notifications/__tests__/decision-engine.test.ts +79 -2
- package/src/notifications/__tests__/deterministic-checks.test.ts +10 -10
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +93 -4
- package/src/notifications/__tests__/notification-utils.test.ts +18 -0
- package/src/notifications/adapters/macos.ts +37 -46
- package/src/notifications/broadcaster.ts +5 -6
- package/src/notifications/channel-allowlist.ts +44 -0
- package/src/notifications/copy-composer.ts +11 -26
- package/src/notifications/decision-engine.ts +24 -14
- package/src/notifications/deterministic-checks.ts +1 -1
- package/src/notifications/emit-signal.ts +123 -37
- package/src/notifications/home-feed-side-effect.ts +9 -7
- package/src/notifications/notification-utils.ts +19 -0
- package/src/notifications/schedule-result-producer.ts +4 -6
- package/src/notifications/signal.ts +4 -19
- package/src/notifications/types.ts +15 -0
- package/src/oauth/AGENTS.md +2 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +36 -1
- package/src/oauth/connection-resolver.ts +1 -2
- package/src/oauth/identity-verifier.ts +24 -0
- package/src/oauth/oauth-store.ts +12 -4
- package/src/oauth/provider-serializer.ts +8 -0
- package/src/oauth/seed-providers.ts +42 -0
- package/src/onboarding/onboarding-events-store.ts +99 -12
- package/src/permissions/confirmation-guardian-request.test.ts +16 -0
- package/src/permissions/confirmation-guardian-request.ts +2 -3
- package/src/permissions/question-prompter.test.ts +31 -0
- package/src/permissions/question-prompter.ts +2 -0
- package/src/persistence/__tests__/client-connection-events-store.test.ts +322 -0
- package/src/persistence/attachments-store.ts +84 -44
- package/src/persistence/client-connection-events-store.ts +377 -0
- package/src/persistence/conversation-crud.ts +166 -26
- package/src/persistence/conversation-mode-sessions.test.ts +424 -0
- package/src/persistence/conversation-mode-sessions.ts +460 -0
- package/src/persistence/conversation-plugin-facade.ts +30 -0
- package/src/persistence/conversation-queries.ts +6 -19
- package/src/persistence/conversation-tool-surface.ts +135 -0
- package/src/persistence/conversation-types.test.ts +32 -0
- package/src/persistence/conversation-types.ts +26 -4
- package/src/persistence/llm-usage-store.ts +3 -9
- package/src/persistence/message-metadata.ts +25 -0
- package/src/persistence/migrations/378-create-conversation-tool-surfaces.test.ts +78 -0
- package/src/persistence/migrations/378-create-conversation-tool-surfaces.ts +29 -0
- package/src/persistence/migrations/379-oauth-providers-response-ok-field.test.ts +92 -0
- package/src/persistence/migrations/379-oauth-providers-response-ok-field.ts +32 -0
- package/src/persistence/migrations/380-conversation-tool-surfaces-delegate-independent-tasks.test.ts +90 -0
- package/src/persistence/migrations/380-conversation-tool-surfaces-delegate-independent-tasks.ts +34 -0
- package/src/persistence/migrations/381-create-conversation-mode-sessions.ts +41 -0
- package/src/persistence/migrations/382-create-client-connection-events.ts +40 -0
- package/src/persistence/migrations/__tests__/382-create-client-connection-events.test.ts +56 -0
- package/src/persistence/schema/conversation-tool-surfaces.ts +32 -0
- package/src/persistence/schema/conversations.ts +67 -0
- package/src/persistence/schema/index.ts +1 -0
- package/src/persistence/schema/infrastructure.ts +29 -0
- package/src/persistence/schema/oauth.ts +1 -0
- package/src/persistence/steps.ts +41 -3
- package/src/plugin-api/__tests__/plugin-name-env.test.ts +249 -0
- package/src/plugin-api/conversation-turn.ts +31 -7
- package/src/plugin-api/effective-context-window.ts +33 -0
- package/src/plugin-api/index.ts +35 -6
- package/src/plugin-api/plugin-channel-turn-trust.test.ts +133 -0
- package/src/plugin-api/plugin-channel-turn-trust.ts +71 -0
- package/src/plugin-api/plugin-name-env.ts +125 -0
- package/src/plugin-api/resolve-credential.ts +79 -20
- package/src/plugin-api/vision-support.test.ts +22 -0
- package/src/plugin-api/vision-support.ts +17 -5
- package/src/plugins/__tests__/active-plugin-env.test.ts +136 -0
- package/src/plugins/active-plugin-env.ts +51 -0
- package/src/plugins/defaults/index.ts +24 -0
- package/src/plugins/defaults/memory/AGENTS.md +39 -5
- package/src/plugins/defaults/memory/__tests__/archive-removal-framing.test.ts +64 -0
- package/src/plugins/defaults/memory/__tests__/buffer-file.test.ts +365 -0
- package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +43 -0
- package/src/plugins/defaults/memory/__tests__/conversation-memory-purge.test.ts +1 -0
- package/src/plugins/defaults/memory/__tests__/db-memory-attach.test.ts +2 -0
- package/src/plugins/defaults/memory/__tests__/fixtures/buffer-appender.ts +17 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +74 -0
- package/src/plugins/defaults/memory/__tests__/memory-run-evidence.test.ts +161 -0
- package/src/plugins/defaults/memory/__tests__/relocated-memory-test-rows.ts +10 -0
- package/src/plugins/defaults/memory/__tests__/worker-control.test.ts +19 -6
- package/src/plugins/defaults/memory/buffer-file.ts +354 -0
- package/src/plugins/defaults/memory/buffer-format.ts +40 -0
- package/src/plugins/defaults/memory/context-search/agent-protocol.ts +9 -12
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +77 -22
- package/src/plugins/defaults/memory/context-search/format.ts +53 -8
- package/src/plugins/defaults/memory/context-search/limits.ts +10 -12
- package/src/plugins/defaults/memory/context-search/recall-input.ts +38 -0
- package/src/plugins/defaults/memory/context-search/search.ts +8 -14
- package/src/plugins/defaults/memory/context-search/sources/conversations.ts +1 -0
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +2 -1
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +2 -1
- package/src/plugins/defaults/memory/context-search/types.ts +1 -11
- package/src/plugins/defaults/memory/conversation-memory-purge.ts +4 -0
- package/src/plugins/defaults/memory/graph/__tests__/handle-remember-v2.test.ts +11 -4
- package/src/plugins/defaults/memory/graph/capability-seed.ts +1 -2
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +13 -63
- package/src/plugins/defaults/memory/graph/tools.ts +35 -64
- package/src/plugins/defaults/memory/host-utils.ts +0 -10
- package/src/plugins/defaults/memory/injectors.ts +4 -3
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +72 -183
- package/src/plugins/defaults/memory/memory-run-evidence.ts +213 -0
- package/src/plugins/defaults/memory/src/memory-eval-routes.ts +2 -3
- package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +6 -1
- package/src/plugins/defaults/memory/src/memory-item-routes.ts +2 -1
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +407 -99
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +10 -0
- package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +107 -7
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +307 -86
- package/src/plugins/defaults/memory/substrate/consolidation-tool-surface.ts +34 -0
- package/src/plugins/defaults/memory/substrate/page-index.ts +2 -1
- package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +91 -51
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +1 -1
- package/src/plugins/defaults/memory/tools.test.ts +124 -9
- package/src/plugins/defaults/memory/tools.ts +38 -11
- package/src/plugins/defaults/memory/v1/graph/consolidation.ts +2 -2
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +2 -1
- package/src/plugins/defaults/memory/v1/graph/retriever.ts +1 -1
- package/src/plugins/defaults/memory/v2/__tests__/migration.test.ts +5 -0
- package/src/plugins/defaults/memory/v2/__tests__/reranker.test.ts +5 -2
- package/src/plugins/defaults/memory/v2/reranker.ts +2 -1
- package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +81 -1
- package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +87 -0
- package/src/plugins/defaults/memory/v3/__tests__/plugin-schema.test.ts +12 -0
- package/src/plugins/defaults/memory/v3/__tests__/pool-log-store.test.ts +332 -15
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +626 -27
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +148 -28
- package/src/plugins/defaults/memory/v3/card.ts +2 -1
- package/src/plugins/defaults/memory/v3/injector.ts +212 -178
- package/src/plugins/defaults/memory/v3/orchestrate.ts +159 -32
- package/src/plugins/defaults/memory/v3/plugin-schema.ts +54 -4
- package/src/plugins/defaults/memory/v3/pool-log-store.ts +275 -37
- package/src/plugins/defaults/memory/v3/pool-select.test.ts +47 -5
- package/src/plugins/defaults/memory/v3/pool-select.ts +710 -59
- package/src/plugins/defaults/memory/v3/sections.ts +2 -1
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +41 -14
- package/src/plugins/defaults/memory/worker-control.ts +15 -5
- package/src/plugins/defaults/task-progress/__tests__/registration.test.ts +70 -0
- package/src/plugins/defaults/task-progress/__tests__/task-progress-context.test.ts +380 -0
- package/src/plugins/defaults/task-progress/hooks/post-compact.ts +19 -0
- package/src/plugins/defaults/task-progress/hooks/user-prompt-submit.ts +24 -0
- package/src/plugins/defaults/task-progress/package.json +14 -0
- package/src/plugins/defaults/task-progress/src/apply-task-progress-context.ts +93 -0
- package/src/plugins/defaults/task-progress/src/format-task-progress-context.ts +67 -0
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +4 -1
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +1 -46
- package/src/prompts/__tests__/parallel-tasks-section.test.ts +25 -0
- package/src/prompts/delegation-gate.ts +57 -0
- package/src/prompts/system-prompt.ts +14 -32
- package/src/providers/__tests__/context-overflow-error.test.ts +10 -0
- package/src/providers/__tests__/search-provider-catalog.test.ts +18 -0
- package/src/providers/anthropic/client.ts +10 -1
- package/src/providers/anthropic-gateway-shared.ts +6 -0
- package/src/providers/fetch-provider-catalog.ts +11 -0
- package/src/providers/gemini/client.ts +25 -8
- package/src/providers/inference/adapter-factory.ts +6 -0
- package/src/providers/jev/client.test.ts +270 -0
- package/src/providers/jev/client.ts +534 -0
- package/src/providers/model-catalog.ts +59 -3
- package/src/providers/openai/__tests__/chat-completions-extra-body.test.ts +94 -1
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +106 -1
- package/src/providers/openai/__tests__/chat-template-flatten.test.ts +24 -0
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +1 -2
- package/src/providers/openai/chat-completions-provider.ts +100 -29
- package/src/providers/openai/google-thought-signature.ts +0 -6
- package/src/providers/openai/responses-provider.ts +23 -1
- package/src/providers/ratelimit.ts +1 -3
- package/src/providers/search-provider-catalog.ts +24 -0
- package/src/providers/server-tool-pairing.ts +16 -7
- package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +24 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +7 -1
- package/src/providers/speech-to-text/__tests__/vellum-managed-realtime.test.ts +50 -1
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +11 -0
- package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +13 -0
- package/src/providers/types.ts +5 -0
- package/src/routes/control.ts +7 -5
- package/src/runtime/AGENTS.md +6 -4
- package/src/runtime/__tests__/agent-wake.test.ts +94 -1
- package/src/runtime/actor-trust-resolver.ts +3 -3
- package/src/runtime/agent-wake.ts +32 -5
- package/src/runtime/approval-message-composer.ts +3 -69
- package/src/runtime/assistant-event-hub.ts +73 -75
- package/src/runtime/assistant-event-publish-options.ts +7 -0
- package/src/runtime/assistant-event-targeting.ts +79 -0
- package/src/runtime/assistant-stream-state.ts +13 -100
- package/src/runtime/background-job-runner.ts +5 -5
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/guardian-action-message-composer.ts +8 -161
- package/src/runtime/guardian-action-service.ts +2 -17
- package/src/runtime/guardian-reply-router.ts +29 -40
- package/src/runtime/http-server.ts +2 -2
- package/src/runtime/http-types.ts +6 -0
- package/src/runtime/message-composer-types.ts +3 -61
- package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +67 -1
- package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +89 -0
- package/src/runtime/migrations/vbundle-builder.ts +24 -1
- package/src/runtime/migrations/vbundle-import-analyzer.ts +13 -4
- package/src/runtime/migrations/vbundle-import-policy.ts +43 -11
- package/src/runtime/migrations/vbundle-importer.ts +4 -1
- package/src/runtime/migrations/vbundle-streaming-importer.ts +4 -1
- package/src/runtime/migrations/vbundle-validator.ts +1 -0
- package/src/runtime/pending-interactions-mode-session.test.ts +62 -0
- package/src/runtime/pending-interactions.ts +29 -0
- package/src/runtime/routes/__tests__/client-history-routes.test.ts +104 -0
- package/src/runtime/routes/__tests__/image-generation-routes.test.ts +97 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +24 -2
- package/src/runtime/routes/__tests__/roadmap-routes.test.ts +29 -6
- package/src/runtime/routes/browser-context.ts +31 -11
- package/src/runtime/routes/browser-routes.ts +43 -4
- package/src/runtime/routes/btw-routes.ts +9 -4
- package/src/runtime/routes/channel-route-shared.ts +1 -9
- package/src/runtime/routes/client-routes.ts +108 -0
- package/src/runtime/routes/conversation-management-routes.ts +4 -1
- package/src/runtime/routes/conversation-routes.ts +125 -14
- package/src/runtime/routes/credential-prompt-routes.ts +4 -3
- package/src/runtime/routes/credential-routes.ts +1 -2
- package/src/runtime/routes/desktop-setup-routes.test.ts +9 -18
- package/src/runtime/routes/desktop-setup-routes.ts +8 -7
- package/src/runtime/routes/documents-routes.ts +51 -0
- package/src/runtime/routes/email-routes.ts +3 -7
- package/src/runtime/routes/events-routes.ts +75 -54
- package/src/runtime/routes/guardian-approval-interception.ts +24 -0
- package/src/runtime/routes/identity-routes.ts +1 -5
- package/src/runtime/routes/image-generation-routes.ts +8 -14
- package/src/runtime/routes/inbound-message-handler.ts +37 -41
- package/src/runtime/routes/inbound-stages/backfill-sender-provenance.test.ts +168 -0
- package/src/runtime/routes/inbound-stages/backfill-sender-provenance.ts +96 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +39 -10
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +9 -9
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +2 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +11 -1
- package/src/runtime/routes/inference-profiles-routes.ts +9 -0
- package/src/runtime/routes/llm-context-normalization.ts +1 -7
- package/src/runtime/routes/migration-routes.ts +73 -2
- package/src/runtime/routes/notification-routes.ts +10 -0
- package/src/runtime/routes/oauth-commands-routes.ts +57 -54
- package/src/runtime/routes/oauth-providers.ts +11 -0
- package/src/runtime/routes/oauth-request-hints.test.ts +246 -0
- package/src/runtime/routes/oauth-request-hints.ts +149 -0
- package/src/runtime/routes/plugins-routes.ts +6 -0
- package/src/runtime/routes/roadmap-routes.ts +47 -8
- package/src/runtime/routes/schedule-routes.ts +2 -0
- package/src/runtime/routes/secret-routes.ts +99 -119
- package/src/runtime/routes/settings-routes.ts +10 -4
- package/src/runtime/routes/watcher-routes.ts +1 -3
- package/src/runtime/trust-class.test.ts +7 -6
- package/src/runtime/trust-class.ts +9 -17
- package/src/schedule/__tests__/worker-control.test.ts +36 -5
- package/src/schedule/__tests__/worker-mcp-bootstrap.test.ts +107 -0
- package/src/schedule/__tests__/worker-mcp-readiness.test.ts +173 -0
- package/src/schedule/__tests__/worker-mcp-tools.test.ts +13 -47
- package/src/schedule/run-script.ts +2 -2
- package/src/schedule/scheduler.ts +47 -11
- package/src/schedule/tool-surface-readiness.ts +65 -0
- package/src/schedule/worker-control.ts +15 -5
- package/src/schedule/worker-mcp.ts +85 -0
- package/src/schedule/worker.ts +9 -31
- package/src/security/secure-keys.ts +74 -112
- package/src/skills/clawhub.ts +3 -8
- package/src/skills/managed-store.ts +1 -5
- package/src/stt/types.ts +4 -0
- package/src/subagent/notify.ts +54 -34
- package/src/subagent/parent-notification.ts +6 -0
- package/src/telemetry/__tests__/phone-call-funnel.test.ts +72 -0
- package/src/telemetry/phone-call-funnel.ts +144 -0
- package/src/tools/__tests__/tool-schema-root-combinator-guard.test.ts +81 -0
- package/src/tools/ask-question/ask-question-tool.test.ts +167 -5
- package/src/tools/ask-question/ask-question-tool.ts +120 -22
- package/src/tools/browser/browser-execution.ts +29 -60
- package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +561 -39
- package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +435 -39
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +1 -8
- package/src/tools/client-os.ts +15 -0
- package/src/tools/computer-use/skill-proxy-bridge.ts +1 -1
- package/src/tools/computer-use/target.ts +42 -0
- package/src/tools/document/document-tool.ts +13 -18
- package/src/tools/execution-target.ts +14 -16
- package/src/tools/host-terminal/host-shell.ts +16 -7
- package/src/tools/network/__tests__/web-fetch-tinyfish.test.ts +250 -0
- package/src/tools/network/__tests__/web-search.test.ts +203 -0
- package/src/tools/network/web-fetch.ts +274 -7
- package/src/tools/network/web-search.ts +468 -6
- package/src/tools/permission-checker.ts +3 -3
- package/src/tools/policy-context.ts +3 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +1 -31
- package/src/tools/shared/shell-output.test.ts +10 -0
- package/src/tools/shared/shell-output.ts +14 -2
- package/src/tools/skills/load.ts +2 -1
- package/src/tools/skills/sandbox-runner.ts +24 -4
- package/src/tools/skills/scaffold-managed.ts +2 -1
- package/src/tools/skills/skill-script-runner.ts +7 -0
- package/src/tools/skills/skill-tool-factory.ts +15 -1
- package/src/tools/terminal/__tests__/safe-env.test.ts +44 -4
- package/src/tools/terminal/__tests__/sanitized-bash.test.ts +0 -14
- package/src/tools/terminal/safe-env.ts +39 -24
- package/src/tools/terminal/sanitized-bash.ts +15 -2
- package/src/tools/terminal/shell-launch.test.ts +162 -0
- package/src/tools/terminal/shell.test.ts +29 -0
- package/src/tools/terminal/shell.ts +15 -7
- package/src/tools/tool-approval-handler.ts +5 -1
- package/src/tools/tool-types.ts +3 -3
- package/src/tools/types.ts +8 -2
- package/src/util/__tests__/worker-process-command.test.ts +5 -4
- package/src/util/abort-reasons.ts +18 -26
- package/src/util/browser-human-verification.ts +14 -0
- package/src/util/canonicalize-identity.test.ts +21 -0
- package/src/util/canonicalize-identity.ts +16 -0
- package/src/util/errors.ts +13 -3
- package/src/util/host-process.test.ts +17 -1
- package/src/util/host-process.ts +24 -0
- package/src/util/provider-error-patterns.ts +3 -0
- package/src/util/unicode.ts +29 -0
- package/src/util/worker-process.ts +77 -35
- package/src/workspace/migrations/157-add-desktop-files-launcher.ts +94 -0
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +4 -10
- package/src/workspace/migrations/__tests__/157-add-desktop-files-launcher.test.ts +113 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/__tests__/computer-use-window-schema.test.ts +0 -24
- package/src/api/events/heartbeat-alert.ts +0 -20
- package/src/daemon/message-types/web-activity.ts +0 -65
- package/src/desktop/desktop-dependencies.test.ts +0 -131
- package/src/tools/computer-use/definitions.ts +0 -607
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Memory v3 — single pool selector.
|
|
3
3
|
*
|
|
4
|
-
* Runs a SINGLE
|
|
4
|
+
* Runs a SINGLE selector call over one unified candidate pool rendered in
|
|
5
5
|
* two segments that share one numbering:
|
|
6
6
|
*
|
|
7
7
|
* 1. STABLE PREFIX — the core+hot lane pages as FULL CARDS (head section +
|
|
@@ -42,22 +42,34 @@
|
|
|
42
42
|
* - infrastructure failure (selector provider unavailable — e.g. a transient
|
|
43
43
|
* CES credential blip drops the API key — or no usable `tool_use` / schema
|
|
44
44
|
* mismatch surviving the short re-prompt retry) → throw
|
|
45
|
-
* {@link MemoryV3RetrievalUnavailableError}. The
|
|
46
|
-
*
|
|
47
|
-
*
|
|
45
|
+
* {@link MemoryV3RetrievalUnavailableError}. The orchestrator keeps the
|
|
46
|
+
* stable prefix unjudged for the turn and drops the finder candidates,
|
|
47
|
+
* recording the pool with `selector_ran = 0`; the live injector renders
|
|
48
|
+
* that prefix and queues a notice that the turn drew on core memories
|
|
49
|
+
* only.
|
|
48
50
|
*/
|
|
49
51
|
|
|
50
52
|
import type {
|
|
51
53
|
ContentBlock,
|
|
52
54
|
Message,
|
|
55
|
+
Provider,
|
|
56
|
+
ProviderResponse,
|
|
53
57
|
ToolUseContent,
|
|
54
58
|
} from "@vellumai/plugin-api";
|
|
55
|
-
import {
|
|
59
|
+
import {
|
|
60
|
+
getConfiguredProvider,
|
|
61
|
+
getEffectiveContextWindow,
|
|
62
|
+
safeStringSlice,
|
|
63
|
+
} from "@vellumai/plugin-api";
|
|
56
64
|
import { z } from "zod";
|
|
57
65
|
|
|
66
|
+
import {
|
|
67
|
+
estimatePromptTokensWithTools,
|
|
68
|
+
estimateTextTokens,
|
|
69
|
+
} from "../../../../context/token-estimator.js";
|
|
58
70
|
import { classifyConversationError } from "../../../../daemon/conversation-error.js";
|
|
59
71
|
import type { PendingConversationNotice } from "../../../../daemon/conversation-notices.js";
|
|
60
|
-
import { redactLogString,
|
|
72
|
+
import { redactLogString, truncate } from "../host-utils.js";
|
|
61
73
|
import {
|
|
62
74
|
cachedTextBlock,
|
|
63
75
|
extractToolUse,
|
|
@@ -69,6 +81,7 @@ import { retryForResult } from "./llm-retry.js";
|
|
|
69
81
|
import { findTerm } from "./section-needle.js";
|
|
70
82
|
import { sectionBody } from "./sections.js";
|
|
71
83
|
import {
|
|
84
|
+
type FinderLane,
|
|
72
85
|
type MemoryRoutingTurn,
|
|
73
86
|
type Section,
|
|
74
87
|
sectionKey,
|
|
@@ -90,12 +103,16 @@ const log = getLogger("memory-v3-pool-select");
|
|
|
90
103
|
*/
|
|
91
104
|
export class MemoryV3RetrievalUnavailableError extends Error {
|
|
92
105
|
readonly conversationNotice?: PendingConversationNotice;
|
|
106
|
+
readonly pool?: SelectorPool;
|
|
107
|
+
readonly turn?: MemoryRoutingTurn;
|
|
93
108
|
|
|
94
109
|
constructor(
|
|
95
110
|
message: string,
|
|
96
111
|
options?: {
|
|
97
112
|
cause?: unknown;
|
|
98
113
|
conversationNotice?: PendingConversationNotice;
|
|
114
|
+
pool?: SelectorPool;
|
|
115
|
+
turn?: MemoryRoutingTurn;
|
|
99
116
|
},
|
|
100
117
|
) {
|
|
101
118
|
super(
|
|
@@ -104,6 +121,8 @@ export class MemoryV3RetrievalUnavailableError extends Error {
|
|
|
104
121
|
);
|
|
105
122
|
this.name = "MemoryV3RetrievalUnavailableError";
|
|
106
123
|
this.conversationNotice = options?.conversationNotice;
|
|
124
|
+
this.pool = options?.pool;
|
|
125
|
+
this.turn = options?.turn;
|
|
107
126
|
}
|
|
108
127
|
}
|
|
109
128
|
|
|
@@ -127,7 +146,7 @@ function providerBillingNoticeFromError(
|
|
|
127
146
|
/** A dynamic-tail (finder) candidate: the slug plus the descriptor that
|
|
128
147
|
* justifies it (a matched section's text for a needle/dense hit, or a
|
|
129
148
|
* curated link description for an edge page), rendered as a one-line
|
|
130
|
-
* snippet and prefixed with the surfacing lane
|
|
149
|
+
* snippet and prefixed with the surfacing lane. A
|
|
131
150
|
* candidate that also carries its matched `section` and the query `terms`
|
|
132
151
|
* that scored it (best first) renders a keyword-in-context snippet instead:
|
|
133
152
|
* a window of the section body around the first of those terms that occurs
|
|
@@ -138,7 +157,7 @@ function providerBillingNoticeFromError(
|
|
|
138
157
|
export interface PoolCandidate {
|
|
139
158
|
slug: Slug;
|
|
140
159
|
descriptor: string;
|
|
141
|
-
lane
|
|
160
|
+
lane: FinderLane;
|
|
142
161
|
section?: Section;
|
|
143
162
|
terms?: string[];
|
|
144
163
|
}
|
|
@@ -147,9 +166,12 @@ export interface PoolCandidate {
|
|
|
147
166
|
* (`renderCard` output — head section + TOC). Cards must be byte-stable
|
|
148
167
|
* across turns for the prefix to ride the provider KV cache, which is why
|
|
149
168
|
* callers pre-render them at lane init rather than per turn. */
|
|
169
|
+
export type StableCandidateLane = "core" | "hot" | "fresh" | "always";
|
|
170
|
+
|
|
150
171
|
export interface StableCandidate {
|
|
151
172
|
slug: Slug;
|
|
152
173
|
card: string;
|
|
174
|
+
lane: StableCandidateLane;
|
|
153
175
|
}
|
|
154
176
|
|
|
155
177
|
/**
|
|
@@ -168,6 +190,7 @@ export interface SelectorPool {
|
|
|
168
190
|
/** Tool name forced via `tool_choice`. Shared constant so tests can match it. */
|
|
169
191
|
const SELECT_PAGES_TOOL_NAME = "select_pages";
|
|
170
192
|
const MEMORY_V3_SELECT_CALL_SITE = "memoryV3SelectL2" as const;
|
|
193
|
+
const SELECTOR_CONTEXT_BUDGET_RATIO = 0.8;
|
|
171
194
|
|
|
172
195
|
/** Finder-line snippets are truncated to keep the dynamic tail compact. */
|
|
173
196
|
const SNIPPET_MAX_CHARS = 300;
|
|
@@ -177,7 +200,8 @@ type PoolSelectorAttemptFailureReason =
|
|
|
177
200
|
| "provider_error"
|
|
178
201
|
| "missing_tool_use"
|
|
179
202
|
| "unexpected_tool_name"
|
|
180
|
-
| "schema_mismatch"
|
|
203
|
+
| "schema_mismatch"
|
|
204
|
+
| "unusable_answers";
|
|
181
205
|
|
|
182
206
|
interface PoolSelectorAttemptFailure {
|
|
183
207
|
attempt: number;
|
|
@@ -394,34 +418,39 @@ function renderCardSegment(stable: StableCandidate[]): string {
|
|
|
394
418
|
}
|
|
395
419
|
|
|
396
420
|
/** A finder line's lane tag: `(lane) `, or `(rare: word) ` for a rare-term
|
|
397
|
-
* line, keyed on the one word it carries as its term
|
|
398
|
-
* without a lane. */
|
|
421
|
+
* line, keyed on the one word it carries as its term. */
|
|
399
422
|
function laneTag(candidate: PoolCandidate): string {
|
|
400
|
-
if (candidate.lane === undefined) {
|
|
401
|
-
return "";
|
|
402
|
-
}
|
|
403
423
|
const word = candidate.lane === "rare" ? candidate.terms?.[0] : undefined;
|
|
404
424
|
return word === undefined
|
|
405
425
|
? `(${candidate.lane}) `
|
|
406
426
|
: `(${candidate.lane}: ${word}) `;
|
|
407
427
|
}
|
|
408
428
|
|
|
429
|
+
/**
|
|
430
|
+
* One finder line as the selector sees it, minus its pool number: the lane
|
|
431
|
+
* tag (naming the keyed word for a rare-term line, `(rare: turnip)`), the
|
|
432
|
+
* slug, and the snippet after a dash
|
|
433
|
+
* (no dash for a candidate with an empty descriptor). Shared with the pool
|
|
434
|
+
* input capture (`pool-log-store.ts`), so the persisted text is exactly the
|
|
435
|
+
* rendered line.
|
|
436
|
+
*/
|
|
437
|
+
export function renderFinderLine(candidate: PoolCandidate): string {
|
|
438
|
+
const snippet = renderSnippet(candidate);
|
|
439
|
+
const lane = laneTag(candidate);
|
|
440
|
+
return snippet.length > 0
|
|
441
|
+
? `${lane}${candidate.slug} — ${snippet}`
|
|
442
|
+
: `${lane}${candidate.slug}`;
|
|
443
|
+
}
|
|
444
|
+
|
|
409
445
|
/**
|
|
410
446
|
* Render the finder tail: one `[m+i] (lane) slug — snippet` line per
|
|
411
|
-
* candidate, numbered continuing after the
|
|
412
|
-
*
|
|
413
|
-
* for a rare-term line (`(rare: turnip)`); a candidate with an empty
|
|
414
|
-
* descriptor renders without the dash.
|
|
447
|
+
* candidate ({@link renderFinderLine}), numbered continuing after the
|
|
448
|
+
* `offset` stable-prefix cards.
|
|
415
449
|
*/
|
|
416
450
|
function renderFinderSegment(finder: PoolCandidate[], offset: number): string {
|
|
417
|
-
const lines = finder.map(
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
const lane = laneTag(c);
|
|
421
|
-
return snippet.length > 0
|
|
422
|
-
? `[${id}] ${lane}${c.slug} — ${snippet}`
|
|
423
|
-
: `[${id}] ${lane}${c.slug}`;
|
|
424
|
-
});
|
|
451
|
+
const lines = finder.map(
|
|
452
|
+
(c, i) => `[${offset + i + 1}] ${renderFinderLine(c)}`,
|
|
453
|
+
);
|
|
425
454
|
return `<candidates>\n${lines.join("\n")}\n</candidates>`;
|
|
426
455
|
}
|
|
427
456
|
|
|
@@ -482,9 +511,563 @@ export function selectAllPoolCandidates(pool: SelectorPool): SelectedPage[] {
|
|
|
482
511
|
);
|
|
483
512
|
}
|
|
484
513
|
|
|
514
|
+
/**
|
|
515
|
+
* Inclusive noul threshold for TypeSafe pool selection. 0.5 is calibrated
|
|
516
|
+
* equal yes/no. This sits slightly below that so a plausible candidate is
|
|
517
|
+
* kept, matching the selector's recall-heavy rule.
|
|
518
|
+
*/
|
|
519
|
+
export const TYPE_SAFE_POOL_KEEP_NOUL = 0.4;
|
|
520
|
+
|
|
521
|
+
/** Catalog id of the TypeSafe System One provider. */
|
|
522
|
+
const TYPE_SAFE_PROVIDER_ID = "typesafe";
|
|
523
|
+
|
|
524
|
+
type TypesafeNoulQuestion = {
|
|
525
|
+
type: "noul";
|
|
526
|
+
instructions: string;
|
|
527
|
+
criteria: { yes: string; no: string };
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
531
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function noulFromAnswer(answer: unknown): number | undefined {
|
|
535
|
+
if (typeof answer === "number" && Number.isFinite(answer)) {
|
|
536
|
+
return answer;
|
|
537
|
+
}
|
|
538
|
+
if (
|
|
539
|
+
isRecord(answer) &&
|
|
540
|
+
typeof answer.noul === "number" &&
|
|
541
|
+
Number.isFinite(answer.noul)
|
|
542
|
+
) {
|
|
543
|
+
return answer.noul;
|
|
544
|
+
}
|
|
545
|
+
return undefined;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function typesafeCandidateEntries(
|
|
549
|
+
pool: SelectorPool,
|
|
550
|
+
): Record<string, { slug: Slug; text: string }> {
|
|
551
|
+
const entries: Record<string, { slug: Slug; text: string }> = {};
|
|
552
|
+
pool.stable.forEach((candidate, index) => {
|
|
553
|
+
entries[String(index + 1)] = {
|
|
554
|
+
slug: candidate.slug,
|
|
555
|
+
text: candidate.card,
|
|
556
|
+
};
|
|
557
|
+
});
|
|
558
|
+
pool.finder.forEach((candidate, index) => {
|
|
559
|
+
entries[String(pool.stable.length + index + 1)] = {
|
|
560
|
+
slug: candidate.slug,
|
|
561
|
+
text: renderFinderLine(candidate),
|
|
562
|
+
};
|
|
563
|
+
});
|
|
564
|
+
return entries;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function typesafeKeepQuestion(id: string): TypesafeNoulQuestion {
|
|
568
|
+
return {
|
|
569
|
+
type: "noul",
|
|
570
|
+
instructions:
|
|
571
|
+
`Would the upcoming assistant reply draw on \`candidates.${id}\`? ` +
|
|
572
|
+
"Lean inclusive. Facts, current task and event state, register, " +
|
|
573
|
+
"framing, calibration, and relationship texture all count. True when " +
|
|
574
|
+
"the candidate could plausibly inform the reply.",
|
|
575
|
+
criteria: {
|
|
576
|
+
yes: "The reply would draw on this candidate.",
|
|
577
|
+
no: "The reply would not draw on this candidate.",
|
|
578
|
+
},
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function buildTypesafeSelectorPayload(
|
|
583
|
+
pool: SelectorPool,
|
|
584
|
+
turn: MemoryRoutingTurn,
|
|
585
|
+
systemPrompt: string,
|
|
586
|
+
): {
|
|
587
|
+
state: Record<string, unknown>;
|
|
588
|
+
questions: Record<string, TypesafeNoulQuestion>;
|
|
589
|
+
} {
|
|
590
|
+
const candidates = typesafeCandidateEntries(pool);
|
|
591
|
+
const questions: Record<string, TypesafeNoulQuestion> = {};
|
|
592
|
+
for (const id of Object.keys(candidates)) {
|
|
593
|
+
questions[id] = typesafeKeepQuestion(id);
|
|
594
|
+
}
|
|
595
|
+
return {
|
|
596
|
+
state: {
|
|
597
|
+
selector_instructions: systemPrompt,
|
|
598
|
+
candidates,
|
|
599
|
+
...(turn.situationalContext
|
|
600
|
+
? { situation: turn.situationalContext }
|
|
601
|
+
: {}),
|
|
602
|
+
recent_context: turn.recentContext,
|
|
603
|
+
current_message: turn.currentMessage,
|
|
604
|
+
},
|
|
605
|
+
questions,
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function buildGenericSelectorMessage(
|
|
610
|
+
pool: SelectorPool,
|
|
611
|
+
turn: MemoryRoutingTurn,
|
|
612
|
+
): Message {
|
|
613
|
+
const content: ContentBlock[] = [];
|
|
614
|
+
if (pool.stable.length > 0) {
|
|
615
|
+
content.push(cachedTextBlock(renderCardSegment(pool.stable)));
|
|
616
|
+
}
|
|
617
|
+
const tailParts: string[] = [];
|
|
618
|
+
if (pool.finder.length > 0) {
|
|
619
|
+
tailParts.push(renderFinderSegment(pool.finder, pool.stable.length));
|
|
620
|
+
}
|
|
621
|
+
if (turn.situationalContext) {
|
|
622
|
+
tailParts.push(`<situation>${turn.situationalContext}</situation>`);
|
|
623
|
+
}
|
|
624
|
+
tailParts.push(`<recent_context>${turn.recentContext}</recent_context>`);
|
|
625
|
+
tailParts.push(`<current_message>${turn.currentMessage}</current_message>`);
|
|
626
|
+
content.push({ type: "text", text: tailParts.join("\n") });
|
|
627
|
+
return { role: "user", content };
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const STABLE_CANDIDATE_PRIORITY: Record<StableCandidateLane, number> = {
|
|
631
|
+
core: 0,
|
|
632
|
+
hot: 5,
|
|
633
|
+
fresh: 6,
|
|
634
|
+
always: 7,
|
|
635
|
+
};
|
|
636
|
+
|
|
637
|
+
const FINDER_CANDIDATE_PRIORITY: Record<FinderLane, number> = {
|
|
638
|
+
needle: 1,
|
|
639
|
+
dense: 2,
|
|
640
|
+
reply: 3,
|
|
641
|
+
span: 4,
|
|
642
|
+
entity: 8,
|
|
643
|
+
rare: 9,
|
|
644
|
+
edge: 10,
|
|
645
|
+
learned: 11,
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
function highestPriorityCandidates(
|
|
649
|
+
pool: SelectorPool,
|
|
650
|
+
retainedCount: number,
|
|
651
|
+
): SelectorPool {
|
|
652
|
+
const ranked = [
|
|
653
|
+
...pool.stable.map((candidate, index) => ({
|
|
654
|
+
segment: "stable" as const,
|
|
655
|
+
index,
|
|
656
|
+
poolIndex: index,
|
|
657
|
+
priority: STABLE_CANDIDATE_PRIORITY[candidate.lane],
|
|
658
|
+
})),
|
|
659
|
+
...pool.finder.map((candidate, index) => ({
|
|
660
|
+
segment: "finder" as const,
|
|
661
|
+
index,
|
|
662
|
+
poolIndex: pool.stable.length + index,
|
|
663
|
+
priority: FINDER_CANDIDATE_PRIORITY[candidate.lane],
|
|
664
|
+
})),
|
|
665
|
+
]
|
|
666
|
+
.sort(
|
|
667
|
+
(left, right) =>
|
|
668
|
+
left.priority - right.priority || left.poolIndex - right.poolIndex,
|
|
669
|
+
)
|
|
670
|
+
.slice(0, retainedCount);
|
|
671
|
+
const retainedStable = new Set(
|
|
672
|
+
ranked
|
|
673
|
+
.filter((candidate) => candidate.segment === "stable")
|
|
674
|
+
.map((candidate) => candidate.index),
|
|
675
|
+
);
|
|
676
|
+
const retainedFinder = new Set(
|
|
677
|
+
ranked
|
|
678
|
+
.filter((candidate) => candidate.segment === "finder")
|
|
679
|
+
.map((candidate) => candidate.index),
|
|
680
|
+
);
|
|
681
|
+
return {
|
|
682
|
+
stable: pool.stable.filter((_, index) => retainedStable.has(index)),
|
|
683
|
+
finder: pool.finder.filter((_, index) => retainedFinder.has(index)),
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function withoutTrailingCurrentMessage(
|
|
688
|
+
turn: MemoryRoutingTurn,
|
|
689
|
+
): MemoryRoutingTurn {
|
|
690
|
+
if (turn.recentContext === turn.currentMessage) {
|
|
691
|
+
return { ...turn, recentContext: "" };
|
|
692
|
+
}
|
|
693
|
+
const duplicateSuffix = `\n${turn.currentMessage}`;
|
|
694
|
+
if (
|
|
695
|
+
turn.currentMessage.length === 0 ||
|
|
696
|
+
!turn.recentContext.endsWith(duplicateSuffix)
|
|
697
|
+
) {
|
|
698
|
+
return turn;
|
|
699
|
+
}
|
|
700
|
+
return {
|
|
701
|
+
...turn,
|
|
702
|
+
recentContext: turn.recentContext.slice(0, -duplicateSuffix.length),
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
function latestTurnContextSuffix(
|
|
707
|
+
turn: MemoryRoutingTurn,
|
|
708
|
+
retainedChars: number,
|
|
709
|
+
): MemoryRoutingTurn {
|
|
710
|
+
let remaining = retainedChars;
|
|
711
|
+
const takeSuffix = (value: string): string => {
|
|
712
|
+
const count = Math.min(remaining, value.length);
|
|
713
|
+
remaining -= count;
|
|
714
|
+
return safeStringSlice(value, value.length - count);
|
|
715
|
+
};
|
|
716
|
+
const currentMessage = takeSuffix(turn.currentMessage);
|
|
717
|
+
const recentContext = takeSuffix(turn.recentContext);
|
|
718
|
+
const situationalContext = turn.situationalContext
|
|
719
|
+
? takeSuffix(turn.situationalContext) || undefined
|
|
720
|
+
: undefined;
|
|
721
|
+
return {
|
|
722
|
+
...turn,
|
|
723
|
+
currentMessage,
|
|
724
|
+
recentContext,
|
|
725
|
+
situationalContext,
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
function turnContextChars(turn: MemoryRoutingTurn): number {
|
|
730
|
+
return (
|
|
731
|
+
turn.currentMessage.length +
|
|
732
|
+
turn.recentContext.length +
|
|
733
|
+
(turn.situationalContext?.length ?? 0)
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
function estimateSelectorInputTokens({
|
|
738
|
+
pool,
|
|
739
|
+
turn,
|
|
740
|
+
systemPrompt,
|
|
741
|
+
provider,
|
|
742
|
+
model,
|
|
743
|
+
}: {
|
|
744
|
+
pool: SelectorPool;
|
|
745
|
+
turn: MemoryRoutingTurn;
|
|
746
|
+
systemPrompt: string;
|
|
747
|
+
provider: Provider;
|
|
748
|
+
model: string;
|
|
749
|
+
}): number {
|
|
750
|
+
if (provider.name === TYPE_SAFE_PROVIDER_ID) {
|
|
751
|
+
const payload = buildTypesafeSelectorPayload(pool, turn, systemPrompt);
|
|
752
|
+
return estimateTextTokens(
|
|
753
|
+
JSON.stringify({
|
|
754
|
+
state: payload.state,
|
|
755
|
+
model,
|
|
756
|
+
questions: payload.questions,
|
|
757
|
+
}),
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
return estimatePromptTokensWithTools(
|
|
761
|
+
[buildGenericSelectorMessage(pool, turn)],
|
|
762
|
+
systemPrompt,
|
|
763
|
+
[SELECT_PAGES_TOOL],
|
|
764
|
+
provider.tokenEstimationProvider ?? provider.name,
|
|
765
|
+
);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
interface SelectorPoolBudget {
|
|
769
|
+
pool: SelectorPool;
|
|
770
|
+
turn: MemoryRoutingTurn;
|
|
771
|
+
budgetTokens: number;
|
|
772
|
+
estimatedInputTokens: number;
|
|
773
|
+
originalEstimatedInputTokens: number;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
function budgetSelectorPool({
|
|
777
|
+
pool,
|
|
778
|
+
turn,
|
|
779
|
+
systemPrompt,
|
|
780
|
+
provider,
|
|
781
|
+
model,
|
|
782
|
+
maxInputTokens,
|
|
783
|
+
}: {
|
|
784
|
+
pool: SelectorPool;
|
|
785
|
+
turn: MemoryRoutingTurn;
|
|
786
|
+
systemPrompt: string;
|
|
787
|
+
provider: Provider;
|
|
788
|
+
model: string;
|
|
789
|
+
maxInputTokens: number;
|
|
790
|
+
}): SelectorPoolBudget {
|
|
791
|
+
const budgetTokens = Math.max(
|
|
792
|
+
1,
|
|
793
|
+
Math.floor(maxInputTokens * SELECTOR_CONTEXT_BUDGET_RATIO),
|
|
794
|
+
);
|
|
795
|
+
const estimate = (
|
|
796
|
+
candidatePool: SelectorPool,
|
|
797
|
+
candidateTurn: MemoryRoutingTurn,
|
|
798
|
+
): number =>
|
|
799
|
+
estimateSelectorInputTokens({
|
|
800
|
+
pool: candidatePool,
|
|
801
|
+
turn: candidateTurn,
|
|
802
|
+
systemPrompt,
|
|
803
|
+
provider,
|
|
804
|
+
model,
|
|
805
|
+
});
|
|
806
|
+
const originalEstimatedInputTokens = estimate(pool, turn);
|
|
807
|
+
if (originalEstimatedInputTokens <= budgetTokens) {
|
|
808
|
+
return {
|
|
809
|
+
pool,
|
|
810
|
+
turn,
|
|
811
|
+
budgetTokens,
|
|
812
|
+
estimatedInputTokens: originalEstimatedInputTokens,
|
|
813
|
+
originalEstimatedInputTokens,
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
const deduplicatedTurn = withoutTrailingCurrentMessage(turn);
|
|
818
|
+
const deduplicatedEstimatedInputTokens = estimate(pool, deduplicatedTurn);
|
|
819
|
+
if (deduplicatedEstimatedInputTokens <= budgetTokens) {
|
|
820
|
+
return {
|
|
821
|
+
pool,
|
|
822
|
+
turn: deduplicatedTurn,
|
|
823
|
+
budgetTokens,
|
|
824
|
+
estimatedInputTokens: deduplicatedEstimatedInputTokens,
|
|
825
|
+
originalEstimatedInputTokens,
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
const minimumTurn = latestTurnContextSuffix(
|
|
830
|
+
deduplicatedTurn,
|
|
831
|
+
deduplicatedTurn.currentMessage.length,
|
|
832
|
+
);
|
|
833
|
+
const fittingPool: SelectorPool = {
|
|
834
|
+
stable: pool.stable.filter(
|
|
835
|
+
(candidate) =>
|
|
836
|
+
estimate({ stable: [candidate], finder: [] }, minimumTurn) <=
|
|
837
|
+
budgetTokens,
|
|
838
|
+
),
|
|
839
|
+
finder: pool.finder.filter(
|
|
840
|
+
(candidate) =>
|
|
841
|
+
estimate({ stable: [], finder: [candidate] }, minimumTurn) <=
|
|
842
|
+
budgetTokens,
|
|
843
|
+
),
|
|
844
|
+
};
|
|
845
|
+
const candidateCount = fittingPool.stable.length + fittingPool.finder.length;
|
|
846
|
+
if (candidateCount === 0) {
|
|
847
|
+
const emptyPool: SelectorPool = { stable: [], finder: [] };
|
|
848
|
+
return {
|
|
849
|
+
pool: emptyPool,
|
|
850
|
+
turn: minimumTurn,
|
|
851
|
+
budgetTokens,
|
|
852
|
+
estimatedInputTokens: estimate(emptyPool, minimumTurn),
|
|
853
|
+
originalEstimatedInputTokens,
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
const preferredCandidatePool = highestPriorityCandidates(fittingPool, 1);
|
|
858
|
+
let budgetedTurn = deduplicatedTurn;
|
|
859
|
+
if (estimate(preferredCandidatePool, budgetedTurn) > budgetTokens) {
|
|
860
|
+
let low = deduplicatedTurn.currentMessage.length;
|
|
861
|
+
let high = turnContextChars(deduplicatedTurn);
|
|
862
|
+
while (low < high) {
|
|
863
|
+
const retainedChars = Math.ceil((low + high) / 2);
|
|
864
|
+
const candidateTurn = latestTurnContextSuffix(
|
|
865
|
+
deduplicatedTurn,
|
|
866
|
+
retainedChars,
|
|
867
|
+
);
|
|
868
|
+
if (estimate(preferredCandidatePool, candidateTurn) <= budgetTokens) {
|
|
869
|
+
low = retainedChars;
|
|
870
|
+
} else {
|
|
871
|
+
high = retainedChars - 1;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
budgetedTurn = latestTurnContextSuffix(deduplicatedTurn, low);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
let low = 1;
|
|
878
|
+
let high = candidateCount;
|
|
879
|
+
while (low < high) {
|
|
880
|
+
const retainedCount = Math.ceil((low + high) / 2);
|
|
881
|
+
const candidatePool = highestPriorityCandidates(fittingPool, retainedCount);
|
|
882
|
+
if (estimate(candidatePool, budgetedTurn) <= budgetTokens) {
|
|
883
|
+
low = retainedCount;
|
|
884
|
+
} else {
|
|
885
|
+
high = retainedCount - 1;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
const budgetedPool = highestPriorityCandidates(fittingPool, low);
|
|
890
|
+
return {
|
|
891
|
+
pool: budgetedPool,
|
|
892
|
+
turn: budgetedTurn,
|
|
893
|
+
budgetTokens,
|
|
894
|
+
estimatedInputTokens: estimate(budgetedPool, budgetedTurn),
|
|
895
|
+
originalEstimatedInputTokens,
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
function answersFromSelectorResponse(
|
|
900
|
+
response: ProviderResponse,
|
|
901
|
+
): Record<string, unknown> | null {
|
|
902
|
+
const raw = response.rawResponse;
|
|
903
|
+
if (isRecord(raw) && isRecord(raw.answers)) {
|
|
904
|
+
return raw.answers;
|
|
905
|
+
}
|
|
906
|
+
const textBlock = response.content.find((block) => block.type === "text");
|
|
907
|
+
if (!textBlock || textBlock.type !== "text") {
|
|
908
|
+
return null;
|
|
909
|
+
}
|
|
910
|
+
try {
|
|
911
|
+
const parsed: unknown = JSON.parse(textBlock.text);
|
|
912
|
+
return isRecord(parsed) ? parsed : null;
|
|
913
|
+
} catch {
|
|
914
|
+
return null;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
async function selectPoolWithTypesafe(
|
|
919
|
+
pool: SelectorPool,
|
|
920
|
+
turn: MemoryRoutingTurn,
|
|
921
|
+
ordered: PoolLine[],
|
|
922
|
+
systemPrompt: string,
|
|
923
|
+
provider: Provider,
|
|
924
|
+
): Promise<PoolSelection> {
|
|
925
|
+
const payload = buildTypesafeSelectorPayload(pool, turn, systemPrompt);
|
|
926
|
+
const userMsg: Message = {
|
|
927
|
+
role: "user",
|
|
928
|
+
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
929
|
+
};
|
|
930
|
+
|
|
931
|
+
const failures: PoolSelectorAttemptFailure[] = [];
|
|
932
|
+
let attempt = 0;
|
|
933
|
+
const recordFailure = (
|
|
934
|
+
failure: Omit<
|
|
935
|
+
PoolSelectorAttemptFailure,
|
|
936
|
+
| "callSite"
|
|
937
|
+
| "providerName"
|
|
938
|
+
| "candidateCount"
|
|
939
|
+
| "stableCount"
|
|
940
|
+
| "finderCount"
|
|
941
|
+
>,
|
|
942
|
+
): void => {
|
|
943
|
+
const diagnostic: PoolSelectorAttemptFailure = {
|
|
944
|
+
...failure,
|
|
945
|
+
callSite: MEMORY_V3_SELECT_CALL_SITE,
|
|
946
|
+
providerName: provider.name,
|
|
947
|
+
candidateCount: ordered.length,
|
|
948
|
+
stableCount: pool.stable.length,
|
|
949
|
+
finderCount: pool.finder.length,
|
|
950
|
+
};
|
|
951
|
+
failures.push(diagnostic);
|
|
952
|
+
log.warn(diagnostic, "pool selector attempt failed");
|
|
953
|
+
};
|
|
954
|
+
|
|
955
|
+
let lastError: unknown = null;
|
|
956
|
+
const parsed = await retryForResult(async () => {
|
|
957
|
+
attempt += 1;
|
|
958
|
+
let response: Awaited<ReturnType<typeof provider.sendMessage>>;
|
|
959
|
+
try {
|
|
960
|
+
response = await provider.sendMessage([userMsg], {
|
|
961
|
+
config: {
|
|
962
|
+
callSite: MEMORY_V3_SELECT_CALL_SITE,
|
|
963
|
+
conversationId: turn.conversationId,
|
|
964
|
+
selectionSeed: turn.conversationId,
|
|
965
|
+
disableTurnStartCache: true,
|
|
966
|
+
},
|
|
967
|
+
});
|
|
968
|
+
lastError = null;
|
|
969
|
+
} catch (error) {
|
|
970
|
+
lastError = error;
|
|
971
|
+
recordFailure({
|
|
972
|
+
attempt,
|
|
973
|
+
reason: "provider_error",
|
|
974
|
+
error: summarizeError(error),
|
|
975
|
+
});
|
|
976
|
+
throw error;
|
|
977
|
+
}
|
|
978
|
+
const answers = answersFromSelectorResponse(response);
|
|
979
|
+
if (!answers) {
|
|
980
|
+
recordFailure({
|
|
981
|
+
attempt,
|
|
982
|
+
reason: "unusable_answers",
|
|
983
|
+
response: summarizeResponse(response),
|
|
984
|
+
});
|
|
985
|
+
return null;
|
|
986
|
+
}
|
|
987
|
+
const picked: number[] = [];
|
|
988
|
+
let parsedCount = 0;
|
|
989
|
+
for (let index = 0; index < ordered.length; index++) {
|
|
990
|
+
const noul = noulFromAnswer(answers[String(index + 1)]);
|
|
991
|
+
if (noul === undefined) {
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
994
|
+
parsedCount += 1;
|
|
995
|
+
if (noul >= TYPE_SAFE_POOL_KEEP_NOUL) {
|
|
996
|
+
picked.push(index);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
if (parsedCount === 0) {
|
|
1000
|
+
recordFailure({
|
|
1001
|
+
attempt,
|
|
1002
|
+
reason: "unusable_answers",
|
|
1003
|
+
response: summarizeResponse(response),
|
|
1004
|
+
});
|
|
1005
|
+
return null;
|
|
1006
|
+
}
|
|
1007
|
+
return {
|
|
1008
|
+
pages: mergeSelectedLines(ordered, picked),
|
|
1009
|
+
keptAll: false,
|
|
1010
|
+
pool,
|
|
1011
|
+
turn,
|
|
1012
|
+
};
|
|
1013
|
+
});
|
|
1014
|
+
|
|
1015
|
+
if (parsed === null) {
|
|
1016
|
+
if (lastError !== null) {
|
|
1017
|
+
const detail =
|
|
1018
|
+
lastError instanceof Error ? lastError.message : String(lastError);
|
|
1019
|
+
const redactedDetail = truncate(
|
|
1020
|
+
redactLogString(detail),
|
|
1021
|
+
ERROR_MESSAGE_MAX_CHARS,
|
|
1022
|
+
);
|
|
1023
|
+
log.warn(
|
|
1024
|
+
{
|
|
1025
|
+
candidateCount: ordered.length,
|
|
1026
|
+
stableCount: pool.stable.length,
|
|
1027
|
+
finderCount: pool.finder.length,
|
|
1028
|
+
callSite: MEMORY_V3_SELECT_CALL_SITE,
|
|
1029
|
+
providerName: provider.name,
|
|
1030
|
+
failures,
|
|
1031
|
+
},
|
|
1032
|
+
"pool selector provider call failed after retries",
|
|
1033
|
+
);
|
|
1034
|
+
throw new MemoryV3RetrievalUnavailableError(
|
|
1035
|
+
`memory-v3 pool selector provider call failed after retries: ${redactedDetail}`,
|
|
1036
|
+
{
|
|
1037
|
+
cause: lastError,
|
|
1038
|
+
conversationNotice: providerBillingNoticeFromError(lastError),
|
|
1039
|
+
pool,
|
|
1040
|
+
turn,
|
|
1041
|
+
},
|
|
1042
|
+
);
|
|
1043
|
+
}
|
|
1044
|
+
log.warn(
|
|
1045
|
+
{
|
|
1046
|
+
candidateCount: ordered.length,
|
|
1047
|
+
stableCount: pool.stable.length,
|
|
1048
|
+
finderCount: pool.finder.length,
|
|
1049
|
+
callSite: MEMORY_V3_SELECT_CALL_SITE,
|
|
1050
|
+
providerName: provider.name,
|
|
1051
|
+
failures,
|
|
1052
|
+
},
|
|
1053
|
+
"pool selector returned no usable TypeSafe answers after retries",
|
|
1054
|
+
);
|
|
1055
|
+
throw new MemoryV3RetrievalUnavailableError(
|
|
1056
|
+
"memory-v3 pool selector returned no usable selection after retries",
|
|
1057
|
+
{ pool, turn },
|
|
1058
|
+
);
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
return parsed;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
485
1064
|
/** A selection plus whether it came from the recall-safe keep-all fallback. */
|
|
486
1065
|
export interface PoolSelection {
|
|
487
1066
|
pages: SelectedPage[];
|
|
1067
|
+
/** The exact candidate pool rendered for the selector request. */
|
|
1068
|
+
pool: SelectorPool;
|
|
1069
|
+
/** The exact context strings rendered for the selector request. */
|
|
1070
|
+
turn: MemoryRoutingTurn;
|
|
488
1071
|
/** True only when the model OMITTED `ids` and every candidate was kept as the
|
|
489
1072
|
* recall-safe fallback — NOT when it explicitly selected a large set. The two
|
|
490
1073
|
* produce the same pages but mean different things (the model gave up judging
|
|
@@ -493,15 +1076,18 @@ export interface PoolSelection {
|
|
|
493
1076
|
}
|
|
494
1077
|
|
|
495
1078
|
/**
|
|
496
|
-
* Run the single
|
|
1079
|
+
* Run the single selector over the unified candidate pool. Returns
|
|
497
1080
|
* the pages to inject, merged per slug (a page selected as a card and on
|
|
498
1081
|
* finder lines yields one entry carrying every selected section), plus a
|
|
499
1082
|
* `keptAll` flag marking the recall-safe fallback.
|
|
500
1083
|
*
|
|
501
|
-
*
|
|
502
|
-
* relevant" signal, `keptAll: true`); an explicit `[]`
|
|
503
|
-
*
|
|
504
|
-
*
|
|
1084
|
+
* On a chat model, an omitted `ids` keeps ALL candidates (the recall-safe
|
|
1085
|
+
* "all of these are relevant" signal, `keptAll: true`); an explicit `[]`
|
|
1086
|
+
* keeps none. TypeSafe answers one noul per candidate and never omits ids,
|
|
1087
|
+
* so `keptAll` is always false on that path. An infrastructure failure
|
|
1088
|
+
* (after a short re-prompt retry) throws
|
|
1089
|
+
* {@link MemoryV3RetrievalUnavailableError}, and the orchestrator keeps the
|
|
1090
|
+
* stable prefix unjudged in its place.
|
|
505
1091
|
*
|
|
506
1092
|
* `systemPrompt` is the selector's instruction scaffold; it defaults to the
|
|
507
1093
|
* bundled {@link SYSTEM_PROMPT} and is overridable via `memory.v3.selectorPromptPath`
|
|
@@ -512,17 +1098,19 @@ export async function selectPool(
|
|
|
512
1098
|
turn: MemoryRoutingTurn,
|
|
513
1099
|
systemPrompt: string = SYSTEM_PROMPT,
|
|
514
1100
|
): Promise<PoolSelection> {
|
|
515
|
-
const
|
|
516
|
-
if (
|
|
517
|
-
return { pages: [], keptAll: false };
|
|
1101
|
+
const originalCandidateCount = pool.stable.length + pool.finder.length;
|
|
1102
|
+
if (originalCandidateCount === 0) {
|
|
1103
|
+
return { pages: [], keptAll: false, pool, turn };
|
|
518
1104
|
}
|
|
519
1105
|
|
|
520
|
-
const provider = await getConfiguredProvider(MEMORY_V3_SELECT_CALL_SITE
|
|
1106
|
+
const provider = await getConfiguredProvider(MEMORY_V3_SELECT_CALL_SITE, {
|
|
1107
|
+
selectionSeed: turn.conversationId,
|
|
1108
|
+
});
|
|
521
1109
|
if (!provider) {
|
|
522
1110
|
log.warn(
|
|
523
1111
|
{
|
|
524
1112
|
callSite: MEMORY_V3_SELECT_CALL_SITE,
|
|
525
|
-
candidateCount:
|
|
1113
|
+
candidateCount: originalCandidateCount,
|
|
526
1114
|
stableCount: pool.stable.length,
|
|
527
1115
|
finderCount: pool.finder.length,
|
|
528
1116
|
},
|
|
@@ -530,28 +1118,77 @@ export async function selectPool(
|
|
|
530
1118
|
);
|
|
531
1119
|
throw new MemoryV3RetrievalUnavailableError(
|
|
532
1120
|
"memory-v3 pool selector provider unavailable",
|
|
1121
|
+
{ pool, turn },
|
|
533
1122
|
);
|
|
534
1123
|
}
|
|
535
1124
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
1125
|
+
const effectiveContextWindow = getEffectiveContextWindow(
|
|
1126
|
+
MEMORY_V3_SELECT_CALL_SITE,
|
|
1127
|
+
{ selectionSeed: turn.conversationId },
|
|
1128
|
+
);
|
|
1129
|
+
const budget = budgetSelectorPool({
|
|
1130
|
+
pool,
|
|
1131
|
+
turn,
|
|
1132
|
+
systemPrompt,
|
|
1133
|
+
provider,
|
|
1134
|
+
model: effectiveContextWindow.model,
|
|
1135
|
+
maxInputTokens: effectiveContextWindow.maxInputTokens,
|
|
1136
|
+
});
|
|
1137
|
+
const budgetedPool = budget.pool;
|
|
1138
|
+
const budgetedTurn = budget.turn;
|
|
1139
|
+
const ordered = orderedLines(budgetedPool);
|
|
1140
|
+
if (
|
|
1141
|
+
ordered.length < originalCandidateCount ||
|
|
1142
|
+
turnContextChars(budgetedTurn) < turnContextChars(turn)
|
|
1143
|
+
) {
|
|
1144
|
+
log.info(
|
|
1145
|
+
{
|
|
1146
|
+
callSite: MEMORY_V3_SELECT_CALL_SITE,
|
|
1147
|
+
providerName: provider.name,
|
|
1148
|
+
model: effectiveContextWindow.model,
|
|
1149
|
+
maxInputTokens: effectiveContextWindow.maxInputTokens,
|
|
1150
|
+
budgetRatio: SELECTOR_CONTEXT_BUDGET_RATIO,
|
|
1151
|
+
budgetTokens: budget.budgetTokens,
|
|
1152
|
+
originalEstimatedInputTokens: budget.originalEstimatedInputTokens,
|
|
1153
|
+
estimatedInputTokens: budget.estimatedInputTokens,
|
|
1154
|
+
originalStableCount: pool.stable.length,
|
|
1155
|
+
retainedStableCount: budgetedPool.stable.length,
|
|
1156
|
+
originalFinderCount: pool.finder.length,
|
|
1157
|
+
retainedFinderCount: budgetedPool.finder.length,
|
|
1158
|
+
originalTurnContextChars: turnContextChars(turn),
|
|
1159
|
+
retainedTurnContextChars: turnContextChars(budgetedTurn),
|
|
1160
|
+
},
|
|
1161
|
+
"trimmed pool selector input to the context budget",
|
|
1162
|
+
);
|
|
542
1163
|
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
1164
|
+
if (ordered.length === 0) {
|
|
1165
|
+
log.warn(
|
|
1166
|
+
{
|
|
1167
|
+
callSite: MEMORY_V3_SELECT_CALL_SITE,
|
|
1168
|
+
providerName: provider.name,
|
|
1169
|
+
model: effectiveContextWindow.model,
|
|
1170
|
+
maxInputTokens: effectiveContextWindow.maxInputTokens,
|
|
1171
|
+
candidateCount: originalCandidateCount,
|
|
1172
|
+
},
|
|
1173
|
+
"no pool selector candidate fits the context budget",
|
|
1174
|
+
);
|
|
1175
|
+
throw new MemoryV3RetrievalUnavailableError(
|
|
1176
|
+
"memory-v3 pool selector has no candidate that fits the context budget",
|
|
1177
|
+
{ pool: budgetedPool, turn: budgetedTurn },
|
|
1178
|
+
);
|
|
546
1179
|
}
|
|
547
|
-
|
|
548
|
-
|
|
1180
|
+
|
|
1181
|
+
if (provider.name === TYPE_SAFE_PROVIDER_ID) {
|
|
1182
|
+
return selectPoolWithTypesafe(
|
|
1183
|
+
budgetedPool,
|
|
1184
|
+
budgetedTurn,
|
|
1185
|
+
ordered,
|
|
1186
|
+
systemPrompt,
|
|
1187
|
+
provider,
|
|
1188
|
+
);
|
|
549
1189
|
}
|
|
550
|
-
tailParts.push(`<recent_context>${turn.recentContext}</recent_context>`);
|
|
551
|
-
tailParts.push(`<current_message>${turn.currentMessage}</current_message>`);
|
|
552
|
-
content.push({ type: "text", text: tailParts.join("\n") });
|
|
553
1190
|
|
|
554
|
-
const userMsg
|
|
1191
|
+
const userMsg = buildGenericSelectorMessage(budgetedPool, budgetedTurn);
|
|
555
1192
|
const failures: PoolSelectorAttemptFailure[] = [];
|
|
556
1193
|
let attempt = 0;
|
|
557
1194
|
const recordFailure = (
|
|
@@ -569,8 +1206,8 @@ export async function selectPool(
|
|
|
569
1206
|
callSite: MEMORY_V3_SELECT_CALL_SITE,
|
|
570
1207
|
providerName: provider.name,
|
|
571
1208
|
candidateCount: ordered.length,
|
|
572
|
-
stableCount:
|
|
573
|
-
finderCount:
|
|
1209
|
+
stableCount: budgetedPool.stable.length,
|
|
1210
|
+
finderCount: budgetedPool.finder.length,
|
|
574
1211
|
};
|
|
575
1212
|
failures.push(diagnostic);
|
|
576
1213
|
log.warn(diagnostic, "pool selector attempt failed");
|
|
@@ -597,6 +1234,7 @@ export async function selectPool(
|
|
|
597
1234
|
config: {
|
|
598
1235
|
callSite: MEMORY_V3_SELECT_CALL_SITE,
|
|
599
1236
|
conversationId: turn.conversationId,
|
|
1237
|
+
selectionSeed: turn.conversationId,
|
|
600
1238
|
tool_choice: { type: "tool" as const, name: SELECT_PAGES_TOOL_NAME },
|
|
601
1239
|
// The last block of this one-shot message varies every turn; the
|
|
602
1240
|
// provider's auto-applied turn-start breakpoint would land on it and
|
|
@@ -663,8 +1301,8 @@ export async function selectPool(
|
|
|
663
1301
|
log.warn(
|
|
664
1302
|
{
|
|
665
1303
|
candidateCount: ordered.length,
|
|
666
|
-
stableCount:
|
|
667
|
-
finderCount:
|
|
1304
|
+
stableCount: budgetedPool.stable.length,
|
|
1305
|
+
finderCount: budgetedPool.finder.length,
|
|
668
1306
|
callSite: MEMORY_V3_SELECT_CALL_SITE,
|
|
669
1307
|
providerName: provider.name,
|
|
670
1308
|
failures,
|
|
@@ -676,14 +1314,16 @@ export async function selectPool(
|
|
|
676
1314
|
{
|
|
677
1315
|
cause: lastError,
|
|
678
1316
|
conversationNotice: providerBillingNoticeFromError(lastError),
|
|
1317
|
+
pool: budgetedPool,
|
|
1318
|
+
turn: budgetedTurn,
|
|
679
1319
|
},
|
|
680
1320
|
);
|
|
681
1321
|
}
|
|
682
1322
|
log.warn(
|
|
683
1323
|
{
|
|
684
1324
|
candidateCount: ordered.length,
|
|
685
|
-
stableCount:
|
|
686
|
-
finderCount:
|
|
1325
|
+
stableCount: budgetedPool.stable.length,
|
|
1326
|
+
finderCount: budgetedPool.finder.length,
|
|
687
1327
|
callSite: MEMORY_V3_SELECT_CALL_SITE,
|
|
688
1328
|
providerName: provider.name,
|
|
689
1329
|
failures,
|
|
@@ -692,12 +1332,18 @@ export async function selectPool(
|
|
|
692
1332
|
);
|
|
693
1333
|
throw new MemoryV3RetrievalUnavailableError(
|
|
694
1334
|
"memory-v3 pool selector returned no usable selection after retries",
|
|
1335
|
+
{ pool: budgetedPool, turn: budgetedTurn },
|
|
695
1336
|
);
|
|
696
1337
|
}
|
|
697
1338
|
|
|
698
1339
|
// Omitted `ids` is the recall-safe "keep all candidates" signal.
|
|
699
1340
|
if (parsed.ids === undefined) {
|
|
700
|
-
return {
|
|
1341
|
+
return {
|
|
1342
|
+
pages: selectAllPoolCandidates(budgetedPool),
|
|
1343
|
+
keptAll: true,
|
|
1344
|
+
pool: budgetedPool,
|
|
1345
|
+
turn: budgetedTurn,
|
|
1346
|
+
};
|
|
701
1347
|
}
|
|
702
1348
|
|
|
703
1349
|
// Map 1-based IDs over the concatenated numbering, dropping out-of-range
|
|
@@ -705,5 +1351,10 @@ export async function selectPool(
|
|
|
705
1351
|
const picked = parsed.ids
|
|
706
1352
|
.filter((id) => id >= 1 && id <= ordered.length)
|
|
707
1353
|
.map((id) => id - 1);
|
|
708
|
-
return {
|
|
1354
|
+
return {
|
|
1355
|
+
pages: mergeSelectedLines(ordered, picked),
|
|
1356
|
+
keptAll: false,
|
|
1357
|
+
pool: budgetedPool,
|
|
1358
|
+
turn: budgetedTurn,
|
|
1359
|
+
};
|
|
709
1360
|
}
|