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
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/tui/session/worktree-setup-notice.mjs - the one-line transcript notice
|
|
3
|
+
* shown while a new worktree's dependencies install (runtime.onWorktreeSetup).
|
|
4
|
+
* Only runs that install anything reach it; one notice per run, patched in
|
|
5
|
+
* place, so every surface showing the transcript (TUI, desktop, remote) sees
|
|
6
|
+
* the same line. The details (output, failures, skips) go to the agent only.
|
|
7
|
+
*/
|
|
8
|
+
const ENDINGS = {
|
|
9
|
+
completed: 'Worktree dependencies installed.',
|
|
10
|
+
cancelled: 'Installing worktree dependencies was cancelled.',
|
|
11
|
+
};
|
|
12
|
+
const INCOMPLETE = 'Some worktree dependencies were not installed; the agent has the details.';
|
|
13
|
+
|
|
14
|
+
/** Notice text and tone for one install progress event. */
|
|
15
|
+
export function worktreeSetupNotice(event) {
|
|
16
|
+
if (event.final) {
|
|
17
|
+
const text = ENDINGS[event.status] ?? INCOMPLETE;
|
|
18
|
+
return { text, tone: event.status === 'completed' ? 'info' : 'warn' };
|
|
19
|
+
}
|
|
20
|
+
const what = event.label || event.command;
|
|
21
|
+
return { text: `Installing worktree dependencies [${event.index + 1}/${event.total}]: ${what}`, tone: 'info' };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function createWorktreeSetupNotices({ pushItem, patchItem, nextId }) {
|
|
25
|
+
const items = new Map();
|
|
26
|
+
return (event) => {
|
|
27
|
+
const notice = worktreeSetupNotice(event);
|
|
28
|
+
const itemId = items.get(event.id);
|
|
29
|
+
if (itemId === undefined) {
|
|
30
|
+
const id = nextId();
|
|
31
|
+
items.set(event.id, id);
|
|
32
|
+
pushItem({ kind: 'notice', id, ...notice });
|
|
33
|
+
} else {
|
|
34
|
+
patchItem(itemId, notice);
|
|
35
|
+
}
|
|
36
|
+
if (event.final) items.delete(event.id);
|
|
37
|
+
};
|
|
38
|
+
}
|
package/src/tui/spinner-meta.mjs
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
* for 30s. One builder, one answer.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import { quietProgressLabel, quietProgressParts } from '../runtime/shared/quiet-progress.mjs';
|
|
13
|
+
|
|
12
14
|
const compactNumberFormatter = new Intl.NumberFormat('en-US', {
|
|
13
15
|
notation: 'compact',
|
|
14
16
|
maximumFractionDigits: 1,
|
|
@@ -67,9 +69,13 @@ export function buildSpinnerMeta({
|
|
|
67
69
|
thinkingSince = 0,
|
|
68
70
|
thinkingMs = 0,
|
|
69
71
|
effort = '',
|
|
72
|
+
// The turn's published quiet state (`spinner.quiet`) and the clock it is read at.
|
|
73
|
+
quiet = null,
|
|
74
|
+
now = Date.now(),
|
|
70
75
|
verbose: _verbose = false,
|
|
71
76
|
} = {}) {
|
|
72
77
|
const tokens = Math.max(0, Number(outputTokens) || 0);
|
|
78
|
+
const quietParts = quietProgressParts(quiet, now, quiet?.warnMs);
|
|
73
79
|
const thinkingParts = spinnerThinkingParts({ thinking, thinkingSince, thinkingMs, effort });
|
|
74
80
|
const thinkingText = spinnerThinkingLabel({ thinking, thinkingSince, thinkingMs, effort });
|
|
75
81
|
return {
|
|
@@ -83,6 +89,10 @@ export function buildSpinnerMeta({
|
|
|
83
89
|
showTokens: tokens > 0,
|
|
84
90
|
thinkingText,
|
|
85
91
|
thinkingActive: Boolean(thinking || Number(thinkingSince) > 0),
|
|
92
|
+
// Quiet-time cue: structured for the desktop's i18n, English for the TUI;
|
|
93
|
+
// `escalated` once the silence reaches the stall watchdog's warn point.
|
|
94
|
+
quietParts,
|
|
95
|
+
quietText: quietProgressLabel(quietParts),
|
|
86
96
|
};
|
|
87
97
|
}
|
|
88
98
|
|
|
@@ -247,6 +247,9 @@ export const SPINNER_MODE_OVERRIDE_VERBS = {
|
|
|
247
247
|
'auto-clear': 'Auto-clearing conversation',
|
|
248
248
|
resuming: 'Resuming conversation',
|
|
249
249
|
'task-wait': '작업 대기 중',
|
|
250
|
+
// The request is sent and its response headers are pending: the model is
|
|
251
|
+
// queued or thinking server-side.
|
|
252
|
+
'model-thinking': 'Model thinking',
|
|
250
253
|
};
|
|
251
254
|
|
|
252
255
|
/**
|
package/src/ui/statusline.mjs
CHANGED
|
@@ -31,7 +31,11 @@ import {
|
|
|
31
31
|
num,
|
|
32
32
|
formatElapsed,
|
|
33
33
|
} from '../runtime/shared/statusline/statusline-format.mjs';
|
|
34
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
shellJobsStatus,
|
|
36
|
+
memoryCycleStatus,
|
|
37
|
+
unconfirmedShellCount,
|
|
38
|
+
} from '../runtime/shared/statusline/statusline-segments.mjs';
|
|
35
39
|
import {
|
|
36
40
|
agentStatuslinePayload,
|
|
37
41
|
classifyAgentWorkers,
|
|
@@ -397,10 +401,16 @@ function activitySegments({ sessionId, clientHostPid, agentWorkers, agentJobs, a
|
|
|
397
401
|
const n = runningWorkers.length;
|
|
398
402
|
segment(`Running ${n} Agent${n === 1 ? '' : 's'}`, agentsElapsed(runningWorkers));
|
|
399
403
|
}
|
|
400
|
-
|
|
401
|
-
|
|
404
|
+
const unconfirmed = unconfirmedShellCount(shellStatus);
|
|
405
|
+
if (shellStatus.count - unconfirmed > 0) {
|
|
406
|
+
const n = shellStatus.count - unconfirmed;
|
|
402
407
|
segment(`Running ${n} Shell${n === 1 ? '' : 's'}`, shellStatus.elapsedLabel);
|
|
403
408
|
}
|
|
409
|
+
if (unconfirmed > 0) {
|
|
410
|
+
parts.push(
|
|
411
|
+
`${YLW}⚠ ${unconfirmed} Shell${unconfirmed === 1 ? '' : 's'} termination unconfirmed${R} ${D}·${R} /shells`
|
|
412
|
+
);
|
|
413
|
+
}
|
|
404
414
|
const webSearch = webSearchActivity(activeTools, { sessionId, clientHostPid });
|
|
405
415
|
if (webSearch.count > 0) segment('Web Searching', webSearch.elapsed);
|
|
406
416
|
// Memory cycle segment — single unified "Memory" wording for all states:
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from '../../../shared/llm/model-pricing-rates.mjs';
|
|
@@ -1,346 +0,0 @@
|
|
|
1
|
-
import { stat } from 'node:fs/promises';
|
|
2
|
-
import { isProcessAlive } from '../store/paths-heartbeat.mjs';
|
|
3
|
-
import {
|
|
4
|
-
isCompletionNotificationEntry,
|
|
5
|
-
isStaleUserInjection,
|
|
6
|
-
lateDeliveryText,
|
|
7
|
-
normalizePendingMessageEntry,
|
|
8
|
-
pendingMessageId,
|
|
9
|
-
pendingMessageText,
|
|
10
|
-
} from './pending-message-entry.mjs';
|
|
11
|
-
import { isInternalRuntimeNotificationText } from './prompt-utils.mjs';
|
|
12
|
-
import { lifecycleTokenClosed } from './pending-lifecycle-epoch.mjs';
|
|
13
|
-
|
|
14
|
-
const HANDOFF_RETRY_MS = 1000;
|
|
15
|
-
const HANDOFF_RELEASE_SLACK_MS = 50;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* One session's spool queue, split into what this process takes and what stays
|
|
19
|
-
* on disk. A foreign user message is taken (and parked with a handoff stamp
|
|
20
|
-
* until the consumer owns a copy); an entry already handed off stays until its
|
|
21
|
-
* release window expires or its owner is gone; everything else is kept.
|
|
22
|
-
*
|
|
23
|
-
* Appends to `request.taken` / `request.released`, tightens
|
|
24
|
-
* `request.rescanDueAt` to the earliest live handoff deadline, and returns the
|
|
25
|
-
* entries the queue keeps.
|
|
26
|
-
*/
|
|
27
|
-
function takeForeignMessagesFromQueue(queue, request, handoffReleaseMs) {
|
|
28
|
-
const now = Date.now();
|
|
29
|
-
const kept = [];
|
|
30
|
-
for (const entry of queue) {
|
|
31
|
-
const id = pendingMessageId(entry);
|
|
32
|
-
const handoffAt = Number(entry?.handoffAt) || 0;
|
|
33
|
-
const handoffPid = Number(entry?.handoffPid) || 0;
|
|
34
|
-
if (handoffAt > 0) {
|
|
35
|
-
const dueAt = handoffAt + handoffReleaseMs;
|
|
36
|
-
const expired = now >= dueAt;
|
|
37
|
-
if (!expired) {
|
|
38
|
-
request.rescanDueAt = request.rescanDueAt === 0 ? dueAt : Math.min(request.rescanDueAt, dueAt);
|
|
39
|
-
}
|
|
40
|
-
if (handoffPid === process.pid) {
|
|
41
|
-
if (expired) {
|
|
42
|
-
if (id) request.released.push(id);
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
kept.push(entry);
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
if (!expired || isProcessAlive(handoffPid)) {
|
|
49
|
-
kept.push(entry);
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
const text = pendingMessageText(entry);
|
|
54
|
-
const foreignUser =
|
|
55
|
-
id &&
|
|
56
|
-
!request.localIds.has(id) &&
|
|
57
|
-
!isCompletionNotificationEntry(entry) &&
|
|
58
|
-
text &&
|
|
59
|
-
!isInternalRuntimeNotificationText(text);
|
|
60
|
-
if (!foreignUser) {
|
|
61
|
-
kept.push(entry);
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
const normalized = normalizePendingMessageEntry(entry);
|
|
65
|
-
const structured = Array.isArray(normalized?.content) || Boolean(normalized?.options);
|
|
66
|
-
let takenText = text;
|
|
67
|
-
let content = normalized?.content ?? text;
|
|
68
|
-
if (isStaleUserInjection(entry)) {
|
|
69
|
-
takenText = lateDeliveryText(text, entry);
|
|
70
|
-
content = Array.isArray(normalized?.content)
|
|
71
|
-
? [{ type: 'text', text: takenText.slice(0, takenText.length - text.length) }, ...normalized.content]
|
|
72
|
-
: takenText;
|
|
73
|
-
}
|
|
74
|
-
request.taken.push({
|
|
75
|
-
...(structured ? { content } : {}),
|
|
76
|
-
text: takenText,
|
|
77
|
-
id,
|
|
78
|
-
...(normalized?.options ? { options: normalized.options } : {}),
|
|
79
|
-
});
|
|
80
|
-
// Park rather than delete until the consumer owns a copy.
|
|
81
|
-
kept.push({ ...entry, handoffAt: now, handoffPid: process.pid });
|
|
82
|
-
}
|
|
83
|
-
return kept;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Owns cross-process pending-message polling and the durable two-phase handoff.
|
|
88
|
-
* Queue storage and lifecycle authority stay in the parent pending-message
|
|
89
|
-
* service and are supplied as narrow callbacks.
|
|
90
|
-
*/
|
|
91
|
-
export class ForeignPendingMessageController {
|
|
92
|
-
constructor(dependencies) {
|
|
93
|
-
this._dependencies = dependencies;
|
|
94
|
-
this._scanLimit = Math.max(128, Number(process.env.MIXDOG_FOREIGN_SPOOL_SCAN_LIMIT) || 512);
|
|
95
|
-
this._handoffReleaseMs = Math.max(1000, Number(process.env.MIXDOG_FOREIGN_HANDOFF_RELEASE_MS) || 10000);
|
|
96
|
-
this._releaseTimers = new Map();
|
|
97
|
-
this._drainRequests = new Map();
|
|
98
|
-
this._scanMemo = new Map();
|
|
99
|
-
this._drainScheduled = false;
|
|
100
|
-
this._drainRunning = false;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
cancelHandoffRelease(sessionId) {
|
|
104
|
-
const existing = this._releaseTimers.get(sessionId);
|
|
105
|
-
if (existing) {
|
|
106
|
-
try {
|
|
107
|
-
clearTimeout(existing.timer);
|
|
108
|
-
} catch {
|
|
109
|
-
/* best-effort */
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
this._releaseTimers.delete(sessionId);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
async drainUserInjections(sessionId) {
|
|
116
|
-
const { currentLifecycleToken, isValidSessionId } = this._dependencies;
|
|
117
|
-
if (!isValidSessionId(sessionId)) return [];
|
|
118
|
-
// One durable read decides both the epoch and the tombstone refusal.
|
|
119
|
-
const epochToken = currentLifecycleToken(sessionId);
|
|
120
|
-
if (lifecycleTokenClosed(epochToken)) return [];
|
|
121
|
-
return new Promise((resolve) => {
|
|
122
|
-
const existing = this._drainRequests.get(sessionId);
|
|
123
|
-
if (existing && existing.epochToken === epochToken) {
|
|
124
|
-
existing.waiters.push(resolve);
|
|
125
|
-
} else {
|
|
126
|
-
if (existing) this._settleDrain(existing, []);
|
|
127
|
-
this._drainRequests.set(sessionId, {
|
|
128
|
-
sessionId,
|
|
129
|
-
epochToken,
|
|
130
|
-
waiters: [resolve],
|
|
131
|
-
localIds: null,
|
|
132
|
-
taken: [],
|
|
133
|
-
released: [],
|
|
134
|
-
rescanDueAt: 0,
|
|
135
|
-
lifecycleDecided: false,
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
this._scheduleDrainBatch();
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
_scheduleHandoffRelease(sessionId, delayMs = this._handoffReleaseMs) {
|
|
143
|
-
const { isValidSessionId } = this._dependencies;
|
|
144
|
-
if (!isValidSessionId(sessionId)) return;
|
|
145
|
-
const delay = Math.max(1, Number(delayMs) || 0);
|
|
146
|
-
const dueAt = Date.now() + delay;
|
|
147
|
-
const existing = this._releaseTimers.get(sessionId);
|
|
148
|
-
if (existing) {
|
|
149
|
-
if (existing.dueAt <= dueAt) return;
|
|
150
|
-
try {
|
|
151
|
-
clearTimeout(existing.timer);
|
|
152
|
-
} catch {
|
|
153
|
-
/* best-effort */
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
const timer = setTimeout(() => {
|
|
157
|
-
this._releaseTimers.delete(sessionId);
|
|
158
|
-
void this._releaseExpiredHandoffs(sessionId);
|
|
159
|
-
}, delay);
|
|
160
|
-
// The row is durable; cleanup must not keep a shutting-down process open.
|
|
161
|
-
try {
|
|
162
|
-
timer.unref?.();
|
|
163
|
-
} catch {
|
|
164
|
-
/* ignore */
|
|
165
|
-
}
|
|
166
|
-
this._releaseTimers.set(sessionId, { timer, dueAt });
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
_releaseExpiredHandoffs(sessionId) {
|
|
170
|
-
const {
|
|
171
|
-
chainSpoolTail,
|
|
172
|
-
currentLifecycleToken,
|
|
173
|
-
getSpoolTail,
|
|
174
|
-
isValidSessionId,
|
|
175
|
-
lifecycleEpochMoved,
|
|
176
|
-
normalizeStore,
|
|
177
|
-
removeInDeliveryIds,
|
|
178
|
-
setSpoolQueue,
|
|
179
|
-
updateSpool,
|
|
180
|
-
warn,
|
|
181
|
-
} = this._dependencies;
|
|
182
|
-
if (!isValidSessionId(sessionId)) return Promise.resolve(0);
|
|
183
|
-
const epochToken = currentLifecycleToken(sessionId);
|
|
184
|
-
const released = [];
|
|
185
|
-
let nextDueIn = 0;
|
|
186
|
-
const preceding = getSpoolTail(sessionId) || Promise.resolve();
|
|
187
|
-
const operation = preceding
|
|
188
|
-
.catch(() => {})
|
|
189
|
-
.then(() =>
|
|
190
|
-
updateSpool(sessionId, (raw) => {
|
|
191
|
-
released.length = 0;
|
|
192
|
-
nextDueIn = 0;
|
|
193
|
-
// A generation move transfers ownership; never clean the new queue.
|
|
194
|
-
if (lifecycleEpochMoved(sessionId, epochToken)) return undefined;
|
|
195
|
-
const next = normalizeStore(raw);
|
|
196
|
-
const queue = Array.isArray(next.sessions[sessionId]) ? next.sessions[sessionId] : [];
|
|
197
|
-
if (queue.length === 0) return undefined;
|
|
198
|
-
const now = Date.now();
|
|
199
|
-
const kept = [];
|
|
200
|
-
for (const entry of queue) {
|
|
201
|
-
const handoffAt = Number(entry?.handoffAt) || 0;
|
|
202
|
-
const handoffPid = Number(entry?.handoffPid) || 0;
|
|
203
|
-
if (handoffAt > 0 && handoffPid === process.pid) {
|
|
204
|
-
const remaining = this._handoffReleaseMs - (now - handoffAt);
|
|
205
|
-
if (remaining <= 0) {
|
|
206
|
-
const id = pendingMessageId(entry);
|
|
207
|
-
if (id) released.push(id);
|
|
208
|
-
continue;
|
|
209
|
-
}
|
|
210
|
-
nextDueIn = nextDueIn === 0 ? remaining : Math.min(nextDueIn, remaining);
|
|
211
|
-
}
|
|
212
|
-
kept.push(entry);
|
|
213
|
-
}
|
|
214
|
-
if (released.length === 0) return undefined;
|
|
215
|
-
setSpoolQueue(next, sessionId, kept);
|
|
216
|
-
next.updatedAt = now;
|
|
217
|
-
return next;
|
|
218
|
-
})
|
|
219
|
-
)
|
|
220
|
-
.then(() => {
|
|
221
|
-
if (released.length > 0) removeInDeliveryIds(sessionId, released);
|
|
222
|
-
if (nextDueIn > 0) {
|
|
223
|
-
this._scheduleHandoffRelease(sessionId, nextDueIn + HANDOFF_RELEASE_SLACK_MS);
|
|
224
|
-
}
|
|
225
|
-
return released.length;
|
|
226
|
-
})
|
|
227
|
-
.catch((err) => {
|
|
228
|
-
warn(`[session] foreign-injection handoff release failed sessionId=${sessionId}: ${err?.message || err}\n`);
|
|
229
|
-
// The rows remain durable, so contention and I/O failures retry.
|
|
230
|
-
this._scheduleHandoffRelease(sessionId, HANDOFF_RETRY_MS);
|
|
231
|
-
return 0;
|
|
232
|
-
});
|
|
233
|
-
return chainSpoolTail(sessionId, operation);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
_rememberScan(sessionId, mtime, rescanDueAt) {
|
|
237
|
-
this._scanMemo.delete(sessionId);
|
|
238
|
-
this._scanMemo.set(sessionId, { mtime, rescanDueAt: Number(rescanDueAt) || 0 });
|
|
239
|
-
while (this._scanMemo.size > this._scanLimit) {
|
|
240
|
-
const oldest = this._scanMemo.keys().next().value;
|
|
241
|
-
if (oldest === undefined) break;
|
|
242
|
-
this._scanMemo.delete(oldest);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
_scanNeeded(sessionId, mtime, now) {
|
|
247
|
-
const memo = this._scanMemo.get(sessionId);
|
|
248
|
-
if (!memo || memo.mtime !== mtime) return true;
|
|
249
|
-
return memo.rescanDueAt > 0 && now >= memo.rescanDueAt;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
_settleDrain(request, value) {
|
|
253
|
-
for (const resolve of request.waiters) {
|
|
254
|
-
try {
|
|
255
|
-
resolve(value);
|
|
256
|
-
} catch {
|
|
257
|
-
/* a consumer cannot break the batch */
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
_scheduleDrainBatch() {
|
|
263
|
-
if (this._drainScheduled || this._drainRunning || this._drainRequests.size === 0) return;
|
|
264
|
-
this._drainScheduled = true;
|
|
265
|
-
setImmediate(() => {
|
|
266
|
-
this._drainScheduled = false;
|
|
267
|
-
void this._flushDrainBatch();
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
async _flushDrainBatch() {
|
|
272
|
-
if (this._drainRunning) return;
|
|
273
|
-
this._drainRunning = true;
|
|
274
|
-
const batch = [...this._drainRequests.values()];
|
|
275
|
-
this._drainRequests.clear();
|
|
276
|
-
try {
|
|
277
|
-
// Each session drains against its own shard and lock, so one busy
|
|
278
|
-
// session never delays (or fails) another's intake.
|
|
279
|
-
await Promise.all(batch.map((request) => this._drainOne(request)));
|
|
280
|
-
} finally {
|
|
281
|
-
this._drainRunning = false;
|
|
282
|
-
this._scheduleDrainBatch();
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
async _drainOne(request) {
|
|
287
|
-
const {
|
|
288
|
-
addInDeliveryIds,
|
|
289
|
-
lifecycleInvalidated,
|
|
290
|
-
localIds,
|
|
291
|
-
normalizeStore,
|
|
292
|
-
removeInDeliveryIds,
|
|
293
|
-
setSpoolQueue,
|
|
294
|
-
spoolPath,
|
|
295
|
-
updateSpool,
|
|
296
|
-
warn,
|
|
297
|
-
} = this._dependencies;
|
|
298
|
-
const { sessionId, epochToken } = request;
|
|
299
|
-
try {
|
|
300
|
-
let mtime = 0;
|
|
301
|
-
try {
|
|
302
|
-
mtime = (await stat(spoolPath(sessionId))).mtimeMs || 0;
|
|
303
|
-
} catch {
|
|
304
|
-
this._settleDrain(request, []);
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
if (!this._scanNeeded(sessionId, mtime, Date.now()) || lifecycleInvalidated(sessionId, epochToken)) {
|
|
308
|
-
this._settleDrain(request, []);
|
|
309
|
-
return;
|
|
310
|
-
}
|
|
311
|
-
request.localIds = localIds(sessionId);
|
|
312
|
-
request.taken = [];
|
|
313
|
-
request.released = [];
|
|
314
|
-
request.rescanDueAt = 0;
|
|
315
|
-
request.lifecycleDecided = false;
|
|
316
|
-
await updateSpool(
|
|
317
|
-
sessionId,
|
|
318
|
-
(raw) => {
|
|
319
|
-
const next = normalizeStore(raw);
|
|
320
|
-
if (lifecycleInvalidated(sessionId, epochToken)) return undefined;
|
|
321
|
-
request.lifecycleDecided = true;
|
|
322
|
-
const queue = Array.isArray(next.sessions[sessionId]) ? next.sessions[sessionId] : [];
|
|
323
|
-
if (queue.length === 0) return undefined;
|
|
324
|
-
const kept = takeForeignMessagesFromQueue(queue, request, this._handoffReleaseMs);
|
|
325
|
-
if (request.taken.length === 0 && request.released.length === 0) return undefined;
|
|
326
|
-
setSpoolQueue(next, sessionId, kept);
|
|
327
|
-
next.updatedAt = Date.now();
|
|
328
|
-
return next;
|
|
329
|
-
},
|
|
330
|
-
{ timeoutMs: 0 }
|
|
331
|
-
);
|
|
332
|
-
if (request.lifecycleDecided) this._rememberScan(sessionId, mtime, request.rescanDueAt);
|
|
333
|
-
if (request.taken.length > 0) {
|
|
334
|
-
addInDeliveryIds(sessionId, request.taken.map((item) => item?.id).filter(Boolean));
|
|
335
|
-
this._scheduleHandoffRelease(sessionId, this._handoffReleaseMs + HANDOFF_RELEASE_SLACK_MS);
|
|
336
|
-
}
|
|
337
|
-
if (request.released.length > 0) removeInDeliveryIds(sessionId, request.released);
|
|
338
|
-
this._settleDrain(request, request.taken);
|
|
339
|
-
} catch (err) {
|
|
340
|
-
if (err?.code !== 'ELOCKCONTENDED') {
|
|
341
|
-
warn(`[session] foreign-injection drain failed: ${err?.message || err}\n`);
|
|
342
|
-
}
|
|
343
|
-
this._settleDrain(request, []);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
// manager/session-owner-liveness.mjs
|
|
2
|
-
// Is another live process driving this session right now?
|
|
3
|
-
//
|
|
4
|
-
// Attach-on-resume guard: resuming a session that another live process is
|
|
5
|
-
// ACTIVELY driving right now must not create a second writer on the same
|
|
6
|
-
// file — that split-brain silently freezes one side's transcript (generation
|
|
7
|
-
// ownership drops the loser's saves). Such a resume ATTACHES instead: the
|
|
8
|
-
// caller gets the live transcript flagged remoteAttached, its submits are
|
|
9
|
-
// persisted into the shared pending spool (the owner's injection poller runs
|
|
10
|
-
// them as normal user turns), and its view refreshes from disk. ONE writer,
|
|
11
|
-
// ONE transcript, every surface talking into the same conversation. Idle
|
|
12
|
-
// sessions keep the normal single-identity handoff.
|
|
13
|
-
//
|
|
14
|
-
// The answer comes from three durable signals — the in-process runtime entry,
|
|
15
|
-
// the `.own` presence sidecar and the `.hb` heartbeat — each verified against
|
|
16
|
-
// the pid the sidecar recorded so a force-killed owner can never look live.
|
|
17
|
-
// The session file's own `lastHeartbeatAt`/`clientHostPid` are NOT liveness
|
|
18
|
-
// evidence: the file outlives its writer, and its host pid can name an older
|
|
19
|
-
// process (or a recycled pid), so a restart within the freshness window
|
|
20
|
-
// reopened the session as a viewer whose prompts spooled to nobody.
|
|
21
|
-
import {
|
|
22
|
-
loadSession,
|
|
23
|
-
saveSession,
|
|
24
|
-
readSessionHeartbeatMtime,
|
|
25
|
-
readSessionPresenceMtime,
|
|
26
|
-
isSessionPresenceOwnerDead,
|
|
27
|
-
deleteSessionPresence,
|
|
28
|
-
isSessionHeartbeatOwnerDead,
|
|
29
|
-
readSessionHeartbeatOwnerPid,
|
|
30
|
-
deleteHeartbeat,
|
|
31
|
-
} from '../store.mjs';
|
|
32
|
-
import { _getRuntimeEntry } from './runtime-liveness.mjs';
|
|
33
|
-
import { clearTurnCheckpoint, recoverTurnCheckpoint } from './turn-checkpoint.mjs';
|
|
34
|
-
|
|
35
|
-
const ACTIVE_OWNER_HB_FRESH_MS = 2 * 60 * 1000; // heartbeat freshness window
|
|
36
|
-
|
|
37
|
-
export function _isActivelyOwnedElsewhere(_session, sessionId) {
|
|
38
|
-
// This process already owns the runtime for the id — switching back to
|
|
39
|
-
// one of our own sessions (desktop tab switch, TUI /resume) never attaches.
|
|
40
|
-
const entry = _getRuntimeEntry(sessionId);
|
|
41
|
-
if (entry && entry.closed !== true) return false;
|
|
42
|
-
// A FORCE-KILLED owner leaves its `.own` sidecar behind looking fresh.
|
|
43
|
-
// The recorded pid is authoritative: when it no longer exists, the owner
|
|
44
|
-
// is gone — clear the stale sidecar and resume with normal ownership
|
|
45
|
-
// instead of viewer-attaching into a spool nobody drains.
|
|
46
|
-
if (isSessionPresenceOwnerDead(sessionId)) {
|
|
47
|
-
deleteSessionPresence(sessionId);
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
const now = Date.now();
|
|
51
|
-
// Presence (`.own`, pid-verified just above) covers the idle gaps between
|
|
52
|
-
// turns: a live interactive surface keeps refreshing it (~20s) for its
|
|
53
|
-
// CURRENT session, so cross-opening an idle-but-open session still
|
|
54
|
-
// attaches as a viewer instead of splitting ownership into two writers
|
|
55
|
-
// that clobber each other's saves.
|
|
56
|
-
const presenceAt = Number(readSessionPresenceMtime(sessionId)) || 0;
|
|
57
|
-
if (presenceAt > 0 && now - presenceAt <= ACTIVE_OWNER_HB_FRESH_MS) return true;
|
|
58
|
-
// Heartbeats publish only while a turn is running (≤5s cadence) and the
|
|
59
|
-
// sidecar is deleted on detach/close, so freshness here means another
|
|
60
|
-
// process is mid-conversation on this session right now — PROVIDED that
|
|
61
|
-
// process still exists. Without the pid check a force-killed owner (app
|
|
62
|
-
// upgrade restart, crash) kept looking live for the whole freshness
|
|
63
|
-
// window, so every cross-open attached as a viewer and the user's
|
|
64
|
-
// messages spooled to a queue nobody drains (silently dropped 30m later).
|
|
65
|
-
if (isSessionHeartbeatOwnerDead(sessionId)) {
|
|
66
|
-
void deleteHeartbeat(sessionId);
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
// Fresh sidecar whose recorded pid is alive: a real owner is driving this
|
|
70
|
-
// session right now. Anything weaker (no sidecar, a pid-less legacy one)
|
|
71
|
-
// proves no live owner, whatever the session file remembers.
|
|
72
|
-
const sidecarAt = Number(readSessionHeartbeatMtime(sessionId)) || 0;
|
|
73
|
-
return sidecarAt > 0 && now - sidecarAt <= ACTIVE_OWNER_HB_FRESH_MS && readSessionHeartbeatOwnerPid(sessionId) > 0;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Viewer self-heal probe: true when a re-resume of this session would NO
|
|
77
|
-
// LONGER attach (owner dead or every liveness signal stale). Attached
|
|
78
|
-
// surfaces poll this so a dead owner promotes the viewer instead of leaving
|
|
79
|
-
// it spooling messages to nobody. Single source of truth: the same guard
|
|
80
|
-
// the resume path uses.
|
|
81
|
-
export function isSessionOwnerGone(sessionId) {
|
|
82
|
-
const session = loadSession(sessionId);
|
|
83
|
-
if (!session) return false;
|
|
84
|
-
return !_isActivelyOwnedElsewhere(session, sessionId);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export function _recoverTurnCheckpointDurably(session, sessionId) {
|
|
88
|
-
const recovery = recoverTurnCheckpoint(session);
|
|
89
|
-
if (!recovery.changed) return recovery;
|
|
90
|
-
// Recovery is a durable reconnect boundary, not a renderer-only view.
|
|
91
|
-
// Persist before removing the checkpoint so a second crash leaves at
|
|
92
|
-
// least one complete copy of the interrupted turn.
|
|
93
|
-
saveSession(session, {
|
|
94
|
-
sync: true,
|
|
95
|
-
expectedGeneration: session.generation,
|
|
96
|
-
});
|
|
97
|
-
if (recovery.turnToken) {
|
|
98
|
-
clearTurnCheckpoint(sessionId, recovery.turnToken);
|
|
99
|
-
}
|
|
100
|
-
// A force-killed owner cannot clear its activity/presence sidecars. Once
|
|
101
|
-
// its checkpoint is committed as interrupted, neither sidecar may keep a
|
|
102
|
-
// restored pane busy or make a new prompt queue behind a dead process.
|
|
103
|
-
deleteSessionPresence(sessionId);
|
|
104
|
-
void deleteHeartbeat(sessionId);
|
|
105
|
-
return recovery;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Reconnect-safe historical read used by desktop pane peeks. Unlike a normal
|
|
109
|
-
// read-only load, it resolves a dead owner's durable turn checkpoint before
|
|
110
|
-
// returning the transcript. A genuinely live foreign owner remains untouched.
|
|
111
|
-
export function recoverSessionAfterProcessRestart(sessionId) {
|
|
112
|
-
const session = loadSession(sessionId);
|
|
113
|
-
if (!session || session.closed === true) return session || null;
|
|
114
|
-
if (_isActivelyOwnedElsewhere(session, sessionId)) return session;
|
|
115
|
-
_recoverTurnCheckpointDurably(session, sessionId);
|
|
116
|
-
return session;
|
|
117
|
-
}
|