@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
package/src/util/process-tree.ts
CHANGED
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
* process that is actually parented to it.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
import {
|
|
13
|
+
listProcessTableAsync,
|
|
14
|
+
type ProcessTableRow,
|
|
15
|
+
readRawProcessCommand,
|
|
16
|
+
} from "./process-table.js";
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Whether a process runs plugin-owned code or is the daemon itself / one of its
|
|
@@ -52,6 +52,12 @@ export interface ProcTreeNode {
|
|
|
52
52
|
children: ProcTreeNode[];
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
interface ProcessTreeBuilder {
|
|
56
|
+
build: (pid: number) => ProcTreeNode;
|
|
57
|
+
byPid: Map<number, ProcInfo>;
|
|
58
|
+
visited: Set<number>;
|
|
59
|
+
}
|
|
60
|
+
|
|
55
61
|
/** Interpreters whose script argument is more descriptive than argv[0]. */
|
|
56
62
|
const RUNTIMES = new Set([
|
|
57
63
|
"bun",
|
|
@@ -64,7 +70,14 @@ const RUNTIMES = new Set([
|
|
|
64
70
|
"env",
|
|
65
71
|
]);
|
|
66
72
|
|
|
67
|
-
const basename = (p: string): string => p.split(
|
|
73
|
+
const basename = (p: string): string => p.split(/[\\/]/).pop() || p;
|
|
74
|
+
|
|
75
|
+
function splitCommand(command: string): string[] {
|
|
76
|
+
return Array.from(
|
|
77
|
+
command.matchAll(/"([^"]*)"|'([^']*)'|(\S+)/g),
|
|
78
|
+
(match) => match[1] ?? match[2] ?? match[3],
|
|
79
|
+
);
|
|
80
|
+
}
|
|
68
81
|
|
|
69
82
|
/** Script extensions whose path we summarize as `<parent>-<file>`. */
|
|
70
83
|
const SCRIPT_EXT_RE = /\.(ts|js|mjs|cjs|py)$/;
|
|
@@ -76,7 +89,7 @@ const SCRIPT_EXT_RE = /\.(ts|js|mjs|cjs|py)$/;
|
|
|
76
89
|
* filename when the script sits at the filesystem root.
|
|
77
90
|
*/
|
|
78
91
|
function scriptName(scriptPath: string): string {
|
|
79
|
-
const parts = scriptPath.split(
|
|
92
|
+
const parts = scriptPath.split(/[\\/]/).filter(Boolean);
|
|
80
93
|
const file = parts[parts.length - 1].replace(SCRIPT_EXT_RE, "");
|
|
81
94
|
const parent = parts.length >= 2 ? parts[parts.length - 2] : "";
|
|
82
95
|
return parent ? `${parent}-${file}` : file;
|
|
@@ -93,13 +106,14 @@ function scriptName(scriptPath: string): string {
|
|
|
93
106
|
* (`/…/vellum-qdrant`) keep their bare executable name.
|
|
94
107
|
*/
|
|
95
108
|
export function deriveName(command: string): string {
|
|
96
|
-
const tokens = command.trim()
|
|
109
|
+
const tokens = splitCommand(command.trim());
|
|
97
110
|
if (tokens.length === 0) {
|
|
98
111
|
return "(unknown)";
|
|
99
112
|
}
|
|
100
113
|
|
|
101
114
|
const argv0 = basename(tokens[0]);
|
|
102
|
-
|
|
115
|
+
const runtimeName = argv0.replace(/\.exe$/i, "").toLowerCase();
|
|
116
|
+
if (RUNTIMES.has(runtimeName)) {
|
|
103
117
|
const args = tokens.slice(1);
|
|
104
118
|
const script = args.find((t) => SCRIPT_EXT_RE.test(t));
|
|
105
119
|
if (script) {
|
|
@@ -109,7 +123,7 @@ export function deriveName(command: string): string {
|
|
|
109
123
|
// "what was run" rather than an opaque `bun`. Flags are dropped as noise.
|
|
110
124
|
const meaningful = args.filter((t) => !t.startsWith("-"));
|
|
111
125
|
if (meaningful.length > 0) {
|
|
112
|
-
return `${
|
|
126
|
+
return `${runtimeName} ${meaningful.join(" ")}`;
|
|
113
127
|
}
|
|
114
128
|
}
|
|
115
129
|
return argv0;
|
|
@@ -126,12 +140,8 @@ export function deriveName(command: string): string {
|
|
|
126
140
|
* {@link deriveName} descriptor.
|
|
127
141
|
*/
|
|
128
142
|
export function readProcessCommand(pid: number): string | null {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
return deriveName(raw.split("\0").filter(Boolean).join(" "));
|
|
132
|
-
} catch {
|
|
133
|
-
return null;
|
|
134
|
-
}
|
|
143
|
+
const command = readRawProcessCommand(pid);
|
|
144
|
+
return command == null ? null : deriveName(command);
|
|
135
145
|
}
|
|
136
146
|
|
|
137
147
|
/**
|
|
@@ -143,7 +153,8 @@ export function readProcessCommand(pid: number): string | null {
|
|
|
143
153
|
* (`.../plugins/defaults/<name>/`). The `plugins-data/` state directory is
|
|
144
154
|
* deliberately not matched — the slash after `plugins` excludes it.
|
|
145
155
|
*/
|
|
146
|
-
const PLUGIN_PATH_RE =
|
|
156
|
+
const PLUGIN_PATH_RE =
|
|
157
|
+
/(?:^|[\\/])plugins[\\/]([^\\/\s]+)(?:[\\/]([^\\/\s]+))?/;
|
|
147
158
|
|
|
148
159
|
/**
|
|
149
160
|
* Classify whether a raw command line belongs to plugin-owned code, and if so
|
|
@@ -169,122 +180,23 @@ export function deriveOrigin(command: string): ProcessOrigin {
|
|
|
169
180
|
return `plugin:${name}`;
|
|
170
181
|
}
|
|
171
182
|
|
|
172
|
-
/**
|
|
173
|
-
* Parse a `/proc/<pid>/stat` line into its leading fields. `comm` (the
|
|
174
|
-
* executable name) may itself contain spaces and parentheses, so the fixed
|
|
175
|
-
* fields are read relative to the final `)` rather than by naive splitting.
|
|
176
|
-
* Returns null if the line is malformed.
|
|
177
|
-
*/
|
|
178
|
-
function parseProcStat(content: string): { comm: string; ppid: number } | null {
|
|
179
|
-
const lparen = content.indexOf("(");
|
|
180
|
-
const rparen = content.lastIndexOf(")");
|
|
181
|
-
if (lparen === -1 || rparen === -1 || rparen < lparen) {
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
const comm = content.slice(lparen + 1, rparen);
|
|
185
|
-
// After ")" come: " <state> <ppid> …" — split the remainder on spaces.
|
|
186
|
-
const rest = content.slice(rparen + 2).split(" ");
|
|
187
|
-
const ppid = Number(rest[1]);
|
|
188
|
-
if (!Number.isInteger(ppid)) {
|
|
189
|
-
return null;
|
|
190
|
-
}
|
|
191
|
-
return { comm, ppid };
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/** Read the live process table from Linux `/proc`. Throws if `/proc` is absent. */
|
|
195
|
-
function listProcessesFromProc(): ProcInfo[] {
|
|
196
|
-
const entries = readdirSync("/proc");
|
|
197
|
-
const procs: ProcInfo[] = [];
|
|
198
|
-
for (const entry of entries) {
|
|
199
|
-
const pid = Number(entry);
|
|
200
|
-
if (!Number.isInteger(pid) || pid <= 0) {
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
let ppid: number;
|
|
205
|
-
let comm: string;
|
|
206
|
-
try {
|
|
207
|
-
const parsed = parseProcStat(readFileSync(`/proc/${pid}/stat`, "utf8"));
|
|
208
|
-
if (!parsed) {
|
|
209
|
-
continue;
|
|
210
|
-
}
|
|
211
|
-
({ ppid, comm } = parsed);
|
|
212
|
-
} catch {
|
|
213
|
-
// Process exited between readdir and read — skip.
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// `/proc/<pid>/cmdline` is NUL-delimited and empty for kernel threads.
|
|
218
|
-
// Redact the raw command line via deriveName to strip secrets (tokens,
|
|
219
|
-
// API keys, database URLs) that are commonly passed as process arguments.
|
|
220
|
-
// The raw command line is read here but never stored — only the derived
|
|
221
|
-
// safe descriptor is kept.
|
|
222
|
-
let command = comm;
|
|
223
|
-
// Origin is derived from the raw command path (which carries the plugin
|
|
224
|
-
// directory), so it is classified before deriveName redacts the path away.
|
|
225
|
-
let origin: ProcessOrigin = "workspace";
|
|
226
|
-
try {
|
|
227
|
-
const raw = readFileSync(`/proc/${pid}/cmdline`, "utf8");
|
|
228
|
-
const joined = raw.split("\0").filter(Boolean).join(" ");
|
|
229
|
-
if (joined) {
|
|
230
|
-
command = deriveName(joined);
|
|
231
|
-
origin = deriveOrigin(joined);
|
|
232
|
-
}
|
|
233
|
-
} catch {
|
|
234
|
-
// Fall back to comm.
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
procs.push({ pid, ppid, command, origin });
|
|
238
|
-
}
|
|
239
|
-
return procs;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/** Read the live process table via the `ps` command (macOS / no `/proc`). */
|
|
243
|
-
async function listProcessesFromPs(): Promise<ProcInfo[]> {
|
|
244
|
-
const { stdout } = await execFileAsync(
|
|
245
|
-
"ps",
|
|
246
|
-
["-A", "-o", "pid=,ppid=,command="],
|
|
247
|
-
{ maxBuffer: 8 * 1024 * 1024 },
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
const procs: ProcInfo[] = [];
|
|
251
|
-
for (const line of stdout.split("\n")) {
|
|
252
|
-
const m = line.match(/^\s*(\d+)\s+(\d+)\s+(.*)$/);
|
|
253
|
-
if (!m) {
|
|
254
|
-
continue;
|
|
255
|
-
}
|
|
256
|
-
const raw = m[3].trim();
|
|
257
|
-
procs.push({
|
|
258
|
-
pid: Number(m[1]),
|
|
259
|
-
ppid: Number(m[2]),
|
|
260
|
-
command: deriveName(raw),
|
|
261
|
-
origin: deriveOrigin(raw),
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
return procs;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
183
|
/**
|
|
268
184
|
* Enumerate the live process table as `(pid, ppid, command)` rows. Prefers
|
|
269
|
-
* Linux `/proc
|
|
185
|
+
* Linux `/proc`, uses `ps` on macOS, and queries Win32_Process on Windows.
|
|
270
186
|
*/
|
|
271
|
-
export async function listProcesses(
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
187
|
+
export async function listProcesses(
|
|
188
|
+
rows?: ProcessTableRow[],
|
|
189
|
+
): Promise<ProcInfo[]> {
|
|
190
|
+
const processRows = rows ?? (await listProcessTableAsync());
|
|
191
|
+
return processRows.map(({ pid, ppid, command }) => ({
|
|
192
|
+
pid,
|
|
193
|
+
ppid,
|
|
194
|
+
command: deriveName(command),
|
|
195
|
+
origin: deriveOrigin(command),
|
|
196
|
+
}));
|
|
277
197
|
}
|
|
278
198
|
|
|
279
|
-
|
|
280
|
-
* Build the process subtree rooted at `rootPid` from a flat process list.
|
|
281
|
-
* Children are ordered by PID. Self-references and already-visited PIDs are
|
|
282
|
-
* skipped so a malformed table cannot produce an infinite tree.
|
|
283
|
-
*/
|
|
284
|
-
export function buildProcessTree(
|
|
285
|
-
procs: ProcInfo[],
|
|
286
|
-
rootPid: number,
|
|
287
|
-
): ProcTreeNode {
|
|
199
|
+
function createProcessTreeBuilder(procs: ProcInfo[]): ProcessTreeBuilder {
|
|
288
200
|
const byPid = new Map<number, ProcInfo>();
|
|
289
201
|
const childrenOf = new Map<number, number[]>();
|
|
290
202
|
for (const p of procs) {
|
|
@@ -316,5 +228,49 @@ export function buildProcessTree(
|
|
|
316
228
|
};
|
|
317
229
|
};
|
|
318
230
|
|
|
319
|
-
return build
|
|
231
|
+
return { build, byPid, visited };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Build the process subtree rooted at `rootPid` from a flat process list.
|
|
236
|
+
* Children are ordered by PID. Self-references and already-visited PIDs are
|
|
237
|
+
* skipped so a malformed table cannot produce an infinite tree.
|
|
238
|
+
*/
|
|
239
|
+
export function buildProcessTree(
|
|
240
|
+
procs: ProcInfo[],
|
|
241
|
+
rootPid: number,
|
|
242
|
+
): ProcTreeNode {
|
|
243
|
+
return createProcessTreeBuilder(procs).build(rootPid);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** Build a synthetic system root containing every enumerated process once. */
|
|
247
|
+
export function buildSystemProcessTree(procs: ProcInfo[]): ProcTreeNode {
|
|
248
|
+
const { build, byPid, visited } = createProcessTreeBuilder(procs);
|
|
249
|
+
const children: ProcTreeNode[] = [];
|
|
250
|
+
const orderedProcesses = [...byPid.values()].sort((a, b) => a.pid - b.pid);
|
|
251
|
+
|
|
252
|
+
for (const processInfo of orderedProcesses) {
|
|
253
|
+
if (
|
|
254
|
+
processInfo.ppid !== processInfo.pid &&
|
|
255
|
+
!byPid.has(processInfo.ppid) &&
|
|
256
|
+
!visited.has(processInfo.pid)
|
|
257
|
+
) {
|
|
258
|
+
children.push(build(processInfo.pid));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Malformed process tables can contain parent cycles with no natural root.
|
|
263
|
+
for (const processInfo of orderedProcesses) {
|
|
264
|
+
if (!visited.has(processInfo.pid)) {
|
|
265
|
+
children.push(build(processInfo.pid));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
pid: 0,
|
|
271
|
+
name: "system",
|
|
272
|
+
command: "",
|
|
273
|
+
origin: "workspace",
|
|
274
|
+
children,
|
|
275
|
+
};
|
|
320
276
|
}
|
|
@@ -25,6 +25,26 @@ export function isVisionNotSupportedError(message: string): boolean {
|
|
|
25
25
|
return VISION_NOT_SUPPORTED_PATTERNS.some((re) => re.test(message));
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
// Provider prose from server-side chat-template renderers that failed to
|
|
29
|
+
// serialize the request into the model's prompt format. Emitted by
|
|
30
|
+
// vLLM/Jinja-style template engines behind OpenAI-compatible endpoints, e.g.
|
|
31
|
+
// Together serving MiniMax M3: "Failed to apply chat template: invalid
|
|
32
|
+
// operation: object is not callable (in chat:22)".
|
|
33
|
+
export const CHAT_TEMPLATE_FAILURE_PATTERNS = [
|
|
34
|
+
/appl\w* (?:the )?chat[ _-]?template/i,
|
|
35
|
+
/chat[ _-]?template (?:error|render)/i,
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Whether a provider error message indicates the endpoint's server-side chat
|
|
40
|
+
* template failed to render the request. These endpoints typically only
|
|
41
|
+
* handle plain-string message content, so structured shapes (content-parts
|
|
42
|
+
* arrays, tool payloads) are what trip the renderer.
|
|
43
|
+
*/
|
|
44
|
+
export function isChatTemplateFailureError(message: string): boolean {
|
|
45
|
+
return CHAT_TEMPLATE_FAILURE_PATTERNS.some((re) => re.test(message));
|
|
46
|
+
}
|
|
47
|
+
|
|
28
48
|
// Vendor-neutral (OpenRouter/Anthropic-style) credit-exhaustion prose. Also
|
|
29
49
|
// covers per-key spend caps: OpenRouter returns 403 "Key limit exceeded" when a
|
|
30
50
|
// key's configured credit limit is reached — a billing/budget condition that
|
package/src/util/spawn.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Shared spawn-with-timeout helper used by media-processing and transcribe tools.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { delimiter } from "node:path";
|
|
6
|
+
|
|
5
7
|
/** Full video preprocessing: mpdecimate analysis, frame extraction, palette analysis. Longest operation. */
|
|
6
8
|
export const FFMPEG_PREPROCESS_TIMEOUT_MS = 600_000;
|
|
7
9
|
|
|
@@ -29,9 +31,14 @@ export function spawnWithTimeout(
|
|
|
29
31
|
stderr: "pipe",
|
|
30
32
|
env: {
|
|
31
33
|
...process.env,
|
|
32
|
-
PATH: [
|
|
34
|
+
PATH: [
|
|
35
|
+
process.env.PATH,
|
|
36
|
+
...(process.platform === "darwin"
|
|
37
|
+
? ["/opt/homebrew/bin", "/usr/local/bin"]
|
|
38
|
+
: []),
|
|
39
|
+
]
|
|
33
40
|
.filter(Boolean)
|
|
34
|
-
.join(
|
|
41
|
+
.join(delimiter),
|
|
35
42
|
},
|
|
36
43
|
});
|
|
37
44
|
const timer = setTimeout(() => {
|
|
@@ -247,6 +247,51 @@ describe("watch retrospective", () => {
|
|
|
247
247
|
);
|
|
248
248
|
});
|
|
249
249
|
|
|
250
|
+
test("puts the report last in the turn, after the skill it hands off to", async () => {
|
|
251
|
+
const summary = recordSession(["renaming the export"]);
|
|
252
|
+
const { calls, dispatch } = recordingDispatch();
|
|
253
|
+
|
|
254
|
+
await runWatchRetro(summary, { dispatch });
|
|
255
|
+
|
|
256
|
+
const { prompt } = calls[0]!;
|
|
257
|
+
// The report is what the user is shown when a session ends, so it has to
|
|
258
|
+
// be the turn's last prose. A client renders the final text block as the
|
|
259
|
+
// response and folds everything before it into collapsed intermediate
|
|
260
|
+
// work, so a `skill_load` issued after the report demotes the report to
|
|
261
|
+
// an "Earlier activity" row the user has to unfold to read.
|
|
262
|
+
expect(prompt).toContain("Load the `skill-management` skill first");
|
|
263
|
+
expect(
|
|
264
|
+
prompt.indexOf("Load the `skill-management` skill first"),
|
|
265
|
+
).toBeLessThan(prompt.indexOf("What I need from you"));
|
|
266
|
+
expect(prompt).toContain("That report is the last thing you do this turn");
|
|
267
|
+
// A sign-off is another text block after the report, which puts the report
|
|
268
|
+
// back on the wrong side of the same rule, so it is refused by name.
|
|
269
|
+
expect(prompt).toContain("no sign-off");
|
|
270
|
+
expect(prompt).toContain("no further tool call");
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test("writes the report even when the skill it hands off to will not load", async () => {
|
|
274
|
+
const summary = recordSession(["filing the receipt"]);
|
|
275
|
+
const { calls, dispatch } = recordingDispatch();
|
|
276
|
+
|
|
277
|
+
await runWatchRetro(summary, { dispatch });
|
|
278
|
+
|
|
279
|
+
const { prompt } = calls[0]!;
|
|
280
|
+
// `skill-management` is a selector, and a managed or workspace skill of the
|
|
281
|
+
// same id replaces the bundled one in the catalog. Putting the load ahead
|
|
282
|
+
// of the report is what lets a shadow, or the refusal a clientless wake
|
|
283
|
+
// gives an inline-command load, land before the user has been told
|
|
284
|
+
// anything. Neither may become the retro: the session is recorded and the
|
|
285
|
+
// account of it is what the user is owed.
|
|
286
|
+
expect(prompt).toContain(
|
|
287
|
+
"Write the report below whether or not that load succeeds",
|
|
288
|
+
);
|
|
289
|
+
expect(prompt).toContain("do not report on the load and do not retry it");
|
|
290
|
+
// The failure is still named, so a missing handoff does not read as a
|
|
291
|
+
// report that simply chose not to ask about authoring.
|
|
292
|
+
expect(prompt).toContain("could not open the skill-authoring flow");
|
|
293
|
+
});
|
|
294
|
+
|
|
250
295
|
test("authors nothing until the user has confirmed", async () => {
|
|
251
296
|
const summary = recordSession(["exporting the sheet"]);
|
|
252
297
|
const { calls, dispatch } = recordingDispatch();
|
|
@@ -605,7 +650,9 @@ describe("watch retrospective", () => {
|
|
|
605
650
|
expect(payload).toBeGreaterThan(opened);
|
|
606
651
|
expect(payload).toBeLessThan(closed);
|
|
607
652
|
// The retrospective's own instructions sit outside it.
|
|
608
|
-
expect(
|
|
653
|
+
expect(
|
|
654
|
+
prompt.indexOf("Load the `skill-management` skill first"),
|
|
655
|
+
).toBeGreaterThan(closed);
|
|
609
656
|
|
|
610
657
|
// One real envelope, so nothing in the recording can pass for a second.
|
|
611
658
|
expect(prompt.match(/<external_content/g)).toHaveLength(1);
|
package/src/watch/watch-retro.ts
CHANGED
|
@@ -76,16 +76,58 @@ const WATCH_RETRO_WAKE_SOURCE = "watch-retro";
|
|
|
76
76
|
* gap between those two is the whole risk of turning a demonstration into a
|
|
77
77
|
* skill. `skill-management` will not scaffold until that step is settled
|
|
78
78
|
* either, so an unasked one stalls the flow it was meant to feed.
|
|
79
|
+
*
|
|
80
|
+
* **The skill loads before the report is written, and nothing follows the
|
|
81
|
+
* report.** This is about where the report lands on screen, not about the
|
|
82
|
+
* order the work happens in. A client renders an assistant turn as its final
|
|
83
|
+
* prose plus the intermediate work that led there, and the web transcript
|
|
84
|
+
* collapses that intermediate half into an "Earlier activity" disclosure that
|
|
85
|
+
* a settled turn shows closed (`finalResponseGroupIndex` in
|
|
86
|
+
* `transcript-message-body.tsx`). Everything before the turn's last non-empty
|
|
87
|
+
* text block is intermediate by that rule, including prose. Writing the report
|
|
88
|
+
* first and loading the skill after it puts a `skill_load` and whatever the
|
|
89
|
+
* model says once the skill is in hand *after* the report, which demotes the
|
|
90
|
+
* report to intermediate work and hides it behind a collapsed row next to the
|
|
91
|
+
* reasoning rows. The user presses stop on a session and is shown a folded-up
|
|
92
|
+
* link where the account of it should be.
|
|
93
|
+
*
|
|
94
|
+
* So the load comes first and the report is last. `skill-management` opens on
|
|
95
|
+
* "Ask before doing anything", so a turn that loads it and then asks is
|
|
96
|
+
* following it rather than jumping the flow, and the report is the alignment
|
|
97
|
+
* its first step calls for. The instruction that nothing follows the report is
|
|
98
|
+
* as load-bearing as the ordering: one trailing line of sign-off is another
|
|
99
|
+
* text block after it, and the report is intermediate again.
|
|
100
|
+
*
|
|
101
|
+
* **The report does not depend on the load succeeding.** `skill-management` is
|
|
102
|
+
* a selector, not a fixed definition: `loadSkillCatalog` lets a managed or
|
|
103
|
+
* workspace skill of the same id replace the bundled one, and
|
|
104
|
+
* `resolveSkillSelector` hands back whichever entry won. So the load can come
|
|
105
|
+
* back as someone else's skill, or as a refusal, and putting it ahead of the
|
|
106
|
+
* report is what makes either one land before the user has been told anything.
|
|
107
|
+
* A refusal is the likelier of the two here: this wake is `clientless`, and an
|
|
108
|
+
* inline-command load with no human present is denied outright
|
|
109
|
+
* (`permissions/checker.ts`, `isDynamicSkillLoadInvocation`). The bundled skill
|
|
110
|
+
* carries no such expansions, so the ordinary path is unaffected, but a shadow
|
|
111
|
+
* that carries them is denied on arrival.
|
|
112
|
+
*
|
|
113
|
+
* Neither outcome is allowed to become the retro. The session was recorded, the
|
|
114
|
+
* timeline is already in this prompt, and the account of it is the one thing the
|
|
115
|
+
* user is owed for having pressed stop; a permission error where that account
|
|
116
|
+
* should be is the same empty thread the `surfaceConversation` guard exists to
|
|
117
|
+
* prevent, just with prose in it. So the instructions write the report either
|
|
118
|
+
* way and say the handoff did not happen, rather than reporting on the load.
|
|
79
119
|
*/
|
|
80
|
-
const RETRO_INSTRUCTIONS = `
|
|
120
|
+
const RETRO_INSTRUCTIONS = `Load the \`skill-management\` skill first, before you write anything, and follow it. Do not author or scaffold a skill until the four points that step names are settled: the report below is the alignment its first step calls for.
|
|
121
|
+
|
|
122
|
+
Write the report below whether or not that load succeeds. If it fails, is refused, or comes back as something other than the skill-management flow, do not report on the load and do not retry it: write the report, and end it with one line saying you could not open the skill-authoring flow, so the user knows the handoff is the part that did not happen.
|
|
123
|
+
|
|
124
|
+
Then write back to the user in two sections, in this order, both as level-2 headings. That report is the last thing you do this turn, and nothing follows it: no sign-off, no note about what you loaded, no further tool call. Their answers come back as an ordinary reply and the flow picks up from there.
|
|
81
125
|
|
|
82
126
|
First, "What I need from you". The questions you cannot answer from the recording, numbered, most consequential first. Each one concrete enough to answer in a sentence, and each one about something you are genuinely guessing at: a value you could not read, a choice whose rule you could not infer, a step you only saw the result of. Always ask what they would say to start this task, in their own words, because the recording cannot tell you that. Ask about the done condition if it is unclear. Always confirm any destructive or irreversible step, even one the recording showed plainly: watching someone do a thing once is not agreement to have it done again unattended, and this is the one place the rule below does not apply. Otherwise do not ask them to confirm something the recording already showed you.
|
|
83
127
|
|
|
84
128
|
Second, "What I saw". Open with one sentence naming the task and what it is for, on its own and not as a list item. Then the steps in order beneath it, one line each and concrete enough to follow, carrying no purpose of their own. This is the record your questions sit on top of, so state it rather than asking about it.
|
|
85
129
|
|
|
86
|
-
Open on the first heading. No preamble, no announcing what you are about to do, no narrating which skills you are loading.
|
|
87
|
-
|
|
88
|
-
Then load the \`skill-management\` skill and follow it, treating the answers to your questions as the alignment its first step calls for. Do not author or scaffold a skill until the four points that step names are settled. Correct your reading against whatever they tell you. If they decide this is not worth keeping, say so and stop.`;
|
|
130
|
+
Open on the first heading. No preamble, no announcing what you are about to do, no narrating which skills you are loading. Correct your reading against whatever they tell you. If they decide this is not worth keeping, say so and stop.`;
|
|
89
131
|
|
|
90
132
|
/**
|
|
91
133
|
* Told to the model whenever the render was bounded, naming the bound that
|
|
@@ -12,6 +12,7 @@ import { join } from "node:path";
|
|
|
12
12
|
import { promisify } from "node:util";
|
|
13
13
|
|
|
14
14
|
import { getConfig } from "../config/loader.js";
|
|
15
|
+
import { isFileUnheld } from "../util/file-use.js";
|
|
15
16
|
import { getLogger } from "../util/logger.js";
|
|
16
17
|
import { Mutex } from "../util/mutex.js";
|
|
17
18
|
import { PromiseGuard } from "../util/promise-guard.js";
|
|
@@ -419,20 +420,9 @@ export class WorkspaceGitService {
|
|
|
419
420
|
return;
|
|
420
421
|
}
|
|
421
422
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
const { stdout } = await execFileAsync("lsof", ["-t", lockPath], {
|
|
426
|
-
timeout: 3000,
|
|
427
|
-
});
|
|
428
|
-
if (stdout.length > 0) {
|
|
429
|
-
log.debug("index.lock held by an active process, skipping removal");
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
|
-
} catch {
|
|
433
|
-
// lsof unavailable or errored — fall through to remove.
|
|
434
|
-
// On platforms without lsof this degrades to unconditional removal,
|
|
435
|
-
// which is the same as the previous behavior.
|
|
423
|
+
if (!(await isFileUnheld(lockPath))) {
|
|
424
|
+
log.debug("Could not prove index.lock is stale, skipping removal");
|
|
425
|
+
return;
|
|
436
426
|
}
|
|
437
427
|
|
|
438
428
|
try {
|