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
|
@@ -71,8 +71,10 @@ export function createContextState({
|
|
|
71
71
|
// Lead workspace record (worktrees/contract.mjs SessionWorkspace): the
|
|
72
72
|
// registered Project, the execution root, the worktree binding and its
|
|
73
73
|
// revision. Null while the session has never left its project folder.
|
|
74
|
-
// Identity-stable until a transition commits.
|
|
75
|
-
|
|
74
|
+
// Identity-stable until a transition commits. A project-folder session in a
|
|
75
|
+
// git repository publishes the folder's branch / detached commit too
|
|
76
|
+
// (getLiveWorkspace reads HEAD without spawning git).
|
|
77
|
+
workspace: runtime.getLiveWorkspace?.() ?? runtime.getWorkspace?.() ?? null,
|
|
76
78
|
systemShell: runtime.systemShell || { source: 'auto', command: '', effective: '' },
|
|
77
79
|
webSearchRoute: runtime.getWebSearchRoute?.() || runtime.webSearchRoute || null,
|
|
78
80
|
autoClear: autoClearState(),
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* commands.mjs — the session object's long-running slash commands: memory
|
|
3
3
|
* control, recall, doctor, and manual compaction (scheduled for turn end when
|
|
4
4
|
* a turn is running). Each shows a command status while it holds the lock.
|
|
5
|
+
* /btw side questions (askSide/cancelSide) run beside them, lock-free.
|
|
5
6
|
*/
|
|
6
7
|
import { compactEventDetail } from '../labels.mjs';
|
|
7
8
|
import { archiveItemsForCompaction, compactionArchivedRows } from '../compaction-view.mjs';
|
|
@@ -175,5 +176,14 @@ export function createSessionCommandsApi(bag) {
|
|
|
175
176
|
});
|
|
176
177
|
},
|
|
177
178
|
compact: compactCommand,
|
|
179
|
+
// /btw: answered from the conversation so far while a turn keeps running.
|
|
180
|
+
// No command lock and no transcript row: the answer is only returned.
|
|
181
|
+
askSide: (question) => runtime.askSide(question),
|
|
182
|
+
// `sessionId` names the session the side question was asked in (it may
|
|
183
|
+
// no longer be the current one after a switch); omitted, the current one.
|
|
184
|
+
cancelSide: (sessionId) => {
|
|
185
|
+
if (typeof sessionId === 'string' && sessionId) runtime.cancelSide(sessionId);
|
|
186
|
+
else runtime.cancelSide();
|
|
187
|
+
},
|
|
178
188
|
};
|
|
179
189
|
}
|
|
@@ -91,8 +91,8 @@ export function createSessionControlsApi(bag) {
|
|
|
91
91
|
);
|
|
92
92
|
return result;
|
|
93
93
|
},
|
|
94
|
-
setCwd: (path, options = {}) => {
|
|
95
|
-
const next = runtime.setCwd(path);
|
|
94
|
+
setCwd: async (path, options = {}) => {
|
|
95
|
+
const next = await runtime.setCwd(path);
|
|
96
96
|
// Republish up-arrow history for the NEW project: current session prompts
|
|
97
97
|
// merged with the cwd-scoped persisted store for the new cwd.
|
|
98
98
|
const sessionList = recomputePromptHistory(getState().items);
|
|
@@ -38,8 +38,8 @@ export function createSessionIntegrationsApi(bag, { oauthFlows }) {
|
|
|
38
38
|
getQuotaHistory: async (options = {}) => {
|
|
39
39
|
return await runtime.getQuotaHistory?.(options);
|
|
40
40
|
},
|
|
41
|
-
getSessionUsage: async () => {
|
|
42
|
-
return await runtime.getSessionUsage?.();
|
|
41
|
+
getSessionUsage: async (options = {}) => {
|
|
42
|
+
return await runtime.getSessionUsage?.(options);
|
|
43
43
|
},
|
|
44
44
|
consumeCodexRateLimitResetCredit: async (options = {}) => {
|
|
45
45
|
// Desktop capability parity: without this delegation the session runtime
|
|
@@ -52,6 +52,12 @@ export function createSessionIntegrationsApi(bag, { oauthFlows }) {
|
|
|
52
52
|
getSessionReviewDiff: async () => {
|
|
53
53
|
return (await runtime.getSessionReviewDiff?.()) ?? { supported: false, files: [], patch: '' };
|
|
54
54
|
},
|
|
55
|
+
getSessionReviewFilePatch: async (file, oldPath) => {
|
|
56
|
+
return (await runtime.getSessionReviewFilePatch?.(file, oldPath)) ?? { supported: false, patch: '' };
|
|
57
|
+
},
|
|
58
|
+
getTurnReviewFilePatch: async (file) => {
|
|
59
|
+
return (await runtime.getTurnReviewFilePatch?.(file)) ?? { supported: false, patch: '' };
|
|
60
|
+
},
|
|
55
61
|
revertTurnReview: async (checkpointId) => {
|
|
56
62
|
return await requireRuntimeMethod('revertTurnReview', 'Turn review revert is unavailable')(checkpointId);
|
|
57
63
|
},
|
|
@@ -140,6 +146,7 @@ export function createSessionIntegrationsApi(bag, { oauthFlows }) {
|
|
|
140
146
|
return result;
|
|
141
147
|
},
|
|
142
148
|
testCustomProvider: (input) => runtime.testCustomProvider(input),
|
|
149
|
+
checkCustomProviderKeyEnv: (name) => runtime.checkCustomProviderKeyEnv(name),
|
|
143
150
|
discoverCustomProviderModels: (input) => runtime.discoverCustomProviderModels(input),
|
|
144
151
|
saveOpenCodeGoUsageAuth: async (opts) => {
|
|
145
152
|
await runtime.saveOpenCodeGoUsageAuth(opts);
|
|
@@ -56,7 +56,8 @@ export function createSessionLifecycleApi(bag, { restoreTranscriptItems, oauthFl
|
|
|
56
56
|
id,
|
|
57
57
|
approved,
|
|
58
58
|
decision?.reason || (approved ? 'approved by user' : 'denied by user'),
|
|
59
|
-
typeof decision?.device === 'string' ? decision.device : ''
|
|
59
|
+
typeof decision?.device === 'string' ? decision.device : '',
|
|
60
|
+
decision?.answers
|
|
60
61
|
);
|
|
61
62
|
},
|
|
62
63
|
pushNotice,
|
|
@@ -106,6 +106,12 @@ export function createSessionSettingsApi(bag) {
|
|
|
106
106
|
deferStatsRefresh();
|
|
107
107
|
return next;
|
|
108
108
|
}),
|
|
109
|
+
getSessionTitleSettings: () => {
|
|
110
|
+
return runtime.getSessionTitleSettings?.() || { aiSessionTitles: true };
|
|
111
|
+
},
|
|
112
|
+
setAiSessionTitles: withCommandLock(async (enabled) => {
|
|
113
|
+
return await runtime.setAiSessionTitles?.(enabled);
|
|
114
|
+
}),
|
|
109
115
|
getToolModuleSettings: () => {
|
|
110
116
|
return (
|
|
111
117
|
runtime.getToolModuleSettings?.() || {
|
|
@@ -33,6 +33,7 @@ export function createSessionApi(bag) {
|
|
|
33
33
|
autoClear: await read(api.getAutoClear),
|
|
34
34
|
compaction: await read(api.getCompactionSettings),
|
|
35
35
|
recap: await read(api.getRecapSettings),
|
|
36
|
+
sessionTitles: await read(api.getSessionTitleSettings),
|
|
36
37
|
toolModules: await read(api.getToolModuleSettings),
|
|
37
38
|
channels: await read(api.getChannelSettings, { includeStatus: false }),
|
|
38
39
|
systemShell: await read(api.getSystemShell),
|
|
@@ -12,17 +12,20 @@ export function createToolApproval({ getState, set, nextId, getDisposed, timeout
|
|
|
12
12
|
|
|
13
13
|
function normalizeToolApprovalRequest(input = {}, id = nextId()) {
|
|
14
14
|
const now = Date.now();
|
|
15
|
+
const requestTimeoutMs =
|
|
16
|
+
Number.isFinite(Number(input?.timeoutMs)) && Number(input.timeoutMs) > 0 ? Number(input.timeoutMs) : timeoutMs;
|
|
15
17
|
return {
|
|
16
18
|
id,
|
|
17
19
|
name: String(input?.name || input?.tool_name || 'tool'),
|
|
20
|
+
kind: typeof input?.kind === 'string' ? input.kind : '',
|
|
18
21
|
args: input?.args ?? input?.tool_input ?? null,
|
|
19
22
|
cwd: input?.cwd || null,
|
|
20
23
|
sessionId: input?.sessionId || input?.session_id || null,
|
|
21
24
|
toolCallId: input?.toolCallId || input?.tool_use_id || null,
|
|
22
25
|
reason: String(input?.reason || input?.message || 'approval requested by hook').trim(),
|
|
23
26
|
requestedAt: now,
|
|
24
|
-
timeoutMs,
|
|
25
|
-
expiresAt: now +
|
|
27
|
+
timeoutMs: requestTimeoutMs,
|
|
28
|
+
expiresAt: now + requestTimeoutMs,
|
|
26
29
|
};
|
|
27
30
|
}
|
|
28
31
|
// Every request expires from ITS OWN requestedAt, queued or active. Arming
|
|
@@ -39,10 +42,10 @@ export function createToolApproval({ getState, set, nextId, getDisposed, timeout
|
|
|
39
42
|
// Settle one entry: drop its timeout, then hand the waiter its answer. The
|
|
40
43
|
// resolve stays guarded — a waiter whose caller already went away must never
|
|
41
44
|
// take the rest of the queue down with it.
|
|
42
|
-
function settleToolApproval(entry, approved, reason) {
|
|
45
|
+
function settleToolApproval(entry, approved, reason, answers) {
|
|
43
46
|
if (entry?.timer) clearTimeout(entry.timer);
|
|
44
47
|
try {
|
|
45
|
-
entry.resolve({ approved, reason });
|
|
48
|
+
entry.resolve(answers === undefined ? { approved, reason } : { approved, reason, answers });
|
|
46
49
|
} catch {}
|
|
47
50
|
}
|
|
48
51
|
function presentNextToolApproval() {
|
|
@@ -56,7 +59,7 @@ export function createToolApproval({ getState, set, nextId, getDisposed, timeout
|
|
|
56
59
|
armToolApprovalTimeout(entry);
|
|
57
60
|
set({ toolApproval: entry.request });
|
|
58
61
|
}
|
|
59
|
-
function finishToolApproval(id, approved, reason = '', device = '') {
|
|
62
|
+
function finishToolApproval(id, approved, reason = '', device = '', answers = undefined) {
|
|
60
63
|
const targetId = String(id || '');
|
|
61
64
|
if (activeToolApproval && activeToolApproval.id === targetId) {
|
|
62
65
|
const entry = activeToolApproval;
|
|
@@ -67,14 +70,14 @@ export function createToolApproval({ getState, set, nextId, getDisposed, timeout
|
|
|
67
70
|
? { id: targetId, approved: approved === true, device: String(device), at: Date.now() }
|
|
68
71
|
: null;
|
|
69
72
|
set({ toolApproval: null, toolApprovalResult });
|
|
70
|
-
settleToolApproval(entry, approved === true, String(reason || ''));
|
|
73
|
+
settleToolApproval(entry, approved === true, String(reason || ''), answers);
|
|
71
74
|
presentNextToolApproval();
|
|
72
75
|
return true;
|
|
73
76
|
}
|
|
74
77
|
const index = toolApprovalQueue.findIndex((entry) => entry.id === targetId);
|
|
75
78
|
if (index >= 0) {
|
|
76
79
|
const [entry] = toolApprovalQueue.splice(index, 1);
|
|
77
|
-
settleToolApproval(entry, approved === true, String(reason || ''));
|
|
80
|
+
settleToolApproval(entry, approved === true, String(reason || ''), answers);
|
|
78
81
|
return true;
|
|
79
82
|
}
|
|
80
83
|
return false;
|
|
@@ -30,6 +30,13 @@ export { cleanupStaleTranscriptSpillDirs };
|
|
|
30
30
|
|
|
31
31
|
let staleSpillSweepDone = false;
|
|
32
32
|
|
|
33
|
+
/** One sweep per process: it lists the whole temp directory synchronously. */
|
|
34
|
+
export function sweepStaleTranscriptSpillDirsOnce() {
|
|
35
|
+
if (staleSpillSweepDone) return;
|
|
36
|
+
staleSpillSweepDone = true;
|
|
37
|
+
cleanupStaleTranscriptSpillDirs();
|
|
38
|
+
}
|
|
39
|
+
|
|
33
40
|
// Serialized pages deliberately release the old item object graph while
|
|
34
41
|
// keeping every byte restorable. Only `items` is render-live and walkable.
|
|
35
42
|
// This buffer owns the page list, the reading cursor and snapshots; the spill
|
|
@@ -47,12 +54,9 @@ export function createTranscriptSpillBuffer({
|
|
|
47
54
|
try {
|
|
48
55
|
writeOwnerRegistry();
|
|
49
56
|
} catch {}
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
staleSpillSweepDone = true;
|
|
54
|
-
cleanupStaleTranscriptSpillDirs();
|
|
55
|
-
}
|
|
57
|
+
// A buffer is created per session; the sweep runs once per process (the
|
|
58
|
+
// daemon runs it in its post-ready warm-up).
|
|
59
|
+
sweepStaleTranscriptSpillDirsOnce();
|
|
56
60
|
const pages = [];
|
|
57
61
|
let cursor = null;
|
|
58
62
|
let spillDir = null;
|
|
@@ -228,13 +232,16 @@ export function reindexItems(itemIndexById, items) {
|
|
|
228
232
|
export function replaceSessionItemsState({ state, items, itemIndexById, preserveStreamingTail = false, extra = {} }) {
|
|
229
233
|
const nextItems = Array.isArray(items) ? items : [];
|
|
230
234
|
reindexItems(itemIndexById, nextItems);
|
|
231
|
-
|
|
235
|
+
const next = {
|
|
232
236
|
...state,
|
|
233
237
|
...extra,
|
|
234
238
|
items: nextItems,
|
|
235
239
|
structureRevision: (Number(state.structureRevision) || 0) + 1,
|
|
236
240
|
streamingTail: preserveStreamingTail ? state.streamingTail : null,
|
|
237
241
|
};
|
|
242
|
+
// The desktop's partial line belongs to the live row it continues.
|
|
243
|
+
if (!preserveStreamingTail && next.streamingPartial) next.streamingPartial = null;
|
|
244
|
+
return next;
|
|
238
245
|
}
|
|
239
246
|
|
|
240
247
|
// Shared by the live session runtime and focused transcript tests so revision/tail
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Per-turn streaming state for the lead TUI turn: the assistant text
|
|
2
2
|
// segments, the thinking (reasoning) timer, and the frame-batched flush that
|
|
3
|
-
// publishes completed visible lines and thinking transitions to the store
|
|
3
|
+
// publishes completed visible lines and thinking transitions to the store,
|
|
4
|
+
// plus the desktop-only unfinished line (`streamingPartial`, see partialNow).
|
|
4
5
|
// `createTurnStream` returns one explicit state object (deps + mutable
|
|
5
6
|
// scalars); every function here takes it as its first argument.
|
|
6
7
|
import { TUI_FRAME_MS, yieldToRenderer } from './render-timing.mjs';
|
|
@@ -11,6 +12,10 @@ import { newTranscriptMessageId } from '../../runtime/shared/transcript-metadata
|
|
|
11
12
|
// 16ms timer left every other terminal frame idle, so completed script lines
|
|
12
13
|
// accumulated and then climbed into view in coarse two-step chunks.
|
|
13
14
|
const STREAM_BATCH_INTERVAL_MS = TUI_FRAME_MS;
|
|
15
|
+
// The desktop's unfinished-line channel (see partialNow): at most one
|
|
16
|
+
// publication per interval, the first chunk after an idle gap on the next
|
|
17
|
+
// frame. Forced flushes (tool calls, notices, finalization) publish at once.
|
|
18
|
+
const PARTIAL_INTERVAL_MS = 40;
|
|
14
19
|
|
|
15
20
|
// deps: getState, set, nextId, updateStreamingTail, settleStreamingTail,
|
|
16
21
|
// clearStreamingTail, isCurrentTurn, transcriptMeta, routeMeta, sealToolBlock.
|
|
@@ -53,42 +58,59 @@ export function createTurnStream(deps) {
|
|
|
53
58
|
publishedThinkingActive: false,
|
|
54
59
|
// Live countdown of a reconnect wait (see startReconnectCountdown).
|
|
55
60
|
reconnectTimer: null,
|
|
61
|
+
// The row identity (id, time, message id) claimed before the row exists,
|
|
62
|
+
// while only the desktop's partial line shows it; the row takes it over.
|
|
63
|
+
assistantIdentity: null,
|
|
64
|
+
// Desktop partial-line publication timer and last publish time.
|
|
65
|
+
partialTimer: null,
|
|
66
|
+
partialPublishedAt: 0,
|
|
67
|
+
// This segment's partial is on screen / was cleared by a transcript
|
|
68
|
+
// replacement (and must not come back).
|
|
69
|
+
partialShown: false,
|
|
70
|
+
partialDropped: false,
|
|
56
71
|
};
|
|
57
72
|
}
|
|
58
73
|
|
|
74
|
+
function claimAssistantIdentity(stream) {
|
|
75
|
+
if (stream.assistantIdentity) return stream.assistantIdentity;
|
|
76
|
+
const id = stream.nextId();
|
|
77
|
+
const meta = stream.transcriptMeta;
|
|
78
|
+
// A non-streaming continuation was persisted before its text reached this
|
|
79
|
+
// row: the runtime left that message's id and time (ask-session
|
|
80
|
+
// takeAssistant), and the row carries them instead of minting new ones.
|
|
81
|
+
const persisted = stream.claimPersistedAssistant ? meta.persistedAssistant : null;
|
|
82
|
+
if (persisted) meta.persistedAssistant = null;
|
|
83
|
+
let assistantAt = persisted?.at;
|
|
84
|
+
let messageId = persisted?.messageId;
|
|
85
|
+
if (!persisted) {
|
|
86
|
+
assistantAt =
|
|
87
|
+
Number.isFinite(Number(meta.assistantAt)) && Number(meta.assistantAt) > 0 ? Number(meta.assistantAt) : Date.now();
|
|
88
|
+
meta.assistantAt = assistantAt;
|
|
89
|
+
// The reply this segment becomes is persisted with this stable id (the
|
|
90
|
+
// runtime takes it with assistantAt), so a compaction that drops the
|
|
91
|
+
// reply greys this row.
|
|
92
|
+
if (!(typeof meta.assistantMessageId === 'string' && meta.assistantMessageId)) {
|
|
93
|
+
meta.assistantMessageId = newTranscriptMessageId();
|
|
94
|
+
}
|
|
95
|
+
messageId = meta.assistantMessageId;
|
|
96
|
+
}
|
|
97
|
+
stream.assistantIdentity = { id, at: assistantAt, messageId };
|
|
98
|
+
return stream.assistantIdentity;
|
|
99
|
+
}
|
|
100
|
+
|
|
59
101
|
function ensureAssistant(stream, initialText = '') {
|
|
60
102
|
if (!stream.currentAssistantId) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
// row: the runtime left that message's id and time (ask-session
|
|
65
|
-
// takeAssistant), and the row carries them instead of minting new ones.
|
|
66
|
-
const persisted = stream.claimPersistedAssistant ? meta.persistedAssistant : null;
|
|
67
|
-
if (persisted) meta.persistedAssistant = null;
|
|
68
|
-
let assistantAt = persisted?.at;
|
|
69
|
-
let messageId = persisted?.messageId;
|
|
70
|
-
if (!persisted) {
|
|
71
|
-
assistantAt =
|
|
72
|
-
Number.isFinite(Number(meta.assistantAt)) && Number(meta.assistantAt) > 0
|
|
73
|
-
? Number(meta.assistantAt)
|
|
74
|
-
: Date.now();
|
|
75
|
-
meta.assistantAt = assistantAt;
|
|
76
|
-
// The reply this segment becomes is persisted with this stable id (the
|
|
77
|
-
// runtime takes it with assistantAt), so a compaction that drops the
|
|
78
|
-
// reply greys this row.
|
|
79
|
-
if (!(typeof meta.assistantMessageId === 'string' && meta.assistantMessageId)) {
|
|
80
|
-
meta.assistantMessageId = newTranscriptMessageId();
|
|
81
|
-
}
|
|
82
|
-
messageId = meta.assistantMessageId;
|
|
83
|
-
}
|
|
103
|
+
const { id, at, messageId } = claimAssistantIdentity(stream);
|
|
104
|
+
stream.assistantIdentity = null;
|
|
105
|
+
stream.currentAssistantId = id;
|
|
84
106
|
// Do NOT reset currentAssistantText here: the first onTextDelta has
|
|
85
107
|
// already accumulated the opening chunk before this batched flush runs.
|
|
86
108
|
// Segment resets are owned by closeAssistantSegment(). Seed the new row
|
|
87
109
|
// with the already-visible text so the ● gutter and the first body line
|
|
88
110
|
// appear in the SAME set()/emit() — no empty "●-only" row.
|
|
89
|
-
stream.updateStreamingTail(
|
|
111
|
+
stream.updateStreamingTail(id, {
|
|
90
112
|
text: String(initialText || ''),
|
|
91
|
-
at
|
|
113
|
+
at,
|
|
92
114
|
messageId,
|
|
93
115
|
...stream.routeMeta,
|
|
94
116
|
});
|
|
@@ -96,6 +118,85 @@ function ensureAssistant(stream, initialText = '') {
|
|
|
96
118
|
return stream.currentAssistantId;
|
|
97
119
|
}
|
|
98
120
|
|
|
121
|
+
// The desktop paints streamed text before its line completes; the terminal
|
|
122
|
+
// keeps the line-gated tail below. `streamingPartial` is the unfinished line
|
|
123
|
+
// beyond the published tail text (`from` is that text's length), cut from the
|
|
124
|
+
// same leak-guarded text the tail is, never raw provider output. Before the
|
|
125
|
+
// row's first completed line there is no tail: the partial then carries all
|
|
126
|
+
// visible text so far plus the row identity (`tail`) the row will take over.
|
|
127
|
+
// Terminal projections drop the field (session-proxy/projection.mjs).
|
|
128
|
+
function partialNow(stream) {
|
|
129
|
+
const text = stream.currentAssistantText;
|
|
130
|
+
const id = stream.currentAssistantId;
|
|
131
|
+
if (id != null) {
|
|
132
|
+
const tail = stream.getState().streamingTail;
|
|
133
|
+
if (!tail || tail.id !== id) return null;
|
|
134
|
+
const shown = typeof tail.text === 'string' ? tail.text : '';
|
|
135
|
+
if (text.length <= shown.length || !text.startsWith(shown)) return null;
|
|
136
|
+
return { id, from: shown.length, text: text.slice(shown.length) };
|
|
137
|
+
}
|
|
138
|
+
// Same rule as the row itself: nothing appears before visible content.
|
|
139
|
+
if (!text.trim()) return null;
|
|
140
|
+
const identity = claimAssistantIdentity(stream);
|
|
141
|
+
return {
|
|
142
|
+
id: identity.id,
|
|
143
|
+
from: 0,
|
|
144
|
+
text,
|
|
145
|
+
tail: {
|
|
146
|
+
kind: 'assistant',
|
|
147
|
+
id: identity.id,
|
|
148
|
+
streaming: true,
|
|
149
|
+
at: identity.at,
|
|
150
|
+
messageId: identity.messageId,
|
|
151
|
+
...stream.routeMeta,
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function samePartial(left, right) {
|
|
157
|
+
if (left === right) return true;
|
|
158
|
+
if (!left || !right) return false;
|
|
159
|
+
return left.id === right.id && left.from === right.from && left.text === right.text && !left.tail === !right.tail;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function clearPartialTimer(stream) {
|
|
163
|
+
if (stream.partialTimer === null) return;
|
|
164
|
+
clearTimeout(stream.partialTimer);
|
|
165
|
+
stream.partialTimer = null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function publishPartial(stream) {
|
|
169
|
+
clearPartialTimer(stream);
|
|
170
|
+
if (!stream.isCurrentTurn()) return;
|
|
171
|
+
const current = stream.getState().streamingPartial ?? null;
|
|
172
|
+
// A transcript replacement cleared the partial this segment showed: the
|
|
173
|
+
// segment stays gone, like a tail the replacement dropped.
|
|
174
|
+
if (stream.partialShown && current === null) stream.partialDropped = true;
|
|
175
|
+
if (stream.partialDropped) return;
|
|
176
|
+
const next = partialNow(stream);
|
|
177
|
+
stream.partialShown = next !== null;
|
|
178
|
+
if (samePartial(current, next)) return;
|
|
179
|
+
stream.partialPublishedAt = Date.now();
|
|
180
|
+
stream.set({ streamingPartial: next });
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function schedulePartial(stream) {
|
|
184
|
+
if (stream.partialTimer !== null) return;
|
|
185
|
+
const wait = Math.max(STREAM_BATCH_INTERVAL_MS, PARTIAL_INTERVAL_MS - (Date.now() - stream.partialPublishedAt));
|
|
186
|
+
stream.partialTimer = setTimeout(() => {
|
|
187
|
+
stream.partialTimer = null;
|
|
188
|
+
flushStreamBatch(stream);
|
|
189
|
+
}, wait);
|
|
190
|
+
stream.partialTimer?.unref?.();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// The segment's text settled (or was dropped): no partial line outlives it.
|
|
194
|
+
function clearPartial(stream) {
|
|
195
|
+
clearPartialTimer(stream);
|
|
196
|
+
stream.partialShown = false;
|
|
197
|
+
if (stream.getState().streamingPartial != null && stream.isCurrentTurn()) stream.set({ streamingPartial: null });
|
|
198
|
+
}
|
|
199
|
+
|
|
99
200
|
function resetLineScan(stream) {
|
|
100
201
|
stream.scanLen = 0;
|
|
101
202
|
stream.lastNewlineIdx = -1;
|
|
@@ -107,7 +208,10 @@ function closeAssistantSegment(stream) {
|
|
|
107
208
|
stream.currentAssistantId = null;
|
|
108
209
|
stream.currentAssistantText = '';
|
|
109
210
|
stream.claimPersistedAssistant = false;
|
|
211
|
+
stream.assistantIdentity = null;
|
|
110
212
|
resetLineScan(stream);
|
|
213
|
+
clearPartial(stream);
|
|
214
|
+
stream.partialDropped = false;
|
|
111
215
|
}
|
|
112
216
|
|
|
113
217
|
export function commitAssistantSegment(stream, { sealToolBlock = false } = {}) {
|
|
@@ -173,12 +277,14 @@ function flushTextBatch(stream) {
|
|
|
173
277
|
const { getState, set } = stream;
|
|
174
278
|
const visibleText = streamingVisibleTextNow(stream);
|
|
175
279
|
const patch = {};
|
|
280
|
+
let rowCreated = false;
|
|
176
281
|
// Do NOT create the assistant row (and scroll the transcript) before there
|
|
177
282
|
// is a completed line with VISIBLE content: the row appears together with
|
|
178
283
|
// its first visible line. `.trim()` also guards a response that opens with
|
|
179
284
|
// leading newlines, whose blank first line would reserve rows and scroll
|
|
180
285
|
// onto an empty band until a non-blank line lands.
|
|
181
286
|
if (stream.currentAssistantId || visibleText.trim()) {
|
|
287
|
+
rowCreated = !stream.currentAssistantId;
|
|
182
288
|
const id = ensureAssistant(stream, visibleText);
|
|
183
289
|
const current = getState().streamingTail;
|
|
184
290
|
if (!current || current.id !== id || !Object.is(current.text, visibleText)) {
|
|
@@ -214,6 +320,9 @@ function flushTextBatch(stream) {
|
|
|
214
320
|
set(patch);
|
|
215
321
|
}
|
|
216
322
|
stream.pendingThinkingLastEndedAt = 0;
|
|
323
|
+
// A completed line created or moved the tail: the partial line restarts
|
|
324
|
+
// after it in the same frame, so the two never disagree on screen.
|
|
325
|
+
if (rowCreated || patch.streamingTail) publishPartial(stream);
|
|
217
326
|
}
|
|
218
327
|
|
|
219
328
|
function thinkingSpinnerPatch(stream, thinkingElapsedMs) {
|
|
@@ -256,7 +365,7 @@ function flushThinkBatch(stream) {
|
|
|
256
365
|
stream.publishedThinkingActive = nextThinkingActive;
|
|
257
366
|
}
|
|
258
367
|
|
|
259
|
-
|
|
368
|
+
function flushPendingBatches(stream) {
|
|
260
369
|
if (stream.batchTimer !== null) {
|
|
261
370
|
clearTimeout(stream.batchTimer);
|
|
262
371
|
stream.batchTimer = null;
|
|
@@ -265,6 +374,7 @@ export function flushStreamBatch(stream) {
|
|
|
265
374
|
stream.pendingTextFlush = false;
|
|
266
375
|
stream.pendingThinkFlush = false;
|
|
267
376
|
stream.pendingThinkingLastEndedAt = 0;
|
|
377
|
+
clearPartialTimer(stream);
|
|
268
378
|
return;
|
|
269
379
|
}
|
|
270
380
|
if (stream.pendingTextFlush) {
|
|
@@ -277,9 +387,17 @@ export function flushStreamBatch(stream) {
|
|
|
277
387
|
}
|
|
278
388
|
}
|
|
279
389
|
|
|
390
|
+
// A forced flush is a boundary (tool call, notice, finalization, error, or
|
|
391
|
+
// the partial-line tick): the line-gated batch lands, and so does the
|
|
392
|
+
// desktop's unfinished line.
|
|
393
|
+
export function flushStreamBatch(stream) {
|
|
394
|
+
flushPendingBatches(stream);
|
|
395
|
+
publishPartial(stream);
|
|
396
|
+
}
|
|
397
|
+
|
|
280
398
|
function scheduleStreamFlush(stream) {
|
|
281
399
|
if (stream.batchTimer !== null) return; // already scheduled; do not re-arm
|
|
282
|
-
stream.batchTimer = setTimeout(() =>
|
|
400
|
+
stream.batchTimer = setTimeout(() => flushPendingBatches(stream), STREAM_BATCH_INTERVAL_MS);
|
|
283
401
|
stream.batchTimer?.unref?.(); // don't prevent process exit
|
|
284
402
|
}
|
|
285
403
|
|
|
@@ -306,6 +424,7 @@ export function appendTextDelta(stream, textChunk) {
|
|
|
306
424
|
stream.pendingTextFlush = true;
|
|
307
425
|
if (thinkingLastEndedAt) stream.pendingThinkingLastEndedAt = thinkingLastEndedAt;
|
|
308
426
|
scheduleStreamFlush(stream);
|
|
427
|
+
schedulePartial(stream);
|
|
309
428
|
}
|
|
310
429
|
|
|
311
430
|
// Mid-turn assistant text that precedes a tool call from a provider that only
|
|
@@ -320,6 +439,9 @@ export function appendAssistantText(stream, full, { committed = false } = {}) {
|
|
|
320
439
|
// id. Any other text (a tool preamble published before its message commits)
|
|
321
440
|
// leaves an earlier recorded id unclaimed, so drop it.
|
|
322
441
|
stream.claimPersistedAssistant = committed;
|
|
442
|
+
// A committed message's row takes its persisted identity, never one claimed
|
|
443
|
+
// earlier for partial text that has since been retracted.
|
|
444
|
+
if (committed && !stream.currentAssistantId) stream.assistantIdentity = null;
|
|
323
445
|
if (!committed && stream.transcriptMeta && typeof stream.transcriptMeta === 'object') {
|
|
324
446
|
stream.transcriptMeta.persistedAssistant = null;
|
|
325
447
|
}
|
|
@@ -364,6 +486,8 @@ export function resetStreamedText(stream, { chars, reasoning } = {}) {
|
|
|
364
486
|
stream.currentAssistantId = null;
|
|
365
487
|
}
|
|
366
488
|
}
|
|
489
|
+
// The retraction applies to the desktop's partial line in the same frame.
|
|
490
|
+
publishPartial(stream);
|
|
367
491
|
return true;
|
|
368
492
|
}
|
|
369
493
|
|
|
@@ -508,18 +632,37 @@ export function settleFinalText(stream, finalText) {
|
|
|
508
632
|
text: stream.currentAssistantText || stream.assistantText,
|
|
509
633
|
});
|
|
510
634
|
}
|
|
635
|
+
clearPartial(stream);
|
|
511
636
|
}
|
|
512
637
|
|
|
513
638
|
// On abort, preserve only the still-open segment: tool boundaries already
|
|
514
639
|
// sealed prior progress segments into their own rows, and replaying the
|
|
515
640
|
// turn-global text would create one giant duplicate after the cancelled card.
|
|
516
641
|
export function settleOpenSegmentOnCancel(stream) {
|
|
517
|
-
if (
|
|
518
|
-
|
|
519
|
-
|
|
642
|
+
if (stream.currentAssistantText.trim()) {
|
|
643
|
+
const id = stream.currentAssistantId || ensureAssistant(stream, stream.currentAssistantText);
|
|
644
|
+
stream.settleStreamingTail(id, { text: stream.currentAssistantText });
|
|
645
|
+
}
|
|
646
|
+
clearPartial(stream);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// A segment that never completed a line has no tail, only the partial row the
|
|
650
|
+
// desktop shows. A turn that ends without finalizing it (provider error,
|
|
651
|
+
// aborted tool loop) keeps that text as its row, like cancellation does. A
|
|
652
|
+
// transcript replacement cleared the partial, and then the text stays gone.
|
|
653
|
+
function settlePartialOnlySegment(stream) {
|
|
654
|
+
const identity = stream.assistantIdentity;
|
|
655
|
+
const open = stream.getState().streamingPartial;
|
|
656
|
+
if (stream.currentAssistantId || stream.partialDropped || !identity || open?.id !== identity.id) return;
|
|
657
|
+
if (!stream.isCurrentTurn()) return;
|
|
658
|
+
const text = stream.currentAssistantText;
|
|
659
|
+
if (!text.trim()) return;
|
|
660
|
+
stream.settleStreamingTail(ensureAssistant(stream, text), { text });
|
|
520
661
|
}
|
|
521
662
|
|
|
522
663
|
export function settleStreamingTailAtClose(stream) {
|
|
664
|
+
settlePartialOnlySegment(stream);
|
|
665
|
+
clearPartial(stream);
|
|
523
666
|
const id = stream.currentAssistantId;
|
|
524
667
|
if (!id || stream.getState().streamingTail?.id !== id) return;
|
|
525
668
|
if (stream.currentAssistantText.trim()) stream.settleStreamingTail(id, { text: stream.currentAssistantText });
|
package/src/tui/session/turn.mjs
CHANGED
|
@@ -15,10 +15,7 @@ import { preserveGoalStateAfterTurn } from './goal-turn-state.mjs';
|
|
|
15
15
|
export { preserveGoalStateAfterTurn } from './goal-turn-state.mjs';
|
|
16
16
|
export { transcriptToolCallDisplayMode } from './turn-tool-cards.mjs';
|
|
17
17
|
import { isRejected429, safeErrorDetails } from '../../runtime/shared/error-presentation.mjs';
|
|
18
|
-
import {
|
|
19
|
-
QUOTA_KINDS,
|
|
20
|
-
providerErrorKind,
|
|
21
|
-
} from '../../runtime/agent/orchestrator/providers/provider-error-kind.mjs';
|
|
18
|
+
import { QUOTA_KINDS, providerErrorKind } from '../../runtime/agent/orchestrator/providers/provider-error-kind.mjs';
|
|
22
19
|
import { registerTaskWaitSteeringCheck } from '../../runtime/agent/orchestrator/session/task-wait-control.mjs';
|
|
23
20
|
import { promptDisplayText, STEERING_SUPPRESSED_DISPLAY } from './queue-helpers.mjs';
|
|
24
21
|
import { yieldToRenderer } from './render-timing.mjs';
|
|
@@ -394,7 +391,15 @@ export function streamHandlers(ctx) {
|
|
|
394
391
|
const { isCurrentTurn, stream } = ctx;
|
|
395
392
|
return {
|
|
396
393
|
onStageChange: async (stage, detail = null) => {
|
|
397
|
-
if (isCurrentTurn())
|
|
394
|
+
if (!isCurrentTurn()) return;
|
|
395
|
+
// A runtime notice (e.g. a tool the provider left out of the request)
|
|
396
|
+
// becomes a transcript notice the TUI and desktop render.
|
|
397
|
+
if (stage === 'notice') {
|
|
398
|
+
flushStreamBatch(stream);
|
|
399
|
+
ctx.pushNotice(detail?.message, detail?.tone === 'warn' ? 'warn' : 'info', { transcript: true });
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
await applyStageChange(stream, stage, detail);
|
|
398
403
|
},
|
|
399
404
|
onTextDelta: (chunk) => {
|
|
400
405
|
const textChunk = String(chunk ?? '');
|
|
@@ -649,6 +654,8 @@ export function createRunTurn(bag) {
|
|
|
649
654
|
set({
|
|
650
655
|
busy: true,
|
|
651
656
|
lastTurn: null,
|
|
657
|
+
// A turn that unwound stale never cleared its desktop partial line.
|
|
658
|
+
...(getState().streamingPartial ? { streamingPartial: null } : {}),
|
|
652
659
|
spinner: {
|
|
653
660
|
active: true,
|
|
654
661
|
verb: pickVerb(turn.index),
|
|
@@ -6,7 +6,9 @@ description: "Handle tasks as requested."
|
|
|
6
6
|
|
|
7
7
|
Consult the user and build the work plan together. Explicit read-only requests
|
|
8
8
|
proceed immediately. When file edits, state mutation, or delegation are
|
|
9
|
-
required, investigate and plan only until the user approves the latest plan.
|
|
9
|
+
required, investigate and plan only until the user approves the latest plan. If the
|
|
10
|
+
plan leaves the user a choice among clear options, ask it with `ask_user`
|
|
11
|
+
before asking for approval.
|
|
10
12
|
|
|
11
13
|
When user input is required, park only the affected work and continue
|
|
12
14
|
unaffected approved work. Ask pending questions together once no such work
|