mixdog 1.4.0 → 1.4.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/package.json +3 -2
- package/scripts/build-shipped-history.mjs +115 -33
- package/scripts/check-voice-runtime-inputs.mjs +25 -0
- package/scripts/gate-local.mjs +183 -63
- package/scripts/latest-guard.mjs +21 -9
- package/scripts/lib/gate-scheduler.mjs +138 -32
- package/scripts/lib/gate-timing-reporter.mjs +16 -0
- package/scripts/lib/isolated-test-env.mjs +10 -5
- package/scripts/lib/run-node-tests.mjs +91 -66
- package/scripts/lib/scoped-temp.d.mts +16 -0
- package/scripts/lib/scoped-temp.mjs +124 -0
- package/scripts/lib/stale-temp.mjs +44 -0
- package/scripts/lib/test-failure-records.mjs +14 -1
- package/scripts/lib/test-failure-summary.mjs +16 -4
- package/scripts/lib/test-spawn-ledger.mjs +12 -3
- package/scripts/post-release-check.mjs +1 -6
- package/scripts/pptx-kit-index.mjs +17 -5
- package/scripts/suite-health.mjs +115 -24
- package/scripts/test-data-dir.mjs +10 -7
- package/scripts/test.mjs +9 -14
- package/src/agents/advisor/AGENT.md +4 -4
- package/src/agents/advisor/agent.json +1 -1
- package/src/agents/explore/AGENT.md +22 -0
- package/src/agents/explore/agent.json +6 -0
- package/src/app.mjs +4 -0
- package/src/cli.mjs +7 -0
- package/src/defaults/agents.json +7 -0
- package/src/defaults/shipped-history.json +23 -2
- package/src/defaults/skills/browser-use/SKILL.md +10 -5
- package/src/defaults/skills/code-tidy/SKILL.md +71 -102
- package/src/defaults/skills/code-tidy/references/agent-cleanup.md +66 -114
- package/src/defaults/skills/code-tidy/references/cleanup-report.md +19 -35
- package/src/defaults/skills/code-tidy/references/dead-code.md +3 -9
- package/src/defaults/skills/code-tidy/references/test-suite-slop.md +16 -27
- package/src/defaults/skills/computer-use/SKILL.md +16 -4
- package/src/defaults/skills/goal-management/SKILL.md +1 -1
- package/src/defaults/skills/setup/references/actions.md +12 -6
- package/src/defaults/skills/setup/references/surfaces.md +2 -2
- package/src/defaults/skills/setup-guide/SKILL.md +5 -2
- package/src/defaults/skills/setup-guide/model-recommendations.json +13 -0
- package/src/defaults/skills/skill-creator/SKILL.md +85 -190
- package/src/defaults/skills/skill-creator/references/design.md +70 -162
- package/src/defaults/skills/skill-creator/references/evaluation.md +33 -92
- package/src/defaults/skills/test-suite/SKILL.md +194 -0
- package/src/defaults/skills/test-suite/references/case-management.md +51 -0
- package/src/defaults/skills/test-suite/references/flaky-policy.md +39 -0
- package/src/defaults/skills/test-suite/references/reliability.md +49 -0
- package/src/defaults/skills/test-suite/references/suite-design.md +78 -0
- package/src/defaults/skills/test-suite/scripts/inventory.mjs +335 -0
- package/src/defaults/skills/visualize/SKILL.md +6 -2
- package/src/lib/rules-builder.cjs +35 -42
- package/src/output-styles/common.md +2 -2
- package/src/output-styles/simple.md +1 -1
- package/src/rules/agent/AGENT.md +5 -4
- package/src/rules/lead/LEAD.md +12 -7
- package/src/rules/shared/10-tool-workflow.md +3 -1
- package/src/rules/shared/20-research.md +3 -6
- package/src/rules/shared/40-editing.md +1 -0
- package/src/rules/shared/45-destructive.md +0 -2
- package/src/rules/shared/50-execution.md +2 -0
- package/src/rules/shared/60-verification.md +10 -9
- package/src/rules/shared/70-delivery.md +8 -9
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/abort-link.mjs +5 -1
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +10 -1
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +156 -24
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +13 -0
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +4 -1
- package/src/runtime/agent/orchestrator/cache-break-trace.mjs +2 -0
- package/src/runtime/agent/orchestrator/config.mjs +12 -7
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +8 -8
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +5 -26
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +3 -1
- package/src/runtime/agent/orchestrator/internal-agents.mjs +3 -14
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +122 -12
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +5 -3
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +13 -2
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +152 -58
- package/src/runtime/agent/orchestrator/mcp/client.mjs +51 -18
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +88 -54
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +42 -26
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +54 -8
- package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +37 -31
- package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +31 -21
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +14 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +6 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +40 -0
- package/src/runtime/agent/orchestrator/providers/custom-provider-keys.mjs +34 -0
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +14 -7
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +248 -41
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
- package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
- package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +0 -18
- package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +138 -6
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -10
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/model-cache.mjs +100 -33
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/oauth-secret-fields.fixtures.mjs +45 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +160 -31
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +22 -12
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +49 -22
- package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/registry.mjs +45 -2
- package/src/runtime/agent/orchestrator/providers/vertex-ai-auth.mjs +622 -0
- package/src/runtime/agent/orchestrator/providers/vertex-ai-maas.mjs +185 -0
- package/src/runtime/agent/orchestrator/providers/vertex-ai-models.mjs +445 -0
- package/src/runtime/agent/orchestrator/providers/vertex-ai.mjs +567 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +41 -13
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +8 -6
- package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +7 -3
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +6 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +14 -5
- package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -0
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +30 -5
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +171 -37
- package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +102 -12
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
- package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +160 -36
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +164 -23
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +139 -19
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +100 -22
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -6
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +33 -3
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +9 -2
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +23 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +23 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +39 -21
- package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +13 -8
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +50 -28
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +30 -13
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +63 -6
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +35 -13
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +24 -14
- package/src/runtime/agent/orchestrator/session/manager/session-scratch-environment.mjs +70 -0
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +0 -3
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +10 -6
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +56 -29
- package/src/runtime/agent/orchestrator/session/manager/turn-reap.mjs +125 -0
- package/src/runtime/agent/orchestrator/session/mixy-name.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +30 -7
- package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +37 -8
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +252 -39
- package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +163 -64
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +164 -4
- package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +142 -18
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +61 -2
- package/src/runtime/agent/orchestrator/stall-policy.mjs +23 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +46 -20
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +67 -31
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +25 -13
- package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +58 -20
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +20 -15
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +96 -20
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +59 -10
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
- package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.mjs +99 -2
- package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +103 -13
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +49 -15
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +45 -27
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +34 -10
- package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +28 -4
- package/src/runtime/agent/orchestrator/tools/lib/wait-until-fixture.mjs +26 -0
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -2
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
- package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
- package/src/runtime/channels/lib/whisper-port.mjs +29 -11
- package/src/runtime/channels/lib/whisper-server.mjs +10 -1
- package/src/runtime/computer-bridge/action-schema.mjs +2 -2
- package/src/runtime/computer-bridge/core-actions.mjs +2 -2
- package/src/runtime/computer-bridge/error-recovery.mjs +5 -4
- package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
- package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -2
- package/src/runtime/connectors/connector-accounts-mcp.mjs +236 -103
- package/src/runtime/connectors/gmail-app-password.mjs +3 -3
- package/src/runtime/explore/explorer.mjs +93 -0
- package/src/runtime/explore/tool-defs.mjs +35 -0
- package/src/runtime/explore/tool.mjs +123 -0
- package/src/runtime/local-provider/server-process.mjs +2 -1
- package/src/runtime/media/adapters/gemini-video.mjs +4 -1
- package/src/runtime/media/adapters/xai-media.mjs +2 -1
- package/src/runtime/memory/index.mjs +4 -0
- package/src/runtime/memory/lib/embedding-provider.mjs +4 -1
- package/src/runtime/memory/lib/embedding-worker.mjs +10 -3
- package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
- package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
- package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
- package/src/runtime/office/design/design-art-direction.mjs +29 -27
- package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
- package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
- package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
- package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
- package/src/runtime/office/quality/assurance-structure-docx.mjs +4 -2
- package/src/runtime/office/quality/design-review.mjs +1 -5
- package/src/runtime/office/quality/document-brief.mjs +1 -5
- package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
- package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
- package/src/runtime/shared/agent-route-config.mjs +17 -2
- package/src/runtime/shared/atomic-file.mjs +103 -2
- package/src/runtime/shared/background-tasks.mjs +15 -6
- package/src/runtime/shared/bot-owned-work.mjs +56 -0
- package/src/runtime/shared/connector-accounts.mjs +5 -5
- package/src/runtime/shared/connector-catalog.d.mts +11 -4
- package/src/runtime/shared/connector-catalog.mjs +60 -52
- package/src/runtime/shared/custom-provider-keys.mjs +20 -0
- package/src/runtime/shared/defaults-separation.mjs +1 -4
- package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
- package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
- package/src/runtime/shared/file-has-data.mjs +26 -2
- package/src/runtime/shared/file-lock.mjs +13 -15
- package/src/runtime/shared/git-launch.d.mts +9 -0
- package/src/runtime/shared/git-launch.mjs +213 -0
- package/src/runtime/shared/git-runner-worker.mjs +2 -1
- package/src/runtime/shared/handoff-artifacts.d.mts +2 -1
- package/src/runtime/shared/handoff-artifacts.mjs +18 -8
- package/src/runtime/shared/idle-auto-clear-gate.mjs +29 -7
- package/src/runtime/shared/llm/cost.mjs +11 -2
- package/src/runtime/shared/llm/http-agent.mjs +313 -29
- package/src/runtime/shared/llm/model-catalog-projection.mjs +3 -2
- package/src/runtime/shared/llm/model-catalog.mjs +46 -6
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +3 -0
- package/src/runtime/shared/llm/system-proxy.mjs +452 -0
- package/src/runtime/shared/loopback-listener.mjs +25 -2
- package/src/runtime/shared/model-route-chain.d.mts +7 -2
- package/src/runtime/shared/model-route-chain.mjs +22 -4
- package/src/runtime/shared/open-url.mjs +10 -0
- package/src/runtime/shared/provider-accounts.mjs +52 -2
- package/src/runtime/shared/session-activity-signal.mjs +14 -0
- package/src/runtime/shared/session-scratch.mjs +37 -0
- package/src/runtime/shared/staged-install-worker.mjs +5 -0
- package/src/runtime/shared/staged-update.mjs +47 -2
- package/src/runtime/shared/tool-card-model/agent-surface.mjs +1 -3
- package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
- package/src/runtime/shared/tool-result-summary.mjs +1 -1
- package/src/runtime/shared/tool-surface.mjs +113 -36
- package/src/runtime/shared/tool-work-units.mjs +6 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
- package/src/runtime/shared/turn-worktree-snapshot.mjs +414 -54
- package/src/runtime/shared/worktrees/activity-gate.mjs +215 -318
- package/src/runtime/shared/worktrees/activity-ledger-worker.mjs +56 -0
- package/src/runtime/shared/worktrees/activity-ledger.mjs +391 -0
- package/src/runtime/shared/worktrees/branch-management.mjs +9 -3
- package/src/runtime/shared/worktrees/contract.mjs +4 -1
- package/src/runtime/shared/worktrees/git.mjs +72 -0
- package/src/runtime/shared/worktrees/manager.mjs +285 -168
- package/src/runtime/shared/worktrees/test-support.mjs +6 -10
- package/src/runtime/tidy/install.mjs +95 -15
- package/src/runtime/tidy/languages.mjs +17 -15
- package/src/runtime/tidy/process.mjs +7 -4
- package/src/runtime/tidy/resolve.mjs +19 -16
- package/src/runtime/tidy/runners/biome.mjs +3 -3
- package/src/runtime/tidy/tool.mjs +1 -1
- package/src/runtime/tidy/worktree.mjs +10 -6
- package/src/runtime/visualize-bridge/client.mjs +6 -1
- package/src/runtime/visualize-bridge/tool-defs.mjs +7 -2
- package/src/runtime/web-search/index.mjs +1 -1
- package/src/session-runtime/agent-home-delivery.mjs +41 -0
- package/src/session-runtime/agent-task-sessions.mjs +458 -312
- package/src/session-runtime/agent-tool-routing.mjs +8 -41
- package/src/session-runtime/boot/tools.mjs +7 -1
- package/src/session-runtime/bot-status.mjs +107 -101
- package/src/session-runtime/bot-target.mjs +38 -0
- package/src/session-runtime/channel-config-api.mjs +6 -1
- package/src/session-runtime/connector-merge-migration.mjs +18 -15
- package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
- package/src/session-runtime/conversation-import/discovery.mjs +401 -36
- package/src/session-runtime/conversation-import/service.mjs +35 -8
- package/src/session-runtime/handoff-attachments.mjs +82 -31
- package/src/session-runtime/held-requests.mjs +3 -1
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +12 -13
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +1 -0
- package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -0
- package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +8 -0
- package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +145 -112
- package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
- package/src/session-runtime/lifecycle/session-catalog.mjs +9 -1
- package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -1
- package/src/session-runtime/lifecycle/teardown.mjs +1 -1
- package/src/session-runtime/mcp-glue/server-catalog.mjs +16 -10
- package/src/session-runtime/mcp-glue/server-input.mjs +4 -1
- package/src/session-runtime/mixy-heartbeat.mjs +1 -1
- package/src/session-runtime/model-route/set-route.mjs +11 -2
- package/src/session-runtime/provider-auth/login.mjs +2 -2
- package/src/session-runtime/provider-catalog-cache.mjs +82 -36
- package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
- package/src/session-runtime/provider-models.mjs +4 -2
- package/src/session-runtime/resource-mcp-api.mjs +98 -35
- package/src/session-runtime/resource-mcp-connector-accounts.mjs +44 -31
- package/src/session-runtime/resource-mcp-secrets.mjs +28 -11
- package/src/session-runtime/resource-mcp-servers.mjs +5 -4
- package/src/session-runtime/resource-recall-api.mjs +6 -1
- package/src/session-runtime/runtime-feature-gates.mjs +5 -1
- package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
- package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
- package/src/session-runtime/services/agent-tool/render.mjs +71 -17
- package/src/session-runtime/services/agent-tool/send-flow.mjs +10 -2
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +8 -3
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +4 -1
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +6 -2
- package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
- package/src/session-runtime/services/agent-watchdog-registry.mjs +32 -4
- package/src/session-runtime/services/channel-worker/daemon-spawn.mjs +2 -0
- package/src/session-runtime/services/daemon-candidate.mjs +4 -1
- package/src/session-runtime/services/memory-runtime-proxy/daemon-spawn.mjs +4 -0
- package/src/session-runtime/services/plugin-admin.mjs +10 -6
- package/src/session-runtime/services/provider-admin.mjs +283 -11
- package/src/session-runtime/session-turn-api.mjs +2 -0
- package/src/session-runtime/settings-builtin-tools-api.mjs +4 -1
- package/src/session-runtime/settings-system-api.mjs +1 -1
- package/src/session-runtime/setup-tool/agent-settings-actions.mjs +13 -3
- package/src/session-runtime/setup-tool/connector-widen-approval.mjs +96 -0
- package/src/session-runtime/setup-tool/executor.mjs +6 -2
- package/src/session-runtime/setup-tool/tool-defs.mjs +2 -1
- package/src/session-runtime/tool-policy/policy-stage.mjs +1 -0
- package/src/session-runtime/turn/agent-ops.mjs +23 -0
- package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +49 -11
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +25 -5
- package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
- package/src/session-runtime/workspace/activity-guards.mjs +176 -0
- package/src/session-runtime/workspace/session-view.mjs +149 -0
- package/src/session-runtime/workspace/side-checkouts.mjs +203 -0
- package/src/session-runtime/workspace/transition-runner.mjs +188 -0
- package/src/session-runtime/workspace/transitions.mjs +108 -1041
- package/src/session-runtime/workspace/workspace-merge.mjs +132 -0
- package/src/session-runtime/workspace/worktree-intents.mjs +242 -0
- package/src/session-runtime/workspace/worktree-maintenance.mjs +80 -0
- package/src/session-runtime/workspace/worktree-setup.mjs +85 -0
- package/src/session-runtime/workspace/worktree-tool.mjs +73 -56
- package/src/standalone/daemon-desktop-runtime.mjs +2 -0
- package/src/standalone/daemon-session-control.mjs +169 -138
- package/src/standalone/daemon.mjs +7 -0
- package/src/standalone/desktop-service-registry.mjs +9 -0
- package/src/standalone/local-session-runtime.mjs +5 -1
- package/src/standalone/session-client/daemon-unavailable.mjs +3 -0
- package/src/standalone/session-client.mjs +5 -1
- package/src/standalone/session-frame-stream.mjs +83 -11
- package/src/standalone/session-protocol.mjs +4 -0
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
- package/src/standalone/session-service/agent-tree.mjs +45 -9
- package/src/standalone/session-service/projection/transcript-window.mjs +700 -64
- package/src/standalone/session-service/session-calls/session-action.mjs +10 -3
- package/src/standalone/session-service/session-calls/session-views.mjs +160 -104
- package/src/standalone/session-service/stored-reader.mjs +127 -40
- package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
- package/src/tui/App.jsx +154 -164
- package/src/tui/app/app-view/floating-panel.jsx +10 -4
- package/src/tui/app/app-view/text-entry-labels.mjs +1 -0
- package/src/tui/app/clipboard.mjs +21 -10
- package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
- package/src/tui/app/model-picker/route-selection.mjs +5 -6
- package/src/tui/app/prompt-handlers/use-prompt-history-nav.mjs +1 -1
- package/src/tui/app/prompt-handlers/use-prompt-interrupt.mjs +3 -0
- package/src/tui/app/prompt-submit/provider-prompts.mjs +3 -1
- package/src/tui/app/provider-setup/api-key-actions.mjs +31 -1
- package/src/tui/app/provider-setup/oauth-actions.mjs +58 -21
- package/src/tui/app/provider-setup/provider-items.mjs +8 -2
- package/src/tui/app/provider-setup-picker.mjs +29 -18
- package/src/tui/app/shell-layout/row-budgets.mjs +12 -10
- package/src/tui/app/slash-commands.mjs +1 -0
- package/src/tui/app/slash-dispatch/session-commands.mjs +46 -0
- package/src/tui/app/slash-dispatch.mjs +1 -1
- package/src/tui/app/use-btw-panel.mjs +29 -0
- package/src/tui/app/use-context-panel-refresh.mjs +31 -0
- package/src/tui/app/use-model-catalog-cache.mjs +13 -10
- package/src/tui/app/use-mouse-input.mjs +11 -0
- package/src/tui/app/use-onboarding-start.mjs +8 -3
- package/src/tui/app/use-panel-transition.mjs +10 -1
- package/src/tui/app/use-pasted-buffers.mjs +28 -20
- package/src/tui/app/use-prompt-draft-flow.mjs +7 -0
- package/src/tui/app/use-prompt-layout-rows.mjs +3 -3
- package/src/tui/app/use-prompt-queue-history.mjs +3 -2
- package/src/tui/app/use-selection-focus.mjs +11 -2
- package/src/tui/app/use-selection-geometry.mjs +18 -9
- package/src/tui/app/use-selection-paint.mjs +4 -4
- package/src/tui/app/use-selection-stitch.mjs +3 -3
- package/src/tui/app/use-slash-palette.mjs +62 -0
- package/src/tui/app/use-terminal-chrome.mjs +2 -2
- package/src/tui/app/use-transcript-activity.mjs +47 -32
- package/src/tui/app/use-transcript-follow.mjs +12 -4
- package/src/tui/app/use-transcript-scroll.mjs +46 -17
- package/src/tui/app/use-transcript-swap-reset.mjs +27 -0
- package/src/tui/app/use-transcript-window/geometry.mjs +19 -5
- package/src/tui/app/use-transcript-window/instance-state.mjs +4 -0
- package/src/tui/app/use-transcript-window/selection-shift.mjs +5 -1
- package/src/tui/app/use-transcript-window.mjs +21 -11
- package/src/tui/app/use-tui-ready.mjs +18 -0
- package/src/tui/app/use-welcome-prompt-hint.mjs +5 -0
- package/src/tui/app/use-workflow-tab-cycle.mjs +5 -3
- package/src/tui/components/Picker.jsx +27 -3
- package/src/tui/components/QuestionPanel.jsx +13 -4
- package/src/tui/components/SignInUrl.jsx +68 -0
- package/src/tui/components/TextEntryPanel.jsx +76 -46
- package/src/tui/components/sign-in-url-layout.mjs +24 -0
- package/src/tui/dist/index.mjs +1963 -1361
- package/src/tui/session/goal-turn-state.mjs +6 -2
- package/src/tui/session/session-api/controls.mjs +4 -0
- package/src/tui/session/session-api/extensions.mjs +8 -0
- package/src/tui/session/session-api/intake/abort.mjs +2 -1
- package/src/tui/session/session-api/intake.mjs +2 -0
- package/src/tui/session/session-api/integrations.mjs +2 -2
- package/src/tui/session/session-api/routes-agent-definitions.mjs +1 -0
- package/src/tui/session/session-api/settings.mjs +2 -2
- package/src/tui/session/session-flow/queue/take-entries.mjs +11 -0
- package/src/tui/session/transcript-spill/spill-writer.mjs +15 -0
- package/src/tui/session/transcript-spill.mjs +92 -4
- package/src/tui/session/transcript-store.mjs +2 -0
- package/src/ui/statusline-agents.mjs +1 -3
- package/src/defaults/skills/skill-creator/references/source-review.md +0 -94
|
@@ -40,7 +40,6 @@ const COPY = {
|
|
|
40
40
|
failureTitle: '로그인 실패',
|
|
41
41
|
failureBody: 'Mixdog로 돌아가 다시 시도해 주세요.',
|
|
42
42
|
reason: '사유',
|
|
43
|
-
close: '닫기',
|
|
44
43
|
},
|
|
45
44
|
en: {
|
|
46
45
|
successTitle: 'Sign-in complete',
|
|
@@ -48,7 +47,6 @@ const COPY = {
|
|
|
48
47
|
failureTitle: 'Sign-in failed',
|
|
49
48
|
failureBody: 'Return to Mixdog and try again.',
|
|
50
49
|
reason: 'Reason',
|
|
51
|
-
close: 'Close',
|
|
52
50
|
},
|
|
53
51
|
};
|
|
54
52
|
|
|
@@ -69,7 +67,6 @@ function renderPage({ kind, providerName, reason, lang }) {
|
|
|
69
67
|
const reasonBlock = reason
|
|
70
68
|
? `<div class="reason"><span>${langs.map((code) => COPY[code].reason).join(' / ')}</span>${escapeHtml(reason)}</div>`
|
|
71
69
|
: '';
|
|
72
|
-
const close = langs.map((code) => COPY[code].close).join(' · ');
|
|
73
70
|
const documentTitle = escapeHtml(
|
|
74
71
|
`${langs.map((code) => COPY[code][kind === 'success' ? 'successTitle' : 'failureTitle']).join(' / ')} · Mixdog`
|
|
75
72
|
);
|
|
@@ -130,20 +127,6 @@ function renderPage({ kind, providerName, reason, lang }) {
|
|
|
130
127
|
word-break: break-word;
|
|
131
128
|
}
|
|
132
129
|
.reason span { display: block; margin-bottom: 4px; color: var(--muted); font-family: inherit; font-size: 12px; }
|
|
133
|
-
button {
|
|
134
|
-
margin-top: 24px;
|
|
135
|
-
min-width: 120px;
|
|
136
|
-
height: 40px;
|
|
137
|
-
padding: 0 20px;
|
|
138
|
-
border: 1px solid var(--line);
|
|
139
|
-
border-radius: 10px;
|
|
140
|
-
background: transparent;
|
|
141
|
-
color: var(--fg);
|
|
142
|
-
font: inherit;
|
|
143
|
-
font-size: 14px;
|
|
144
|
-
cursor: pointer;
|
|
145
|
-
}
|
|
146
|
-
button:hover { border-color: var(--muted); }
|
|
147
130
|
</style>
|
|
148
131
|
</head>
|
|
149
132
|
<body>
|
|
@@ -151,7 +134,6 @@ function renderPage({ kind, providerName, reason, lang }) {
|
|
|
151
134
|
<div class="logo">${LOGO_SVG}</div>
|
|
152
135
|
${blocks}
|
|
153
136
|
${reasonBlock}
|
|
154
|
-
<button type="button" onclick="window.close()">${escapeHtml(close)}</button>
|
|
155
137
|
</main>
|
|
156
138
|
</body>
|
|
157
139
|
</html>`;
|
|
@@ -140,14 +140,28 @@ export function isDefinitiveOAuthFailure(text, status = 0) {
|
|
|
140
140
|
return s === 401;
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
// Fields whose string values are secrets, snake_case or camelCase. `code` is
|
|
144
|
+
// not one: it is also every API's error/status code. The authorization code
|
|
145
|
+
// is redacted by value — exchange paths pass it in `secretValues`.
|
|
146
|
+
const SECRET_FIELD =
|
|
147
|
+
'(?:access_?token|refresh_?token|id_?token|client_?secret|code_?verifier|code_?challenge|device_?code|api_?key|token|key|secret|password|assertion)';
|
|
148
|
+
const SECRET_KEY_RE = new RegExp(`^${SECRET_FIELD}$`, 'i');
|
|
149
|
+
// Fallback for text that is not JSON. `"field": value` — a string (possibly
|
|
150
|
+
// cut off) or a bare non-numeric value in malformed JSON.
|
|
151
|
+
const JSON_SECRET_RE = new RegExp(
|
|
152
|
+
`"(${SECRET_FIELD})"\\s*:\\s*(?:"(?:[^"\\\\]|\\\\.)*"?|(?!-?\\d|true\\b|false\\b|null\\b)[^,}\\]\\s]+)`,
|
|
153
|
+
'gi'
|
|
154
|
+
);
|
|
155
|
+
// Form-encoded or query `field=value`.
|
|
156
|
+
const FORM_SECRET_RE = new RegExp(`(^|[^A-Za-z0-9_])(${SECRET_FIELD})=[^&\\s"']*`, 'gi');
|
|
157
|
+
const JWT_RE = /\beyJ[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}(?:\.[A-Za-z0-9_-]*)?/g;
|
|
158
|
+
|
|
159
|
+
// Bearer/JWT/Anthropic tokens and the given secret values, anywhere in `text`.
|
|
160
|
+
function scrubTokenText(text, secretValues) {
|
|
161
|
+
let scrubbed = text
|
|
145
162
|
.replace(/Bearer [A-Za-z0-9._-]+/gi, 'Bearer [REDACTED]')
|
|
146
163
|
.replace(/sk-ant-[A-Za-z0-9._-]+/g, '[REDACTED]')
|
|
147
|
-
.replace(
|
|
148
|
-
/"(accessToken|refreshToken|access_token|refresh_token|code|key)"\s*:\s*"[^"]+"/g,
|
|
149
|
-
(_match, key) => `"${key}":"[REDACTED]"`
|
|
150
|
-
);
|
|
164
|
+
.replace(JWT_RE, '[REDACTED]');
|
|
151
165
|
for (const secret of secretValues) {
|
|
152
166
|
if (typeof secret === 'string' && secret) {
|
|
153
167
|
scrubbed = scrubbed.split(secret).join('[REDACTED]');
|
|
@@ -155,3 +169,121 @@ export function scrubOAuthSecrets(text, secretValues = []) {
|
|
|
155
169
|
}
|
|
156
170
|
return scrubbed;
|
|
157
171
|
}
|
|
172
|
+
|
|
173
|
+
// A JSON object or array is redacted on its parsed form, so escaped key names
|
|
174
|
+
// (`client\u005fsecret`) cannot slip past a pattern. Numbers keep their exact
|
|
175
|
+
// source text; an untouched body is returned as it came. Null when `text` is
|
|
176
|
+
// not a JSON object or array.
|
|
177
|
+
function scrubJsonSecrets(text, secretValues) {
|
|
178
|
+
let parsed;
|
|
179
|
+
try {
|
|
180
|
+
parsed = JSON.parse(text, (_key, value, context) =>
|
|
181
|
+
typeof value === 'number' ? JSON.rawJSON(context.source) : value
|
|
182
|
+
);
|
|
183
|
+
} catch {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
if (!parsed || typeof parsed !== 'object' || JSON.isRawJSON(parsed)) return null;
|
|
187
|
+
let changed = false;
|
|
188
|
+
// Each decoded string (keys too) gets the full redaction: it may hold
|
|
189
|
+
// form-encoded fields, a query string or JSON of its own.
|
|
190
|
+
const scrubString = (value) => {
|
|
191
|
+
const scrubbed = scrubOAuthSecrets(value, secretValues);
|
|
192
|
+
if (scrubbed !== value) changed = true;
|
|
193
|
+
return scrubbed;
|
|
194
|
+
};
|
|
195
|
+
const walk = (value) => {
|
|
196
|
+
if (typeof value === 'string') return scrubString(value);
|
|
197
|
+
if (!value || typeof value !== 'object' || JSON.isRawJSON(value)) return value;
|
|
198
|
+
if (Array.isArray(value)) return value.map(walk);
|
|
199
|
+
return Object.fromEntries(
|
|
200
|
+
Object.entries(value).map(([key, inner]) => {
|
|
201
|
+
if (SECRET_KEY_RE.test(key) && typeof inner === 'string') {
|
|
202
|
+
changed = true;
|
|
203
|
+
return [key, '[REDACTED]'];
|
|
204
|
+
}
|
|
205
|
+
return [scrubString(key), walk(inner)];
|
|
206
|
+
})
|
|
207
|
+
);
|
|
208
|
+
};
|
|
209
|
+
const scrubbed = walk(parsed);
|
|
210
|
+
return changed ? JSON.stringify(scrubbed) : text;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** Redact OAuth secrets from a body or message: secret fields (decoded JSON
|
|
214
|
+
* keys, or JSON-like and form-encoded text), bearer/JWT/Anthropic tokens,
|
|
215
|
+
* and the given values. Numeric and status codes are kept. */
|
|
216
|
+
export function scrubOAuthSecrets(text, secretValues = []) {
|
|
217
|
+
const raw = String(text || '');
|
|
218
|
+
const json = scrubJsonSecrets(raw, secretValues);
|
|
219
|
+
if (json !== null) return json;
|
|
220
|
+
return scrubTokenText(
|
|
221
|
+
raw
|
|
222
|
+
.replace(JSON_SECRET_RE, (_match, key) => `"${key}":"[REDACTED]"`)
|
|
223
|
+
.replace(FORM_SECRET_RE, (_match, lead, key) => `${lead}${key}=[REDACTED]`),
|
|
224
|
+
secretValues
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// --- Token-exchange failures a sign-in page may show ---
|
|
229
|
+
|
|
230
|
+
export const OAUTH_NETWORK_HINT = 'Check your proxy/VPN: Mixdog uses the system proxy, or HTTPS_PROXY.';
|
|
231
|
+
const NETWORK_CODE = /^(?:E[A-Z0-9]+|UND_ERR_[A-Z0-9_]+|ERR_[A-Z0-9_]+)$/;
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* The transport cause of a fetch that got no answer: its error code
|
|
235
|
+
* (ECONNRESET, UND_ERR_CONNECT_TIMEOUT, ENOTFOUND…) or "timed out". Only codes,
|
|
236
|
+
* never messages, which may quote anything. Null for a cancel or a non-network
|
|
237
|
+
* error.
|
|
238
|
+
*/
|
|
239
|
+
export function oauthNetworkCause(error) {
|
|
240
|
+
if (!error || typeof error !== 'object' || error.name === 'AbortError') return null;
|
|
241
|
+
if (error.name === 'TimeoutError') return 'timed out';
|
|
242
|
+
for (let e = error, depth = 0; e && typeof e === 'object' && depth < 5; e = e.cause, depth += 1) {
|
|
243
|
+
if (typeof e.code === 'string' && NETWORK_CODE.test(e.code)) return e.code;
|
|
244
|
+
}
|
|
245
|
+
return error.name === 'TypeError' ? 'network error' : null;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Errors whose message was built only from codes, statuses and redacted
|
|
249
|
+
// bodies: the only ones a failure page quotes.
|
|
250
|
+
const publicErrors = new WeakSet();
|
|
251
|
+
|
|
252
|
+
/** Mark `error` as safe to show on the sign-in failure page. */
|
|
253
|
+
export function oauthPublicError(error) {
|
|
254
|
+
publicErrors.add(error);
|
|
255
|
+
return error;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** A fetch failure as `<operation> request failed (<cause>). <hint>`; other errors unchanged. */
|
|
259
|
+
export function oauthRequestError(operation, error) {
|
|
260
|
+
const cause = oauthNetworkCause(error);
|
|
261
|
+
return cause ? oauthPublicError(new Error(`${operation} request failed (${cause}). ${OAUTH_NETWORK_HINT}`)) : error;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** A token endpoint's body for an error message: secrets redacted, whitespace folded, shortened. */
|
|
265
|
+
export function oauthBodySummary(text, secretValues = []) {
|
|
266
|
+
return scrubOAuthSecrets(text, secretValues).replace(/\s+/g, ' ').trim().slice(0, 500);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** A token endpoint's HTTP failure as `<operation> <status>: <redacted body>`. */
|
|
270
|
+
export function oauthHttpError(operation, status, text, secretValues = []) {
|
|
271
|
+
const summary = oauthBodySummary(text, secretValues);
|
|
272
|
+
return oauthPublicError(new Error(`${operation} ${status}${summary ? `: ${summary}` : ''}`));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** The token response's JSON. A body that does not parse is named by its
|
|
276
|
+
* status only: the parser's message quotes the body, tokens included. */
|
|
277
|
+
export async function oauthResponseJson(response, operation) {
|
|
278
|
+
try {
|
|
279
|
+
return await response.json();
|
|
280
|
+
} catch (error) {
|
|
281
|
+
if (error?.name !== 'SyntaxError') throw oauthRequestError(operation, error);
|
|
282
|
+
throw oauthPublicError(new Error(`${operation} returned a malformed response (HTTP ${response.status})`));
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** The failure page reason: the summary, plus the error's message only when it is page-safe. */
|
|
287
|
+
export function oauthFailureReason(summary, error) {
|
|
288
|
+
return error && publicErrors.has(error) ? `${summary}\n${error.message}` : summary;
|
|
289
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// X25519 public key we send in the authorize URL; the reply (loopback redirect
|
|
3
3
|
// or a pasted code) carries `u` = ephemeralPub(32) | nonce(12) | ciphertext | tag(16).
|
|
4
4
|
import { createServer } from 'node:http';
|
|
5
|
+
import { listenOn } from './lib/listen-on.mjs';
|
|
5
6
|
import { lookup } from 'node:dns/promises';
|
|
6
7
|
import { createDecipheriv, createHash, createPublicKey, diffieHellman, generateKeyPairSync } from 'node:crypto';
|
|
7
8
|
import { openLoginUrl } from './lib/open-login-url.mjs';
|
|
@@ -112,16 +113,6 @@ async function loopbackAddresses() {
|
|
|
112
113
|
return ['127.0.0.1'];
|
|
113
114
|
}
|
|
114
115
|
|
|
115
|
-
function listenOn(server, port, host) {
|
|
116
|
-
return new Promise((resolve, reject) => {
|
|
117
|
-
server.once('error', reject);
|
|
118
|
-
server.listen(port, host, () => {
|
|
119
|
-
server.off('error', reject);
|
|
120
|
-
resolve();
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
116
|
export async function beginOAuthLogin({
|
|
126
117
|
openBrowser = true,
|
|
127
118
|
timeoutMs = LOGIN_TIMEOUT_MS,
|
|
@@ -195,7 +195,7 @@ export class MimoOAuthProvider {
|
|
|
195
195
|
// publishes nothing. `_listRows` revalidates `seq` and the key before
|
|
196
196
|
// touching shared state, since enrichment runs after this returns.
|
|
197
197
|
let published = true;
|
|
198
|
-
if (items.length) published = publish(items.map(rosterRow));
|
|
198
|
+
if (items.length) published = await publish(items.map(rosterRow));
|
|
199
199
|
else items = seedModelItems();
|
|
200
200
|
return { items, published, seq, apiKey };
|
|
201
201
|
}
|
|
@@ -1,7 +1,31 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { getPluginData } from '../config.mjs';
|
|
4
|
-
import {
|
|
4
|
+
import { updateJsonAtomic, writeJsonAtomicAsync } from '../../../shared/atomic-file.mjs';
|
|
5
|
+
|
|
6
|
+
// Disk writes never block the event loop: they run on the async atomic-file
|
|
7
|
+
// primitives, one at a time per cache file, in the order they were requested
|
|
8
|
+
// (a later write is never overtaken by an earlier one finishing late). Every
|
|
9
|
+
// instance of one file shares these module-level tables, since several
|
|
10
|
+
// instances and several providers can address the same path.
|
|
11
|
+
const writeTails = new Map(); // path → tail of that file's write chain
|
|
12
|
+
// path → JSON text of the newest save() whose write has not finished. Sync
|
|
13
|
+
// readers see it, so a save is visible at once although its disk write is not.
|
|
14
|
+
const unsettledSaves = new Map();
|
|
15
|
+
|
|
16
|
+
function enqueueWrite(p, task) {
|
|
17
|
+
const run = (writeTails.get(p) ?? Promise.resolve()).then(task);
|
|
18
|
+
writeTails.set(p, run);
|
|
19
|
+
run.then(() => {
|
|
20
|
+
if (writeTails.get(p) === run) writeTails.delete(p);
|
|
21
|
+
});
|
|
22
|
+
return run;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Resolves once every cache write requested so far has settled (shutdown, tests). */
|
|
26
|
+
export async function flushModelCacheWrites() {
|
|
27
|
+
while (writeTails.size) await Promise.all([...writeTails.values()]);
|
|
28
|
+
}
|
|
5
29
|
|
|
6
30
|
// Shared model-catalog disk-cache CRUD, parameterized over the small bits each
|
|
7
31
|
// provider varies: cache file name, TTL, an optional schema-version gate, and an
|
|
@@ -15,9 +39,10 @@ export function makeModelCache({ fileName, ttlMs, version = null, onSave = null
|
|
|
15
39
|
// allowStale skips the TTL only; the schema-version gate still applies.
|
|
16
40
|
function loadEntrySync({ allowStale = false } = {}) {
|
|
17
41
|
const p = path();
|
|
18
|
-
|
|
42
|
+
const unsettled = unsettledSaves.get(p);
|
|
43
|
+
if (unsettled === undefined && !existsSync(p)) return null;
|
|
19
44
|
try {
|
|
20
|
-
const raw = JSON.parse(readFileSync(p, 'utf-8'));
|
|
45
|
+
const raw = JSON.parse(unsettled ?? readFileSync(p, 'utf-8'));
|
|
21
46
|
if (version != null && raw?.version !== version) return null;
|
|
22
47
|
if (!raw?.fetchedAt || !Array.isArray(raw.models)) return null;
|
|
23
48
|
if (!allowStale && Date.now() - raw.fetchedAt > ttlMs) return null;
|
|
@@ -31,56 +56,98 @@ export function makeModelCache({ fileName, ttlMs, version = null, onSave = null
|
|
|
31
56
|
return loadEntrySync(options)?.models ?? null;
|
|
32
57
|
}
|
|
33
58
|
|
|
59
|
+
// The in-memory state (sync readers, onSave mirror) updates at once; the
|
|
60
|
+
// returned promise settles, never rejecting, when the disk write has.
|
|
34
61
|
function save(models, extra = {}) {
|
|
62
|
+
const p = path();
|
|
63
|
+
// The value as of this call: later mutation of `models` must not change
|
|
64
|
+
// what lands on disk (the old synchronous write captured it the same way).
|
|
65
|
+
let text;
|
|
66
|
+
let record;
|
|
67
|
+
try {
|
|
68
|
+
text = JSON.stringify({
|
|
69
|
+
...extra,
|
|
70
|
+
...(version != null ? { version } : {}),
|
|
71
|
+
fetchedAt: Date.now(),
|
|
72
|
+
models,
|
|
73
|
+
});
|
|
74
|
+
record = JSON.parse(text);
|
|
75
|
+
unsettledSaves.set(p, text);
|
|
76
|
+
} catch {
|
|
77
|
+
return Promise.resolve();
|
|
78
|
+
}
|
|
35
79
|
try {
|
|
36
|
-
writeJsonAtomicSync(
|
|
37
|
-
path(),
|
|
38
|
-
{
|
|
39
|
-
...extra,
|
|
40
|
-
...(version != null ? { version } : {}),
|
|
41
|
-
fetchedAt: Date.now(),
|
|
42
|
-
models,
|
|
43
|
-
},
|
|
44
|
-
{ lock: true, fsyncDir: true }
|
|
45
|
-
);
|
|
46
80
|
if (onSave) onSave(models);
|
|
47
81
|
} catch {
|
|
48
82
|
/* best-effort */
|
|
49
83
|
}
|
|
84
|
+
return enqueueWrite(p, async () => {
|
|
85
|
+
try {
|
|
86
|
+
await writeJsonAtomicAsync(p, record, { lock: true, fsyncDir: true });
|
|
87
|
+
} catch {
|
|
88
|
+
/* best-effort */
|
|
89
|
+
} finally {
|
|
90
|
+
if (unsettledSaves.get(p) === text) unsettledSaves.delete(p);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
50
93
|
}
|
|
51
94
|
|
|
52
95
|
// A discovery request claims a generation when it starts. Its roster is
|
|
53
96
|
// written only if no newer request started meanwhile — in this process
|
|
54
97
|
// (generation) or another one (`requestedAt` checked under the file lock) —
|
|
55
98
|
// and `isCurrent()` (the credential it was fetched with is still active) holds.
|
|
99
|
+
// The returned publish() resolves, never rejecting, to whether the roster was
|
|
100
|
+
// written; the cross-process check runs under the file lock, so the answer
|
|
101
|
+
// is only known once the queued write has run.
|
|
56
102
|
let generation = 0;
|
|
57
103
|
function beginSave(isCurrent) {
|
|
58
104
|
const claimed = ++generation;
|
|
59
105
|
const requestedAt = Date.now();
|
|
106
|
+
const stillWanted = () => claimed === generation && isCurrent();
|
|
60
107
|
return (models, extra = {}) => {
|
|
61
|
-
if (
|
|
62
|
-
|
|
108
|
+
if (!stillWanted()) return Promise.resolve(false);
|
|
109
|
+
const p = path();
|
|
110
|
+
// The roster as of this call, not as mutated while the write waits.
|
|
111
|
+
let snapshot;
|
|
63
112
|
try {
|
|
64
|
-
|
|
65
|
-
path(),
|
|
66
|
-
(cur) => {
|
|
67
|
-
if (Number(cur?.requestedAt) > requestedAt) return undefined;
|
|
68
|
-
written = true;
|
|
69
|
-
return {
|
|
70
|
-
...extra,
|
|
71
|
-
...(version != null ? { version } : {}),
|
|
72
|
-
fetchedAt: Date.now(),
|
|
73
|
-
requestedAt,
|
|
74
|
-
models,
|
|
75
|
-
};
|
|
76
|
-
},
|
|
77
|
-
{ fsyncDir: true }
|
|
78
|
-
);
|
|
113
|
+
snapshot = JSON.parse(JSON.stringify({ extra, models }));
|
|
79
114
|
} catch {
|
|
80
|
-
|
|
115
|
+
return Promise.resolve(false);
|
|
81
116
|
}
|
|
82
|
-
|
|
83
|
-
|
|
117
|
+
return enqueueWrite(p, async () => {
|
|
118
|
+
// A newer request may have started while this write waited its turn.
|
|
119
|
+
if (!stillWanted()) return false;
|
|
120
|
+
let written = false;
|
|
121
|
+
try {
|
|
122
|
+
await updateJsonAtomic(
|
|
123
|
+
p,
|
|
124
|
+
(cur) => {
|
|
125
|
+
if (Number(cur?.requestedAt) > requestedAt) return undefined;
|
|
126
|
+
// The lock wait is asynchronous: a newer request or a credential
|
|
127
|
+
// change may have landed since the checks above. Re-decide now,
|
|
128
|
+
// right before accepting the data.
|
|
129
|
+
if (!stillWanted()) return undefined;
|
|
130
|
+
written = true;
|
|
131
|
+
return {
|
|
132
|
+
...snapshot.extra,
|
|
133
|
+
...(version != null ? { version } : {}),
|
|
134
|
+
fetchedAt: Date.now(),
|
|
135
|
+
requestedAt,
|
|
136
|
+
models: snapshot.models,
|
|
137
|
+
};
|
|
138
|
+
},
|
|
139
|
+
{ fsyncDir: true }
|
|
140
|
+
);
|
|
141
|
+
} catch {
|
|
142
|
+
/* best-effort */
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
if (written && onSave) onSave(models);
|
|
146
|
+
} catch {
|
|
147
|
+
/* best-effort */
|
|
148
|
+
}
|
|
149
|
+
return written;
|
|
150
|
+
});
|
|
84
151
|
};
|
|
85
152
|
}
|
|
86
153
|
|
|
@@ -435,7 +435,7 @@ export class MuseCodeOAuthProvider {
|
|
|
435
435
|
});
|
|
436
436
|
const rows = discovered.map((row) => museRow(row.id, row));
|
|
437
437
|
// A superseded or other-credential response never replaces the live roster either.
|
|
438
|
-
if (publish(discovered)) inner._enrichedModels = rows;
|
|
438
|
+
if (await publish(discovered)) inner._enrichedModels = rows;
|
|
439
439
|
return rows;
|
|
440
440
|
}
|
|
441
441
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// The authorization code a test login sends: redacted by value, since `code`
|
|
2
|
+
// alone is also an error/status field.
|
|
3
|
+
export const AUTH_CODE = 'SECRET-CODE';
|
|
4
|
+
|
|
5
|
+
// Every secret field a token endpoint might echo, each with a distinct value
|
|
6
|
+
// that a test can search for in pages, errors and logs.
|
|
7
|
+
export const SECRET_FIELDS = {
|
|
8
|
+
access_token: 'SECRET-ACCESS',
|
|
9
|
+
refresh_token: 'SECRET-REFRESH',
|
|
10
|
+
id_token: 'SECRET-ID',
|
|
11
|
+
client_secret: 'SECRET-CLIENT',
|
|
12
|
+
code: AUTH_CODE,
|
|
13
|
+
code_verifier: 'SECRET-VERIFIER',
|
|
14
|
+
code_challenge: 'SECRET-CHALLENGE',
|
|
15
|
+
device_code: 'SECRET-DEVICE',
|
|
16
|
+
accessToken: 'SECRET-CAMEL-ACCESS',
|
|
17
|
+
refreshToken: 'SECRET-CAMEL-REFRESH',
|
|
18
|
+
idToken: 'SECRET-CAMEL-ID',
|
|
19
|
+
clientSecret: 'SECRET-CAMEL-CLIENT',
|
|
20
|
+
token: 'SECRET-TOKEN',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const SECRET_RE = /SECRET-/;
|
|
24
|
+
|
|
25
|
+
/** Valid JSON whose secret keys are written with escapes, nested in objects and arrays. */
|
|
26
|
+
export const ESCAPED_SECRET_BODY = String.raw`{"error":{"code":400,"details":[{"client\u005fsecret":"SECRET-ESC-CLIENT","Refresh\u005FToken":"SECRET-ESC-REFRESH"},["x",{"\u0069d_token":"SECRET-ESC-ID"}]],"meta":{"api\u005Fkey":"SECRET-ESC-KEY","acc\u0065ss\u005ftoken":"SECRET-ESC-ACCESS"}}}`;
|
|
27
|
+
|
|
28
|
+
/** Valid JSON whose secrets sit inside string values: form-encoded, a query string, nested JSON. */
|
|
29
|
+
export const STRING_SECRET_BODY = JSON.stringify({
|
|
30
|
+
error: 'invalid_request',
|
|
31
|
+
detail: 'access_token=SECRET-FORM-ACCESS&client_secret=SECRET-FORM-CLIENT',
|
|
32
|
+
error_uri: 'https://auth.example/cb?client_secret=SECRET-QUERY-CLIENT&state=ok',
|
|
33
|
+
nested: JSON.stringify({ refresh_token: 'SECRET-NESTED', inner: String.raw`{"client\u005fsecret":"SECRET-DEEP"}` }),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
/** Token-endpoint failure bodies carrying every secret field (JSON, escaped JSON, strings, form-encoded). */
|
|
37
|
+
export const SECRET_BODIES = [
|
|
38
|
+
JSON.stringify({ error: 'invalid_grant', ...SECRET_FIELDS }),
|
|
39
|
+
ESCAPED_SECRET_BODY,
|
|
40
|
+
STRING_SECRET_BODY,
|
|
41
|
+
new URLSearchParams({ error: 'invalid_grant', ...SECRET_FIELDS }).toString(),
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
/** A 200 body that is not JSON but carries tokens (the parser would quote it). */
|
|
45
|
+
export const MALFORMED_TOKEN_BODY = '{"access_token":"SECRET-ACCESS","refresh_token":"SECRET-REFRESH" oops';
|
|
@@ -184,6 +184,7 @@ export async function sendCompatChat(provider, messages, useModel, tools, opts)
|
|
|
184
184
|
signal: attemptSignal,
|
|
185
185
|
label: provider.name,
|
|
186
186
|
providerName: provider.name,
|
|
187
|
+
sessionId: opts.sessionId,
|
|
187
188
|
onStreamDelta: opts.onStreamDelta,
|
|
188
189
|
onToolCall: opts.onToolCall,
|
|
189
190
|
onTextDelta: opts.onTextDelta,
|
|
@@ -5,7 +5,14 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { mergeToolCallDelta } from './tool-call-acc.mjs';
|
|
7
7
|
import { reportStreamItemStart } from '../lib/send-callbacks.mjs';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
appendText,
|
|
10
|
+
drainLeakedCalls,
|
|
11
|
+
relayText,
|
|
12
|
+
reportLifecycle,
|
|
13
|
+
reportProgress,
|
|
14
|
+
reportTransport,
|
|
15
|
+
} from './stream-state.mjs';
|
|
9
16
|
import { chatContentParts } from '../openai-compat-reasoning-field.mjs';
|
|
10
17
|
|
|
11
18
|
function appendReasoning(state, text, source) {
|
|
@@ -50,7 +57,7 @@ export function applyCompatChunk(state, chunk) {
|
|
|
50
57
|
const choice = chunk?.choices?.[0];
|
|
51
58
|
const delta = choice?.delta;
|
|
52
59
|
if (typeof delta?.role === 'string' && delta.role) {
|
|
53
|
-
|
|
60
|
+
reportLifecycle(state, 'semantic');
|
|
54
61
|
}
|
|
55
62
|
const contentParts = chatContentParts(delta?.content);
|
|
56
63
|
if (contentParts) applyContentParts(state, contentParts);
|
|
@@ -70,6 +70,13 @@ export function reportProgress(state, kind) {
|
|
|
70
70
|
} catch {}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/** Chunk metadata without output (a role-only delta): progress once the
|
|
74
|
+
* model has produced output; before that it neither ends the first-output
|
|
75
|
+
* phase nor starts the semantic idle (the chunk already reported transport). */
|
|
76
|
+
export function reportLifecycle(state, kind) {
|
|
77
|
+
if (state.semanticIdleDeadlineAt > 0) reportProgress(state, kind);
|
|
78
|
+
}
|
|
79
|
+
|
|
73
80
|
/** Text the model exposed: accumulate, refresh the idle deadline, relay live. */
|
|
74
81
|
export function appendText(state, text) {
|
|
75
82
|
state.content += text;
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
PROVIDER_SSE_IDLE_WATCHDOG_ENABLED,
|
|
11
11
|
PROVIDER_SEMANTIC_IDLE_TIMEOUT_MS,
|
|
12
12
|
createTimeoutSignal,
|
|
13
|
+
providerFirstOutputTimeoutMs,
|
|
13
14
|
} from '../stall-policy.mjs';
|
|
14
15
|
import { TIMEOUT_PHASE } from '../../../shared/provider-timeout-phase.mjs';
|
|
15
16
|
import {
|
|
@@ -28,6 +29,7 @@ import {
|
|
|
28
29
|
toolWorkStarted,
|
|
29
30
|
} from './openai-compat-chat-stream/stream-state.mjs';
|
|
30
31
|
import { applyCompatChunk } from './openai-compat-chat-stream/chunk-events.mjs';
|
|
32
|
+
import { streamFirstOutputWindow } from './lib/first-output-window.mjs';
|
|
31
33
|
import { incompleteStreamError, settleCompatStream } from './openai-compat-chat-stream/stream-result.mjs';
|
|
32
34
|
|
|
33
35
|
export async function consumeCompatChatCompletionStream(
|
|
@@ -45,11 +47,20 @@ export async function consumeCompatChatCompletionStream(
|
|
|
45
47
|
semanticIdleTimeoutMs,
|
|
46
48
|
firstByteTimeoutMs,
|
|
47
49
|
leakedToolCallRecovery = true,
|
|
50
|
+
sessionId = null,
|
|
48
51
|
} = {}
|
|
49
52
|
) {
|
|
50
53
|
const idleOverrideEnabled = Number.isFinite(Number(semanticIdleTimeoutMs)) && Number(semanticIdleTimeoutMs) > 0;
|
|
51
54
|
const idleEnabled = idleOverrideEnabled || PROVIDER_SSE_IDLE_WATCHDOG_ENABLED;
|
|
52
55
|
const idleMs = idleOverrideEnabled ? Number(semanticIdleTimeoutMs) : PROVIDER_SEMANTIC_IDLE_TIMEOUT_MS;
|
|
56
|
+
// Until the first semantic output the logical request's first-output window
|
|
57
|
+
// bounds the stream; a request that already had output runs under the
|
|
58
|
+
// semantic idle from the stream's opening.
|
|
59
|
+
const firstOutput = streamFirstOutputWindow({
|
|
60
|
+
sessionId,
|
|
61
|
+
onStreamDelta,
|
|
62
|
+
timeoutMs: providerFirstOutputTimeoutMs(idleMs),
|
|
63
|
+
});
|
|
53
64
|
const state = createCompatStreamState({
|
|
54
65
|
providerName,
|
|
55
66
|
knownToolNames,
|
|
@@ -60,6 +71,7 @@ export async function consumeCompatChatCompletionStream(
|
|
|
60
71
|
onStreamItemStart,
|
|
61
72
|
leakedToolCallRecovery,
|
|
62
73
|
});
|
|
74
|
+
if (firstOutput.outputSeen) state.semanticIdleDeadlineAt = Date.now() + idleMs;
|
|
63
75
|
reportTransport(state);
|
|
64
76
|
const iterator = stream[Symbol.asyncIterator]();
|
|
65
77
|
let iteratorDone = false;
|
|
@@ -72,9 +84,8 @@ export async function consumeCompatChatCompletionStream(
|
|
|
72
84
|
while (true) {
|
|
73
85
|
const { value: chunk, done } = await nextAsyncWithWatchdog(iterator, {
|
|
74
86
|
signal: state.sawFirstEvent ? signal : firstByteTimeout.signal,
|
|
75
|
-
idleMs,
|
|
76
|
-
|
|
77
|
-
idleEnabled: state.sawFirstEvent && idleEnabled && state.semanticIdleDeadlineAt > 0,
|
|
87
|
+
...firstOutput.idleWatch(state.semanticIdleDeadlineAt, idleMs),
|
|
88
|
+
idleEnabled: state.sawFirstEvent && idleEnabled,
|
|
78
89
|
idleLabel: `${label} SSE idle`,
|
|
79
90
|
streamLabel: label,
|
|
80
91
|
emittedToolCall: () => toolWorkStarted(state),
|
|
@@ -147,7 +147,8 @@ function settleMaxOutputIncomplete(event, state, ctx, reason) {
|
|
|
147
147
|
function onCreated(event, state, ctx) {
|
|
148
148
|
if (event.response?.model) state.model = event.response.model;
|
|
149
149
|
if (event.response?.id) state.responseId = event.response.id;
|
|
150
|
-
|
|
150
|
+
// Metadata, not output: progress only once the stream has seen output.
|
|
151
|
+
ctx.onLifecycle?.('semantic');
|
|
151
152
|
}
|
|
152
153
|
|
|
153
154
|
function onOutputTextDelta(event, state, ctx) {
|
|
@@ -196,7 +197,8 @@ function onOutputItemAdded(event, state, ctx) {
|
|
|
196
197
|
state.toolInFlight = true;
|
|
197
198
|
}
|
|
198
199
|
reportStreamItemStart(ctx.onStreamItemStart, responsesItemStart(event.item));
|
|
199
|
-
|
|
200
|
+
if (state.toolInFlight) signal(ctx, 'tool');
|
|
201
|
+
else ctx.onLifecycle?.('semantic');
|
|
200
202
|
}
|
|
201
203
|
|
|
202
204
|
// A tool call's args (or custom-tool input) are streaming — mark tool work
|
|
@@ -241,6 +241,7 @@ async function streamCompatResponses({ provider, params, label, signal, opts, he
|
|
|
241
241
|
return await consumeCompatResponsesStream(stream, {
|
|
242
242
|
signal: attemptSignal,
|
|
243
243
|
label,
|
|
244
|
+
sessionId: opts.sessionId,
|
|
244
245
|
onStreamDelta: opts.onStreamDelta,
|
|
245
246
|
onToolCall: opts.onToolCall,
|
|
246
247
|
onTextDelta: opts.onTextDelta,
|
|
@@ -168,9 +168,11 @@ export async function nextAsyncWithWatchdog(
|
|
|
168
168
|
}
|
|
169
169
|
// The SDK JSON-decodes each frame inside next(): a SyntaxError here is a
|
|
170
170
|
// malformed wire frame, typed at this boundary and nowhere else.
|
|
171
|
-
iterator
|
|
172
|
-
|
|
173
|
-
|
|
171
|
+
iterator
|
|
172
|
+
.next()
|
|
173
|
+
.then(resolve, (error) =>
|
|
174
|
+
reject(error instanceof SyntaxError ? malformedCompatStreamError(streamLabel || idleLabel, error) : error)
|
|
175
|
+
);
|
|
174
176
|
});
|
|
175
177
|
} catch (err) {
|
|
176
178
|
if (idleTimedOut)
|