@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
|
@@ -54,6 +54,18 @@ const conversationThresholdCache = new Map<
|
|
|
54
54
|
>();
|
|
55
55
|
const CONVERSATION_CACHE_TTL_MS = 5_000;
|
|
56
56
|
|
|
57
|
+
// ── Contact threshold cache (5s TTL) ─────────────────────────────────────────
|
|
58
|
+
// Same short TTL as the conversation override: the owner can change a
|
|
59
|
+
// contact's ceiling mid-conversation, but a burst of tool calls in one
|
|
60
|
+
// turn should not pay an IPC round-trip each time. Negative entries (no
|
|
61
|
+
// ceiling) are cached too. Transport failures are not.
|
|
62
|
+
|
|
63
|
+
const contactThresholdCache = new Map<
|
|
64
|
+
string,
|
|
65
|
+
{ threshold: string | null; timestamp: number }
|
|
66
|
+
>();
|
|
67
|
+
const CONTACT_CACHE_TTL_MS = 5_000;
|
|
68
|
+
|
|
57
69
|
// ── Channel-permission cell cache (5s TTL) ───────────────────────────────────
|
|
58
70
|
// The permission-matrix cell for (adapter × channel-type × channel-ID ×
|
|
59
71
|
// contact-type). Same short TTL as the conversation cache: a guardian can
|
|
@@ -188,9 +200,25 @@ export function _clearGlobalCacheForTesting(): void {
|
|
|
188
200
|
cachedGlobalThresholds = null;
|
|
189
201
|
cachedGlobalTimestamp = 0;
|
|
190
202
|
conversationThresholdCache.clear();
|
|
203
|
+
contactThresholdCache.clear();
|
|
191
204
|
channelPermissionCellCache.clear();
|
|
192
205
|
}
|
|
193
206
|
|
|
207
|
+
/**
|
|
208
|
+
* Drop the cached ceiling for one contact.
|
|
209
|
+
*/
|
|
210
|
+
export function invalidateContactThresholdCache(contactId: string): void {
|
|
211
|
+
contactThresholdCache.delete(contactId);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Drop every cached contact ceiling. `contacts_changed` calls this so the
|
|
216
|
+
* next approval re-reads after any contact ACL write.
|
|
217
|
+
*/
|
|
218
|
+
export function invalidateAllContactThresholdCaches(): void {
|
|
219
|
+
contactThresholdCache.clear();
|
|
220
|
+
}
|
|
221
|
+
|
|
194
222
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
195
223
|
|
|
196
224
|
function mapExecutionContextToField(
|
|
@@ -214,6 +242,78 @@ function isValidThreshold(value: string): value is AutoApproveThreshold {
|
|
|
214
242
|
);
|
|
215
243
|
}
|
|
216
244
|
|
|
245
|
+
type ContactThresholdLookup =
|
|
246
|
+
| { status: "hit"; threshold: AutoApproveThreshold }
|
|
247
|
+
| { status: "miss" }
|
|
248
|
+
| { status: "failure" };
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Read the contact-level auto-approve ceiling from the gateway.
|
|
252
|
+
*
|
|
253
|
+
* A hit is a usable `none | low | medium | high`. A miss is an unknown
|
|
254
|
+
* contact, an unset or corrupt column, or an unexpected response shape.
|
|
255
|
+
* A failure is a transport error and is never cached, so a later call
|
|
256
|
+
* can still see a real ceiling.
|
|
257
|
+
*/
|
|
258
|
+
async function lookupContactThreshold(
|
|
259
|
+
contactId: string,
|
|
260
|
+
options?: { bypassCache?: boolean },
|
|
261
|
+
): Promise<ContactThresholdLookup> {
|
|
262
|
+
if (!options?.bypassCache) {
|
|
263
|
+
const cached = contactThresholdCache.get(contactId);
|
|
264
|
+
if (cached && Date.now() - cached.timestamp < CONTACT_CACHE_TTL_MS) {
|
|
265
|
+
if (cached.threshold !== null && isValidThreshold(cached.threshold)) {
|
|
266
|
+
return { status: "hit", threshold: cached.threshold };
|
|
267
|
+
}
|
|
268
|
+
return { status: "miss" };
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const result = (await ipcCall("get_contact_threshold", {
|
|
273
|
+
contactId,
|
|
274
|
+
})) as ConversationThreshold | null | undefined;
|
|
275
|
+
|
|
276
|
+
if (result === undefined) {
|
|
277
|
+
noteFailure(
|
|
278
|
+
"contact_threshold",
|
|
279
|
+
{ contactId },
|
|
280
|
+
"IPC call failed for contact threshold",
|
|
281
|
+
);
|
|
282
|
+
return { status: "failure" };
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
noteSuccess("contact_threshold");
|
|
286
|
+
if (result && isValidThreshold(result.threshold)) {
|
|
287
|
+
contactThresholdCache.set(contactId, {
|
|
288
|
+
threshold: result.threshold,
|
|
289
|
+
timestamp: Date.now(),
|
|
290
|
+
});
|
|
291
|
+
return { status: "hit", threshold: result.threshold };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
contactThresholdCache.set(contactId, {
|
|
295
|
+
threshold: null,
|
|
296
|
+
timestamp: Date.now(),
|
|
297
|
+
});
|
|
298
|
+
return { status: "miss" };
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* The contact-level auto-approve ceiling for `contactId`, or `null` when
|
|
303
|
+
* the contact has none (unset, unknown, corrupt) or the lookup failed.
|
|
304
|
+
* Approval-time lift uses this so a live `contacts` row is the source of
|
|
305
|
+
* truth.
|
|
306
|
+
*/
|
|
307
|
+
export async function getContactAutoApproveThreshold(
|
|
308
|
+
contactId: string | undefined,
|
|
309
|
+
): Promise<AutoApproveThreshold | null> {
|
|
310
|
+
if (!contactId) {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
const result = await lookupContactThreshold(contactId);
|
|
314
|
+
return result.status === "hit" ? result.threshold : null;
|
|
315
|
+
}
|
|
316
|
+
|
|
217
317
|
/**
|
|
218
318
|
* Result of a cell lookup. `ok: false` is a transport failure — distinct
|
|
219
319
|
* from `resolved: null` (a successful round-trip that found no cell) so the
|
|
@@ -322,9 +422,16 @@ export async function channelNoCellDefault(
|
|
|
322
422
|
/**
|
|
323
423
|
* Read the auto-approve threshold from the gateway via IPC.
|
|
324
424
|
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
425
|
+
* Cascade, most specific first:
|
|
426
|
+
* 1. Per-conversation override (when `"conversation"` context has an id)
|
|
427
|
+
* 2. Contact-level ceiling (when `contactId` looks up a usable threshold)
|
|
428
|
+
* 3. Channel-permission matrix cell (collapsed for contacts)
|
|
429
|
+
* 4. Global defaults for the execution context
|
|
430
|
+
*
|
|
431
|
+
* The contact ceiling is the owner's override for that person, read live
|
|
432
|
+
* from the gateway `contacts` row. It is not collapsed to the channel's
|
|
433
|
+
* two levels, so `medium` and `high` can authorize sandbox bash. A miss
|
|
434
|
+
* (unset, unknown, transport failure) inherits the cell / global cascade.
|
|
328
435
|
*
|
|
329
436
|
* Caches global thresholds for 30 seconds to avoid hammering the gateway.
|
|
330
437
|
* On any IPC error or unexpected response, returns `"none"` (Strict) so
|
|
@@ -334,6 +441,7 @@ export async function getAutoApproveThreshold(
|
|
|
334
441
|
conversationId: string | undefined,
|
|
335
442
|
executionContext?: ExecutionContext,
|
|
336
443
|
cellQuery?: ResolveChannelPermissionRequest,
|
|
444
|
+
contactId?: string,
|
|
337
445
|
): Promise<AutoApproveThreshold> {
|
|
338
446
|
const ctx: ExecutionContext = executionContext ?? "conversation";
|
|
339
447
|
|
|
@@ -386,6 +494,17 @@ export async function getAutoApproveThreshold(
|
|
|
386
494
|
}
|
|
387
495
|
}
|
|
388
496
|
|
|
497
|
+
// Contact-level ceiling: the owner's override for this person, looked up
|
|
498
|
+
// by contact id. Beats the collapsed room / trust-class cascade.
|
|
499
|
+
// Conversation override above is more specific (this chat) and still
|
|
500
|
+
// wins when set. A miss or transport failure falls through.
|
|
501
|
+
if (contactId) {
|
|
502
|
+
const contact = await lookupContactThreshold(contactId);
|
|
503
|
+
if (contact.status === "hit") {
|
|
504
|
+
return contact.threshold;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
389
508
|
// Channel-permission matrix cell: for non-guardian actors this layer is
|
|
390
509
|
// total in every direction — a successful walk that finds no cell resolves
|
|
391
510
|
// the room default (the collapsed global), and a failed read resolves
|
|
@@ -467,13 +586,15 @@ async function fetchGlobalThresholds(options?: {
|
|
|
467
586
|
* Used by the permission checker immediately before surfacing an
|
|
468
587
|
* interactive prompt: the cached snapshot (5s conversation TTL with
|
|
469
588
|
* negative caching, 30s global TTL) may predate a threshold change the
|
|
470
|
-
* user just made
|
|
471
|
-
* write path invalidates
|
|
472
|
-
* through the gateway HTTP route, the desktop picker
|
|
473
|
-
* `set_conversation_threshold` IPC).
|
|
474
|
-
*
|
|
475
|
-
*
|
|
476
|
-
*
|
|
589
|
+
* user just made (for example switching to Full access) because no
|
|
590
|
+
* conversation or global write path invalidates those caches (the web
|
|
591
|
+
* picker writes through the gateway HTTP route, the desktop picker
|
|
592
|
+
* through the `set_conversation_threshold` IPC). A `contacts_changed`
|
|
593
|
+
* event clears every cached contact ceiling after a contact ACL write.
|
|
594
|
+
* Prompting from a stale threshold directly contradicts the user's
|
|
595
|
+
* visible setting. A prompt is already a rare, user-visible
|
|
596
|
+
* interruption, so the extra IPC round-trip is cheap relative to a
|
|
597
|
+
* wrong prompt.
|
|
477
598
|
*
|
|
478
599
|
* Returns the freshly-resolved threshold, or `null` when the gateway
|
|
479
600
|
* could not be reached. Callers must keep their original decision on
|
|
@@ -496,6 +617,7 @@ export async function refreshAutoApproveThreshold(
|
|
|
496
617
|
conversationId: string | undefined,
|
|
497
618
|
executionContext?: ExecutionContext,
|
|
498
619
|
cellQuery?: ResolveChannelPermissionRequest,
|
|
620
|
+
contactId?: string,
|
|
499
621
|
): Promise<AutoApproveThreshold | null> {
|
|
500
622
|
const ctx: ExecutionContext = executionContext ?? "conversation";
|
|
501
623
|
|
|
@@ -528,6 +650,18 @@ export async function refreshAutoApproveThreshold(
|
|
|
528
650
|
});
|
|
529
651
|
}
|
|
530
652
|
|
|
653
|
+
if (contactId) {
|
|
654
|
+
const contact = await lookupContactThreshold(contactId, {
|
|
655
|
+
bypassCache: true,
|
|
656
|
+
});
|
|
657
|
+
if (contact.status === "failure") {
|
|
658
|
+
return null;
|
|
659
|
+
}
|
|
660
|
+
if (contact.status === "hit") {
|
|
661
|
+
return contact.threshold;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
531
665
|
// Fresh cell read (cache bypassed, then primed). A transport failure here
|
|
532
666
|
// returns null — the caller keeps its prompt rather than falling through
|
|
533
667
|
// to a global threshold that may be looser than the unreadable cell
|
|
@@ -14,8 +14,6 @@ const log = getLogger("permission-prompter");
|
|
|
14
14
|
|
|
15
15
|
type ConfirmResult = {
|
|
16
16
|
decision: UserDecision;
|
|
17
|
-
selectedPattern?: string;
|
|
18
|
-
selectedScope?: string;
|
|
19
17
|
decisionContext?: string;
|
|
20
18
|
wasTimeout?: boolean;
|
|
21
19
|
wasSystemCancel?: boolean;
|
|
@@ -208,8 +206,6 @@ export class PermissionPrompter {
|
|
|
208
206
|
requestId: string,
|
|
209
207
|
decision: UserDecision,
|
|
210
208
|
options?: {
|
|
211
|
-
selectedPattern?: string;
|
|
212
|
-
selectedScope?: string;
|
|
213
209
|
decisionContext?: string;
|
|
214
210
|
},
|
|
215
211
|
): void {
|
|
@@ -226,8 +222,6 @@ export class PermissionPrompter {
|
|
|
226
222
|
this.ownedIds.delete(requestId);
|
|
227
223
|
(interaction?.rpcResolve as ((v: ConfirmResult) => void) | undefined)?.({
|
|
228
224
|
decision,
|
|
229
|
-
selectedPattern: options?.selectedPattern,
|
|
230
|
-
selectedScope: options?.selectedScope,
|
|
231
225
|
decisionContext: options?.decisionContext,
|
|
232
226
|
});
|
|
233
227
|
}
|
package/src/permissions/types.ts
CHANGED
|
@@ -36,10 +36,6 @@ export const THRESHOLD_ORDINAL: Record<string, number> = {
|
|
|
36
36
|
|
|
37
37
|
export type UserDecision = "allow" | "deny";
|
|
38
38
|
|
|
39
|
-
export function isAllowDecision(decision: UserDecision): boolean {
|
|
40
|
-
return decision === "allow";
|
|
41
|
-
}
|
|
42
|
-
|
|
43
39
|
export interface PermissionCheckResult {
|
|
44
40
|
decision: "allow" | "deny" | "prompt";
|
|
45
41
|
reason: string;
|
|
@@ -90,4 +86,9 @@ export interface PolicyContext {
|
|
|
90
86
|
* grant then never fires.
|
|
91
87
|
*/
|
|
92
88
|
procToSkillsActive?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Contact ID of the requester's member record. The threshold reader looks
|
|
91
|
+
* up this contact's auto-approve ceiling from the gateway at use time.
|
|
92
|
+
*/
|
|
93
|
+
requesterContactId?: string;
|
|
93
94
|
}
|
|
@@ -6,7 +6,6 @@ import { describe, expect, test } from "bun:test";
|
|
|
6
6
|
import { assertNotLiveDb } from "../../__tests__/assert-not-live-db.js";
|
|
7
7
|
import { bulkWriteGateHolder, withBulkWriteGate } from "../bulk-write-gate.js";
|
|
8
8
|
import { deleteConversationRowsInBatches } from "../conversation-row-batch-delete.js";
|
|
9
|
-
import { copyForkMessagesViaSubprocess } from "../fork-message-copy.js";
|
|
10
9
|
|
|
11
10
|
/** A promise whose resolution the test controls. */
|
|
12
11
|
function deferred(): { promise: Promise<void>; release: () => void } {
|
|
@@ -73,43 +72,6 @@ describe("withBulkWriteGate", () => {
|
|
|
73
72
|
});
|
|
74
73
|
|
|
75
74
|
describe("gated bulk writers", () => {
|
|
76
|
-
test("fork message-copy queues behind a held gate", async () => {
|
|
77
|
-
const holder = deferred();
|
|
78
|
-
const holding = withBulkWriteGate("holder", () => holder.promise);
|
|
79
|
-
|
|
80
|
-
let copySettled = false;
|
|
81
|
-
const copy = copyForkMessagesViaSubprocess({
|
|
82
|
-
forkConversationId: "fork-1",
|
|
83
|
-
idPairs: [{ oldId: "old-1", newId: "new-1" }],
|
|
84
|
-
forceInProcess: true,
|
|
85
|
-
}).then((result) => {
|
|
86
|
-
copySettled = true;
|
|
87
|
-
return result;
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
await Bun.sleep(20);
|
|
91
|
-
expect(copySettled).toBe(false);
|
|
92
|
-
|
|
93
|
-
holder.release();
|
|
94
|
-
await holding;
|
|
95
|
-
await copy;
|
|
96
|
-
expect(copySettled).toBe(true);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
test("empty fork message-copy skips the gate entirely", async () => {
|
|
100
|
-
const holder = deferred();
|
|
101
|
-
const holding = withBulkWriteGate("holder", () => holder.promise);
|
|
102
|
-
|
|
103
|
-
const result = await copyForkMessagesViaSubprocess({
|
|
104
|
-
forkConversationId: "fork-1",
|
|
105
|
-
idPairs: [],
|
|
106
|
-
});
|
|
107
|
-
expect(result.ok).toBe(true);
|
|
108
|
-
|
|
109
|
-
holder.release();
|
|
110
|
-
await holding;
|
|
111
|
-
});
|
|
112
|
-
|
|
113
75
|
test("main-DB batched delete queues behind a held gate", async () => {
|
|
114
76
|
const holder = deferred();
|
|
115
77
|
const holding = withBulkWriteGate("holder", () => holder.promise);
|
|
@@ -2,16 +2,15 @@
|
|
|
2
2
|
// Bulk-write gate — process-wide FIFO serialization of bulk main-DB writers.
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
//
|
|
5
|
-
// The batched bulk
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// run in parallel.
|
|
5
|
+
// The batched bulk writer (conversation-row batch delete) holds the main
|
|
6
|
+
// database's write lock only briefly per batch — but SQLite has a single
|
|
7
|
+
// writer, so two bulk streams running concurrently gain no throughput. They
|
|
8
|
+
// only convoy: every batch of each stream contends with the other's (waiting
|
|
9
|
+
// up to `busy_timeout`), and the inter-batch yield each stream inserts for
|
|
10
|
+
// foreground fairness is consumed by the sibling stream instead of the live
|
|
11
|
+
// user turn it was meant for. Concurrent streams are reachable because the
|
|
12
|
+
// memory jobs worker groups jobs by `(type, conversationId)` — work for two
|
|
13
|
+
// different conversations can run in parallel.
|
|
15
14
|
//
|
|
16
15
|
// This gate serializes those streams within the process: one bulk writer runs
|
|
17
16
|
// at a time, the rest queue FIFO, and the write lock (plus the yields) go back
|