mixdog 1.2.3 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/scripts/gate-local.mjs +6 -4
- package/scripts/lib/gate-scheduler.mjs +29 -11
- package/scripts/probe-provider-catalog.mjs +31 -0
- package/scripts/provider-pingpong-live.mjs +128 -38
- package/scripts/session-sweep.mjs +5 -25
- package/src/agents/maintainer/AGENT.md +1 -1
- package/src/agents/maintainer/agent.json +1 -1
- package/src/agents/reviewer/AGENT.md +2 -4
- package/src/agents/security/AGENT.md +1 -2
- package/src/agents/writer/AGENT.md +2 -3
- package/src/defaults/shipped-history.json +6 -1
- package/src/defaults/skills/image/SKILL.md +21 -23
- package/src/lib/keychain-cjs.cjs +13 -11
- package/src/rules/agent/AGENT.md +7 -5
- package/src/rules/lead/LEAD.md +7 -9
- package/src/rules/shared/45-destructive.md +4 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +28 -7
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -11
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +101 -23
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-api-key-fetch.mjs +36 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +26 -44
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +2 -5
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +41 -12
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +13 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +81 -10
- package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +109 -5
- package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +107 -37
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-native-context.mjs +48 -0
- package/src/runtime/agent/orchestrator/providers/cursor-wire-exec.mjs +144 -34
- package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +28 -4
- package/src/runtime/agent/orchestrator/providers/cursor-wire-request.mjs +8 -4
- package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +41 -11
- package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +67 -5
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +14 -4
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +14 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +9 -6
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -2
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +26 -4
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +12 -6
- package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +14 -6
- package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/gemini-rest-stream/watchdogs.mjs +16 -5
- package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +22 -10
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -2
- package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +33 -7
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +11 -2
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +9 -5
- package/src/runtime/agent/orchestrator/providers/lib/request-compression.mjs +86 -0
- package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +4 -2
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -5
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +60 -16
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +11 -13
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +30 -19
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +5 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +42 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +13 -9
- package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +1 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +18 -7
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +36 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +51 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +11 -1
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +64 -52
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +1 -4
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +0 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +7 -6
- package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +13 -2
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/openrouter.mjs +1 -2
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +5 -4
- package/src/runtime/agent/orchestrator/providers/registry.mjs +9 -1
- package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +11 -4
- package/src/runtime/agent/orchestrator/providers/wire-route.mjs +14 -4
- package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +15 -3
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +6 -2
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +8 -6
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +4 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-store.mjs +30 -2
- package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +3 -2
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +8 -3
- package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +3 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge-contract.mjs +6 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +10 -10
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +3 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +46 -8
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -8
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +1 -2
- package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +24 -0
- package/src/runtime/agent/orchestrator/session/cache/tool-result-reuse.mjs +10 -2
- package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +2 -3
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +3 -11
- package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +6 -22
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +16 -2
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +28 -0
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +0 -6
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +0 -6
- package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/historical-session-closure.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +2 -5
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +2 -4
- package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +0 -27
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/output-limit.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +4 -10
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +2 -9
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +1 -4
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +76 -0
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +8 -9
- package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +1 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +3 -4
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +33 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-support.mjs +0 -4
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +21 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +38 -13
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-failure.mjs +33 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +19 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +57 -13
- package/src/runtime/agent/orchestrator/session/manager/failed-turn-rewind.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +6 -120
- package/src/runtime/agent/orchestrator/session/manager/pending-persist-queue.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +29 -8
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +42 -49
- package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +50 -0
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +32 -14
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +17 -33
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +29 -13
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-recovery.mjs +45 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +2 -9
- package/src/runtime/agent/orchestrator/session/send-instrumentation.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/side-question.mjs +12 -7
- package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +5 -12
- package/src/runtime/agent/orchestrator/session/store/lifecycle-read.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store/listing.mjs +2 -6
- package/src/runtime/agent/orchestrator/session/store/live-cache-eviction.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store/liveness-veto.mjs +46 -37
- package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store/load-session.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +5 -11
- package/src/runtime/agent/orchestrator/session/store/session-paths.mjs +59 -0
- package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store/sweep/stale-sweep.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +27 -32
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-plan.mjs +0 -8
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +6 -18
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +33 -106
- package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store-agent-worker-pool.mjs +5 -8
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +136 -196
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +50 -116
- package/src/runtime/agent/orchestrator/session/store-summary-locations.mjs +3 -29
- package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +7 -8
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +9 -20
- package/src/runtime/agent/orchestrator/session/store.mjs +3 -16
- package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +11 -12
- package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +37 -13
- package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +15 -4
- package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +4 -2
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +72 -15
- package/src/runtime/agent/orchestrator/session/turn-failure.mjs +45 -0
- package/src/runtime/agent/orchestrator/stall-policy.mjs +50 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +5 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/inline-hoist.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +204 -10
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +160 -39
- package/src/runtime/agent/orchestrator/tools/builtin/git-partial-stage.mjs +7 -5
- package/src/runtime/agent/orchestrator/tools/builtin/grep-formatting.mjs +14 -5
- package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-context-expander.mjs +4 -3
- package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-output.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-path-fanout.mjs +48 -44
- package/src/runtime/agent/orchestrator/tools/builtin/lib/search-input-helpers.mjs +10 -3
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +200 -17
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +5 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +20 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +101 -2
- package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +67 -0
- package/src/runtime/agent/orchestrator/tools/lib/process-table-fixture.mjs +50 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +303 -63
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +108 -18
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +77 -11
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +81 -5
- package/src/runtime/attachments/store.d.mts +9 -0
- package/src/runtime/attachments/store.mjs +80 -9
- package/src/runtime/browser-bridge/browser-action-contract.d.mts +0 -1
- package/src/runtime/browser-bridge/browser-action-contract.mjs +0 -2
- package/src/runtime/computer-bridge/action-schema.mjs +178 -178
- package/src/runtime/computer-bridge/linux-readiness.d.mts +0 -2
- package/src/runtime/computer-bridge/linux-readiness.mjs +0 -16
- package/src/runtime/effort-judge/judge-client.mjs +21 -4
- package/src/runtime/effort-judge/judge-worker.mjs +4 -1
- package/src/runtime/github/tool.mjs +118 -17
- package/src/runtime/media/adapters/codex-image.mjs +140 -21
- package/src/runtime/media/catalog.mjs +4 -4
- package/src/runtime/media/chatgpt-usage.mjs +55 -0
- package/src/runtime/media/defaults.mjs +37 -0
- package/src/runtime/media/jobs.mjs +12 -2
- package/src/runtime/media/lanes.mjs +12 -0
- package/src/runtime/media/tool-defs.mjs +15 -4
- package/src/runtime/media/tool.mjs +41 -12
- package/src/runtime/media/upstream-error.mjs +72 -6
- package/src/runtime/memory/lib/ingest/transcript-rows.mjs +1 -0
- package/src/runtime/memory/lib/pg/process.mjs +5 -7
- package/src/runtime/memory/lib/pg/supervisor.mjs +4 -1
- package/src/runtime/office/authoring/docx-html-runner.mjs +1 -1
- package/src/runtime/office/authoring/html-browser.mjs +18 -8
- package/src/runtime/office/authoring/pdf-html-runner.mjs +1 -1
- package/src/runtime/office/authoring/pptx-html-measure.mjs +1 -1
- package/src/runtime/office/authoring/xlsx-html-runner.mjs +1 -1
- package/src/runtime/office/core/office-write-ledger.mjs +1 -5
- package/src/runtime/office/index.mjs +2 -3
- package/src/runtime/office/pdf/pdf-analysis.mjs +0 -68
- package/src/runtime/session-search/session-search-ingest.mjs +1 -1
- package/src/runtime/shared/agent-reap-state.mjs +13 -0
- package/src/runtime/shared/atomic-file.d.mts +1 -1
- package/src/runtime/shared/background-tasks.mjs +6 -1
- package/src/runtime/shared/bounded-line-diff.mjs +11 -2
- package/src/runtime/shared/browser-launch.mjs +15 -0
- package/src/runtime/shared/err-text.mjs +42 -4
- package/src/runtime/shared/file-lock.mjs +6 -5
- package/src/runtime/shared/llm/anthropic-betas.mjs +1 -2
- package/src/runtime/shared/llm/provider-model-identities.mjs +1 -0
- package/src/runtime/shared/model-route-chain.d.mts +8 -0
- package/src/runtime/shared/model-route-chain.mjs +48 -0
- package/src/runtime/shared/model-route-fallback.mjs +22 -0
- package/src/runtime/shared/pid-liveness.mjs +12 -0
- package/src/runtime/shared/provider-api-key.mjs +6 -0
- package/src/runtime/shared/provider-timeout-phase.mjs +17 -0
- package/src/runtime/shared/quiet-progress.d.mts +35 -0
- package/src/runtime/shared/quiet-progress.mjs +101 -0
- package/src/runtime/shared/quota-resume-store.mjs +32 -1
- package/src/runtime/shared/read-row-numbers.mjs +44 -0
- package/src/runtime/shared/remote-intent.mjs +2 -42
- package/src/runtime/shared/slow-span.mjs +8 -1
- package/src/runtime/shared/statusline/statusline-segments.mjs +24 -2
- package/src/runtime/shared/tool-card-model/card-header.mjs +2 -1
- package/src/runtime/shared/tool-output-budget.mjs +2 -0
- package/src/runtime/shared/tool-result-summary.mjs +16 -2
- package/src/runtime/shared/tool-status.mjs +3 -2
- package/src/runtime/shared/tool-surface.mjs +2 -3
- package/src/runtime/shared/tool-work-units.mjs +22 -15
- package/src/runtime/shared/transcript-metadata.mjs +49 -1
- package/src/runtime/shared/transcript-turn-keys.mjs +33 -0
- package/src/runtime/shared/transcript-writer/transcript-file.mjs +10 -1
- package/src/runtime/shared/transcript-writer.mjs +14 -4
- package/src/runtime/shared/turn-changes.mjs +2 -1
- package/src/runtime/shared/turn-snapshot.mjs +11 -3
- package/src/runtime/shared/worktrees/activity-gate.mjs +175 -0
- package/src/runtime/shared/worktrees/branch-management.mjs +179 -0
- package/src/runtime/shared/worktrees/carry.mjs +6 -4
- package/src/runtime/shared/worktrees/contract.mjs +27 -1
- package/src/runtime/shared/worktrees/dependencies.mjs +456 -0
- package/src/runtime/shared/worktrees/file-recovery.mjs +29 -13
- package/src/runtime/shared/worktrees/git-head.mjs +22 -13
- package/src/runtime/shared/worktrees/include.mjs +125 -7
- package/src/runtime/shared/worktrees/lock.mjs +1 -1
- package/src/runtime/shared/worktrees/maintenance.mjs +20 -2
- package/src/runtime/shared/worktrees/manager.mjs +378 -32
- package/src/runtime/shared/worktrees/owner.mjs +2 -1
- package/src/runtime/shared/worktrees/preparation-report.mjs +79 -0
- package/src/runtime/shared/worktrees/setup-commands.mjs +232 -0
- package/src/runtime/shared/worktrees/workspace-visibility.mjs +17 -0
- package/src/runtime/terminal-bridge/client.mjs +14 -10
- package/src/session-runtime/boot/apis.mjs +7 -1
- package/src/session-runtime/boot/config.mjs +2 -0
- package/src/session-runtime/boot/facade.mjs +3 -0
- package/src/session-runtime/boot/session.mjs +3 -2
- package/src/session-runtime/boot/tools.mjs +3 -0
- package/src/session-runtime/conversation-import/service.mjs +1 -1
- package/src/session-runtime/goal-facade-api.mjs +2 -2
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +44 -18
- package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +4 -1
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +22 -8
- package/src/session-runtime/internal-tool-executor.mjs +2 -0
- package/src/session-runtime/lifecycle/cold-heir.mjs +0 -1
- package/src/session-runtime/lifecycle/session-catalog.mjs +14 -16
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +18 -53
- package/src/session-runtime/lifecycle/session-switch.mjs +1 -1
- package/src/session-runtime/lifecycle/shared.mjs +5 -6
- package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -5
- package/src/session-runtime/lifecycle/teardown.mjs +7 -16
- package/src/session-runtime/media-api.mjs +8 -0
- package/src/session-runtime/model-route/route-ownership.mjs +60 -0
- package/src/session-runtime/model-route/route-persist.mjs +3 -2
- package/src/session-runtime/model-route/set-route.mjs +114 -34
- package/src/session-runtime/model-route/web-search-route.mjs +7 -2
- package/src/session-runtime/model-route-api.mjs +4 -2
- package/src/session-runtime/native-web-search/routes.mjs +37 -24
- package/src/session-runtime/native-web-search.mjs +9 -8
- package/src/session-runtime/provider-auth/setup.mjs +4 -0
- package/src/session-runtime/provider-models/catalog-load.mjs +9 -0
- package/src/session-runtime/provider-models/web-search-catalog.mjs +23 -3
- package/src/session-runtime/provider-models.mjs +1 -0
- package/src/session-runtime/provider-usage.mjs +4 -0
- package/src/session-runtime/route-resolution.mjs +3 -1
- package/src/session-runtime/runtime-core.mjs +4 -0
- package/src/session-runtime/runtime-facade.mjs +6 -0
- package/src/session-runtime/services/agent-task-status.mjs +2 -0
- package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +0 -3
- package/src/session-runtime/services/agent-tool/close-flow.mjs +6 -7
- package/src/session-runtime/services/agent-tool/execute-send.mjs +2 -2
- package/src/session-runtime/services/agent-tool/execute-spawn.mjs +36 -3
- package/src/session-runtime/services/agent-tool/execute.mjs +9 -1
- package/src/session-runtime/services/agent-tool/helpers.mjs +30 -7
- package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +7 -2
- package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +6 -3
- package/src/session-runtime/services/agent-tool/lead-worker-index/lead-rows.mjs +3 -4
- package/src/session-runtime/services/agent-tool/lead-worker-index/reap-timers.mjs +9 -3
- package/src/session-runtime/services/agent-tool/lead-worker-index/row-settlement.mjs +7 -18
- package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +9 -6
- package/src/session-runtime/services/agent-tool/model-select.mjs +82 -0
- package/src/session-runtime/services/agent-tool/send-flow.mjs +24 -6
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +3 -1
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -5
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +5 -4
- package/src/session-runtime/services/agent-tool/spawn-flow.mjs +2 -1
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
- package/src/session-runtime/services/agent-tool/tag-registry/tag-lookup.mjs +1 -3
- package/src/session-runtime/services/agent-tool/tag-registry/tag-refresh.mjs +1 -3
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +3 -9
- package/src/session-runtime/services/agent-tool/terminal-reaper/scan-admission.mjs +8 -41
- package/src/session-runtime/services/agent-tool/terminal-reaper/stale-rows.mjs +3 -3
- package/src/session-runtime/services/agent-tool/terminal-reaper.mjs +6 -23
- package/src/session-runtime/services/agent-tool/terminal-trace.mjs +0 -1
- package/src/session-runtime/services/agent-tool/tool-def.mjs +7 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +32 -20
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -2
- package/src/session-runtime/services/agent-tool/worker-index.mjs +28 -8
- package/src/session-runtime/services/agent-tool.mjs +4 -0
- package/src/session-runtime/services/agent-watchdog-registry.mjs +2 -0
- package/src/session-runtime/services/provider-admin.mjs +77 -0
- package/src/session-runtime/session-create/session-options.mjs +7 -5
- package/src/session-runtime/session-create/session-start-hook.mjs +2 -1
- package/src/session-runtime/session-transcript.mjs +4 -5
- package/src/session-runtime/session-turn-api.mjs +3 -9
- package/src/session-runtime/settings-builtin-tools-api.mjs +9 -6
- package/src/session-runtime/setup-tool/executor.mjs +23 -5
- package/src/session-runtime/setup-tool/extended-actions.mjs +6 -6
- package/src/session-runtime/setup-tool/settings-contract.mjs +7 -2
- package/src/session-runtime/setup-tool/tool-defs.mjs +13 -1
- package/src/session-runtime/tool-policy-refresh.mjs +83 -32
- package/src/session-runtime/turn/agent-ops.mjs +49 -2
- package/src/session-runtime/turn/side-question-ops.mjs +1 -4
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +2 -0
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +4 -0
- package/src/session-runtime/turn/turn-run/turn-settle.mjs +8 -2
- package/src/session-runtime/turn/turn-run.mjs +1 -36
- package/src/session-runtime/usage-stats-api.mjs +4 -1
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +16 -1
- package/src/session-runtime/workspace/admission-gate.mjs +44 -15
- package/src/session-runtime/workspace/project-branches.mjs +28 -36
- package/src/session-runtime/workspace/transition-steps.mjs +96 -95
- package/src/session-runtime/workspace/transitions.mjs +248 -68
- package/src/standalone/channel-transport.mjs +3 -4
- package/src/standalone/daemon-desktop-runtime.mjs +32 -1
- package/src/standalone/daemon-pg-stop.mjs +1 -3
- package/src/standalone/daemon-session-control.mjs +88 -0
- package/src/standalone/daemon-stored-session-views.mjs +23 -3
- package/src/standalone/daemon.mjs +2 -0
- package/src/standalone/local-session-runtime.mjs +11 -12
- package/src/standalone/session-protocol.mjs +7 -0
- package/src/standalone/session-runtime-inline-host.mjs +2 -0
- package/src/standalone/session-service/agent-tree/agent-cancel.mjs +91 -49
- package/src/standalone/session-service/agent-tree/agent-child.mjs +19 -1
- package/src/standalone/session-service/agent-tree/agent-registry.mjs +25 -0
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +9 -1
- package/src/standalone/session-service/agent-tree.mjs +28 -2
- package/src/standalone/session-service/entries/session-loads.mjs +16 -5
- package/src/standalone/session-service/entries.mjs +1 -0
- package/src/standalone/session-service/projection/transcript-window.mjs +39 -0
- package/src/standalone/session-service/quota-resume.mjs +28 -13
- package/src/standalone/session-service/session-calls/session-turns.mjs +17 -6
- package/src/standalone/session-service/session-calls/session-views.mjs +6 -0
- package/src/standalone/session-tool-output.mjs +82 -0
- package/src/tui/app/app-view.jsx +2 -1
- package/src/tui/app/provider-setup/provider-items.mjs +8 -0
- package/src/tui/app/provider-setup-picker.mjs +15 -3
- package/src/tui/app/slash-commands.mjs +6 -0
- package/src/tui/app/slash-dispatch/session-commands.mjs +49 -0
- package/src/tui/components/Spinner.jsx +4 -0
- package/src/tui/components/SpinnerByline.jsx +21 -2
- package/src/tui/components/status-line/local-line.mjs +10 -2
- package/src/tui/dist/index.mjs +478 -251
- package/src/tui/session/active-tool-summary.mjs +1 -1
- package/src/tui/session/context-state.mjs +0 -3
- package/src/tui/session/goal-continuation/continuation-queue.mjs +2 -3
- package/src/tui/session/goal-continuation/deadline-delivery.mjs +4 -2
- package/src/tui/session/goal-continuation/session-boundary.mjs +21 -0
- package/src/tui/session/quota-resume.mjs +140 -61
- package/src/tui/session/session-api/commands.mjs +4 -10
- package/src/tui/session/session-api/controls.mjs +12 -0
- package/src/tui/session/session-api/lifecycle/dispose.mjs +2 -9
- package/src/tui/session/session-api/lifecycle/resume.mjs +2 -7
- package/src/tui/session/session-api/lifecycle.mjs +13 -11
- package/src/tui/session/session-api/media.mjs +2 -0
- package/src/tui/session/session-api/routes.mjs +81 -57
- package/src/tui/session/session-api/settings.mjs +3 -3
- package/src/tui/session/session-api/worktree.mjs +3 -2
- package/src/tui/session/session-flow/auto-clear.mjs +1 -3
- package/src/tui/session/session-flow/drain.mjs +6 -4
- package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -0
- package/src/tui/session/session-flow/queue/take-entries.mjs +2 -1
- package/src/tui/session/session-flow/queue.mjs +1 -1
- package/src/tui/session/tool-approval.mjs +17 -8
- package/src/tui/session/transcript-store/streaming-tail.mjs +1 -1
- package/src/tui/session/tui-steering-persist.mjs +3 -5
- package/src/tui/session/turn-quiet.mjs +53 -0
- package/src/tui/session/turn-stream.mjs +5 -1
- package/src/tui/session/turn.mjs +38 -31
- package/src/tui/session/worktree-setup-notice.mjs +38 -0
- package/src/tui/spinner-meta.mjs +10 -0
- package/src/tui/spinner-verbs.mjs +3 -0
- package/src/ui/statusline.mjs +13 -3
- package/src/runtime/agent/orchestrator/providers/model-pricing-rates.mjs +0 -1
- package/src/runtime/agent/orchestrator/session/manager/foreign-pending-messages.mjs +0 -346
- package/src/runtime/agent/orchestrator/session/manager/session-owner-liveness.mjs +0 -117
- package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +0 -302
- package/src/session-runtime/services/agent-tool/terminal-reaper/reap-timers.mjs +0 -142
- package/src/session-runtime/turn/share-ops.mjs +0 -77
- package/src/tui/session/cross-surface-share.mjs +0 -406
- package/src/tui/session/live-share/delta.mjs +0 -153
- package/src/tui/session/live-share/mirror.mjs +0 -106
- package/src/tui/session/live-share/owner.mjs +0 -254
- package/src/tui/session/live-share/retry.mjs +0 -54
- package/src/tui/session/live-share/viewer/frame-router.mjs +0 -32
- package/src/tui/session/live-share/viewer/pipe-client.mjs +0 -108
- package/src/tui/session/live-share/viewer/submit-acks.mjs +0 -65
- package/src/tui/session/live-share/viewer/sync-waiters.mjs +0 -53
- package/src/tui/session/live-share/viewer.mjs +0 -99
- package/src/tui/session/live-share/wire.mjs +0 -44
- package/src/tui/session/live-share.mjs +0 -154
- package/src/tui/session/shared-dir-watch.mjs +0 -69
|
@@ -1,406 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* src/tui/session/cross-surface-share.mjs - owner/viewer wiring for one session
|
|
3
|
-
* opened on several surfaces (terminal + desktop pane).
|
|
4
|
-
*
|
|
5
|
-
* Presence + the durable pending spool remain the ownership/base layer; a
|
|
6
|
-
* local pipe layer (live-share) streams frame deltas so co-open surfaces
|
|
7
|
-
* mirror each other in real time.
|
|
8
|
-
* - presence: mark OUR current session as held open (idle included) so a
|
|
9
|
-
* cross-open elsewhere attaches as a viewer instead of splitting
|
|
10
|
-
* ownership; cleared on session switch here and on dispose
|
|
11
|
-
* (session-api-ext), with sidecar staleness covering crashes.
|
|
12
|
-
* - owner leg: host the live pipe, push transcript/tail/spinner deltas,
|
|
13
|
-
* and run foreign submits through the normal queue — full user bubble +
|
|
14
|
-
* streaming on every surface. The spool drain stays as the fallback
|
|
15
|
-
* intake (instant via fs.watch, 3s tick as safety net).
|
|
16
|
-
* - viewer leg: connect to the owner's pipe and mirror its live state.
|
|
17
|
-
* While the pipe is up the disk-mtime re-resume is skipped (no turn-end
|
|
18
|
-
* flicker); when the owner disappears the quiet re-resume promotes this
|
|
19
|
-
* surface to real ownership.
|
|
20
|
-
*/
|
|
21
|
-
import { statSync } from 'node:fs';
|
|
22
|
-
import { basename, dirname } from 'node:path';
|
|
23
|
-
import { sessionPath } from '../../runtime/agent/orchestrator/session/store/paths-heartbeat.mjs';
|
|
24
|
-
import { createLiveShare, forwardViewerSubmit, liveSharePipePath } from './live-share.mjs';
|
|
25
|
-
import { promptDisplayText } from './queue-helpers.mjs';
|
|
26
|
-
import { sharedDirWatch } from './shared-dir-watch.mjs';
|
|
27
|
-
|
|
28
|
-
const SPOOL_WATCH_DEBOUNCE_MS = 120;
|
|
29
|
-
const OWNER_CLOSED_PROMOTE_DELAY_MS = 1500;
|
|
30
|
-
const REMOTE_ATTACH_TICK_MS = 3000;
|
|
31
|
-
// A healthy local owner serves its full frame on connect within tens of ms; a
|
|
32
|
-
// dead presence sidecar or an owner blocked mid-turn never will. The old
|
|
33
|
-
// 1500ms default made exactly those sessions stall entry for the full budget
|
|
34
|
-
// (measured 1571/1519ms resumes — user: 세션 로드가 가끔 매우 느림). Cap the
|
|
35
|
-
// boundary wait low: late owner frames still land through viewerApply and
|
|
36
|
-
// simply replace the disk restore when they arrive.
|
|
37
|
-
const VIEWER_SYNC_WAIT_MS = 400;
|
|
38
|
-
|
|
39
|
-
// The spool drain waits for its cross-process lock OFF the event loop now, so
|
|
40
|
-
// a watch event and the 3s tick can overlap: one drain at a time.
|
|
41
|
-
function createRemoteInjectionDrain({ runtime, bag }) {
|
|
42
|
-
let inFlight = null;
|
|
43
|
-
return () => {
|
|
44
|
-
if (inFlight) return inFlight;
|
|
45
|
-
const run = (async () => {
|
|
46
|
-
const injected = (await runtime.takeRemoteInjections?.()) || [];
|
|
47
|
-
if (injected.length === 0) return;
|
|
48
|
-
for (const item of injected) {
|
|
49
|
-
if (!item || typeof item !== 'object' || (item.text == null && item.content == null)) continue;
|
|
50
|
-
const content = item.content ?? item.text;
|
|
51
|
-
const id = typeof item.id === 'string' && item.id.trim() ? item.id.trim() : undefined;
|
|
52
|
-
bag.enqueue(content, {
|
|
53
|
-
...(item.options && typeof item.options === 'object' ? item.options : {}),
|
|
54
|
-
...(item.text ? { displayText: item.text } : {}),
|
|
55
|
-
...(id ? { id } : {}),
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
void bag.drain();
|
|
59
|
-
})().catch(() => {
|
|
60
|
-
/* the watch/tick pair retries */
|
|
61
|
-
});
|
|
62
|
-
const tracked = run.finally(() => {
|
|
63
|
-
if (inFlight === tracked) inFlight = null;
|
|
64
|
-
});
|
|
65
|
-
inFlight = tracked;
|
|
66
|
-
return tracked;
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const quietResume = (api, id) =>
|
|
71
|
-
void Promise.resolve(api.resume(id, { quiet: true })).catch(() => {
|
|
72
|
-
/* next tick retries */
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
function createSessionLiveShare({ api, bag, flags, getState, getPublishedState, listeners, set, createShare }) {
|
|
76
|
-
const liveShare = createShare({
|
|
77
|
-
ownerSessionId: () => {
|
|
78
|
-
const state = getState();
|
|
79
|
-
return flags.disposed || flags.pendingSessionReset || state.sessionRemoteAttached
|
|
80
|
-
? ''
|
|
81
|
-
: String(state.sessionId || '');
|
|
82
|
-
},
|
|
83
|
-
viewerSessionId: () => {
|
|
84
|
-
const state = getState();
|
|
85
|
-
return flags.disposed || !state.sessionRemoteAttached ? '' : String(state.sessionId || '');
|
|
86
|
-
},
|
|
87
|
-
socketPathFor: (id) => liveSharePipePath(id, sessionPath(id)),
|
|
88
|
-
getPublishedState,
|
|
89
|
-
listeners,
|
|
90
|
-
onRemoteSubmit: (prompt, meta) => {
|
|
91
|
-
// A refusal must be REPORTED, never silent. This session runtime can be unable to
|
|
92
|
-
// take a foreign prompt (disposed, or it became an attached viewer
|
|
93
|
-
// itself); swallowing it here is what made a submitted message vanish
|
|
94
|
-
// with no transcript row and no error (user: 입력이 씹힘). The ack sent
|
|
95
|
-
// by live-share carries this verdict back so the sender can re-deliver.
|
|
96
|
-
if (flags.disposed || getState().sessionRemoteAttached) return false;
|
|
97
|
-
// Preserve the viewer's submission id end-to-end: the queue entry and
|
|
98
|
-
// the settled user item then carry the id the submitting surface used
|
|
99
|
-
// for its optimistic row, so that row releases instead of duplicating.
|
|
100
|
-
const queued = bag.enqueue(prompt, meta && typeof meta === 'object' ? meta : {}) !== false;
|
|
101
|
-
void bag.drain();
|
|
102
|
-
return queued;
|
|
103
|
-
},
|
|
104
|
-
onRemoteAbort: () => {
|
|
105
|
-
// Forwarded viewer stop: interrupt OUR active turn (we are the owner).
|
|
106
|
-
if (flags.disposed || getState().sessionRemoteAttached) return;
|
|
107
|
-
try {
|
|
108
|
-
api.abort?.();
|
|
109
|
-
} catch {
|
|
110
|
-
/* abort is best-effort */
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
onOwnerClosed: (id) => {
|
|
114
|
-
// Owner left (clean close or crash): promote via the normal quiet
|
|
115
|
-
// re-resume once its final save/presence-clear has landed.
|
|
116
|
-
const timer = setTimeout(() => {
|
|
117
|
-
const state = getState();
|
|
118
|
-
if (flags.disposed || !state.sessionRemoteAttached) return;
|
|
119
|
-
if (String(state.sessionId || '') !== id) return;
|
|
120
|
-
if (liveShare.viewerConnected()) return;
|
|
121
|
-
quietResume(api, id);
|
|
122
|
-
}, OWNER_CLOSED_PROMOTE_DELAY_MS);
|
|
123
|
-
timer.unref?.();
|
|
124
|
-
},
|
|
125
|
-
viewerApply: {
|
|
126
|
-
getState,
|
|
127
|
-
set,
|
|
128
|
-
replaceItems: (...args) => bag.replaceItems(...args),
|
|
129
|
-
patchItem: (...args) => bag.patchItem(...args),
|
|
130
|
-
appendItems: (...args) => bag.appendItems(...args),
|
|
131
|
-
updateStreamingTail: (...args) => bag.updateStreamingTail(...args),
|
|
132
|
-
clearStreamingTail: (...args) => bag.clearStreamingTail(...args),
|
|
133
|
-
},
|
|
134
|
-
});
|
|
135
|
-
return liveShare;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Viewer legs ride the owner's pipe (instant user bubble + shared streaming);
|
|
139
|
-
// the durable spool remains the fallback. Every intake boundary the daemon or
|
|
140
|
-
// UI can reach (submit, submitAsync, abort) is wrapped, otherwise a hosted
|
|
141
|
-
// viewer queues the prompt locally and its own user row stands beside the
|
|
142
|
-
// owner's mirrored twin.
|
|
143
|
-
function wrapViewerApi({ api, bag, runtime, liveShare, getState, ensureLiveShare }) {
|
|
144
|
-
// Returns null when this surface is NOT an attached viewer, so the local
|
|
145
|
-
// session runtime keeps the prompt.
|
|
146
|
-
const viewerSubmitIntake = (prompt, options = {}) => {
|
|
147
|
-
if (!getState().sessionRemoteAttached) return null;
|
|
148
|
-
const text = String(promptDisplayText(prompt, options) || '').trim();
|
|
149
|
-
if (!text) return { accepted: false };
|
|
150
|
-
return {
|
|
151
|
-
accepted: forwardViewerSubmit({
|
|
152
|
-
prompt,
|
|
153
|
-
text,
|
|
154
|
-
options,
|
|
155
|
-
share: liveShare,
|
|
156
|
-
// Writing to the owner's spool instead of starting a fake local turn
|
|
157
|
-
// that would render an error/synthetic assistant message here.
|
|
158
|
-
spool: (submissionId) =>
|
|
159
|
-
runtime.enqueueRemoteAttachedPrompt?.({
|
|
160
|
-
content: prompt,
|
|
161
|
-
text,
|
|
162
|
-
id: submissionId,
|
|
163
|
-
options,
|
|
164
|
-
}) === true,
|
|
165
|
-
}),
|
|
166
|
-
};
|
|
167
|
-
};
|
|
168
|
-
if (typeof api.submit === 'function') {
|
|
169
|
-
const baseSubmit = api.submit;
|
|
170
|
-
api.submit = (prompt, options = {}) => {
|
|
171
|
-
const forwarded = viewerSubmitIntake(prompt, options);
|
|
172
|
-
return forwarded ? forwarded.accepted : baseSubmit(prompt, options);
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
if (typeof api.submitAsync === 'function') {
|
|
176
|
-
const baseSubmitAsync = api.submitAsync;
|
|
177
|
-
api.submitAsync = async (prompt, options = {}) => {
|
|
178
|
-
const forwarded = viewerSubmitIntake(prompt, options);
|
|
179
|
-
return forwarded ? forwarded.accepted : baseSubmitAsync(prompt, options);
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
// Viewer stop button: the local session runtime has no in-flight turn to
|
|
183
|
-
// cancel — forward the interrupt to the owner over the pipe. Falls back to
|
|
184
|
-
// the local abort (no-op safe) when the pipe is down.
|
|
185
|
-
if (typeof api.abort === 'function') {
|
|
186
|
-
const baseAbort = api.abort;
|
|
187
|
-
api.abort = (...args) => {
|
|
188
|
-
if (getState().sessionRemoteAttached && liveShare.viewerConnected() && liveShare.sendAbort()) {
|
|
189
|
-
return true;
|
|
190
|
-
}
|
|
191
|
-
return baseAbort(...args);
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
// Attach-time pipe fast-path: session entry (resume) reconciles the live
|
|
195
|
-
// pipe IMMEDIATELY instead of waiting for the 3s share tick. The attach
|
|
196
|
-
// render comes from the last disk save WITHOUT the in-flight turn, so that
|
|
197
|
-
// tick-wide window is exactly when a running tool call / mid-turn
|
|
198
|
-
// conversation looks missing and then pops in late (user report). The
|
|
199
|
-
// owner leg benefits equally: its pipe server starts the moment the
|
|
200
|
-
// session opens, so cross-surface viewers can connect at once.
|
|
201
|
-
for (const method of ['resume', 'newSession', 'switchContext']) {
|
|
202
|
-
if (typeof api[method] !== 'function') continue;
|
|
203
|
-
const base = api[method].bind(api);
|
|
204
|
-
api[method] = async (...args) => {
|
|
205
|
-
const result = await base(...args);
|
|
206
|
-
bag.cancelQueuedGoalContinuations?.();
|
|
207
|
-
bag.refreshGoalState?.();
|
|
208
|
-
bag.scheduleGoalContinuation?.();
|
|
209
|
-
ensureLiveShare();
|
|
210
|
-
if (method === 'resume' && result === true && getState().sessionRemoteAttached) {
|
|
211
|
-
const id = String(getState().sessionId || '');
|
|
212
|
-
// The session projection holds renderer publications across resume.
|
|
213
|
-
// Keep that hold until the owner's first FULL frame replaces the
|
|
214
|
-
// persisted transcript, then synchronously publish the complete draft
|
|
215
|
-
// before getState().
|
|
216
|
-
if (id && (await liveShare.waitForViewerSync(id, VIEWER_SYNC_WAIT_MS))) bag.flushEmit();
|
|
217
|
-
}
|
|
218
|
-
return result;
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Instant input pickup: watch the shared pending spool so an attached
|
|
224
|
-
// surface's fallback submit reaches this owner immediately instead of on the
|
|
225
|
-
// 3s tick. Best-effort — the tick remains the safety net. The spool sits
|
|
226
|
-
// directly in the data dir, so that is the narrowest directory to watch; the
|
|
227
|
-
// handle is shared by every session in the process (shared-dir-watch.mjs)
|
|
228
|
-
// and each session keeps its own filter/debounce below.
|
|
229
|
-
function startSpoolWatcher({ runtime, flags, getState, drainRemoteInjections, watchDir }) {
|
|
230
|
-
let release = null;
|
|
231
|
-
let debounce = null;
|
|
232
|
-
try {
|
|
233
|
-
const spoolPath = String(runtime.pendingSpoolPath?.() || '');
|
|
234
|
-
if (spoolPath) {
|
|
235
|
-
const spoolFile = basename(spoolPath);
|
|
236
|
-
release = watchDir(dirname(spoolPath), (_event, filename) => {
|
|
237
|
-
if (filename && String(filename) !== spoolFile) return;
|
|
238
|
-
if (flags.disposed || debounce) return;
|
|
239
|
-
debounce = setTimeout(() => {
|
|
240
|
-
debounce = null;
|
|
241
|
-
if (flags.disposed || flags.pendingSessionReset) return;
|
|
242
|
-
const state = getState();
|
|
243
|
-
if (state.busy || state.commandBusy || state.sessionRemoteAttached) return;
|
|
244
|
-
try {
|
|
245
|
-
void drainRemoteInjections();
|
|
246
|
-
} catch {
|
|
247
|
-
/* tick fallback */
|
|
248
|
-
}
|
|
249
|
-
}, SPOOL_WATCH_DEBOUNCE_MS);
|
|
250
|
-
debounce.unref?.();
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
} catch {
|
|
254
|
-
/* spool watch is an optimization; the 3s tick remains */
|
|
255
|
-
}
|
|
256
|
-
return {
|
|
257
|
-
close: () => {
|
|
258
|
-
release?.();
|
|
259
|
-
release = null;
|
|
260
|
-
if (debounce) {
|
|
261
|
-
clearTimeout(debounce);
|
|
262
|
-
debounce = null;
|
|
263
|
-
}
|
|
264
|
-
},
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Viewer leg of the tick: follow the owner live over the pipe, otherwise
|
|
269
|
-
// re-resume from disk when the owner saved or disappeared.
|
|
270
|
-
function createViewerTick({ runtime, api, liveShare, getState }) {
|
|
271
|
-
let storeMtime = 0;
|
|
272
|
-
return {
|
|
273
|
-
reset: () => {
|
|
274
|
-
storeMtime = 0;
|
|
275
|
-
},
|
|
276
|
-
run: () => {
|
|
277
|
-
const id = String(getState().sessionId || '');
|
|
278
|
-
if (!id) return;
|
|
279
|
-
// Pipe-connected viewers follow the owner live; the disk-mtime
|
|
280
|
-
// re-resume would only reload mid-stream state and flicker.
|
|
281
|
-
if (liveShare.viewerConnected()) {
|
|
282
|
-
storeMtime = 0;
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
// Self-heal: a force-killed owner never announces onOwnerClosed and
|
|
286
|
-
// its final save never bumps the store mtime, so without this probe
|
|
287
|
-
// the surface stays a viewer forever, spooling messages to nobody.
|
|
288
|
-
// When the resume guard says the owner is gone, promote via the same
|
|
289
|
-
// quiet re-resume (it drains the pending spool on the next tick).
|
|
290
|
-
if (runtime.sessionOwnerGone?.(id) === true) {
|
|
291
|
-
storeMtime = 0;
|
|
292
|
-
quietResume(api, id);
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
let mtime = 0;
|
|
296
|
-
try {
|
|
297
|
-
mtime = statSync(sessionPath(id)).mtimeMs || 0;
|
|
298
|
-
} catch {
|
|
299
|
-
return;
|
|
300
|
-
}
|
|
301
|
-
// First attached tick only baselines: the resume that attached this
|
|
302
|
-
// surface already loaded the current on-disk transcript.
|
|
303
|
-
if (!storeMtime) {
|
|
304
|
-
storeMtime = mtime;
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
if (mtime > storeMtime) {
|
|
308
|
-
storeMtime = mtime;
|
|
309
|
-
quietResume(api, id);
|
|
310
|
-
}
|
|
311
|
-
},
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
function startRemoteAttachTicker({ runtime, api, flags, getState, liveShare, spoolWatcher, drainRemoteInjections }) {
|
|
316
|
-
let heldPresenceId = '';
|
|
317
|
-
const viewerTick = createViewerTick({ runtime, api, liveShare, getState });
|
|
318
|
-
const timer = setInterval(() => {
|
|
319
|
-
if (flags.disposed) {
|
|
320
|
-
clearInterval(timer);
|
|
321
|
-
try {
|
|
322
|
-
liveShare.dispose();
|
|
323
|
-
} catch {
|
|
324
|
-
/* best-effort */
|
|
325
|
-
}
|
|
326
|
-
spoolWatcher.close();
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
if (flags.pendingSessionReset) return;
|
|
330
|
-
try {
|
|
331
|
-
const heldId = runtime.publishSessionPresence?.() || '';
|
|
332
|
-
if (heldPresenceId && heldPresenceId !== heldId) runtime.clearSessionPresence?.(heldPresenceId);
|
|
333
|
-
heldPresenceId = heldId;
|
|
334
|
-
} catch {
|
|
335
|
-
/* best-effort */
|
|
336
|
-
}
|
|
337
|
-
try {
|
|
338
|
-
liveShare.ensure();
|
|
339
|
-
} catch {
|
|
340
|
-
/* next tick retries */
|
|
341
|
-
}
|
|
342
|
-
try {
|
|
343
|
-
const state = getState();
|
|
344
|
-
if (state.busy || state.commandBusy) return;
|
|
345
|
-
if (state.sessionRemoteAttached) {
|
|
346
|
-
viewerTick.run();
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
viewerTick.reset();
|
|
350
|
-
void drainRemoteInjections();
|
|
351
|
-
} catch {
|
|
352
|
-
/* best-effort */
|
|
353
|
-
}
|
|
354
|
-
}, REMOTE_ATTACH_TICK_MS);
|
|
355
|
-
timer.unref?.();
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
export function attachCrossSurfaceShare({
|
|
359
|
-
runtime,
|
|
360
|
-
api,
|
|
361
|
-
bag,
|
|
362
|
-
flags,
|
|
363
|
-
getState,
|
|
364
|
-
getPublishedState,
|
|
365
|
-
listeners,
|
|
366
|
-
set,
|
|
367
|
-
// Test seams: the pipe layer is replaced by a fake share in unit tests, and
|
|
368
|
-
// the process-wide directory watch by an isolated registry.
|
|
369
|
-
createShare = createLiveShare,
|
|
370
|
-
watchDir = sharedDirWatch.subscribe,
|
|
371
|
-
}) {
|
|
372
|
-
const drainRemoteInjections = createRemoteInjectionDrain({ runtime, bag });
|
|
373
|
-
const liveShare = createSessionLiveShare({
|
|
374
|
-
api,
|
|
375
|
-
bag,
|
|
376
|
-
flags,
|
|
377
|
-
getState,
|
|
378
|
-
getPublishedState,
|
|
379
|
-
listeners,
|
|
380
|
-
set,
|
|
381
|
-
createShare,
|
|
382
|
-
});
|
|
383
|
-
// Immediate live-share reconcile for session entry/promotion. Waiting for
|
|
384
|
-
// the 3s share tick left a just-resumed live-owned session showing the
|
|
385
|
-
// stale disk snapshot, then full-swapped the transcript mid-view once the
|
|
386
|
-
// pipe finally connected (visible up/down lurch until heights resettled).
|
|
387
|
-
// resume() calls this right after installing the restored items so the
|
|
388
|
-
// owner's full frame lands at the entry boundary instead of seconds later.
|
|
389
|
-
const ensureLiveShare = () => {
|
|
390
|
-
try {
|
|
391
|
-
liveShare.ensure();
|
|
392
|
-
} catch {
|
|
393
|
-
/* share tick retries */
|
|
394
|
-
}
|
|
395
|
-
};
|
|
396
|
-
bag.ensureLiveShare = ensureLiveShare;
|
|
397
|
-
// Pulse guard: while this surface is an attached viewer with a live pipe,
|
|
398
|
-
// owner frames own stats/agent/tool state (see the runtime pulse timer).
|
|
399
|
-
bag.liveShareMirroring = () => getState().sessionRemoteAttached && liveShare.viewerConnected();
|
|
400
|
-
wrapViewerApi({ api, bag, runtime, liveShare, getState, ensureLiveShare });
|
|
401
|
-
// Cover session runtimes whose runtime already has a session at construction
|
|
402
|
-
// time; do not wait for a lifecycle method or the 3s safety pulse to open the pipe.
|
|
403
|
-
ensureLiveShare();
|
|
404
|
-
const spoolWatcher = startSpoolWatcher({ runtime, flags, getState, drainRemoteInjections, watchDir });
|
|
405
|
-
startRemoteAttachTicker({ runtime, api, flags, getState, liveShare, spoolWatcher, drainRemoteInjections });
|
|
406
|
-
}
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* src/tui/session/live-share/delta.mjs - owner-side frame shaping: the live
|
|
3
|
-
* state subset mirrored to viewers, the full snapshot frame, and the
|
|
4
|
-
* frame-to-frame delta against the last published baseline.
|
|
5
|
-
*/
|
|
6
|
-
// Above this many patched rows per frame the delta degenerates to a full
|
|
7
|
-
// items push (bulk rewrites: compaction, clear, history restore).
|
|
8
|
-
const MAX_FRAME_PATCHES = 48;
|
|
9
|
-
|
|
10
|
-
// Live-state mirror (attach parity): the transcript alone left an attached
|
|
11
|
-
// viewer blind to the owner's activity — busy/stop state, the queued
|
|
12
|
-
// follow-up list, the web-search summary, agent workers/jobs, and the
|
|
13
|
-
// context gauge stats all live in owner process state. Mirror a compact
|
|
14
|
-
// subset so viewer surfaces render them natively. queued entries are
|
|
15
|
-
// projected to display fields only (content parts may carry images).
|
|
16
|
-
const LIVE_STATS_KEYS = [
|
|
17
|
-
'currentContextSource',
|
|
18
|
-
'currentContextTokens',
|
|
19
|
-
'currentEstimatedContextTokens',
|
|
20
|
-
'currentContextUpdatedAt',
|
|
21
|
-
'costUsd',
|
|
22
|
-
'turns',
|
|
23
|
-
'inputTokens',
|
|
24
|
-
'outputTokens',
|
|
25
|
-
'latestInputTokens',
|
|
26
|
-
'latestPromptTokens',
|
|
27
|
-
'contextTokens',
|
|
28
|
-
];
|
|
29
|
-
|
|
30
|
-
function liveStateOf(st) {
|
|
31
|
-
const stats = st.stats && typeof st.stats === 'object' ? st.stats : {};
|
|
32
|
-
const statsSubset = {};
|
|
33
|
-
for (const key of LIVE_STATS_KEYS) {
|
|
34
|
-
if (stats[key] !== undefined) statsSubset[key] = stats[key];
|
|
35
|
-
}
|
|
36
|
-
return {
|
|
37
|
-
busy: st.busy === true,
|
|
38
|
-
commandBusy: st.commandBusy === true,
|
|
39
|
-
queued: (Array.isArray(st.queued) ? st.queued : []).map((entry) => ({
|
|
40
|
-
id: entry?.id,
|
|
41
|
-
text: String(entry?.displayText ?? entry?.text ?? entry?.message ?? '').slice(0, 2000),
|
|
42
|
-
...(entry?.enqueuedAt ? { enqueuedAt: entry.enqueuedAt } : {}),
|
|
43
|
-
})),
|
|
44
|
-
activeToolSummary: st.activeToolSummary || null,
|
|
45
|
-
activeTools: st.activeTools || null,
|
|
46
|
-
agentWorkers: Array.isArray(st.agentWorkers) ? st.agentWorkers : [],
|
|
47
|
-
agentJobs: Array.isArray(st.agentJobs) ? st.agentJobs : [],
|
|
48
|
-
ownerClientHostPid: Number(st.clientHostPid) || process.pid,
|
|
49
|
-
displayContextWindow: Number(st.displayContextWindow) || 0,
|
|
50
|
-
compactBoundaryTokens: Number(st.compactBoundaryTokens) || 0,
|
|
51
|
-
autoCompactTokenLimit: Number(st.autoCompactTokenLimit) || 0,
|
|
52
|
-
stats: statsSubset,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function fullFrame(sessionId, st) {
|
|
57
|
-
return {
|
|
58
|
-
t: 'full',
|
|
59
|
-
sessionId,
|
|
60
|
-
items: Array.isArray(st.items) ? st.items : [],
|
|
61
|
-
tail: st.streamingTail || null,
|
|
62
|
-
spinner: st.spinner || null,
|
|
63
|
-
live: liveStateOf(st),
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// References the next delta is computed against. Pure pointer assignments,
|
|
68
|
-
// so re-baselining on every publish with no viewers is effectively free.
|
|
69
|
-
export function baselineOf(st, liveSig = JSON.stringify(liveStateOf(st))) {
|
|
70
|
-
return { items: st.items, tail: st.streamingTail, spinner: st.spinner, liveSig };
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export const EMPTY_BASELINE = Object.freeze({ items: null, tail: null, spinner: null, liveSig: '' });
|
|
74
|
-
|
|
75
|
-
function itemsDelta(prevItems, nextItems) {
|
|
76
|
-
const prev = Array.isArray(prevItems) ? prevItems : [];
|
|
77
|
-
const next = Array.isArray(nextItems) ? nextItems : [];
|
|
78
|
-
let structural = next.length < prev.length;
|
|
79
|
-
const changed = [];
|
|
80
|
-
if (!structural) {
|
|
81
|
-
for (let i = 0; i < prev.length; i++) {
|
|
82
|
-
if (next[i] === prev[i]) continue;
|
|
83
|
-
if (!next[i]?.id || next[i].id !== prev[i]?.id) {
|
|
84
|
-
structural = true;
|
|
85
|
-
break;
|
|
86
|
-
}
|
|
87
|
-
changed.push(next[i]);
|
|
88
|
-
if (changed.length > MAX_FRAME_PATCHES) {
|
|
89
|
-
structural = true;
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
if (structural) return { items: next };
|
|
95
|
-
const delta = {};
|
|
96
|
-
if (changed.length) delta.changed = changed;
|
|
97
|
-
if (next.length > prev.length) delta.appended = next.slice(prev.length);
|
|
98
|
-
return delta;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Streaming text grows append-only frame to frame: ship just the new suffix
|
|
102
|
-
// so long responses stay a few bytes per frame, not O(text).
|
|
103
|
-
function tailDelta(prevTail, nextTail) {
|
|
104
|
-
const next = nextTail || null;
|
|
105
|
-
const prev = prevTail || null;
|
|
106
|
-
const appendOnly =
|
|
107
|
-
next &&
|
|
108
|
-
prev &&
|
|
109
|
-
next.id === prev.id &&
|
|
110
|
-
typeof next.text === 'string' &&
|
|
111
|
-
typeof prev.text === 'string' &&
|
|
112
|
-
next.text.length >= prev.text.length &&
|
|
113
|
-
next.text.startsWith(prev.text);
|
|
114
|
-
if (!appendOnly) return { tail: next };
|
|
115
|
-
const meta = {};
|
|
116
|
-
for (const [key, value] of Object.entries(next)) {
|
|
117
|
-
if (key === 'text') continue;
|
|
118
|
-
if (!Object.is(prev[key], value)) meta[key] = value;
|
|
119
|
-
}
|
|
120
|
-
return {
|
|
121
|
-
tailAppend: {
|
|
122
|
-
id: next.id,
|
|
123
|
-
base: prev.text.length,
|
|
124
|
-
text: next.text.slice(prev.text.length),
|
|
125
|
-
...(Object.keys(meta).length ? { meta } : {}),
|
|
126
|
-
},
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** Delta of `st` against `baseline`; `frame` is null when nothing changed. */
|
|
131
|
-
export function deltaFrame(baseline, st) {
|
|
132
|
-
const frame = { t: 'delta' };
|
|
133
|
-
let dirty = false;
|
|
134
|
-
if (st.items !== baseline.items) {
|
|
135
|
-
Object.assign(frame, itemsDelta(baseline.items, st.items));
|
|
136
|
-
dirty = true;
|
|
137
|
-
}
|
|
138
|
-
if (st.streamingTail !== baseline.tail) {
|
|
139
|
-
Object.assign(frame, tailDelta(baseline.tail, st.streamingTail));
|
|
140
|
-
dirty = true;
|
|
141
|
-
}
|
|
142
|
-
if (st.spinner !== baseline.spinner) {
|
|
143
|
-
frame.spinner = st.spinner || null;
|
|
144
|
-
dirty = true;
|
|
145
|
-
}
|
|
146
|
-
const live = liveStateOf(st);
|
|
147
|
-
const liveSig = JSON.stringify(live);
|
|
148
|
-
if (liveSig !== baseline.liveSig) {
|
|
149
|
-
frame.live = live;
|
|
150
|
-
dirty = true;
|
|
151
|
-
}
|
|
152
|
-
return { frame: dirty ? frame : null, baseline: baselineOf(st, liveSig) };
|
|
153
|
-
}
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* src/tui/session/live-share/mirror.mjs - viewer-side application of owner
|
|
3
|
-
* frames into the local session store.
|
|
4
|
-
*/
|
|
5
|
-
const LIVE_LIMIT_KEYS = ['displayContextWindow', 'compactBoundaryTokens', 'autoCompactTokenLimit'];
|
|
6
|
-
|
|
7
|
-
export function createMirror(viewerApply) {
|
|
8
|
-
const upsertItem = (item) => {
|
|
9
|
-
if (!item || item.id == null) return;
|
|
10
|
-
const items = viewerApply.getState().items;
|
|
11
|
-
const exists = Array.isArray(items) && items.some((it) => it?.id === item.id);
|
|
12
|
-
if (exists) viewerApply.patchItem(item.id, item);
|
|
13
|
-
else viewerApply.appendItems([item]);
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
// Mirror the owner's live-state subset into the viewer store. Context stats
|
|
17
|
-
// merge over the local stats object so unmirrored accumulator fields keep
|
|
18
|
-
// their last local values instead of vanishing.
|
|
19
|
-
const applyLiveState = (live) => {
|
|
20
|
-
if (!live || typeof live !== 'object') return;
|
|
21
|
-
const patch = {
|
|
22
|
-
busy: live.busy === true,
|
|
23
|
-
commandBusy: live.commandBusy === true,
|
|
24
|
-
queued: Array.isArray(live.queued) ? live.queued : [],
|
|
25
|
-
activeToolSummary: live.activeToolSummary || null,
|
|
26
|
-
activeTools: live.activeTools || null,
|
|
27
|
-
agentWorkers: Array.isArray(live.agentWorkers) ? live.agentWorkers : [],
|
|
28
|
-
agentJobs: Array.isArray(live.agentJobs) ? live.agentJobs : [],
|
|
29
|
-
ownerClientHostPid: Number(live.ownerClientHostPid) || 0,
|
|
30
|
-
};
|
|
31
|
-
for (const key of LIVE_LIMIT_KEYS) {
|
|
32
|
-
if (Number(live[key]) > 0) patch[key] = Number(live[key]);
|
|
33
|
-
}
|
|
34
|
-
if (live.stats && typeof live.stats === 'object' && Object.keys(live.stats).length > 0) {
|
|
35
|
-
const current = viewerApply.getState().stats;
|
|
36
|
-
patch.stats = { ...(current && typeof current === 'object' ? current : {}), ...live.stats };
|
|
37
|
-
}
|
|
38
|
-
viewerApply.set(patch);
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
// Owner gone (clean close, crash, or pipe drop): the mirrored activity is
|
|
42
|
-
// no longer authoritative — clear it so the viewer never shows a frozen
|
|
43
|
-
// spinner/queue while the promotion path takes over.
|
|
44
|
-
const clearLiveState = () => {
|
|
45
|
-
try {
|
|
46
|
-
viewerApply?.set?.({
|
|
47
|
-
busy: false,
|
|
48
|
-
commandBusy: false,
|
|
49
|
-
spinner: null,
|
|
50
|
-
queued: [],
|
|
51
|
-
activeToolSummary: null,
|
|
52
|
-
activeTools: null,
|
|
53
|
-
agentWorkers: [],
|
|
54
|
-
agentJobs: [],
|
|
55
|
-
ownerClientHostPid: 0,
|
|
56
|
-
});
|
|
57
|
-
} catch {
|
|
58
|
-
/* viewer store already disposed */
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
// Returns false when a tail suffix did not match the local base — this
|
|
63
|
-
// mirror lost a frame and the caller must request a full resync.
|
|
64
|
-
const applyTailAppend = (tailAppend) => {
|
|
65
|
-
const current = viewerApply.getState().streamingTail;
|
|
66
|
-
const base = Number(tailAppend.base) || 0;
|
|
67
|
-
const matches =
|
|
68
|
-
current && current.id === tailAppend.id && typeof current.text === 'string' && current.text.length === base;
|
|
69
|
-
if (!matches) return false;
|
|
70
|
-
viewerApply.updateStreamingTail(tailAppend.id, {
|
|
71
|
-
...(tailAppend.meta || {}),
|
|
72
|
-
text: current.text + String(tailAppend.text || ''),
|
|
73
|
-
});
|
|
74
|
-
return true;
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
/** Apply one owner frame; false means the mirror is desynced. */
|
|
78
|
-
const applyFrame = (frame) => {
|
|
79
|
-
if (frame.t === 'full') {
|
|
80
|
-
viewerApply.replaceItems(Array.isArray(frame.items) ? frame.items : []);
|
|
81
|
-
if (frame.tail) viewerApply.updateStreamingTail(frame.tail.id, frame.tail, {}, { resetText: true });
|
|
82
|
-
viewerApply.set({ spinner: frame.spinner || null });
|
|
83
|
-
applyLiveState(frame.live);
|
|
84
|
-
return true;
|
|
85
|
-
}
|
|
86
|
-
if (frame.t !== 'delta') return true;
|
|
87
|
-
if (Array.isArray(frame.items)) {
|
|
88
|
-
viewerApply.replaceItems(frame.items, { preserveStreamingTail: true });
|
|
89
|
-
} else {
|
|
90
|
-
for (const item of frame.changed || []) upsertItem(item);
|
|
91
|
-
for (const item of frame.appended || []) upsertItem(item);
|
|
92
|
-
}
|
|
93
|
-
let synced = true;
|
|
94
|
-
if (frame.tailAppend) {
|
|
95
|
-
synced = applyTailAppend(frame.tailAppend);
|
|
96
|
-
} else if ('tail' in frame) {
|
|
97
|
-
if (frame.tail) viewerApply.updateStreamingTail(frame.tail.id, frame.tail, {}, { resetText: true });
|
|
98
|
-
else viewerApply.clearStreamingTail();
|
|
99
|
-
}
|
|
100
|
-
if ('spinner' in frame) viewerApply.set({ spinner: frame.spinner || null });
|
|
101
|
-
if ('live' in frame) applyLiveState(frame.live);
|
|
102
|
-
return synced;
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
return { applyFrame, clearLiveState };
|
|
106
|
-
}
|