@vellumai/assistant 0.11.6 → 0.11.7-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/ARCHITECTURE.md +31 -19
- package/Dockerfile +1 -0
- package/docs/architecture/security.md +3 -3
- package/docs/flux-turn-detection-spike.md +8 -7
- package/docs/runbook-trusted-contacts.md +14 -0
- package/docs/stt-provider-onboarding.md +8 -4
- package/knip.json +1 -0
- package/node_modules/@vellumai/avatar-manifest/package.json +18 -0
- package/node_modules/@vellumai/avatar-manifest/src/__tests__/manifest.test.ts +173 -0
- package/node_modules/@vellumai/avatar-manifest/src/__tests__/read.test.ts +114 -0
- package/node_modules/@vellumai/avatar-manifest/src/index.ts +29 -0
- package/node_modules/@vellumai/avatar-manifest/src/layout.ts +18 -0
- package/node_modules/@vellumai/avatar-manifest/src/manifest.ts +156 -0
- package/node_modules/@vellumai/avatar-manifest/src/read.ts +109 -0
- package/node_modules/@vellumai/avatar-manifest/tsconfig.json +20 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
- package/node_modules/@vellumai/ces-client/src/http-credentials.ts +161 -0
- package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
- package/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/environments/src/shell.test.ts +56 -0
- package/node_modules/@vellumai/environments/src/shell.ts +65 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/contact-read-contracts.test.ts +32 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/gateway-client.test.ts +1 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/inbound-event-kind.test.ts +82 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/plugin-admission-denied-contract.test.ts +56 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +18 -0
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +5 -11
- package/node_modules/@vellumai/gateway-client/src/channel-permission-contract.ts +3 -1
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +9 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +4 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-event-kind.ts +75 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +17 -0
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +8 -0
- package/node_modules/@vellumai/gateway-client/src/plugin-admission-denied-contract.ts +47 -0
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +11 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
- package/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
- package/openapi.yaml +419 -55
- package/package.json +4 -1
- package/scripts/sync-llm-catalog.ts +6 -0
- package/src/__tests__/approval-card-edit-inbound-handoff.test.ts +199 -0
- package/src/__tests__/approval-card-edit.test.ts +153 -0
- package/src/__tests__/attachments-store-heic-normalize.test.ts +1 -1
- package/src/__tests__/available-skills.test.ts +54 -0
- package/src/__tests__/avatar-identity-sync.test.ts +5 -1
- package/src/__tests__/catalog-install-normalize.test.ts +9 -1
- package/src/__tests__/channel-policy.test.ts +1 -3
- package/src/__tests__/channel-readiness-discord.test.ts +9 -70
- package/src/__tests__/compactor-call-site-logging.test.ts +61 -0
- package/src/__tests__/config-loader-backfill.test.ts +69 -0
- package/src/__tests__/config-schema.test.ts +2 -3
- package/src/__tests__/conversation-agent-loop.test.ts +238 -0
- package/src/__tests__/conversation-attachments.test.ts +0 -1
- package/src/__tests__/conversation-error.test.ts +44 -0
- package/src/__tests__/conversation-evictor.test.ts +19 -0
- package/src/__tests__/conversation-fork-referential.test.ts +73 -63
- package/src/__tests__/conversation-fork-retrospective.test.ts +99 -74
- package/src/__tests__/conversation-runtime-assembly.test.ts +23 -0
- package/src/__tests__/conversation-slash.test.ts +9 -0
- package/src/__tests__/conversation-title-service.test.ts +97 -0
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +16 -0
- package/src/__tests__/credential-broker-server-use.test.ts +0 -218
- package/src/__tests__/credential-broker.test.ts +43 -420
- package/src/__tests__/credential-record-write-through.test.ts +78 -0
- package/src/__tests__/credential-routes.test.ts +17 -1
- package/src/__tests__/credential-security-invariants.test.ts +8 -9
- package/src/__tests__/default-profile-catalog-fallback.test.ts +289 -0
- package/src/__tests__/delete-propagation.test.ts +92 -2
- package/src/__tests__/discord-callback-round-trip.test.ts +93 -0
- package/src/__tests__/discord-channel-config.test.ts +237 -0
- package/src/__tests__/edit-propagation.test.ts +42 -2
- package/src/__tests__/evict-conversations-for-reload.test.ts +51 -1
- package/src/__tests__/fallback-breaker.test.ts +1335 -0
- package/src/__tests__/gateway-threshold-reader-mock.ts +6 -0
- package/src/__tests__/guardian-card-withdrawal.test.ts +30 -0
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -2
- package/src/__tests__/image-conversion.test.ts +38 -4
- package/src/__tests__/install-skill-routing.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +4 -0
- package/src/__tests__/llm-resolver.test.ts +74 -0
- package/src/__tests__/llm-schema-fallback-profile.test.ts +359 -0
- package/src/__tests__/managed-fallback-dispatch.test.ts +430 -0
- package/src/__tests__/managed-profile-guard.test.ts +28 -0
- package/src/__tests__/managed-speech-defaults.test.ts +196 -1
- package/src/__tests__/mcp-cli.test.ts +23 -1
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +43 -34
- package/src/__tests__/media-stream-stt-session.test.ts +2 -0
- package/src/__tests__/notification-decision-fallback.test.ts +106 -0
- package/src/__tests__/notification-discord-adapter.test.ts +162 -0
- package/src/__tests__/oauth-connect-orchestrator.test.ts +95 -0
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +6 -8
- package/src/__tests__/persist-unsendable-image.test.ts +3 -3
- package/src/__tests__/platform.test.ts +19 -1
- package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -1
- package/src/__tests__/provider-catalog-visibility.test.ts +17 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +8 -1
- package/src/__tests__/provider-usage-tracking.test.ts +56 -0
- package/src/__tests__/require-fresh-approval.test.ts +5 -2
- package/src/__tests__/retry-fallback-escalation.test.ts +1093 -0
- package/src/__tests__/schedule-retry.test.ts +183 -4
- package/src/__tests__/scheduler-reuse-conversation.test.ts +6 -6
- package/src/__tests__/secret-routes-platform-proxy.test.ts +55 -0
- package/src/__tests__/skill-load-tool.test.ts +34 -0
- package/src/__tests__/skill-tool-factory.test.ts +6 -1
- package/src/__tests__/skill-tool-manifest.test.ts +20 -0
- package/src/__tests__/skills.test.ts +25 -0
- package/src/__tests__/subagent-manager-notify.test.ts +38 -0
- package/src/__tests__/terminal-tools.test.ts +2 -0
- package/src/__tests__/tool-approval-handler.test.ts +146 -2
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +108 -2
- package/src/__tests__/tool-audit.test.ts +111 -40
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +228 -18
- package/src/__tests__/ui-shape-teaching.test.ts +1 -1
- package/src/__tests__/verification-control-plane-policy.test.ts +6 -2
- package/src/__tests__/voice-config-update.test.ts +43 -0
- package/src/__tests__/workspace-migration-147-rename-colliding-backup-profile-names.test.ts +480 -0
- package/src/__tests__/workspace-migration-148-strip-unsupported-fallback-profiles.test.ts +167 -0
- package/src/__tests__/workspace-migration-149-repoint-backup-profile-selections.test.ts +144 -0
- package/src/agent/loop.ts +31 -2
- package/src/approvals/guardian-card-withdrawal.ts +22 -0
- package/src/avatar/__tests__/ensure-raster.test.ts +291 -0
- package/src/avatar/avatar-manifest.ts +48 -100
- package/src/avatar/avatar-store.ts +7 -7
- package/src/avatar/ensure-raster.ts +146 -0
- package/src/avatar/resvg-lazy.test.ts +0 -1
- package/src/avatar/traits-png-sync.ts +9 -10
- package/src/background-wake/wake-intent-hooks.test.ts +0 -1
- package/src/backup/__tests__/paths.test.ts +6 -2
- package/src/backup/paths.ts +6 -4
- package/src/bundler/app-compiler.ts +2 -2
- package/src/bundler/compiler-tools.test.ts +36 -0
- package/src/bundler/compiler-tools.ts +54 -18
- package/src/calls/__tests__/telephony-synthesis-language.test.ts +81 -0
- package/src/calls/__tests__/voice-session-bridge.test.ts +57 -0
- package/src/calls/media-stream-stt-session.ts +2 -1
- package/src/calls/telephony-synthesis-language.ts +13 -5
- package/src/calls/voice-session-bridge.ts +19 -4
- package/src/channels/config.ts +23 -12
- package/src/cli/AGENTS.md +3 -1
- package/src/cli/commands/backup.help.ts +10 -13
- package/src/cli/commands/contacts.help.ts +16 -4
- package/src/cli/commands/contacts.ts +5 -1
- package/src/cli/commands/credentials.help.ts +1 -1
- package/src/cli/commands/inference-providers.ts +18 -53
- package/src/cli/commands/mcp.help.ts +6 -5
- package/src/cli/commands/mcp.ts +1 -1
- package/src/cli/commands/memory/memory-retrospective.ts +1 -0
- package/src/cli/commands/stt.help.ts +2 -2
- package/src/config/__tests__/backup-schema.test.ts +1 -1
- package/src/config/__tests__/default-profile-catalog.test.ts +83 -2
- package/src/config/__tests__/loader-sparse-array-cleanup.test.ts +1 -1
- package/src/config/__tests__/memory-retrospective-schema.test.ts +12 -15
- package/src/config/bundled-skills/computer-use/SKILL.md +6 -6
- package/src/config/bundled-skills/computer-use/TOOLS.json +9 -6
- package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +3 -1
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +1 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +16 -15
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +2 -1
- package/src/config/bundled-skills/settings/TOOLS.json +11 -5
- package/src/config/bundled-skills/settings/tools/open-system-settings.test.ts +85 -0
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +33 -8
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +54 -3
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +1 -1
- package/src/config/default-profile-catalog.ts +195 -10
- package/src/config/default-profile-names.ts +41 -0
- package/src/config/feature-flag-registry.json +34 -2
- package/src/config/inference-profile-validation.ts +11 -2
- package/src/config/llm-resolver.ts +25 -0
- package/src/config/loader.ts +3 -0
- package/src/config/managed-speech-defaults.ts +160 -10
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/live-voice.test.ts +7 -6
- package/src/config/schemas/__tests__/stt.test.ts +94 -0
- package/src/config/schemas/backup.ts +1 -1
- package/src/config/schemas/channels.ts +32 -0
- package/src/config/schemas/live-voice.ts +13 -4
- package/src/config/schemas/llm.ts +263 -15
- package/src/config/schemas/mcp.ts +10 -2
- package/src/config/schemas/memory-retrospective.ts +1 -19
- package/src/config/schemas/services.ts +24 -0
- package/src/config/schemas/stt.ts +128 -5
- package/src/config/seed-inference-profiles.ts +32 -3
- package/src/config/skill-state.ts +5 -0
- package/src/config/skills.ts +21 -0
- package/src/contacts/notify-contacts-changed.ts +5 -2
- package/src/context/compactor.ts +74 -1
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +138 -1
- package/src/daemon/config-watcher.ts +2 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +27 -0
- package/src/daemon/conversation-agent-loop.ts +86 -10
- package/src/daemon/conversation-error.ts +25 -0
- package/src/daemon/conversation-evictor.ts +2 -4
- package/src/daemon/conversation-runtime-assembly.ts +19 -7
- package/src/daemon/conversation-skill-tools.ts +5 -1
- package/src/daemon/conversation-slash.ts +2 -2
- package/src/daemon/conversation-store.ts +8 -1
- package/src/daemon/conversation-tool-setup.ts +37 -0
- package/src/daemon/conversation.ts +0 -4
- package/src/daemon/handlers/channel-config-result.ts +30 -0
- package/src/daemon/handlers/config-discord-channel.ts +301 -0
- package/src/daemon/handlers/config-ingress.ts +9 -0
- package/src/daemon/handlers/config-slack-channel.ts +13 -15
- package/src/daemon/handlers/config-telegram.ts +2 -5
- package/src/daemon/handlers/conversations.ts +0 -2
- package/src/daemon/handlers/skills.ts +25 -7
- package/src/daemon/lifecycle.ts +3 -1
- package/src/daemon/message-types/messages.ts +0 -2
- package/src/daemon/tool-setup-types.ts +5 -3
- package/src/daemon/trust-context-types.test.ts +33 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +58 -4
- package/src/live-voice/__tests__/live-voice-managed-speech-fallback.test.ts +8 -3
- package/src/live-voice/__tests__/live-voice-session-manager.test.ts +73 -11
- package/src/live-voice/__tests__/live-voice-session-preflight.test.ts +148 -2
- package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +64 -0
- package/src/live-voice/__tests__/live-voice-stt.test.ts +3 -0
- package/src/live-voice/__tests__/live-voice-text-turn.test.ts +391 -0
- package/src/live-voice/__tests__/protocol.test.ts +224 -0
- package/src/live-voice/live-voice-credential-preflight.ts +5 -1
- package/src/live-voice/live-voice-session-manager.ts +39 -2
- package/src/live-voice/live-voice-session.ts +224 -9
- package/src/live-voice/protocol.ts +187 -1
- package/src/messaging/providers/discord/api.ts +2 -1
- package/src/messaging/providers/discord/withdraw.ts +62 -0
- package/src/messaging/read-provider-metadata.ts +32 -0
- package/src/monitoring/__tests__/file-descriptors.test.ts +32 -0
- package/src/monitoring/__tests__/process-memory.test.ts +34 -1
- package/src/monitoring/db-integrity-sample.ts +1 -0
- package/src/monitoring/file-descriptors.ts +39 -6
- package/src/monitoring/process-memory.ts +37 -5
- package/src/monitoring/resource-sampler.ts +25 -8
- package/src/notifications/__tests__/activity-failed-dedupe.test.ts +161 -0
- package/src/notifications/__tests__/background-failure-signal.test.ts +107 -0
- package/src/notifications/__tests__/connected-channels.test.ts +12 -0
- package/src/notifications/__tests__/copy-composer.test.ts +83 -0
- package/src/notifications/__tests__/destination-resolver.test.ts +38 -0
- package/src/notifications/activity-failed-dedupe.ts +95 -0
- package/src/notifications/adapters/discord.ts +132 -0
- package/src/notifications/adapters/shared.ts +16 -0
- package/src/notifications/adapters/slack.ts +6 -1
- package/src/notifications/adapters/telegram.ts +5 -8
- package/src/notifications/approval-card-data.ts +94 -1
- package/src/notifications/background-failure-signal.ts +116 -0
- package/src/notifications/broadcaster.ts +3 -9
- package/src/notifications/copy-composer.ts +41 -8
- package/src/notifications/decision-engine.ts +55 -0
- package/src/notifications/destination-resolver.ts +45 -5
- package/src/notifications/emit-signal.ts +19 -5
- package/src/notifications/guardian-question-mode.ts +57 -0
- package/src/notifications/types.ts +2 -8
- package/src/oauth/__tests__/seed-providers-managed.test.ts +18 -0
- package/src/oauth/connect-orchestrator.ts +16 -0
- package/src/oauth/seed-providers.ts +262 -0
- package/src/permissions/checker.test.ts +21 -0
- package/src/permissions/checker.ts +6 -0
- package/src/permissions/gateway-threshold-reader.test.ts +184 -0
- package/src/permissions/gateway-threshold-reader.ts +144 -10
- package/src/permissions/prompter.ts +0 -6
- package/src/permissions/types.ts +5 -4
- package/src/persistence/__tests__/bulk-write-gate.test.ts +0 -38
- package/src/persistence/bulk-write-gate.ts +9 -10
- package/src/persistence/conversation-crud.ts +88 -134
- package/src/persistence/conversation-title-service.ts +43 -29
- package/src/persistence/embeddings/embedding-local.ts +14 -85
- package/src/persistence/embeddings/embedding-runtime-manager.ts +1 -0
- package/src/persistence/lifecycle-events-store.test.ts +50 -0
- package/src/persistence/lifecycle-events-store.ts +75 -5
- package/src/platform/platform-patch-queue.test.ts +368 -0
- package/src/platform/platform-patch-queue.ts +190 -0
- package/src/platform/sync-avatar.test.ts +527 -0
- package/src/platform/sync-avatar.ts +209 -0
- package/src/platform/sync-identity.test.ts +75 -0
- package/src/platform/sync-identity.ts +17 -110
- package/src/plugin-api/model-profiles.test.ts +2 -2
- package/src/plugin-api/model-profiles.ts +7 -7
- package/src/plugin-api/transcription-session.ts +1 -1
- package/src/plugins/defaults/compaction/window-manager.ts +4 -0
- package/src/plugins/defaults/image-recovery/recover.ts +2 -2
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +133 -34
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +21 -31
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +13 -9
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +31 -4
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +0 -11
- package/src/plugins/defaults/memory/memory-retrospective-fork-boundary.ts +27 -13
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +183 -113
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +13 -13
- package/src/plugins/defaults/memory/src/memory-v2-routes.ts +2 -2
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +1 -1
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +4 -2
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +9 -42
- package/src/plugins/defaults/memory/v2/rerank-local.ts +1 -0
- package/src/plugins/defaults/memory/worker.ts +7 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
- package/src/providers/__tests__/retry-callsite.test.ts +55 -0
- package/src/providers/__tests__/vellum-connection-routing.test.ts +21 -3
- package/src/providers/connection-resolution.ts +2 -17
- package/src/providers/fallback-breaker.ts +389 -0
- package/src/providers/inference/__tests__/adapter-factory-opencode.test.ts +92 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +16 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +12 -0
- package/src/providers/inference/adapter-factory.ts +246 -3
- package/src/providers/inference/auth.ts +15 -14
- package/src/providers/inference/chatgpt-subscription-auth.ts +70 -0
- package/src/providers/inference/chatgpt-subscription-credentials.ts +84 -0
- package/src/providers/inference/resolve-auth.ts +1 -1
- package/src/providers/media-resolve.ts +2 -3
- package/src/providers/model-catalog.ts +100 -0
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +22 -0
- package/src/providers/openai/__tests__/chat-template-flatten.test.ts +198 -0
- package/src/providers/openai/api-error-normalization.ts +13 -0
- package/src/providers/openai/chat-completions-provider.ts +84 -0
- package/src/providers/opencode/client.test.ts +150 -0
- package/src/providers/opencode/client.ts +61 -0
- package/src/providers/platform-proxy/constants.ts +5 -0
- package/src/providers/provider-resolvability.ts +20 -0
- package/src/providers/provider-secret-catalog.ts +3 -2
- package/src/providers/retry.ts +956 -68
- package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +42 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +136 -0
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +120 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +316 -12
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +83 -3
- package/src/providers/speech-to-text/deepgram-flux-realtime.ts +132 -4
- package/src/providers/speech-to-text/provider-catalog.ts +191 -3
- package/src/providers/speech-to-text/resolve.ts +132 -24
- package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +296 -0
- package/src/providers/types.ts +39 -0
- package/src/providers/usage-tracking.ts +12 -1
- package/src/providers/vellum/client.ts +29 -0
- package/src/providers/vellum-model-routing.test.ts +2 -0
- package/src/providers/vellum-model-routing.ts +3 -3
- package/src/runtime/AGENTS.md +1 -2
- package/src/runtime/__tests__/agent-wake.test.ts +105 -3
- package/src/runtime/__tests__/background-job-runner.test.ts +89 -3
- package/src/runtime/agent-wake.ts +44 -5
- package/src/runtime/background-job-runner.ts +68 -50
- package/src/runtime/channel-readiness-service.ts +0 -39
- package/src/runtime/http-server.ts +12 -4
- package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +18 -18
- package/src/runtime/routes/__tests__/backup-routes.test.ts +9 -5
- package/src/runtime/routes/__tests__/chatgpt-subscription-auth-routes.test.ts +372 -0
- package/src/runtime/routes/__tests__/contact-routes.test.ts +7 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +96 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +37 -0
- package/src/runtime/routes/__tests__/ingress-status-routes.test.ts +2 -1
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +87 -0
- package/src/runtime/routes/acp-claude-auth-routes.ts +25 -54
- package/src/runtime/routes/approval-routes.ts +0 -12
- package/src/runtime/routes/avatar-routes.ts +17 -30
- package/src/runtime/routes/backup-routes.ts +2 -2
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +296 -113
- package/src/runtime/routes/contact-prompt-routes.ts +65 -6
- package/src/runtime/routes/contact-routes.ts +20 -15
- package/src/runtime/routes/conversation-query-routes.ts +98 -10
- package/src/runtime/routes/conversation-routes.ts +2 -2
- package/src/runtime/routes/credential-routes.ts +2 -1
- package/src/runtime/routes/guardian-approval-interception.ts +12 -9
- package/src/runtime/routes/inbound-message-handler.ts +47 -48
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +18 -13
- package/src/runtime/routes/inbound-stages/admission-policy.ts +2 -2
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +20 -2
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +9 -8
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +1 -1
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +2 -2
- package/src/runtime/routes/inference-profiles-routes.ts +12 -7
- package/src/runtime/routes/inference-provider-connection-routes.ts +2 -2
- package/src/runtime/routes/inference-send-routes.ts +2 -2
- package/src/runtime/routes/ingress-status-routes.ts +2 -2
- package/src/runtime/routes/integrations/discord.ts +119 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +2 -2
- package/src/runtime/routes/mcp-auth-routes.ts +12 -6
- package/src/runtime/routes/migration-routes.ts +3 -1
- package/src/runtime/routes/oauth-pending-flows.ts +136 -0
- package/src/runtime/routes/secret-routes.ts +7 -0
- package/src/runtime/routes/stt-routes.ts +38 -6
- package/src/runtime/routes/wake-conversation-routes.ts +8 -1
- package/src/runtime/routes/watch-routes.ts +9 -5
- package/src/runtime/sync/resource-sync-events.ts +2 -0
- package/src/runtime/trust-verdict-consumer.ts +3 -3
- package/src/schedule/__tests__/worker-feature-flags.test.ts +58 -0
- package/src/schedule/retry-policy.ts +8 -8
- package/src/schedule/run-script.ts +14 -23
- package/src/schedule/schedule-recovery.ts +0 -1
- package/src/schedule/scheduler.ts +89 -51
- package/src/schedule/worker.ts +57 -0
- package/src/security/__tests__/openai-device-auth.test.ts +355 -0
- package/src/security/ces-rpc-record-backend.ts +123 -0
- package/src/security/oauth2.ts +7 -0
- package/src/security/openai-device-auth.ts +364 -0
- package/src/security/secure-keys.ts +15 -0
- package/src/skills/available-skills.ts +10 -1
- package/src/skills/catalog-install.ts +19 -1
- package/src/skills/inline-command-runner.ts +6 -2
- package/src/skills/platform-compatibility.test.ts +49 -0
- package/src/skills/platform-compatibility.ts +65 -0
- package/src/skills/tool-manifest.ts +20 -0
- package/src/stt/__tests__/roles.test.ts +152 -0
- package/src/stt/daemon-batch-transcriber.ts +4 -1
- package/src/stt/roles.ts +180 -0
- package/src/stt/stt-stream-session.ts +3 -2
- package/src/stt/types.ts +33 -3
- package/src/subagent/manager.ts +12 -0
- package/src/telemetry/__tests__/live-voice-funnel.test.ts +34 -0
- package/src/telemetry/live-voice-funnel.ts +12 -2
- package/src/telemetry/telemetry-wire.generated.ts +6 -0
- package/src/telemetry/tool-audit.ts +83 -30
- package/src/telemetry/tool-executed-events-store.test.ts +0 -1
- package/src/telemetry/turn-outcome.ts +75 -4
- package/src/tools/client-os.ts +115 -0
- package/src/tools/computer-use/definitions.ts +11 -7
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -2
- package/src/tools/credentials/broker-types.ts +0 -71
- package/src/tools/credentials/broker.ts +6 -201
- package/src/tools/credentials/metadata-store.ts +65 -16
- package/src/tools/credentials/store.ts +2 -0
- package/src/tools/executor.ts +2 -8
- package/src/tools/host-shell.test.ts +75 -0
- package/src/tools/host-shell.ts +24 -0
- package/src/tools/host-terminal/host-shell.ts +15 -44
- package/src/tools/mcp/mcp-tool-factory.ts +44 -9
- package/src/tools/permission-checker.ts +73 -21
- package/src/tools/policy-context.ts +1 -0
- package/src/tools/shared/filesystem/image-read.ts +1 -1
- package/src/tools/skills/find-similar-skills.ts +10 -2
- package/src/tools/skills/load.ts +41 -6
- package/src/tools/skills/sandbox-runner.ts +8 -16
- package/src/tools/skills/skill-tool-factory.ts +1 -0
- package/src/tools/terminal/__tests__/safe-env.test.ts +64 -1
- package/src/tools/terminal/safe-env.ts +39 -9
- package/src/tools/terminal/shell.ts +12 -31
- package/src/tools/tool-approval-handler.ts +64 -10
- package/src/tools/tool-types.ts +10 -0
- package/src/tools/types.ts +19 -3
- package/src/tools/ui-surface/surface-shape-docs.ts +4 -3
- package/src/tools/workflows/manage-workflows.ts +2 -2
- package/src/tools/workflows/run-workflow.test.ts +2 -1
- package/src/util/__tests__/file-use.test.ts +61 -0
- package/src/util/__tests__/process-table.test.ts +66 -0
- package/src/util/__tests__/process-tree.test.ts +46 -0
- package/src/util/errors.ts +1 -0
- package/src/util/file-use.ts +91 -0
- package/src/util/host-process.test.ts +17 -0
- package/src/util/host-process.ts +75 -0
- package/src/util/image-conversion.ts +39 -7
- package/src/util/platform.ts +32 -13
- package/src/util/process-table.ts +249 -0
- package/src/util/process-tree.ts +85 -129
- package/src/util/provider-error-patterns.ts +20 -0
- package/src/util/spawn.ts +9 -2
- package/src/watch/__tests__/watch-retro.test.ts +48 -1
- package/src/watch/watch-retro.ts +46 -4
- package/src/workspace/git-service.ts +4 -14
- package/src/workspace/migrations/147-rename-colliding-backup-profile-names.ts +360 -0
- package/src/workspace/migrations/148-strip-unsupported-fallback-profiles.ts +117 -0
- package/src/workspace/migrations/149-repoint-backup-profile-selections.ts +204 -0
- package/src/workspace/migrations/150-stt-flux-provider-to-model-family.ts +139 -0
- package/src/workspace/migrations/__tests__/150-stt-flux-provider-to-model-family.test.ts +160 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/__tests__/permission-types.test.ts +0 -17
- package/src/channels/gateway-discord-admission.ts +0 -32
- package/src/persistence/fork-message-copy.ts +0 -239
- package/src/plugins/defaults/memory/__tests__/fork-message-copy.test.ts +0 -221
- package/src/security/__tests__/oauth2-device-code.test.ts +0 -431
- package/src/security/oauth2-device-code.ts +0 -312
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
|
|
43
43
|
import {
|
|
44
44
|
addMessage,
|
|
45
|
+
type AgentLoopExitReason,
|
|
45
46
|
type ContentBlock,
|
|
46
47
|
type ConversationRow,
|
|
47
48
|
deleteConversation,
|
|
@@ -51,8 +52,10 @@ import {
|
|
|
51
52
|
} from "@vellumai/plugin-api";
|
|
52
53
|
|
|
53
54
|
import {
|
|
55
|
+
type ClientOs,
|
|
54
56
|
type InterfaceId,
|
|
55
57
|
isInteractiveInterface,
|
|
58
|
+
parseClientOs,
|
|
56
59
|
parseInterfaceId,
|
|
57
60
|
} from "../../../channels/types.js";
|
|
58
61
|
import { isV3TierActive } from "../../../config/memory-v3-gate.js";
|
|
@@ -88,7 +91,6 @@ import {
|
|
|
88
91
|
MEMORY_RETROSPECTIVE_FORK_SOURCE,
|
|
89
92
|
MEMORY_RETROSPECTIVE_GROUP_ID,
|
|
90
93
|
MEMORY_RETROSPECTIVE_INSTRUCTION_KIND,
|
|
91
|
-
MEMORY_RETROSPECTIVE_NO_FINDINGS_TEXT,
|
|
92
94
|
MEMORY_RETROSPECTIVE_ORIGIN,
|
|
93
95
|
MEMORY_RETROSPECTIVE_SOURCE,
|
|
94
96
|
SKILL_MANAGEMENT_SKILL_ID,
|
|
@@ -128,6 +130,14 @@ export const STALE_SOURCE_PROCESSING_OVERRIDE_MS = 6 * 60 * 60 * 1000;
|
|
|
128
130
|
/** Watchdog check_name for the per-run retrospective outcome counter. */
|
|
129
131
|
const MEMORY_RETROSPECTIVE_RUN_CHECK_NAME = "memory_retrospective_run";
|
|
130
132
|
|
|
133
|
+
/**
|
|
134
|
+
* The agent-loop exit that means the MODEL ended the run: it answered without
|
|
135
|
+
* asking for another tool. Under every other exit something ended the run for
|
|
136
|
+
* it, so whatever it had said by then is a fragment of a review rather than a
|
|
137
|
+
* verdict on the window.
|
|
138
|
+
*/
|
|
139
|
+
const MODEL_DRIVEN_STOP_EXIT_REASON: AgentLoopExitReason = "no_tool_calls";
|
|
140
|
+
|
|
131
141
|
export type MemoryRetrospectiveOutcome =
|
|
132
142
|
| { kind: "disabled" }
|
|
133
143
|
| { kind: "no_new_messages" }
|
|
@@ -142,6 +152,12 @@ export type MemoryRetrospectiveOutcome =
|
|
|
142
152
|
cutoffMessageId: string;
|
|
143
153
|
newMessageCount: number;
|
|
144
154
|
followUpJobIds: string[];
|
|
155
|
+
/**
|
|
156
|
+
* The pass reviewed its window and had nothing durable to save, so it
|
|
157
|
+
* advanced the cursor without writing a memory. Separates the two
|
|
158
|
+
* shapes a healthy run takes, which the outcome kind alone conflates.
|
|
159
|
+
*/
|
|
160
|
+
noFindings: boolean;
|
|
145
161
|
};
|
|
146
162
|
|
|
147
163
|
export async function memoryRetrospectiveJob(
|
|
@@ -176,14 +192,20 @@ export async function memoryRetrospectiveJob(
|
|
|
176
192
|
// resolved model) is visible without log access. The emitter itself
|
|
177
193
|
// never throws — the run's outcome must reach the jobs worker
|
|
178
194
|
// regardless.
|
|
179
|
-
const emitRunOutcome = (
|
|
195
|
+
const emitRunOutcome = (
|
|
196
|
+
outcome: string,
|
|
197
|
+
detail?: { reason?: string; noFindings?: boolean },
|
|
198
|
+
): void => {
|
|
180
199
|
try {
|
|
181
200
|
recordWatchdogEvent({
|
|
182
201
|
checkName: MEMORY_RETROSPECTIVE_RUN_CHECK_NAME,
|
|
183
202
|
value: 1,
|
|
184
203
|
detail: {
|
|
185
204
|
outcome,
|
|
186
|
-
...(reason ? { reason: reason.slice(0, 200) } : {}),
|
|
205
|
+
...(detail?.reason ? { reason: detail.reason.slice(0, 200) } : {}),
|
|
206
|
+
...(detail?.noFindings !== undefined
|
|
207
|
+
? { noFindings: detail.noFindings }
|
|
208
|
+
: {}),
|
|
187
209
|
},
|
|
188
210
|
});
|
|
189
211
|
} catch {
|
|
@@ -199,15 +221,17 @@ export async function memoryRetrospectiveJob(
|
|
|
199
221
|
enforceUserActivityGate: true,
|
|
200
222
|
});
|
|
201
223
|
} catch (err) {
|
|
202
|
-
emitRunOutcome("error",
|
|
224
|
+
emitRunOutcome("error", {
|
|
225
|
+
reason: err instanceof Error ? err.message : String(err),
|
|
226
|
+
});
|
|
203
227
|
throw err;
|
|
204
228
|
}
|
|
205
|
-
emitRunOutcome(
|
|
206
|
-
outcome.kind
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
);
|
|
229
|
+
emitRunOutcome(outcome.kind, {
|
|
230
|
+
...(outcome.kind === "wake_failed" || outcome.kind === "no_usable_output"
|
|
231
|
+
? { reason: outcome.reason }
|
|
232
|
+
: {}),
|
|
233
|
+
...(outcome.kind === "invoked" ? { noFindings: outcome.noFindings } : {}),
|
|
234
|
+
});
|
|
211
235
|
return outcome;
|
|
212
236
|
}
|
|
213
237
|
|
|
@@ -396,16 +420,16 @@ export async function runForkBasedRetrospective(
|
|
|
396
420
|
// advances to `cutoffMessageId`, causing the next retrospective to
|
|
397
421
|
// reprocess (and potentially re-`remember`) those same turns.
|
|
398
422
|
//
|
|
399
|
-
// The fork
|
|
400
|
-
//
|
|
401
|
-
//
|
|
423
|
+
// The fork is referential: it carries the inherited compaction summary
|
|
424
|
+
// and the source's hidden-prefix count, and reads the source's rows
|
|
425
|
+
// through the fork point. Compacted source ⇒ summary + tail visible to
|
|
426
|
+
// the agent natively.
|
|
402
427
|
let forkConversationRow: Awaited<
|
|
403
428
|
ReturnType<typeof forkConversationForRetrospective>
|
|
404
429
|
>;
|
|
405
430
|
try {
|
|
406
|
-
//
|
|
407
|
-
//
|
|
408
|
-
// event loop (health probes / gateway IPC) on a large database.
|
|
431
|
+
// Referential fork: one conversation row plus memory-state seeding.
|
|
432
|
+
// There is no source message-row copy.
|
|
409
433
|
forkConversationRow = await forkConversationForRetrospective({
|
|
410
434
|
conversationId: sourceConversationId,
|
|
411
435
|
throughMessageId: cutoffMessageId,
|
|
@@ -493,6 +517,7 @@ export async function runForkBasedRetrospective(
|
|
|
493
517
|
// persisted message — the wake's hint sandwich would only duplicate it.
|
|
494
518
|
let wakeSucceeded = false;
|
|
495
519
|
let failureReason: string | undefined;
|
|
520
|
+
let wakeExitReason: AgentLoopExitReason | undefined;
|
|
496
521
|
let threw: unknown;
|
|
497
522
|
try {
|
|
498
523
|
const result = await wakeAgentForOpportunity({
|
|
@@ -562,6 +587,7 @@ export async function runForkBasedRetrospective(
|
|
|
562
587
|
});
|
|
563
588
|
wakeSucceeded = result.invoked;
|
|
564
589
|
failureReason = result.reason;
|
|
590
|
+
wakeExitReason = result.exitReason;
|
|
565
591
|
} catch (err) {
|
|
566
592
|
threw = err;
|
|
567
593
|
failureReason = err instanceof Error ? err.message : String(err);
|
|
@@ -576,15 +602,21 @@ export async function runForkBasedRetrospective(
|
|
|
576
602
|
// went live, not that the run produced anything. The agent loop swallows
|
|
577
603
|
// provider rejections into a normal no-output return, an exhausted output
|
|
578
604
|
// budget can stop a run before any visible text or tool call, and a
|
|
579
|
-
// model may
|
|
580
|
-
//
|
|
581
|
-
// one of:
|
|
605
|
+
// model may stop mid-review without saving. Advancement past the window
|
|
606
|
+
// therefore requires POSITIVE evidence from THIS run, one of:
|
|
582
607
|
// - a memory-writing tool call on the fork's post-boundary tail whose
|
|
583
608
|
// execution verifiably succeeded (matching non-error tool_result), or
|
|
584
|
-
// -
|
|
585
|
-
//
|
|
586
|
-
// memory
|
|
587
|
-
//
|
|
609
|
+
// - a reviewed-and-nothing-to-save pass: the model ENDED the run by
|
|
610
|
+
// answering in its own words (its final assistant row carries text),
|
|
611
|
+
// attempted no memory write at all, and the loop ended because IT
|
|
612
|
+
// stopped asking for tools rather than because something cut the run
|
|
613
|
+
// short. A run that attempted a save and failed cannot advance by
|
|
614
|
+
// talking instead, a run whose narration went live but whose final
|
|
615
|
+
// response was empty has not concluded, and a run the provider or
|
|
616
|
+
// the output ceiling ended mid-review has not reviewed its window.
|
|
617
|
+
// The proof of an empty-handed review is the shape of the run, never the
|
|
618
|
+
// wording of the reply: a model that reaches its own end with nothing to
|
|
619
|
+
// write has reviewed the window, in whatever words it says so.
|
|
588
620
|
// The evidence read is run-specific by construction
|
|
589
621
|
// (`loadRetrospectiveRunMessages` scopes to rows after the fork
|
|
590
622
|
// boundary), so a prior run's persisted saves can never satisfy it, and
|
|
@@ -596,8 +628,9 @@ export async function runForkBasedRetrospective(
|
|
|
596
628
|
// remains retryable.
|
|
597
629
|
const runEvidence = await collectRetrospectiveRunEvidence(forkId);
|
|
598
630
|
const reviewedNoFindings =
|
|
599
|
-
runEvidence.
|
|
600
|
-
runEvidence.durableToolAttemptCount === 0
|
|
631
|
+
runEvidence.committedTextReply &&
|
|
632
|
+
runEvidence.durableToolAttemptCount === 0 &&
|
|
633
|
+
wakeExitReason === MODEL_DRIVEN_STOP_EXIT_REASON;
|
|
601
634
|
if (runEvidence.durableToolCallCount === 0 && !reviewedNoFindings) {
|
|
602
635
|
log.warn(
|
|
603
636
|
{
|
|
@@ -605,9 +638,12 @@ export async function runForkBasedRetrospective(
|
|
|
605
638
|
forkId,
|
|
606
639
|
newMessageCount: newMessages.length,
|
|
607
640
|
durableToolAttempts: runEvidence.durableToolAttemptCount,
|
|
641
|
+
committedTextReply: runEvidence.committedTextReply,
|
|
642
|
+
exitReason: wakeExitReason ?? null,
|
|
608
643
|
},
|
|
609
|
-
"memory-retrospective (fork): run produced neither a verified durable write nor
|
|
644
|
+
"memory-retrospective (fork): run produced neither a verified durable write nor a completed empty-handed review; leaving window retryable",
|
|
610
645
|
);
|
|
646
|
+
failureReason = describeUnusableRun(runEvidence, wakeExitReason);
|
|
611
647
|
} else {
|
|
612
648
|
return await finalizeSuccessfulRetrospective({
|
|
613
649
|
config,
|
|
@@ -618,11 +654,11 @@ export async function runForkBasedRetrospective(
|
|
|
618
654
|
prior,
|
|
619
655
|
priorRemembers,
|
|
620
656
|
runRemembers: runEvidence.remembers,
|
|
657
|
+
noFindings: reviewedNoFindings,
|
|
621
658
|
logFields: {
|
|
622
659
|
kind: "fork",
|
|
623
660
|
windowStartTimestamp,
|
|
624
661
|
durationMs: Date.now() - startedAtMs,
|
|
625
|
-
noFindings: reviewedNoFindings,
|
|
626
662
|
},
|
|
627
663
|
});
|
|
628
664
|
}
|
|
@@ -642,12 +678,15 @@ export async function runForkBasedRetrospective(
|
|
|
642
678
|
}
|
|
643
679
|
|
|
644
680
|
if (wakeSucceeded) {
|
|
681
|
+
// Reached only through the evidence gate above, which set `failureReason`
|
|
682
|
+
// to why the run could not consume its window.
|
|
645
683
|
return {
|
|
646
684
|
kind: "no_usable_output",
|
|
647
|
-
reason: failureReason
|
|
685
|
+
reason: failureReason,
|
|
648
686
|
conversationId: forkId,
|
|
649
687
|
};
|
|
650
688
|
}
|
|
689
|
+
|
|
651
690
|
return {
|
|
652
691
|
kind: "wake_failed",
|
|
653
692
|
reason: failureReason,
|
|
@@ -655,6 +694,26 @@ export async function runForkBasedRetrospective(
|
|
|
655
694
|
};
|
|
656
695
|
}
|
|
657
696
|
|
|
697
|
+
/**
|
|
698
|
+
* Why a run that went live still could not consume its window, phrased for
|
|
699
|
+
* the job row's `last_error` and the CLI. The distinction it draws is the one
|
|
700
|
+
* an operator needs: a lost memory write reads differently from a review that
|
|
701
|
+
* never reached a conclusion, and the fork is deleted on failure, so this
|
|
702
|
+
* string is what survives the run.
|
|
703
|
+
*/
|
|
704
|
+
function describeUnusableRun(
|
|
705
|
+
evidence: { durableToolAttemptCount: number; committedTextReply: boolean },
|
|
706
|
+
exitReason: AgentLoopExitReason | undefined,
|
|
707
|
+
): string {
|
|
708
|
+
if (evidence.durableToolAttemptCount > 0) {
|
|
709
|
+
return `run attempted ${evidence.durableToolAttemptCount} memory write(s), none of which persisted a successful result`;
|
|
710
|
+
}
|
|
711
|
+
if (!evidence.committedTextReply) {
|
|
712
|
+
return "run committed neither a memory write nor a concluding reply";
|
|
713
|
+
}
|
|
714
|
+
return `run replied without saving anything, but ended on ${exitReason ?? "no terminal exit"} rather than a completed review`;
|
|
715
|
+
}
|
|
716
|
+
|
|
658
717
|
function enqueueFollowUpJobs(): string[] {
|
|
659
718
|
const followUpJobIds: string[] = [];
|
|
660
719
|
for (const jobType of FOLLOW_UP_JOB_TYPES) {
|
|
@@ -711,6 +770,8 @@ interface SourceParityPins {
|
|
|
711
770
|
*
|
|
712
771
|
* `toolContextPin.transportInterface` — the interface the source's most
|
|
713
772
|
* recent live turns ran on (see {@link resolveSourceLiveInterface}).
|
|
773
|
+
* `toolContextPin.clientOs` is recovered from the same persisted user-message
|
|
774
|
+
* metadata, with the transport interface as a fallback.
|
|
714
775
|
* `channelCapabilities` is left unset: desktop/web HTTP turns never set
|
|
715
776
|
* channel capabilities, and for channel-routed sources (whose live turns do
|
|
716
777
|
* carry them) every tool gate resolves identically under
|
|
@@ -745,6 +806,7 @@ function resolveSourceParityPins(
|
|
|
745
806
|
// with an unmappable channel stay undefined (their live turns were
|
|
746
807
|
// clientless either way).
|
|
747
808
|
const transportInterface = recovered ?? (channelRouted ? undefined : "web");
|
|
809
|
+
const clientOs = resolveSourceLiveClientOs(sliceMessages, transportInterface);
|
|
748
810
|
const hasNoClient =
|
|
749
811
|
transportInterface == null || !isInteractiveInterface(transportInterface);
|
|
750
812
|
const personaOverride: SystemPromptPersonaOverride = channelRouted
|
|
@@ -763,6 +825,7 @@ function resolveSourceParityPins(
|
|
|
763
825
|
toolContextPin: {
|
|
764
826
|
hasNoClient,
|
|
765
827
|
transportInterface,
|
|
828
|
+
clientOs,
|
|
766
829
|
requestOrigin: MEMORY_RETROSPECTIVE_ORIGIN,
|
|
767
830
|
},
|
|
768
831
|
};
|
|
@@ -814,6 +877,37 @@ function resolveSourceLiveInterface(
|
|
|
814
877
|
);
|
|
815
878
|
}
|
|
816
879
|
|
|
880
|
+
/** Pin the source's live client OS so OS-gated tools match on wake. */
|
|
881
|
+
function resolveSourceLiveClientOs(
|
|
882
|
+
sliceMessages: Array<{ role: string; metadata: string | null }>,
|
|
883
|
+
transportInterface: InterfaceId | undefined,
|
|
884
|
+
): ClientOs | undefined {
|
|
885
|
+
for (let i = sliceMessages.length - 1; i >= 0; i--) {
|
|
886
|
+
const row = sliceMessages[i]!;
|
|
887
|
+
if (row.role !== "user" || !row.metadata) {
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
890
|
+
let meta: unknown;
|
|
891
|
+
try {
|
|
892
|
+
meta = JSON.parse(row.metadata);
|
|
893
|
+
} catch {
|
|
894
|
+
continue;
|
|
895
|
+
}
|
|
896
|
+
if (!meta || typeof meta !== "object") {
|
|
897
|
+
continue;
|
|
898
|
+
}
|
|
899
|
+
const { clientOsFromRequest, client } = meta as Record<string, unknown>;
|
|
900
|
+
if (clientOsFromRequest !== true || !client || typeof client !== "object") {
|
|
901
|
+
continue;
|
|
902
|
+
}
|
|
903
|
+
const clientOs = parseClientOs((client as Record<string, unknown>).os);
|
|
904
|
+
if (clientOs) {
|
|
905
|
+
return clientOs;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
return parseClientOs(transportInterface) ?? undefined;
|
|
909
|
+
}
|
|
910
|
+
|
|
817
911
|
type PriorRetrospective = NonNullable<
|
|
818
912
|
ReturnType<typeof findMostRecentRetrospectiveFor>
|
|
819
913
|
>;
|
|
@@ -862,6 +956,8 @@ async function finalizeSuccessfulRetrospective(args: {
|
|
|
862
956
|
* advancement is exactly the evidence folded into the log.
|
|
863
957
|
*/
|
|
864
958
|
runRemembers: string[];
|
|
959
|
+
/** Whether the run advanced on a reviewed-and-nothing-to-save pass. */
|
|
960
|
+
noFindings: boolean;
|
|
865
961
|
/** Per-kind extras for the success log line (e.g. `kind`, fork anchor). */
|
|
866
962
|
logFields: Record<string, unknown>;
|
|
867
963
|
}): Promise<MemoryRetrospectiveOutcome> {
|
|
@@ -874,6 +970,7 @@ async function finalizeSuccessfulRetrospective(args: {
|
|
|
874
970
|
prior,
|
|
875
971
|
priorRemembers,
|
|
876
972
|
runRemembers,
|
|
973
|
+
noFindings,
|
|
877
974
|
logFields,
|
|
878
975
|
} = args;
|
|
879
976
|
|
|
@@ -901,6 +998,7 @@ async function finalizeSuccessfulRetrospective(args: {
|
|
|
901
998
|
cutoffMessageId,
|
|
902
999
|
newMessageCount,
|
|
903
1000
|
priorRememberCount: priorRemembers.length,
|
|
1001
|
+
noFindings,
|
|
904
1002
|
...logFields,
|
|
905
1003
|
},
|
|
906
1004
|
"memory-retrospective invoked",
|
|
@@ -911,6 +1009,7 @@ async function finalizeSuccessfulRetrospective(args: {
|
|
|
911
1009
|
cutoffMessageId,
|
|
912
1010
|
newMessageCount,
|
|
913
1011
|
followUpJobIds,
|
|
1012
|
+
noFindings,
|
|
914
1013
|
};
|
|
915
1014
|
}
|
|
916
1015
|
|
|
@@ -1119,12 +1218,14 @@ async function collectRetrospectiveRunEvidence(
|
|
|
1119
1218
|
/** Memory-writing tool calls the run attempted, regardless of outcome. */
|
|
1120
1219
|
durableToolAttemptCount: number;
|
|
1121
1220
|
/**
|
|
1122
|
-
* The run
|
|
1123
|
-
*
|
|
1124
|
-
*
|
|
1125
|
-
*
|
|
1221
|
+
* The run ENDED by answering in its own words: the last persisted
|
|
1222
|
+
* assistant row carries a text block with non-whitespace content. Any
|
|
1223
|
+
* wording qualifies, so a pass that found nothing durable proves it
|
|
1224
|
+
* reviewed the window by replying, not by reproducing a phrase; but the
|
|
1225
|
+
* reply must be the run's final word, so narration followed by an empty
|
|
1226
|
+
* last response does not qualify.
|
|
1126
1227
|
*/
|
|
1127
|
-
|
|
1228
|
+
committedTextReply: boolean;
|
|
1128
1229
|
}> {
|
|
1129
1230
|
const conv = await getConversation(conversationId);
|
|
1130
1231
|
const runMessages = await loadRetrospectiveRunMessages(
|
|
@@ -1136,7 +1237,7 @@ async function collectRetrospectiveRunEvidence(
|
|
|
1136
1237
|
remembers: [],
|
|
1137
1238
|
durableToolCallCount: 0,
|
|
1138
1239
|
durableToolAttemptCount: 0,
|
|
1139
|
-
|
|
1240
|
+
committedTextReply: false,
|
|
1140
1241
|
};
|
|
1141
1242
|
}
|
|
1142
1243
|
const succeededIds = collectSuccessfulToolResultIds(runMessages);
|
|
@@ -1144,46 +1245,34 @@ async function collectRetrospectiveRunEvidence(
|
|
|
1144
1245
|
remembers: extractRememberContents(runMessages, succeededIds),
|
|
1145
1246
|
durableToolCallCount: countDurableToolUses(runMessages, succeededIds),
|
|
1146
1247
|
durableToolAttemptCount: countDurableToolUses(runMessages, null),
|
|
1147
|
-
|
|
1248
|
+
committedTextReply: hasCommittedTextReply(runMessages),
|
|
1148
1249
|
};
|
|
1149
1250
|
}
|
|
1150
1251
|
|
|
1151
1252
|
/**
|
|
1152
|
-
* Whether
|
|
1153
|
-
*
|
|
1154
|
-
*
|
|
1155
|
-
* pass
|
|
1156
|
-
*
|
|
1253
|
+
* Whether the LAST persisted assistant row on the run's tail carries a text
|
|
1254
|
+
* block with non-whitespace content. Paired with a model-driven stop and
|
|
1255
|
+
* zero memory-write attempts, that closing reply is the persisted artifact
|
|
1256
|
+
* of a pass that read its window and had nothing to save: the model spoke
|
|
1257
|
+
* and then chose to end the run. Reading only the final row separates it
|
|
1258
|
+
* from a run whose narration went live but whose actual conclusion was
|
|
1259
|
+
* empty, as well as from a response that committed nothing at all
|
|
1260
|
+
* (thinking-only output, an empty content array).
|
|
1157
1261
|
*/
|
|
1158
|
-
function
|
|
1159
|
-
for (
|
|
1262
|
+
function hasCommittedTextReply(messages: MessageLike[]): boolean {
|
|
1263
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1264
|
+
const msg = messages[i]!;
|
|
1160
1265
|
if (msg.role !== "assistant") {
|
|
1161
1266
|
continue;
|
|
1162
1267
|
}
|
|
1163
|
-
|
|
1164
|
-
if (
|
|
1165
|
-
|
|
1166
|
-
blocks = JSON.parse(blocks);
|
|
1167
|
-
} catch {
|
|
1168
|
-
continue;
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
if (!Array.isArray(blocks)) {
|
|
1172
|
-
continue;
|
|
1173
|
-
}
|
|
1174
|
-
for (const block of blocks) {
|
|
1175
|
-
if (!block || typeof block !== "object") {
|
|
1176
|
-
continue;
|
|
1177
|
-
}
|
|
1178
|
-
const b = block as Record<string, unknown>;
|
|
1179
|
-
if (
|
|
1180
|
-
b.type === "text" &&
|
|
1181
|
-
typeof b.text === "string" &&
|
|
1182
|
-
b.text.trim() === MEMORY_RETROSPECTIVE_NO_FINDINGS_TEXT
|
|
1183
|
-
) {
|
|
1184
|
-
return true;
|
|
1185
|
-
}
|
|
1268
|
+
const blocks = parseMessageBlocks(msg);
|
|
1269
|
+
if (blocks === null) {
|
|
1270
|
+
return false;
|
|
1186
1271
|
}
|
|
1272
|
+
return blocks.some(
|
|
1273
|
+
(b) =>
|
|
1274
|
+
b.type === "text" && typeof b.text === "string" && b.text.trim() !== "",
|
|
1275
|
+
);
|
|
1187
1276
|
}
|
|
1188
1277
|
return false;
|
|
1189
1278
|
}
|
|
@@ -1199,22 +1288,7 @@ function collectSuccessfulToolResultIds(messages: MessageLike[]): Set<string> {
|
|
|
1199
1288
|
if (msg.role !== "user") {
|
|
1200
1289
|
continue;
|
|
1201
1290
|
}
|
|
1202
|
-
|
|
1203
|
-
if (typeof blocks === "string") {
|
|
1204
|
-
try {
|
|
1205
|
-
blocks = JSON.parse(blocks);
|
|
1206
|
-
} catch {
|
|
1207
|
-
continue;
|
|
1208
|
-
}
|
|
1209
|
-
}
|
|
1210
|
-
if (!Array.isArray(blocks)) {
|
|
1211
|
-
continue;
|
|
1212
|
-
}
|
|
1213
|
-
for (const block of blocks) {
|
|
1214
|
-
if (!block || typeof block !== "object") {
|
|
1215
|
-
continue;
|
|
1216
|
-
}
|
|
1217
|
-
const b = block as Record<string, unknown>;
|
|
1291
|
+
for (const b of parseMessageBlocks(msg) ?? []) {
|
|
1218
1292
|
// guard:allow-tool-result-only: success evidence for locally-executed
|
|
1219
1293
|
// durable memory tools; server-side web_search_tool_result never
|
|
1220
1294
|
// corresponds to a durable write and carries no is_error flag.
|
|
@@ -1246,22 +1320,7 @@ function countDurableToolUses(
|
|
|
1246
1320
|
if (msg.role !== "assistant") {
|
|
1247
1321
|
continue;
|
|
1248
1322
|
}
|
|
1249
|
-
|
|
1250
|
-
if (typeof blocks === "string") {
|
|
1251
|
-
try {
|
|
1252
|
-
blocks = JSON.parse(blocks);
|
|
1253
|
-
} catch {
|
|
1254
|
-
continue;
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
if (!Array.isArray(blocks)) {
|
|
1258
|
-
continue;
|
|
1259
|
-
}
|
|
1260
|
-
for (const block of blocks) {
|
|
1261
|
-
if (!block || typeof block !== "object") {
|
|
1262
|
-
continue;
|
|
1263
|
-
}
|
|
1264
|
-
const b = block as Record<string, unknown>;
|
|
1323
|
+
for (const b of parseMessageBlocks(msg) ?? []) {
|
|
1265
1324
|
if (
|
|
1266
1325
|
b.type === "tool_use" &&
|
|
1267
1326
|
DURABLE_RETROSPECTIVE_TOOLS.has(String(b.name)) &&
|
|
@@ -1280,6 +1339,32 @@ interface MessageLike {
|
|
|
1280
1339
|
content: string | ContentBlock[];
|
|
1281
1340
|
}
|
|
1282
1341
|
|
|
1342
|
+
/**
|
|
1343
|
+
* Parse a message row's content into its block objects, or `null` when the
|
|
1344
|
+
* content is malformed (unparseable JSON, not an array). Non-object entries
|
|
1345
|
+
* are dropped. Every evidence reader in this module goes through this so
|
|
1346
|
+
* malformed rows degrade the same way everywhere: skipped, not propagated.
|
|
1347
|
+
*/
|
|
1348
|
+
function parseMessageBlocks(
|
|
1349
|
+
msg: MessageLike,
|
|
1350
|
+
): Record<string, unknown>[] | null {
|
|
1351
|
+
let blocks: unknown = msg.content;
|
|
1352
|
+
if (typeof blocks === "string") {
|
|
1353
|
+
try {
|
|
1354
|
+
blocks = JSON.parse(blocks);
|
|
1355
|
+
} catch {
|
|
1356
|
+
return null;
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
if (!Array.isArray(blocks)) {
|
|
1360
|
+
return null;
|
|
1361
|
+
}
|
|
1362
|
+
return blocks.filter(
|
|
1363
|
+
(block): block is Record<string, unknown> =>
|
|
1364
|
+
typeof block === "object" && block !== null,
|
|
1365
|
+
);
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1283
1368
|
/**
|
|
1284
1369
|
* Scan an array of message rows for `tool_use` blocks where `name` is
|
|
1285
1370
|
* `"remember"` and return the `input.content` strings in order. Robust to
|
|
@@ -1294,22 +1379,7 @@ function extractRememberContents(
|
|
|
1294
1379
|
if (msg.role !== "assistant") {
|
|
1295
1380
|
continue;
|
|
1296
1381
|
}
|
|
1297
|
-
|
|
1298
|
-
if (typeof blocks === "string") {
|
|
1299
|
-
try {
|
|
1300
|
-
blocks = JSON.parse(blocks);
|
|
1301
|
-
} catch {
|
|
1302
|
-
continue;
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
if (!Array.isArray(blocks)) {
|
|
1306
|
-
continue;
|
|
1307
|
-
}
|
|
1308
|
-
for (const block of blocks) {
|
|
1309
|
-
if (!block || typeof block !== "object") {
|
|
1310
|
-
continue;
|
|
1311
|
-
}
|
|
1312
|
-
const b = block as Record<string, unknown>;
|
|
1382
|
+
for (const b of parseMessageBlocks(msg) ?? []) {
|
|
1313
1383
|
if (b.type !== "tool_use") {
|
|
1314
1384
|
continue;
|
|
1315
1385
|
}
|
|
@@ -27,7 +27,6 @@
|
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
import { getLogger } from "./logging.js";
|
|
30
|
-
import { MEMORY_RETROSPECTIVE_NO_FINDINGS_TEXT } from "./memory-retrospective-constants.js";
|
|
31
30
|
import { getWorkspaceDir } from "./paths.js";
|
|
32
31
|
import { loadPromptOverride } from "./prompt-override.js";
|
|
33
32
|
|
|
@@ -45,11 +44,13 @@ function neutralizeSentinels(s: string): string {
|
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
/**
|
|
48
|
-
* The sentence
|
|
49
|
-
*
|
|
50
|
-
*
|
|
47
|
+
* The sentence asking a pass that found nothing to say so and stop. The
|
|
48
|
+
* finalizer reads the SHAPE of such a run (a committed reply, no memory-write
|
|
49
|
+
* attempts, a model-driven stop), so the wording is free; what the sentence
|
|
50
|
+
* buys is a run that answers at all rather than committing nothing.
|
|
51
51
|
*/
|
|
52
|
-
const NO_FINDINGS_MANDATE =
|
|
52
|
+
const NO_FINDINGS_MANDATE =
|
|
53
|
+
"If nothing new is worth saving, say so briefly and stop.";
|
|
53
54
|
|
|
54
55
|
/**
|
|
55
56
|
* Bundled fork-instruction template. Exported so tests (and any future
|
|
@@ -149,8 +150,8 @@ export interface ForkInstructionArgs {
|
|
|
149
150
|
* the shared {@link loadPromptOverride}), else the bundled
|
|
150
151
|
* {@link RETROSPECTIVE_INSTRUCTION_TEMPLATE}; both get the same placeholder
|
|
151
152
|
* substitution. An override additionally gets the no-findings mandate
|
|
152
|
-
* appended after its body:
|
|
153
|
-
*
|
|
153
|
+
* appended after its body: a no-findings pass advances only by replying,
|
|
154
|
+
* so the ask-for-a-reply holds regardless of what the override says.
|
|
154
155
|
*/
|
|
155
156
|
export function buildForkInstruction({
|
|
156
157
|
windowStartTimestamp,
|
|
@@ -199,12 +200,11 @@ export function buildForkInstruction({
|
|
|
199
200
|
if (override == null) {
|
|
200
201
|
return rendered;
|
|
201
202
|
}
|
|
202
|
-
//
|
|
203
|
-
//
|
|
204
|
-
//
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
// contract.
|
|
203
|
+
// A pass that saves nothing advances only on a committed reply, so the
|
|
204
|
+
// mandate is part of the advancement contract, not prompt styling: an
|
|
205
|
+
// override body that leaves a no-findings pass free to answer with nothing
|
|
206
|
+
// at all would leave those windows permanently retryable. Appended outside
|
|
207
|
+
// the override so a custom prompt cannot break the contract.
|
|
208
208
|
return `${rendered}\n\n${NO_FINDINGS_MANDATE}`;
|
|
209
209
|
}
|
|
210
210
|
|
|
@@ -7,7 +7,7 @@ import { join } from "node:path";
|
|
|
7
7
|
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { getUserSelectableProfilesForProvider } from "../../../../config/default-profile-catalog.js";
|
|
11
11
|
import { loadConfig } from "../../../../config/loader.js";
|
|
12
12
|
import { usesConceptPageMemory } from "../../../../config/memory-v3-gate.js";
|
|
13
13
|
import type { AssistantConfig } from "../../../../config/types.js";
|
|
@@ -523,7 +523,7 @@ export async function handleSimulateRouter({
|
|
|
523
523
|
// through (the resolver tolerates missing override-profile references by
|
|
524
524
|
// design, but the playground wants the user to know they typo'd).
|
|
525
525
|
if (profileOverride !== undefined) {
|
|
526
|
-
const profiles =
|
|
526
|
+
const profiles = getUserSelectableProfilesForProvider(
|
|
527
527
|
liveConfig.llm?.profiles,
|
|
528
528
|
liveConfig.llm?.defaultProvider ?? null,
|
|
529
529
|
);
|
|
@@ -1138,7 +1138,7 @@ describe("memoryV2ConsolidateJob — consecutive-failure state", () => {
|
|
|
1138
1138
|
ok: false,
|
|
1139
1139
|
error: new Error("provider refused"),
|
|
1140
1140
|
errorKind: "model_provider",
|
|
1141
|
-
...(failureCode !== undefined ? { failureCode } : {}),
|
|
1141
|
+
...(failureCode !== undefined ? { turnFailure: { failureCode } } : {}),
|
|
1142
1142
|
});
|
|
1143
1143
|
}
|
|
1144
1144
|
|
|
@@ -486,12 +486,14 @@ export async function memoryV2ConsolidateJob(
|
|
|
486
486
|
// everything else (network blip, model hiccup, timeout) is transient
|
|
487
487
|
// and stays on the short curve.
|
|
488
488
|
const failureKind: ConsolidationFailureKind =
|
|
489
|
-
runResult.failureCode === "PROVIDER_BILLING"
|
|
489
|
+
runResult.turnFailure?.failureCode === "PROVIDER_BILLING"
|
|
490
|
+
? "billing"
|
|
491
|
+
: "transient";
|
|
490
492
|
log.error(
|
|
491
493
|
{
|
|
492
494
|
conversationId: runResult.conversationId,
|
|
493
495
|
errorKind: runResult.errorKind,
|
|
494
|
-
failureCode: runResult.failureCode,
|
|
496
|
+
failureCode: runResult.turnFailure?.failureCode,
|
|
495
497
|
failureKind,
|
|
496
498
|
err: runResult.error?.message,
|
|
497
499
|
},
|