mixdog 1.2.2 → 1.2.3
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/README.md +18 -0
- package/package.json +2 -1
- package/scripts/provider-pingpong-live.mjs +509 -0
- package/src/agents/advisor/AGENT.md +1 -1
- package/src/agents/advisor/agent.json +1 -1
- package/src/defaults/shipped-history.json +5 -0
- package/src/defaults/skills/setup/SKILL.md +6 -1
- package/src/defaults/skills/setup/references/actions.md +44 -22
- package/src/defaults/skills/setup/references/surfaces.md +10 -8
- package/src/headless-exec.mjs +15 -8
- package/src/lib/keychain-cjs.cjs +7 -4
- package/src/output-styles/common.md +11 -10
- package/src/rules/agent/AGENT.md +5 -5
- package/src/rules/lead/LEAD.md +5 -5
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +92 -61
- package/src/runtime/agent/orchestrator/cache-mtime.mjs +10 -1
- package/src/runtime/agent/orchestrator/config-providers-merge.mjs +7 -0
- package/src/runtime/agent/orchestrator/config.mjs +5 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +1 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +26 -7
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +112 -7
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +48 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +24 -17
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +7 -6
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +29 -10
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-blocks.mjs +20 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-delta.mjs +15 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +11 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/message-events.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +12 -5
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-outcome.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-state.mjs +14 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +6 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +58 -7
- package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +29 -10
- package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +16 -3
- package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +10 -61
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +12 -6
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +114 -72
- package/src/runtime/agent/orchestrator/providers/compat-request-policy.mjs +78 -2
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +37 -5
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +43 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +64 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +70 -6
- package/src/runtime/agent/orchestrator/providers/custom-reasoning.mjs +34 -7
- package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +32 -6
- package/src/runtime/agent/orchestrator/providers/deepseek.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +11 -4
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +25 -8
- package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +87 -5
- package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +41 -5
- package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +13 -13
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +355 -37
- package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +1 -2
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +93 -47
- package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +55 -10
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +26 -8
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/lib/action-input-contract.mjs +60 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-native-blocks.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +246 -42
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-server-tool-usage.mjs +18 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-system-blocks.mjs +27 -6
- package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +388 -0
- package/src/runtime/agent/orchestrator/providers/lib/inclusive-usage.mjs +54 -0
- package/src/runtime/agent/orchestrator/providers/lib/origin-bound-fetch.mjs +16 -0
- package/src/runtime/agent/orchestrator/providers/lib/responses-terminal-fields.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +48 -22
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +119 -18
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +13 -15
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +1 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +33 -1
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +121 -61
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +35 -14
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +37 -7
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/tool-call-acc.mjs +2 -9
- package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +47 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +47 -25
- package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +5 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +151 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +42 -24
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +63 -25
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2901 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +172 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +53 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +103 -26
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +23 -4
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +11 -6
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +15 -23
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +23 -10
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +25 -4
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +10 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-items.mjs +18 -7
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +5 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/text-relay.mjs +9 -1
- package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +80 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +71 -24
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +323 -0
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +44 -16
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +7 -16
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/refusals.mjs +60 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +51 -35
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state.mjs +30 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +12 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/terminal-frames.mjs +25 -6
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +11 -3
- package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +170 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +75 -70
- package/src/runtime/agent/orchestrator/providers/openrouter.mjs +75 -0
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +147 -8
- package/src/runtime/agent/orchestrator/providers/registry.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +5 -0
- package/src/runtime/agent/orchestrator/providers/transport-scope.d.mts +6 -0
- package/src/runtime/agent/orchestrator/providers/transport-scope.mjs +28 -0
- package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +157 -0
- package/src/runtime/agent/orchestrator/providers/wire-route.mjs +512 -0
- package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +1131 -0
- package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +183 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +100 -0
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +21 -0
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -0
- package/src/runtime/agent/orchestrator/runtime-core/provider-request-snapshot.mjs +7 -3
- package/src/runtime/agent/orchestrator/runtime-core/session-title.mjs +21 -1
- package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +14 -3
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +526 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +10 -7
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +176 -71
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +62 -10
- package/src/runtime/agent/orchestrator/runtime-core/tool-exposure.mjs +96 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/compact/constants.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +14 -93
- package/src/runtime/agent/orchestrator/session/compact/summary-anchor.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +120 -0
- package/src/runtime/agent/orchestrator/session/context-tool-schema.mjs +17 -2
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -4
- package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +6 -1
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +91 -1
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +56 -0
- package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +18 -8
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +21 -10
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +132 -0
- package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +30 -0
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +63 -20
- package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-call-resolution.mjs +95 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +30 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +15 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +150 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +34 -12
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +57 -25
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +208 -14
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +15 -3
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +25 -3
- package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +82 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +19 -2
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/manager.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +96 -0
- package/src/runtime/agent/orchestrator/session/side-question.mjs +192 -0
- package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +17 -3
- package/src/runtime/agent/orchestrator/session/store/live-state.mjs +23 -0
- package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +58 -2
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +41 -28
- package/src/runtime/agent/orchestrator/session/store/session-observers.mjs +4 -2
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +12 -1
- package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/store-summary-archived-agent.mjs +34 -15
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +10 -1
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +10 -2
- package/src/runtime/agent/orchestrator/session/store.mjs +19 -3
- package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +7 -1
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +77 -0
- package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +8 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +14 -10
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +49 -34
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +23 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +73 -39
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +18 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -4
- package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +88 -12
- package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +2 -0
- package/src/runtime/computer-bridge/action-schema.mjs +44 -21
- package/src/runtime/github/tool.mjs +92 -2
- package/src/runtime/memory/index.mjs +1 -0
- package/src/runtime/memory/lib/http-router/ingest-routes.mjs +22 -1
- package/src/runtime/memory/lib/ingest/transcript-offsets.mjs +2 -0
- package/src/runtime/memory/lib/pg/adapter.mjs +98 -1
- package/src/runtime/memory/lib/pg/process.mjs +31 -26
- package/src/runtime/memory/lib/pg/run-child.mjs +49 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +108 -3
- package/src/runtime/memory/lib/runtime-fetcher.mjs +12 -13
- package/src/runtime/memory/lib/transcript-ingest.mjs +14 -0
- package/src/runtime/office/README.md +1 -1
- package/src/runtime/office/authoring/html-document-author.mjs +2 -0
- package/src/runtime/office/authoring/pptx-contact-sheet.mjs +2 -0
- package/src/runtime/office/authoring/pptx-html-measure.mjs +2 -0
- package/src/runtime/office/authoring/pptx-html-runner.mjs +2 -0
- package/src/runtime/office/authoring/xlsx-html-runner.mjs +2 -0
- package/src/runtime/office/core/office-actions-read.mjs +3 -0
- package/src/runtime/office/core/office-render-preview.mjs +2 -0
- package/src/runtime/office/core/office-session-dispatch.mjs +7 -0
- package/src/runtime/office/core/office-sessionless-actions.mjs +8 -0
- package/src/runtime/office/core/office-sessions.mjs +5 -0
- package/src/runtime/office/core/office-transactions.mjs +3 -0
- package/src/runtime/office/core/office-write-ledger.mjs +98 -0
- package/src/runtime/office/design/library/templates/mixdog-executive.mixdog-edit.pptx +0 -0
- package/src/runtime/office/index.mjs +30 -0
- package/src/runtime/office/pdf/pdf-edit-document.mjs +2 -0
- package/src/runtime/office/pdf/pdf-render.mjs +23 -7
- package/src/runtime/office/pdf/pdf-security.mjs +2 -0
- package/src/runtime/office/portable/portable-opc.mjs +4 -1
- package/src/runtime/office/quality/visual-diff.mjs +2 -0
- package/src/runtime/shared/bounded-file-read.mjs +13 -0
- package/src/runtime/shared/bounded-line-diff.mjs +182 -0
- package/src/runtime/shared/buffered-appender.mjs +18 -0
- package/src/runtime/shared/config.mjs +78 -18
- package/src/runtime/shared/err-text.mjs +6 -0
- package/src/runtime/shared/file-permissions.d.mts +6 -1
- package/src/runtime/shared/file-permissions.mjs +54 -6
- package/src/runtime/shared/llm/anthropic-effort.mjs +6 -0
- package/src/runtime/shared/llm/usage-accounting.mjs +5 -2
- package/src/runtime/shared/llm/usage-ledger-rollup.mjs +3 -0
- package/src/runtime/shared/loopback-listener.mjs +9 -0
- package/src/runtime/shared/provider-api-key.mjs +2 -0
- package/src/runtime/shared/review-diff.mjs +52 -0
- package/src/runtime/shared/session-sidebar-host.d.mts +4 -0
- package/src/runtime/shared/session-sidebar-host.mjs +38 -0
- package/src/runtime/shared/slow-span.mjs +114 -0
- package/src/runtime/shared/statusline/statusline-segments.mjs +28 -4
- package/src/runtime/shared/tool-card-model/card-header.mjs +6 -1
- package/src/runtime/shared/tool-card-model/generic-detail.mjs +39 -5
- package/src/runtime/shared/tool-card-model.mjs +5 -2
- package/src/runtime/shared/tool-output-limit.mjs +4 -0
- package/src/runtime/shared/tool-primitives.mjs +13 -0
- package/src/runtime/shared/tool-result-summary.mjs +8 -1
- package/src/runtime/shared/tool-surface.mjs +18 -3
- package/src/runtime/shared/tool-work-units.mjs +40 -4
- package/src/runtime/shared/transcript-writer/purge.mjs +74 -0
- package/src/runtime/shared/transcript-writer.mjs +10 -2
- package/src/runtime/shared/turn-changes.mjs +268 -100
- package/src/runtime/shared/turn-review-reply.mjs +8 -39
- package/src/runtime/shared/turn-snapshot-store.mjs +19 -108
- package/src/runtime/shared/turn-snapshot.mjs +857 -683
- package/src/runtime/shared/turn-worktree-snapshot.mjs +183 -255
- package/src/runtime/shared/user-data-guard.mjs +262 -49
- package/src/runtime/shared/worktrees/carry.mjs +531 -0
- package/src/runtime/shared/worktrees/contract.mjs +34 -0
- package/src/runtime/shared/worktrees/file-recovery.mjs +68 -0
- package/src/runtime/shared/worktrees/git-head.mjs +205 -0
- package/src/runtime/shared/worktrees/git.mjs +8 -3
- package/src/runtime/shared/worktrees/maintenance.mjs +1 -1
- package/src/runtime/shared/worktrees/manager.mjs +156 -8
- package/src/runtime/shared/worktrees/merge.mjs +31 -21
- package/src/runtime/shared/worktrees/paths.d.mts +6 -0
- package/src/runtime/terminal-bridge/client.mjs +44 -11
- package/src/runtime/terminal-bridge/tool-defs.mjs +13 -6
- package/src/runtime/tidy/apply.mjs +47 -2
- package/src/runtime/tidy/run-engines.mjs +11 -5
- package/src/session-runtime/agent-tool-routing.mjs +19 -13
- package/src/session-runtime/boot/apis.mjs +13 -5
- package/src/session-runtime/boot/facade.mjs +1 -0
- package/src/session-runtime/boot/infrastructure.mjs +6 -1
- package/src/session-runtime/boot/session.mjs +0 -1
- package/src/session-runtime/boot/tools.mjs +4 -1
- package/src/session-runtime/cwd-plugins/cwd-apply.mjs +12 -5
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +65 -0
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -1
- package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +11 -4
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +115 -18
- package/src/session-runtime/internal-tool-executor.mjs +6 -2
- package/src/session-runtime/lifecycle/cold-heir.mjs +188 -0
- package/src/session-runtime/lifecycle/inheritance.mjs +71 -27
- package/src/session-runtime/lifecycle/session-catalog.mjs +12 -3
- package/src/session-runtime/lifecycle/session-switch/resume.mjs +6 -0
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +29 -6
- package/src/session-runtime/lifecycle/teardown.mjs +72 -7
- package/src/session-runtime/lifecycle-api.mjs +1 -0
- package/src/session-runtime/mcp-glue/connect.mjs +17 -5
- package/src/session-runtime/model-route/set-route.mjs +2 -2
- package/src/session-runtime/native-web-search-usage.mjs +51 -0
- package/src/session-runtime/native-web-search.mjs +10 -1
- package/src/session-runtime/new-session-config.mjs +4 -2
- package/src/session-runtime/provider-auth/login.mjs +4 -0
- package/src/session-runtime/resource-scope-decorate.mjs +10 -3
- package/src/session-runtime/runtime-review-api.mjs +8 -2
- package/src/session-runtime/services/agent-tool/index-write-queue.mjs +81 -20
- package/src/session-runtime/services/agent-tool/lead-worker-index/forgotten-leads.mjs +29 -0
- package/src/session-runtime/services/agent-tool/lead-worker-index/index-file.mjs +1 -1
- package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +73 -7
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +15 -1
- package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +10 -1
- package/src/session-runtime/services/agent-tool/worker-index.mjs +46 -7
- package/src/session-runtime/services/agent-tool.mjs +3 -0
- package/src/session-runtime/services/channel-admin.mjs +19 -3
- package/src/session-runtime/services/memory-runtime-proxy/calls.mjs +4 -0
- package/src/session-runtime/services/memory-runtime-proxy.mjs +1 -0
- package/src/session-runtime/services/provider-admin.mjs +81 -15
- package/src/session-runtime/session-transcript-purge.mjs +32 -0
- package/src/session-runtime/session-turn-api.mjs +14 -5
- package/src/session-runtime/settings-builtin-tools-api.mjs +9 -0
- package/src/session-runtime/setup-tool/executor.mjs +36 -6
- package/src/session-runtime/setup-tool/extended-actions.mjs +86 -6
- package/src/session-runtime/setup-tool/settings-contract.mjs +13 -3
- package/src/session-runtime/setup-tool/tool-defs.mjs +13 -4
- package/src/session-runtime/skills-api.mjs +3 -5
- package/src/session-runtime/tool-surface.mjs +5 -1
- package/src/session-runtime/turn/compaction-gate.mjs +25 -0
- package/src/session-runtime/turn/session-ops.mjs +7 -5
- package/src/session-runtime/turn/side-question-ops.mjs +22 -0
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +5 -1
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +8 -3
- package/src/session-runtime/turn/turn-run/turn-settle.mjs +10 -0
- package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
- package/src/session-runtime/turn/turn-run.mjs +25 -9
- package/src/session-runtime/usage-stats-api.mjs +43 -11
- package/src/session-runtime/workspace/project-branches.mjs +7 -5
- package/src/session-runtime/workspace/transition-steps.mjs +129 -31
- package/src/session-runtime/workspace/transitions.mjs +190 -15
- package/src/standalone/daemon-agent-control.mjs +9 -1
- package/src/standalone/daemon-local-session-bridge.mjs +14 -1
- package/src/standalone/daemon-owner.mjs +17 -4
- package/src/standalone/daemon-pg-stop.mjs +51 -0
- package/src/standalone/daemon-stop-budget.d.mts +2 -0
- package/src/standalone/daemon-stop-budget.mjs +18 -0
- package/src/standalone/daemon-stored-session-views.mjs +2 -0
- package/src/standalone/daemon-telemetry.mjs +19 -0
- package/src/standalone/daemon.mjs +61 -1
- package/src/standalone/desktop-service-registry.mjs +5 -0
- package/src/standalone/local-session-runtime.mjs +70 -19
- package/src/standalone/session-client.mjs +42 -0
- package/src/standalone/session-protocol.mjs +7 -0
- package/src/standalone/session-proxy/projection.mjs +41 -4
- package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +15 -1
- package/src/standalone/session-runtime-inline-host.mjs +1 -0
- package/src/standalone/session-service/session-calls/session-action.mjs +18 -3
- package/src/standalone/session-service-api.mjs +6 -1
- package/src/standalone/session-state-patch.d.mts +2 -1
- package/src/standalone/session-state-patch.mjs +41 -1
- package/src/tui/App.jsx +25 -0
- package/src/tui/app/app-view/floating-panel.jsx +26 -1
- package/src/tui/app/btw-controller.mjs +118 -0
- package/src/tui/app/create-app-pickers.mjs +2 -0
- package/src/tui/app/shell-layout/row-budgets.mjs +11 -1
- package/src/tui/app/shell-layout/surfaces.mjs +3 -1
- package/src/tui/app/slash-commands.mjs +13 -3
- package/src/tui/app/slash-dispatch/session-commands.mjs +19 -6
- package/src/tui/app/use-global-key-input/approval-shortcut.mjs +12 -0
- package/src/tui/app/use-global-key-input.mjs +5 -12
- package/src/tui/app/use-prompt-handlers.mjs +11 -0
- package/src/tui/components/BtwPanel.jsx +53 -0
- package/src/tui/components/QuestionPanel.jsx +275 -0
- package/src/tui/components/question-panel-model.mjs +156 -0
- package/src/tui/dist/index.mjs +2413 -1465
- package/src/tui/session/context-state.mjs +4 -2
- package/src/tui/session/session-api/commands.mjs +10 -0
- package/src/tui/session/session-api/controls.mjs +2 -2
- package/src/tui/session/session-api/integrations.mjs +9 -2
- package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
- package/src/tui/session/session-api/lifecycle.mjs +2 -1
- package/src/tui/session/session-api/settings.mjs +6 -0
- package/src/tui/session/session-api.mjs +1 -0
- package/src/tui/session/tool-approval.mjs +10 -7
- package/src/tui/session/transcript-spill.mjs +14 -7
- package/src/tui/session/turn-stream.mjs +174 -31
- package/src/tui/session/turn.mjs +12 -5
- package/src/workflows/default/WORKFLOW.md +3 -1
|
@@ -279,7 +279,7 @@ function _stripResponseItemsFromHead(items, responseItems) {
|
|
|
279
279
|
// Official OpenAI Responses WebSocket guide: response.create WS frames mirror
|
|
280
280
|
// the Responses body EXCEPT the transport-only fields `stream`/`background`,
|
|
281
281
|
// which the socket carries implicitly and the guide says to omit. This set is
|
|
282
|
-
// stripped from a frame (any build shape)
|
|
282
|
+
// always stripped from a frame (any build shape).
|
|
283
283
|
const TRANSPORT_ONLY_FRAME_FIELDS = new Set(['stream', 'background']);
|
|
284
284
|
|
|
285
285
|
// Canonical response.create frame builder. Every WS send (main full-frame
|
|
@@ -293,14 +293,12 @@ const TRANSPORT_ONLY_FRAME_FIELDS = new Set(['stream', 'background']);
|
|
|
293
293
|
// response, so dropping them here strips the system/lead prompt from every
|
|
294
294
|
// continuation turn. Only an empty instructions string is omitted.
|
|
295
295
|
// Full frames pass the body unchanged and keep every key in place.
|
|
296
|
-
// omitTransportFields is used by wire-parity helpers to drop stream/background.
|
|
297
296
|
export function _buildResponseCreateFrame(
|
|
298
297
|
body,
|
|
299
|
-
{ previousResponseId = null, inputOverride
|
|
298
|
+
{ previousResponseId = null, inputOverride } = {}
|
|
300
299
|
) {
|
|
301
300
|
const src = body && typeof body === 'object' ? body : {};
|
|
302
301
|
if (previousResponseId == null && inputOverride === undefined) {
|
|
303
|
-
if (!omitTransportFields) return { type: 'response.create', ...src };
|
|
304
302
|
const frame = { type: 'response.create' };
|
|
305
303
|
for (const key of Object.keys(src)) {
|
|
306
304
|
if (TRANSPORT_ONLY_FRAME_FIELDS.has(key)) continue;
|
|
@@ -310,7 +308,7 @@ export function _buildResponseCreateFrame(
|
|
|
310
308
|
}
|
|
311
309
|
const frame = { type: 'response.create' };
|
|
312
310
|
for (const key of Object.keys(src)) {
|
|
313
|
-
if (
|
|
311
|
+
if (TRANSPORT_ONLY_FRAME_FIELDS.has(key)) continue;
|
|
314
312
|
if (key === 'instructions') {
|
|
315
313
|
const instr = src.instructions;
|
|
316
314
|
if (typeof instr === 'string' && instr.length) frame.instructions = instr;
|
|
@@ -366,17 +364,10 @@ export function _computeDelta({ entry, body, traceProvider }) {
|
|
|
366
364
|
if (!entry?.lastRequestSansInput || !entry.lastResponseId) {
|
|
367
365
|
return { mode: 'full', reason: 'no_anchor', frame: buildFrame(body) };
|
|
368
366
|
}
|
|
369
|
-
//
|
|
370
|
-
//
|
|
371
|
-
//
|
|
372
|
-
//
|
|
373
|
-
// previous_response_not_found, so the opt-out degrades to full frames.
|
|
374
|
-
// openai-oauth/Codex (per-WS-session server state) and xAI (in-connection
|
|
375
|
-
// ZDR continuation with encrypted reasoning) both anchor without the store
|
|
376
|
-
// and are deliberately excluded from this gate.
|
|
377
|
-
if (traceProvider === 'openai-direct' && body?.store !== true) {
|
|
378
|
-
return { mode: 'full', reason: 'store_disabled', frame: buildFrame(body) };
|
|
379
|
-
}
|
|
367
|
+
// WebSocket mode keeps the latest response in a connection-local cache, so
|
|
368
|
+
// previous_response_id continues on the same socket even with store:false.
|
|
369
|
+
// If the server no longer has the anchor (previous_response_not_found), the
|
|
370
|
+
// caller recovers with a full-context frame.
|
|
380
371
|
if (!deltaForce && !deltaRefs && !entry.turnState) {
|
|
381
372
|
return { mode: 'full', reason: 'delta_missing_turn_state', frame: buildFrame(body) };
|
|
382
373
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Refusal content of one Responses stream. The model's refusal arrives as
|
|
2
|
+
// `response.refusal.delta` / `response.refusal.done` and as a
|
|
3
|
+
// `{type:'refusal', refusal}` content part of the message item
|
|
4
|
+
// (output_item.done and the terminal output bundle). Parts are keyed by item
|
|
5
|
+
// id + content index so the streamed, item-done and terminal copies of one
|
|
6
|
+
// part fold into one text; a full (done / final-part) text replaces the
|
|
7
|
+
// accumulated deltas. Shared by the WS and HTTP/SSE collectors.
|
|
8
|
+
export function createRefusals() {
|
|
9
|
+
const parts = new Map();
|
|
10
|
+
const keyOf = (itemId, contentIndex) => `${itemId || ''}:${Number.isInteger(contentIndex) ? contentIndex : 0}`;
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
/** response.refusal.delta */
|
|
14
|
+
delta(event) {
|
|
15
|
+
if (typeof event?.delta !== 'string') return;
|
|
16
|
+
const key = keyOf(event.item_id, event.content_index);
|
|
17
|
+
parts.set(key, (parts.get(key) || '') + event.delta);
|
|
18
|
+
},
|
|
19
|
+
/** response.refusal.done */
|
|
20
|
+
done(event) {
|
|
21
|
+
if (typeof event?.refusal !== 'string') return;
|
|
22
|
+
parts.set(keyOf(event.item_id, event.content_index), event.refusal);
|
|
23
|
+
},
|
|
24
|
+
/** A message item's refusal parts. Returns whether it carried any. */
|
|
25
|
+
absorbMessage(item) {
|
|
26
|
+
if (item?.type !== 'message' || !Array.isArray(item.content)) return false;
|
|
27
|
+
let found = false;
|
|
28
|
+
item.content.forEach((part, index) => {
|
|
29
|
+
if (part?.type !== 'refusal' || typeof part.refusal !== 'string') return;
|
|
30
|
+
found = true;
|
|
31
|
+
// An id-less item cannot be matched to its streamed copy; the
|
|
32
|
+
// streamed text already stands for it.
|
|
33
|
+
if (!item.id && parts.size > 0) return;
|
|
34
|
+
parts.set(keyOf(item.id, index), part.refusal);
|
|
35
|
+
});
|
|
36
|
+
return found;
|
|
37
|
+
},
|
|
38
|
+
get seen() {
|
|
39
|
+
return parts.size > 0;
|
|
40
|
+
},
|
|
41
|
+
get text() {
|
|
42
|
+
return [...parts.values()].filter(Boolean).join('\n');
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** The typed stop reason of a refused turn (read by the agent loop). */
|
|
48
|
+
export const REFUSAL_STOP_REASON = 'refusal';
|
|
49
|
+
|
|
50
|
+
/** Result fields for a refused turn — the shared result shape:
|
|
51
|
+
* stopReason: REFUSAL_STOP_REASON
|
|
52
|
+
* refusal: the refusal text (joined parts; may be '')
|
|
53
|
+
* content: the output_text, or the refusal text when there was none
|
|
54
|
+
* so a refusal never reads as an empty successful answer. Empty object when
|
|
55
|
+
* the response carried no refusal. */
|
|
56
|
+
export function refusalResultFields(refusals, content) {
|
|
57
|
+
if (!refusals.seen) return {};
|
|
58
|
+
const refusal = refusals.text;
|
|
59
|
+
return { content: content || refusal, refusal, stopReason: REFUSAL_STOP_REASON };
|
|
60
|
+
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { makeInvalidToolArgsMarker } from '../lib/openai-tool-args.mjs';
|
|
2
2
|
import { synthLeakedOpenAICall } from '../openai-compat-stream-common.mjs';
|
|
3
3
|
import { createToolCallDedupe, dedupeToolCallList } from '../lib/leaked-toolcall.mjs';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
customToolCallFromResponseItem,
|
|
6
|
+
nativeToolSearchCallFromArguments,
|
|
7
|
+
qualifiedToolCallName,
|
|
8
|
+
withToolCallNamespace,
|
|
9
|
+
} from '../custom-tool-wire.mjs';
|
|
5
10
|
import { createActiveToolItemTracker } from '../tool-stream-state.mjs';
|
|
6
11
|
|
|
7
12
|
// tool_search_call.arguments parse. Module-scope (exported) for direct test
|
|
@@ -64,7 +69,7 @@ export function createWsToolCalls({ onToolCall, midState, replayItems }) {
|
|
|
64
69
|
} catch {}
|
|
65
70
|
}
|
|
66
71
|
function emit(call) {
|
|
67
|
-
if (!toolDedupe.shouldDispatch(call
|
|
72
|
+
if (!toolDedupe.shouldDispatch(qualifiedToolCallName(call), call?.arguments, call?.id)) return;
|
|
68
73
|
fire(call);
|
|
69
74
|
}
|
|
70
75
|
function settle() {
|
|
@@ -84,6 +89,8 @@ export function createWsToolCalls({ onToolCall, midState, replayItems }) {
|
|
|
84
89
|
}
|
|
85
90
|
function pushToolSearchCall(item) {
|
|
86
91
|
if (item?.type !== 'tool_search_call') return;
|
|
92
|
+
// Server-executed search already ran inside the response; replay-only.
|
|
93
|
+
if (item.execution === 'server') return;
|
|
87
94
|
const callId = item.call_id || item.id || '';
|
|
88
95
|
if (!callId || toolCalls.some((call) => call.id === callId)) return;
|
|
89
96
|
const call = nativeToolSearchCallFromArguments(callId, parseToolSearchArgs(item.arguments));
|
|
@@ -106,7 +113,10 @@ export function createWsToolCalls({ onToolCall, midState, replayItems }) {
|
|
|
106
113
|
const type = item?.type;
|
|
107
114
|
if (type === 'function_call') {
|
|
108
115
|
markStarted(item);
|
|
109
|
-
pendingCalls.set(
|
|
116
|
+
pendingCalls.set(
|
|
117
|
+
item.id || '',
|
|
118
|
+
withToolCallNamespace({ name: item.name || '', callId: item.call_id || '' }, item)
|
|
119
|
+
);
|
|
110
120
|
} else if (type === 'custom_tool_call' || type === 'tool_search_call') {
|
|
111
121
|
// tool_search is marked in-flight at item-added time too, so the
|
|
112
122
|
// semantic-idle stall gate's pendingToolUse never drops a mid-flight
|
|
@@ -134,7 +144,7 @@ export function createWsToolCalls({ onToolCall, midState, replayItems }) {
|
|
|
134
144
|
argumentsText: event.arguments || JSON.stringify(args),
|
|
135
145
|
});
|
|
136
146
|
if (pending?.callId && pending?.name) {
|
|
137
|
-
const call = { id: pending.callId, name: pending.name, arguments: args };
|
|
147
|
+
const call = withToolCallNamespace({ id: pending.callId, name: pending.name, arguments: args }, pending);
|
|
138
148
|
toolCalls.push(call);
|
|
139
149
|
emit(call);
|
|
140
150
|
pendingCalls.delete(itemId);
|
|
@@ -150,31 +160,45 @@ export function createWsToolCalls({ onToolCall, midState, replayItems }) {
|
|
|
150
160
|
// bundle. If salvage also fails the stream fails explicitly — masking the
|
|
151
161
|
// gap with a synthetic id just shifts the failure one turn later under a
|
|
152
162
|
// confusing "No tool output found for function call" error.
|
|
153
|
-
toolCalls.push(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
163
|
+
toolCalls.push(
|
|
164
|
+
withToolCallNamespace(
|
|
165
|
+
{
|
|
166
|
+
id: pending?.callId || '',
|
|
167
|
+
name: pending?.name || '',
|
|
168
|
+
arguments: args,
|
|
169
|
+
_pendingItemId: itemId,
|
|
170
|
+
_deferred: true,
|
|
171
|
+
},
|
|
172
|
+
pending
|
|
173
|
+
)
|
|
174
|
+
);
|
|
160
175
|
}
|
|
161
176
|
|
|
162
|
-
/**
|
|
177
|
+
/** A complete function_call item (output_item.done, or the terminal
|
|
178
|
+
* response output bundle) through the one deduplicated collector: it
|
|
179
|
+
* resolves the deferred placeholder for its item (canonical call_id/name
|
|
180
|
+
* from the item, missing ones kept from the placeholder), is skipped when
|
|
181
|
+
* its call_id is already recorded, and is otherwise created and
|
|
182
|
+
* dispatched — so a call present only in the terminal bundle still runs. */
|
|
163
183
|
function completeFunctionCallItem(item) {
|
|
164
184
|
const itemId = item.id || '';
|
|
165
185
|
const callId = item.call_id || '';
|
|
166
186
|
const name = item.name || '';
|
|
167
187
|
const args = parseFunctionCallArgs(argsTextOf(item.arguments));
|
|
168
188
|
const deferred = toolCalls.find((tc) => tc?._deferred && (!itemId || tc._pendingItemId === itemId));
|
|
169
|
-
if (deferred
|
|
170
|
-
deferred.id = callId;
|
|
171
|
-
deferred.name = name;
|
|
172
|
-
deferred
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
189
|
+
if (deferred) {
|
|
190
|
+
deferred.id = callId || deferred.id;
|
|
191
|
+
deferred.name = name || deferred.name;
|
|
192
|
+
withToolCallNamespace(deferred, item);
|
|
193
|
+
if (deferred.id && deferred.name) {
|
|
194
|
+
deferred.arguments =
|
|
195
|
+
deferred.arguments && Object.keys(deferred.arguments).length > 0 ? deferred.arguments : args;
|
|
196
|
+
delete deferred._deferred;
|
|
197
|
+
delete deferred._pendingItemId;
|
|
198
|
+
emit(deferred);
|
|
199
|
+
}
|
|
176
200
|
} else if (callId && name && !toolCalls.some((tc) => tc?.id === callId)) {
|
|
177
|
-
const call = { id: callId, name, arguments: args };
|
|
201
|
+
const call = withToolCallNamespace({ id: callId, name, arguments: args }, item);
|
|
178
202
|
toolCalls.push(call);
|
|
179
203
|
emit(call);
|
|
180
204
|
}
|
|
@@ -193,20 +217,6 @@ export function createWsToolCalls({ onToolCall, midState, replayItems }) {
|
|
|
193
217
|
settle();
|
|
194
218
|
}
|
|
195
219
|
|
|
196
|
-
/** response.completed salvage: when arguments.done fired before (or
|
|
197
|
-
* without) a matching output_item.added, the deferred placeholder has an
|
|
198
|
-
* empty id/name; the bundle carries the canonical call_id/name. */
|
|
199
|
-
function salvageFunctionCall(item) {
|
|
200
|
-
const tc = toolCalls.find((t) => t._deferred && t._pendingItemId === (item.id || ''));
|
|
201
|
-
if (!tc) return;
|
|
202
|
-
if (!tc.id && item.call_id) tc.id = item.call_id;
|
|
203
|
-
if (!tc.name && item.name) tc.name = item.name;
|
|
204
|
-
if (tc.id && tc.name) {
|
|
205
|
-
delete tc._deferred;
|
|
206
|
-
delete tc._pendingItemId;
|
|
207
|
-
emit(tc);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
220
|
|
|
211
221
|
return {
|
|
212
222
|
get calls() {
|
|
@@ -238,6 +248,12 @@ export function createWsToolCalls({ onToolCall, midState, replayItems }) {
|
|
|
238
248
|
completeFunctionCallItem,
|
|
239
249
|
completeToolSearchItem,
|
|
240
250
|
completeCustomToolItem,
|
|
241
|
-
|
|
251
|
+
/** A server-truncated call item: kept in flight, never collected. */
|
|
252
|
+
holdIncompleteItem: markStarted,
|
|
253
|
+
// Terminal-bundle salvage (response.completed / success response.done):
|
|
254
|
+
// the same collector as output_item.done. When arguments.done fired
|
|
255
|
+
// before (or without) output_item.added, the deferred placeholder has an
|
|
256
|
+
// empty id/name and the bundle carries the canonical call_id/name.
|
|
257
|
+
salvageFunctionCall: completeFunctionCallItem,
|
|
242
258
|
};
|
|
243
259
|
}
|
|
@@ -13,6 +13,8 @@ import { createReplayItems } from './openai-ws-response-state/replay-items.mjs';
|
|
|
13
13
|
import { createCitations } from './openai-ws-response-state/citations.mjs';
|
|
14
14
|
import { createWsToolCalls } from './openai-ws-response-state/tool-calls.mjs';
|
|
15
15
|
import { normalizeWsUsage } from './openai-ws-response-state/usage.mjs';
|
|
16
|
+
import { createRefusals, refusalResultFields } from './openai-ws-response-state/refusals.mjs';
|
|
17
|
+
import { isIncompleteToolItem } from './lib/responses-terminal-fields.mjs';
|
|
16
18
|
|
|
17
19
|
export { parseToolSearchArgs } from './openai-ws-response-state/tool-calls.mjs';
|
|
18
20
|
|
|
@@ -37,6 +39,7 @@ export function createWsResponseState({ onToolCall, onStreamDelta, midState, tra
|
|
|
37
39
|
const reasoningDeltas = { text: 0, summary: 0, other: 0 };
|
|
38
40
|
const replayItems = createReplayItems();
|
|
39
41
|
const citations = createCitations();
|
|
42
|
+
const refusals = createRefusals();
|
|
40
43
|
const tools = createWsToolCalls({ onToolCall, midState, replayItems });
|
|
41
44
|
const replayProvider = traceProvider === 'xai' ? 'xai-responses' : 'openai-responses';
|
|
42
45
|
const progress = (kind) => {
|
|
@@ -49,6 +52,11 @@ export function createWsResponseState({ onToolCall, onStreamDelta, midState, tra
|
|
|
49
52
|
// anything the per-item events never carried. Returns whether any progress
|
|
50
53
|
// was reported.
|
|
51
54
|
function salvageCompletedItem(item, relayFinalText) {
|
|
55
|
+
if (isIncompleteToolItem(item)) {
|
|
56
|
+
tools.holdIncompleteItem(item);
|
|
57
|
+
progress('tool');
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
52
60
|
replayItems.push(item);
|
|
53
61
|
let reported = false;
|
|
54
62
|
if (item.type === 'message') {
|
|
@@ -64,6 +72,10 @@ export function createWsResponseState({ onToolCall, onStreamDelta, midState, tra
|
|
|
64
72
|
}
|
|
65
73
|
citations.pushOutputTextAnnotations(c);
|
|
66
74
|
}
|
|
75
|
+
if (refusals.absorbMessage(item)) {
|
|
76
|
+
progress('semantic');
|
|
77
|
+
reported = true;
|
|
78
|
+
}
|
|
67
79
|
}
|
|
68
80
|
if (item.type === 'web_search_call') {
|
|
69
81
|
citations.pushWebSearchCall(item);
|
|
@@ -146,10 +158,19 @@ export function createWsResponseState({ onToolCall, onStreamDelta, midState, tra
|
|
|
146
158
|
noteToolInputDelta: tools.noteToolInputDelta,
|
|
147
159
|
/** response.function_call_arguments.done */
|
|
148
160
|
completeFunctionCallArguments: tools.completeFunctionCallArguments,
|
|
161
|
+
/** response.refusal.delta */
|
|
162
|
+
noteRefusalDelta: refusals.delta,
|
|
163
|
+
/** response.refusal.done */
|
|
164
|
+
noteRefusalDone: refusals.done,
|
|
149
165
|
/** response.output_item.done. Returns the progress kind to report. */
|
|
150
166
|
completeOutputItem(item) {
|
|
167
|
+
if (isIncompleteToolItem(item)) {
|
|
168
|
+
tools.holdIncompleteItem(item);
|
|
169
|
+
return 'tool';
|
|
170
|
+
}
|
|
151
171
|
replayItems.push(item);
|
|
152
172
|
const type = item?.type || '';
|
|
173
|
+
if (type === 'message') refusals.absorbMessage(item);
|
|
153
174
|
// function_call / output_text already arrive via their dedicated
|
|
154
175
|
// streaming events; `reasoning` carries encrypted_content that keeps the
|
|
155
176
|
// openai-oauth server-side prompt cache prefix warm.
|
|
@@ -189,6 +210,13 @@ export function createWsResponseState({ onToolCall, onStreamDelta, midState, tra
|
|
|
189
210
|
if (!responseId && response?.id) responseId = response.id;
|
|
190
211
|
return usage;
|
|
191
212
|
},
|
|
213
|
+
/** A cutoff frame's output bundle: call items the server truncated stay
|
|
214
|
+
* in flight so the cutoff reads as a pending-tool turn. */
|
|
215
|
+
holdIncompleteBundleItems(response) {
|
|
216
|
+
for (const item of response?.output || []) {
|
|
217
|
+
if (isIncompleteToolItem(item)) tools.holdIncompleteItem(item);
|
|
218
|
+
}
|
|
219
|
+
},
|
|
192
220
|
/** max_output_tokens maps cleanly to Anthropic's stop_reason=max_tokens. */
|
|
193
221
|
markMaxOutputIncomplete(reason) {
|
|
194
222
|
incompleteReason = reason;
|
|
@@ -227,6 +255,8 @@ export function createWsResponseState({ onToolCall, onStreamDelta, midState, tra
|
|
|
227
255
|
...(typeof endTurn === 'boolean' ? { endTurn } : {}),
|
|
228
256
|
responseId: responseId || undefined,
|
|
229
257
|
serviceTier: responseServiceTier || undefined,
|
|
258
|
+
// A refused turn: typed stop reason and the refusal text.
|
|
259
|
+
...refusalResultFields(refusals, content),
|
|
230
260
|
};
|
|
231
261
|
},
|
|
232
262
|
};
|
|
@@ -88,6 +88,18 @@ export function createWsEventSwitch({ response, textRelay, watchdogs, midState,
|
|
|
88
88
|
progress('tool');
|
|
89
89
|
watchdogs.bumpSemantic();
|
|
90
90
|
break;
|
|
91
|
+
// Refusal text is model output but never visible answer text: it is
|
|
92
|
+
// collected for the typed refusal result, not relayed live.
|
|
93
|
+
case 'response.refusal.delta':
|
|
94
|
+
response.noteRefusalDelta(event);
|
|
95
|
+
progress('semantic');
|
|
96
|
+
watchdogs.bumpSemantic();
|
|
97
|
+
break;
|
|
98
|
+
case 'response.refusal.done':
|
|
99
|
+
response.noteRefusalDone(event);
|
|
100
|
+
progress('semantic');
|
|
101
|
+
watchdogs.bumpSemantic();
|
|
102
|
+
break;
|
|
91
103
|
case 'response.output_item.done': {
|
|
92
104
|
const kind = response.completeOutputItem(event.item);
|
|
93
105
|
// Item-done is genuine lifecycle progress — reset semantic idle so
|
|
@@ -27,11 +27,30 @@ export function createTerminalFrameHandlers({ response, textRelay, midState, err
|
|
|
27
27
|
|
|
28
28
|
// Most incomplete reasons are real failures. max_output_tokens maps cleanly
|
|
29
29
|
// to Anthropic's stop_reason=max_tokens; treating it as an error makes
|
|
30
|
-
// Claude retry the same over-budget turn
|
|
30
|
+
// Claude retry the same over-budget turn — unless a tool call's input was
|
|
31
|
+
// still streaming: its arguments were truncated, so the cutoff is a
|
|
32
|
+
// stalled pending-tool turn (same guard as HTTP/SSE), never a clean length
|
|
33
|
+
// completion that would surface the partial call as successful.
|
|
31
34
|
function settleIncomplete(event, label, options) {
|
|
32
35
|
const usage = response.noteTerminal(event.response);
|
|
33
36
|
const reasonStr = _incompleteReasonFromEvent(event);
|
|
34
37
|
if (_isMaxOutputIncompleteReason(reasonStr)) {
|
|
38
|
+
response.holdIncompleteBundleItems(event.response);
|
|
39
|
+
if (response.pendingToolUse) {
|
|
40
|
+
const error = new Error(`${label} (max_output_tokens) with tool call in flight`);
|
|
41
|
+
try {
|
|
42
|
+
textRelay.flush();
|
|
43
|
+
} catch {
|
|
44
|
+
/* best-effort */
|
|
45
|
+
}
|
|
46
|
+
Object.assign(error, response.partialState());
|
|
47
|
+
error.streamStalled = true;
|
|
48
|
+
error.pendingToolUse = true;
|
|
49
|
+
error.incompleteReason = reasonStr;
|
|
50
|
+
if (usage) error.partialUsage = usage;
|
|
51
|
+
failWith(error, options);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
35
54
|
response.markMaxOutputIncomplete(reasonStr);
|
|
36
55
|
completeTurn();
|
|
37
56
|
return;
|
|
@@ -105,11 +124,11 @@ export function createTerminalFrameHandlers({ response, textRelay, midState, err
|
|
|
105
124
|
failWith(responseDoneStatusError(status, `${errLabel} response.done unexpected status`), { markDone: true });
|
|
106
125
|
return;
|
|
107
126
|
}
|
|
108
|
-
// Success-shaped response.done (status '' or 'completed')
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
127
|
+
// Success-shaped response.done (status '' or 'completed') is the same
|
|
128
|
+
// terminal as response.completed: usage, end_turn, and its output bundle
|
|
129
|
+
// reconciled through the same deduplicated collectors (calls that appear
|
|
130
|
+
// only in the bundle are created; unresolvable deferred calls fail).
|
|
131
|
+
onResponseCompleted(event);
|
|
113
132
|
}
|
|
114
133
|
|
|
115
134
|
function onResponseIncomplete(event) {
|
|
@@ -111,6 +111,10 @@ export class OpenAIDirectProvider {
|
|
|
111
111
|
}
|
|
112
112
|
return null;
|
|
113
113
|
}
|
|
114
|
+
/** Drop a disposed transport scope's sticky transport record. */
|
|
115
|
+
releaseTransportScope(scope) {
|
|
116
|
+
this._httpFallbackPoolKeys.delete(scope);
|
|
117
|
+
}
|
|
114
118
|
send(messages, model, tools, sendOpts) {
|
|
115
119
|
return withEffortConfigurationFallback('openai', model, sendOpts || {}, (opts) =>
|
|
116
120
|
this._sendOnce(messages, model, tools, opts)
|
|
@@ -139,8 +143,9 @@ export class OpenAIDirectProvider {
|
|
|
139
143
|
// poolKey MUST be sessionId-only. Falling back to promptCacheKey would
|
|
140
144
|
// let unrelated raw sessions sharing the same provider-scoped cache
|
|
141
145
|
// bucket reuse each other's pooled socket and inherit lastResponseId
|
|
142
|
-
// delta state, producing cross-session prompt corruption.
|
|
143
|
-
|
|
146
|
+
// delta state, producing cross-session prompt corruption. A forked
|
|
147
|
+
// request runs in its own transport scope (transport-scope.mjs).
|
|
148
|
+
const poolKey = opts.transportScope || opts.sessionId || null;
|
|
144
149
|
// cacheKey (prompt_cache_key) only groups the server-side prefix-cache
|
|
145
150
|
// shard — safe to share across sessions, unlike the sessionId poolKey
|
|
146
151
|
// above. buildRequestBody derives it from the base namespace plus a
|
|
@@ -228,7 +233,10 @@ export class OpenAIDirectProvider {
|
|
|
228
233
|
// ws-delta only affects the delta gate inside openai-ws-delta.mjs.
|
|
229
234
|
if (
|
|
230
235
|
transportPolicy.transport === 'http' ||
|
|
231
|
-
(poolKey &&
|
|
236
|
+
(poolKey &&
|
|
237
|
+
httpFallbackEnabled &&
|
|
238
|
+
// A forked scope follows its session's refusal; it records its own.
|
|
239
|
+
(this._httpFallbackPoolKeys.has(poolKey) || this._httpFallbackPoolKeys.has(opts.sessionId)))
|
|
232
240
|
) {
|
|
233
241
|
return await sendHttpWithKeyReload(auth);
|
|
234
242
|
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode Go's Gemini route: `@ai-sdk/google` models are served by the
|
|
3
|
+
* gateway's generateContent endpoint, `{base}/models/{id}:streamGenerateContent`
|
|
4
|
+
* (https://opencode.ai/docs/go/ — Endpoints table). This engine reuses the
|
|
5
|
+
* direct Gemini adapter's request builder, stream consumer, retry ladder and
|
|
6
|
+
* result shaping, and only moves the transport onto the gateway: the Go key
|
|
7
|
+
* (never GEMINI_API_KEY) goes to the Go origin only, as a bearer token and the
|
|
8
|
+
* `x-goog-api-key` header, with the session / User-Agent headers of every Go
|
|
9
|
+
* request. Gateway models have no explicit cachedContents.
|
|
10
|
+
*/
|
|
11
|
+
import { GeminiProvider, geminiRestError } from './gemini.mjs';
|
|
12
|
+
import { GEMINI_FIRST_BYTE_TIMEOUT_MS, consumeGeminiRestStreamResponse } from './gemini-stream.mjs';
|
|
13
|
+
import { geminiBareModelId } from './gemini-cache-policy.mjs';
|
|
14
|
+
import { geminiFailureUsage } from './gemini-response.mjs';
|
|
15
|
+
import { geminiEndsWithFunctionResponse, runGeminiStreamAttempts } from './gemini-retry-policy.mjs';
|
|
16
|
+
import { geminiTextLeakGuardFor } from './gemini-sdk-request.mjs';
|
|
17
|
+
import { emitProviderRetryStage } from './retry-classifier.mjs';
|
|
18
|
+
import { originBoundFetch } from './lib/origin-bound-fetch.mjs';
|
|
19
|
+
import { createPassthroughSignal, createTimeoutSignal } from '../stall-policy.mjs';
|
|
20
|
+
import { getLlmDispatcher } from '../../../shared/llm/http-agent.mjs';
|
|
21
|
+
import { reloadProviderApiKey, resolveProviderApiKey } from '../../../shared/provider-api-key.mjs';
|
|
22
|
+
|
|
23
|
+
const PROVIDER = 'opencode-go';
|
|
24
|
+
|
|
25
|
+
/** The generateContent request body for a built Gemini request. */
|
|
26
|
+
export function openCodeGoGeminiBody({ contents, systemInstruction, geminiTools, toolConfig, generationConfig, safetySettings }) {
|
|
27
|
+
return {
|
|
28
|
+
contents,
|
|
29
|
+
...(systemInstruction ? { systemInstruction: { parts: [{ text: systemInstruction }] } } : {}),
|
|
30
|
+
...(geminiTools ? { tools: geminiTools } : {}),
|
|
31
|
+
...(toolConfig ? { toolConfig } : {}),
|
|
32
|
+
...(generationConfig ? { generationConfig } : {}),
|
|
33
|
+
...(safetySettings ? { safetySettings } : {}),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class OpenCodeGoGeminiEngine extends GeminiProvider {
|
|
38
|
+
name = PROVIDER;
|
|
39
|
+
|
|
40
|
+
/** @param {object} config the Go provider config; `baseURL` is the OpenAI-style `.../v1` base */
|
|
41
|
+
constructor(config) {
|
|
42
|
+
const base = String(config.baseURL || '').replace(/\/+$/, '');
|
|
43
|
+
super({
|
|
44
|
+
...config,
|
|
45
|
+
fetchFn: originBoundFetch(new URL(base).origin, config.fetchImpl, PROVIDER),
|
|
46
|
+
// No Google host to warm and no Google SDK client: this engine never uses either.
|
|
47
|
+
preconnectFn: () => {},
|
|
48
|
+
createGenAI: () => null,
|
|
49
|
+
});
|
|
50
|
+
this.gatewayBase = base;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
_getApiKey() {
|
|
54
|
+
return resolveProviderApiKey(PROVIDER, this.config);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
reloadApiKey() {
|
|
58
|
+
const key = reloadProviderApiKey(PROVIDER, this.config);
|
|
59
|
+
if (key) this.config = { ...(this.config || {}), apiKey: key };
|
|
60
|
+
return key || '';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async isAvailable() {
|
|
64
|
+
return Boolean(this._getApiKey());
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// The gateway has no cachedContents resource.
|
|
68
|
+
async _ensureGeminiCache() {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
_headers(opts) {
|
|
73
|
+
const key = this._getApiKey();
|
|
74
|
+
return {
|
|
75
|
+
'Content-Type': 'application/json',
|
|
76
|
+
...(key ? { Authorization: `Bearer ${key}`, 'x-goog-api-key': key } : {}),
|
|
77
|
+
...(this.config?.extraHeaders || {}),
|
|
78
|
+
...(opts?.requestHeaders || {}),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
_url(model, method) {
|
|
83
|
+
return `${this.gatewayBase}/models/${encodeURIComponent(geminiBareModelId(model))}:${method}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async _openGatewayStream(url, body, opts, attemptSignal) {
|
|
87
|
+
const openFirstByte = createTimeoutSignal(attemptSignal, GEMINI_FIRST_BYTE_TIMEOUT_MS, 'OpenCode Go Gemini first byte');
|
|
88
|
+
let res;
|
|
89
|
+
try {
|
|
90
|
+
res = await this._fetch(url, {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
headers: this._headers(opts),
|
|
93
|
+
body: JSON.stringify(body),
|
|
94
|
+
signal: openFirstByte.signal,
|
|
95
|
+
dispatcher: getLlmDispatcher(),
|
|
96
|
+
});
|
|
97
|
+
} finally {
|
|
98
|
+
openFirstByte.cleanup();
|
|
99
|
+
}
|
|
100
|
+
if (!res.ok) {
|
|
101
|
+
throw geminiRestError(res, await res.text().catch(() => ''), 'OpenCode Go Gemini streamGenerateContent');
|
|
102
|
+
}
|
|
103
|
+
return res;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async _generateContent(model, body, opts, signal) {
|
|
107
|
+
const res = await this._fetch(this._url(model, 'generateContent'), {
|
|
108
|
+
method: 'POST',
|
|
109
|
+
headers: this._headers(opts),
|
|
110
|
+
body: JSON.stringify(body),
|
|
111
|
+
signal,
|
|
112
|
+
dispatcher: getLlmDispatcher(),
|
|
113
|
+
});
|
|
114
|
+
if (!res.ok) {
|
|
115
|
+
throw geminiRestError(res, await res.text().catch(() => ''), 'OpenCode Go Gemini generateContent');
|
|
116
|
+
}
|
|
117
|
+
return await res.json();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async _streamViaSdk(stream) {
|
|
121
|
+
const { opts, signal, useModel, contents, callbacks } = stream;
|
|
122
|
+
const body = openCodeGoGeminiBody(stream);
|
|
123
|
+
const url = `${this._url(useModel, 'streamGenerateContent')}?alt=sse`;
|
|
124
|
+
const passthrough = createPassthroughSignal(signal);
|
|
125
|
+
try {
|
|
126
|
+
return await runGeminiStreamAttempts({
|
|
127
|
+
signal: passthrough.signal,
|
|
128
|
+
sleepFn: this.config?.sleepFn,
|
|
129
|
+
onRetry: ({ attempt, maxAttempts, lastErr, delayMs }) => {
|
|
130
|
+
emitProviderRetryStage(opts.onStageChange, { attempt: attempt + 1, maxAttempts, lastErr, delayMs });
|
|
131
|
+
process.stderr.write(
|
|
132
|
+
`[opencode-go:gemini] retry attempt ${attempt + 1} after ${lastErr?.message || lastErr?.code || 'transient error'}\n`
|
|
133
|
+
);
|
|
134
|
+
},
|
|
135
|
+
afterToolResult: geminiEndsWithFunctionResponse(contents),
|
|
136
|
+
run: async ({ signal: attemptSignal, connected }) => {
|
|
137
|
+
try {
|
|
138
|
+
opts.onStageChange?.('requesting');
|
|
139
|
+
} catch {
|
|
140
|
+
/* heartbeat best-effort */
|
|
141
|
+
}
|
|
142
|
+
const res = await this._openGatewayStream(url, body, opts, attemptSignal);
|
|
143
|
+
connected();
|
|
144
|
+
const textLeakGuard = geminiTextLeakGuardFor(stream);
|
|
145
|
+
const response = await consumeGeminiRestStreamResponse(res, {
|
|
146
|
+
signal: attemptSignal,
|
|
147
|
+
onStreamDelta: callbacks.onStreamDelta,
|
|
148
|
+
onTextDelta: callbacks.onTextDelta,
|
|
149
|
+
textLeakGuard,
|
|
150
|
+
label: 'OpenCode Go Gemini streamGenerateContent',
|
|
151
|
+
failureUsage: geminiFailureUsage(opts, null, useModel),
|
|
152
|
+
});
|
|
153
|
+
return { response, textLeakGuard };
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
} catch (err) {
|
|
157
|
+
const recovered = await this._recoverGeminiNonStreaming({
|
|
158
|
+
streamErr: err,
|
|
159
|
+
signal: passthrough.signal,
|
|
160
|
+
opts,
|
|
161
|
+
model: useModel,
|
|
162
|
+
generate: (fallbackSignal) => this._generateContent(useModel, body, opts, fallbackSignal),
|
|
163
|
+
});
|
|
164
|
+
if (!recovered) throw err;
|
|
165
|
+
return { response: recovered, textLeakGuard: null };
|
|
166
|
+
} finally {
|
|
167
|
+
passthrough.cleanup();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|