mixdog 1.4.1 → 1.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -3
- package/scripts/build-shipped-history.mjs +125 -33
- package/scripts/gate-local.mjs +220 -63
- package/scripts/graph-parity.mjs +8 -3
- package/scripts/lib/gate-scheduler.mjs +139 -32
- package/scripts/lib/gate-timing-reporter.mjs +16 -0
- package/scripts/lib/run-node-tests.mjs +71 -34
- package/scripts/lib/scoped-temp.mjs +1 -0
- package/scripts/lib/test-failure-records.mjs +14 -1
- package/scripts/lib/test-failure-summary.mjs +9 -4
- package/scripts/lib/test-spawn-ledger.mjs +43 -3
- package/scripts/post-release-check.mjs +26 -16
- package/scripts/pptx-kit-index.mjs +5 -6
- package/scripts/provider-pingpong-live.mjs +1 -1
- package/scripts/prune-embedding-runtime.mjs +3 -1
- package/scripts/release-paths.mjs +7 -1
- package/scripts/suite-health.mjs +110 -28
- package/scripts/test.mjs +9 -14
- package/src/agents/advisor/AGENT.md +4 -4
- package/src/agents/advisor/agent.json +1 -1
- package/src/agents/explore/AGENT.md +22 -0
- package/src/agents/explore/agent.json +6 -0
- package/src/cli.mjs +7 -0
- package/src/defaults/agents.json +7 -0
- package/src/defaults/shipped-history.json +38 -2
- package/src/defaults/skills/browser-use/SKILL.md +4 -1
- package/src/defaults/skills/code-tidy/SKILL.md +71 -102
- package/src/defaults/skills/code-tidy/references/agent-cleanup.md +66 -114
- package/src/defaults/skills/code-tidy/references/cleanup-report.md +19 -35
- package/src/defaults/skills/code-tidy/references/dead-code.md +3 -9
- package/src/defaults/skills/code-tidy/references/test-suite-slop.md +16 -27
- package/src/defaults/skills/computer-use/SKILL.md +12 -2
- package/src/defaults/skills/setup/SKILL.md +2 -2
- package/src/defaults/skills/setup/references/actions.md +28 -9
- package/src/defaults/skills/setup/references/surfaces.md +5 -2
- package/src/defaults/skills/setup-guide/SKILL.md +11 -3
- package/src/defaults/skills/setup-guide/model-recommendations.json +13 -0
- package/src/defaults/skills/skill-creator/SKILL.md +85 -190
- package/src/defaults/skills/skill-creator/references/design.md +70 -162
- package/src/defaults/skills/skill-creator/references/evaluation.md +33 -92
- package/src/defaults/skills/test-suite/SKILL.md +194 -0
- package/src/defaults/skills/test-suite/references/case-management.md +51 -0
- package/src/defaults/skills/test-suite/references/flaky-policy.md +39 -0
- package/src/defaults/skills/test-suite/references/reliability.md +49 -0
- package/src/defaults/skills/test-suite/references/suite-design.md +78 -0
- package/src/defaults/skills/test-suite/scripts/inventory.mjs +335 -0
- package/src/defaults/skills/visualize/SKILL.md +6 -2
- package/src/lib/instruction-text.cjs +36 -0
- package/src/lib/rules-builder.cjs +40 -50
- package/src/orchestration/balanced.md +23 -0
- package/src/orchestration/focused.md +22 -0
- package/src/orchestration/none.md +5 -0
- package/src/orchestration/swarm.md +24 -0
- package/src/rules/agent-mode/AGENT_MODE.md +3 -1
- package/src/rules/agent-mode/MIXY.md +2 -2
- package/src/rules/lead/LEAD.md +3 -3
- package/src/rules/shared/20-research.md +3 -6
- package/src/rules/shared/40-editing.md +1 -0
- package/src/rules/shared/60-verification.md +10 -9
- package/src/rules/shared/70-delivery.md +4 -3
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/abort-link.mjs +5 -1
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +3 -3
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +10 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +132 -21
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +13 -9
- package/src/runtime/agent/orchestrator/agent-runtime/heartbeat-completion.mjs +2 -2
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +4 -1
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +67 -0
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +1 -0
- package/src/runtime/agent/orchestrator/config.mjs +12 -7
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +7 -7
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +9 -30
- package/src/runtime/agent/orchestrator/internal-agents.mjs +3 -14
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +124 -12
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +204 -49
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +16 -7
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +157 -57
- package/src/runtime/agent/orchestrator/mcp/client.mjs +54 -23
- package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +1 -1
- package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +8 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +83 -52
- package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +1 -1
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +14 -0
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +43 -27
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +43 -8
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +30 -28
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +31 -21
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-stop.mjs +3 -3
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +40 -0
- package/src/runtime/agent/orchestrator/providers/custom-provider-keys.mjs +34 -0
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +14 -7
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +48 -31
- package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +0 -18
- package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +138 -6
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/model-cache.mjs +100 -33
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/oauth-secret-fields.fixtures.mjs +45 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +4 -6
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2784 -2872
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +5 -8
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +26 -28
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +288 -300
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/registry.mjs +33 -2
- package/src/runtime/agent/orchestrator/providers/vertex-ai-auth.mjs +622 -0
- package/src/runtime/agent/orchestrator/providers/vertex-ai-maas.mjs +185 -0
- package/src/runtime/agent/orchestrator/providers/vertex-ai-models.mjs +445 -0
- package/src/runtime/agent/orchestrator/providers/vertex-ai.mjs +576 -0
- package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +10 -30
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +32 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +0 -11
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +8 -0
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +17 -3
- package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +47 -27
- package/src/runtime/agent/orchestrator/runtime-core/goal-deadlines.mjs +141 -10
- package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +26 -63
- package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +28 -3
- package/src/runtime/agent/orchestrator/runtime-core/goal-runtime.mjs +36 -11
- package/src/runtime/agent/orchestrator/runtime-core/goal-state.mjs +88 -56
- package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +4 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-store.mjs +51 -32
- package/src/runtime/agent/orchestrator/runtime-core/goal-text.mjs +45 -145
- package/src/runtime/agent/orchestrator/runtime-core/goal-tool-defs.mjs +15 -98
- package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +32 -99
- package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +23 -78
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +69 -47
- package/src/runtime/agent/orchestrator/runtime-core/todo-list.mjs +367 -0
- package/src/runtime/agent/orchestrator/runtime-core/todo-tool-defs.mjs +47 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-availability.mjs +155 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +12 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +119 -35
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +4 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +15 -6
- package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +28 -22
- package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +30 -0
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +30 -5
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +171 -37
- package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +94 -10
- package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -12
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +4 -4
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +11 -6
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +6 -6
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +7 -3
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +11 -1
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/human-gate/held-message.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +37 -8
- package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/image-strip-recovery.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +11 -1
- package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +8 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/todo-check.mjs +196 -0
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +116 -50
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +8 -1
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +5 -6
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +27 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +18 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +23 -1
- package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +13 -8
- package/src/runtime/agent/orchestrator/session/loop/turn-report.mjs +35 -0
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +103 -50
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +9 -7
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +21 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-failure.mjs +27 -11
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-open.mjs +37 -34
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +25 -15
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +79 -24
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +12 -1
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +6 -1
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +9 -1
- package/src/runtime/agent/orchestrator/session/manager/pending-spool-path.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/prompt-settings.mjs +598 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +20 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +22 -14
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +56 -14
- package/src/runtime/agent/orchestrator/session/manager/session-lock.mjs +31 -0
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +59 -20
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-scratch-environment.mjs +70 -0
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +0 -3
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +18 -4
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +8 -9
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +339 -88
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-recovery.mjs +135 -11
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +377 -104
- package/src/runtime/agent/orchestrator/session/manager/turn-durability.mjs +144 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +48 -30
- package/src/runtime/agent/orchestrator/session/manager/turn-reap.mjs +125 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/mixy-name.mjs +12 -6
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +8 -1
- package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/save-session-worker.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store/liveness-veto.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +23 -2
- package/src/runtime/agent/orchestrator/session/store/sweep/sub-agent-retention.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/store/transcript-durability.mjs +88 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/store-summary-log.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +243 -38
- package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +163 -64
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +177 -8
- package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +142 -18
- package/src/runtime/agent/orchestrator/session/store.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +7 -1
- package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +9 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/agent-envelope.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +2 -2
- package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -5
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +21 -3
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/worktree-add-guard.mjs +77 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +0 -1
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +42 -28
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +30 -14
- package/src/runtime/agent/orchestrator/tools/builtin/lib/search-input-helpers.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +58 -20
- package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +0 -1
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +20 -15
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +0 -10
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +95 -20
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +59 -10
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +1 -24
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary/child-kill.mjs +4 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +1 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/modes/calls.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/modes/symbols.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +4 -5
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +3 -4
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4 -22
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.mjs +99 -2
- package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +105 -15
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +49 -15
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +45 -27
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +35 -11
- package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/lib/wait-until-fixture.mjs +26 -0
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -6
- package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +0 -1
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
- package/src/runtime/attachments/attachment-reference.mjs +1 -1
- package/src/runtime/attachments/pdf-extract.mjs +1 -1
- package/src/runtime/attachments/store.d.mts +1 -0
- package/src/runtime/attachments/store.mjs +2 -2
- package/src/runtime/channels/lib/config-reload-watch.mjs +42 -0
- package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +29 -9
- package/src/runtime/channels/lib/scheduler.mjs +33 -0
- package/src/runtime/channels/lib/schedules-db.mjs +11 -4
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +4 -1
- package/src/runtime/channels/lib/webhooks-db.mjs +9 -3
- package/src/runtime/channels/lib/whisper-port.mjs +19 -10
- package/src/runtime/channels/lib/whisper-server.mjs +22 -13
- package/src/runtime/channels/lib/worker-main.mjs +9 -31
- package/src/runtime/computer-bridge/linux-readiness.mjs +10 -14
- package/src/runtime/connectors/connector-accounts-mcp.mjs +372 -103
- package/src/runtime/connectors/gmail-app-password.mjs +4 -4
- package/src/runtime/connectors/google-mcp.mjs +1 -1
- package/src/runtime/effort-judge/judge-client.mjs +2 -2
- package/src/runtime/effort-judge/judge-input.mjs +4 -4
- package/src/runtime/explore/explorer.mjs +88 -0
- package/src/runtime/explore/tool-defs.mjs +35 -0
- package/src/runtime/explore/tool.mjs +123 -0
- package/src/runtime/local-provider/hardware.mjs +1 -1
- package/src/runtime/media/adapters/codex-image.mjs +3 -3
- package/src/runtime/media/adapters/gemini-video.mjs +1 -1
- package/src/runtime/media/media-usage.mjs +1 -1
- package/src/runtime/media/store.mjs +1 -1
- package/src/runtime/memory/index.mjs +15 -1
- package/src/runtime/memory/lib/embedding-provider.mjs +4 -1
- package/src/runtime/memory/lib/embedding-reindex.mjs +27 -0
- package/src/runtime/memory/lib/embedding-worker.mjs +4 -0
- package/src/runtime/memory/lib/memory-embed.mjs +83 -14
- package/src/runtime/memory/lib/memory-process-lock.mjs +159 -36
- package/src/runtime/memory/lib/memory-text-utils.mjs +6 -1
- package/src/runtime/memory/lib/memory.mjs +17 -0
- package/src/runtime/memory/lib/pg/process.mjs +1 -1
- package/src/runtime/memory/lib/pg/supervisor.mjs +16 -96
- package/src/runtime/memory/lib/recall-hybrid/hybrid-sql.mjs +12 -3
- package/src/runtime/office/authoring/docx-html-runner.mjs +1 -1
- package/src/runtime/office/authoring/pptx-author-action.mjs +1 -1
- package/src/runtime/office/authoring/xlsx-html-build.mjs +1 -1
- package/src/runtime/office/authoring/xlsx-html-runner.mjs +1 -1
- package/src/runtime/office/capabilities.mjs +1 -1
- package/src/runtime/office/core/office-core.mjs +1 -1
- package/src/runtime/office/design/content-model.mjs +3 -3
- package/src/runtime/office/design/docx/design-docx/section.mjs +6 -6
- package/src/runtime/office/design/xlsx/design-xlsx-components.mjs +1 -1
- package/src/runtime/office/design/xlsx/design-xlsx.mjs +1 -1
- package/src/runtime/office/pdf/ocr-engine.mjs +1 -1
- package/src/runtime/office/pdf/pdf-writer.mjs +4 -4
- package/src/runtime/office/portable/portable-docx-operations.mjs +2 -2
- package/src/runtime/office/portable/portable-soffice.mjs +8 -6
- package/src/runtime/office/portable/xlsx-contract.mjs +1 -1
- package/src/runtime/office/quality/assurance-structure-docx.mjs +1 -1
- package/src/runtime/shared/agent-route-config.mjs +17 -2
- package/src/runtime/shared/atomic-file.d.mts +8 -0
- package/src/runtime/shared/atomic-file.mjs +103 -2
- package/src/runtime/shared/background-tasks.mjs +37 -10
- package/src/runtime/shared/background-wait.mjs +66 -0
- package/src/runtime/shared/child-guardian.mjs +17 -7
- package/src/runtime/shared/config.mjs +313 -16
- package/src/runtime/shared/connector-accounts.mjs +5 -5
- package/src/runtime/shared/connector-catalog.d.mts +14 -8
- package/src/runtime/shared/connector-catalog.mjs +70 -52
- package/src/runtime/shared/custom-provider-keys.mjs +20 -0
- package/src/runtime/shared/file-has-data.mjs +26 -2
- package/src/runtime/shared/file-lock.mjs +21 -18
- package/src/runtime/shared/git-launch.d.mts +9 -0
- package/src/runtime/shared/git-launch.mjs +213 -0
- package/src/runtime/shared/git-runner-worker.mjs +2 -1
- package/src/runtime/shared/git-runner.mjs +7 -2
- package/src/runtime/shared/idle-gc.mjs +30 -7
- package/src/runtime/shared/llm/cost.mjs +6 -1
- package/src/runtime/shared/llm/http-agent.mjs +316 -29
- package/src/runtime/shared/llm/model-catalog-projection.mjs +3 -2
- package/src/runtime/shared/llm/model-catalog.mjs +46 -6
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +3 -0
- package/src/runtime/shared/llm/system-proxy.mjs +453 -0
- package/src/runtime/shared/llm/usage-ledger-quota.mjs +31 -12
- package/src/runtime/shared/llm/usage-ledger-rollup.mjs +84 -8
- package/src/runtime/shared/llm/usage-ledger.mjs +3 -0
- package/src/runtime/shared/llm/usage-rollup.mjs +48 -0
- package/src/runtime/shared/maintenance-schedule.mjs +109 -0
- package/src/runtime/shared/process-runner.mjs +8 -4
- package/src/runtime/shared/provider-accounts.mjs +52 -2
- package/src/runtime/shared/routine-trigger.d.mts +13 -0
- package/src/runtime/shared/routine-trigger.mjs +34 -0
- package/src/runtime/shared/run-child.d.mts +2 -1
- package/src/runtime/shared/run-child.mjs +70 -18
- package/src/runtime/shared/session-scratch.mjs +14 -2
- package/src/runtime/shared/shipped-definitions.mjs +7 -1
- package/src/runtime/shared/staged-install-worker.mjs +5 -0
- package/src/runtime/shared/staged-update.mjs +47 -2
- package/src/runtime/shared/tool-card-model.golden.json +3 -12
- package/src/runtime/shared/tool-execution-contract.mjs +2 -0
- package/src/runtime/shared/tool-surface.mjs +110 -36
- package/src/runtime/shared/tool-work-units.mjs +35 -6
- package/src/runtime/shared/turn-snapshot.mjs +26 -4
- package/src/runtime/shared/turn-worktree-snapshot.mjs +409 -54
- package/src/runtime/shared/worker-requests.mjs +17 -3
- package/src/runtime/shared/worktrees/activity-gate.mjs +215 -318
- package/src/runtime/shared/worktrees/activity-ledger-worker.mjs +56 -0
- package/src/runtime/shared/worktrees/activity-ledger.mjs +391 -0
- package/src/runtime/shared/worktrees/branch-management.mjs +5 -3
- package/src/runtime/shared/worktrees/contract.mjs +16 -6
- package/src/runtime/shared/worktrees/dependencies.mjs +127 -89
- package/src/runtime/shared/worktrees/disk.mjs +79 -2
- package/src/runtime/shared/worktrees/git.mjs +97 -7
- package/src/runtime/shared/worktrees/include.mjs +6 -4
- package/src/runtime/shared/worktrees/maintenance.mjs +422 -30
- package/src/runtime/shared/worktrees/manager.mjs +962 -197
- package/src/runtime/shared/worktrees/settings.mjs +33 -7
- package/src/runtime/shared/worktrees/test-support.mjs +5 -1
- package/src/runtime/shared/worktrees/worker-snapshots.mjs +82 -0
- package/src/runtime/tidy/install.mjs +95 -15
- package/src/runtime/tidy/languages.mjs +17 -15
- package/src/runtime/tidy/process.mjs +9 -5
- package/src/runtime/tidy/resolve.mjs +19 -16
- package/src/runtime/tidy/runners/biome.mjs +3 -3
- package/src/runtime/tidy/tool.mjs +1 -1
- package/src/runtime/tidy/worktree.mjs +10 -6
- package/src/runtime/visualize-bridge/client.mjs +4 -1
- package/src/runtime/visualize-bridge/tool-defs.mjs +7 -2
- package/src/runtime/web-search/index.mjs +4 -23
- package/src/runtime/web-search/lib/web-tools.mjs +2 -2
- package/src/runtime/web-search/tool-defs.mjs +17 -26
- package/src/session-runtime/agent-profile-changes.mjs +55 -4
- package/src/session-runtime/agent-task-sessions.mjs +410 -353
- package/src/session-runtime/boot/apis.mjs +27 -4
- package/src/session-runtime/boot/begin.mjs +2 -0
- package/src/session-runtime/boot/config.mjs +10 -1
- package/src/session-runtime/boot/facade.mjs +1 -0
- package/src/session-runtime/boot/providers.mjs +70 -3
- package/src/session-runtime/boot/session.mjs +2 -0
- package/src/session-runtime/boot/tools.mjs +71 -25
- package/src/session-runtime/bot-status.mjs +103 -103
- package/src/session-runtime/channel-config-api.mjs +1 -0
- package/src/session-runtime/config-change-effects.mjs +179 -0
- package/src/session-runtime/config-lifecycle/config-writers.mjs +1 -0
- package/src/session-runtime/config-lifecycle.mjs +160 -24
- package/src/session-runtime/connector-merge-migration.mjs +18 -15
- package/src/session-runtime/conversation-import/discovery.mjs +245 -47
- package/src/session-runtime/conversation-import/service.mjs +12 -5
- package/src/session-runtime/goal-facade-api.mjs +49 -3
- package/src/session-runtime/handoff-attachments.mjs +1 -1
- package/src/session-runtime/held-requests.mjs +3 -1
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +3 -0
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -2
- package/src/session-runtime/internal-tool-executor/feature-tools.mjs +5 -11
- package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +8 -0
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +161 -120
- package/src/session-runtime/internal-tool-executor.mjs +12 -19
- package/src/session-runtime/lifecycle/cold-heir.mjs +7 -0
- package/src/session-runtime/lifecycle/memory-ingest.mjs +3 -3
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +53 -5
- package/src/session-runtime/lifecycle/session-switch.mjs +13 -2
- package/src/session-runtime/lifecycle/teardown/session-close.mjs +10 -5
- package/src/session-runtime/lifecycle/teardown.mjs +1 -1
- package/src/session-runtime/lifecycle-api.mjs +1 -0
- package/src/session-runtime/mcp-glue/server-catalog.mjs +27 -10
- package/src/session-runtime/mcp-glue/server-connection.mjs +34 -8
- package/src/session-runtime/mcp-glue/server-input.mjs +4 -1
- package/src/session-runtime/mixy-heartbeat.mjs +1 -1
- package/src/session-runtime/model-route/set-route.mjs +40 -7
- package/src/session-runtime/native-web-search/routes.mjs +3 -3
- package/src/session-runtime/provider-auth/login.mjs +2 -2
- package/src/session-runtime/provider-auth/refresh.mjs +6 -3
- package/src/session-runtime/provider-auth/setup.mjs +1 -1
- package/src/session-runtime/provider-readiness.mjs +53 -4
- package/src/session-runtime/resource-api.mjs +1 -1
- package/src/session-runtime/resource-extension-sync.mjs +4 -8
- package/src/session-runtime/resource-mcp-api.mjs +98 -35
- package/src/session-runtime/resource-mcp-connector-accounts.mjs +49 -32
- package/src/session-runtime/resource-mcp-secrets.mjs +27 -10
- package/src/session-runtime/resource-mcp-servers.mjs +5 -4
- package/src/session-runtime/resource-plugins-api.mjs +4 -0
- package/src/session-runtime/routine-executor.mjs +152 -0
- package/src/session-runtime/routine-home-turn.mjs +46 -5
- package/src/session-runtime/runtime-facade.mjs +5 -0
- package/src/session-runtime/runtime-feature-gates.mjs +2 -11
- package/src/session-runtime/schedule-session-run.mjs +1 -0
- package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +6 -1
- package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +7 -0
- package/src/session-runtime/services/agent-tool/send-flow.mjs +120 -8
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +12 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +5 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +8 -3
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +28 -7
- package/src/session-runtime/services/agent-tool.mjs +51 -1
- package/src/session-runtime/services/agent-watchdog-registry.mjs +32 -4
- package/src/session-runtime/services/channel-admin.mjs +18 -0
- package/src/session-runtime/services/channel-worker/daemon-spawn.mjs +2 -0
- package/src/session-runtime/services/daemon-candidate.mjs +4 -1
- package/src/session-runtime/services/hook-bus/command-handler.mjs +7 -11
- package/src/session-runtime/services/memory-runtime-proxy/daemon-spawn.mjs +4 -0
- package/src/session-runtime/services/plugin-admin.mjs +14 -6
- package/src/session-runtime/services/provider-admin.mjs +281 -10
- package/src/session-runtime/services/seeds.mjs +7 -0
- package/src/session-runtime/services/usage-stats-model.mjs +32 -11
- package/src/session-runtime/session-create/materialize.mjs +4 -2
- package/src/session-runtime/session-create/session-options.mjs +2 -0
- package/src/session-runtime/session-turn-api.mjs +2 -0
- package/src/session-runtime/settings-builtin-tools-api.mjs +5 -10
- package/src/session-runtime/settings-system-api.mjs +1 -1
- package/src/session-runtime/setup-tool/agent-settings-actions.mjs +13 -3
- package/src/session-runtime/setup-tool/connector-widen-approval.mjs +96 -0
- package/src/session-runtime/setup-tool/executor.mjs +14 -3
- package/src/session-runtime/setup-tool/extended-actions.mjs +2 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +28 -0
- package/src/session-runtime/setup-tool/tool-defs.mjs +28 -2
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +40 -43
- package/src/session-runtime/tool-policy/mode-switch.mjs +1 -1
- package/src/session-runtime/tool-policy/policy-stage.mjs +106 -8
- package/src/session-runtime/tool-policy/workflow-overlay.mjs +2 -1
- package/src/session-runtime/tool-policy-refresh.mjs +182 -5
- package/src/session-runtime/tool-surface.mjs +38 -1
- package/src/session-runtime/turn/agent-ops.mjs +33 -2
- package/src/session-runtime/turn/session-ops.mjs +2 -0
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +5 -0
- package/src/session-runtime/turn/turn-run.mjs +1 -1
- package/src/session-runtime/webhook-session-run.mjs +2 -0
- package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +46 -11
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +2 -0
- package/src/session-runtime/workflow-agents-api/instruction-files.mjs +165 -0
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +5 -4
- package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +2 -3
- package/src/session-runtime/workflow-agents-api.mjs +2 -0
- package/src/session-runtime/workspace/activity-guards.mjs +176 -0
- package/src/session-runtime/workspace/project-branches.mjs +1 -1
- package/src/session-runtime/workspace/session-view.mjs +149 -0
- package/src/session-runtime/workspace/side-checkouts.mjs +203 -0
- package/src/session-runtime/workspace/transition-runner.mjs +188 -0
- package/src/session-runtime/workspace/transitions.mjs +128 -1070
- package/src/session-runtime/workspace/workspace-merge.mjs +132 -0
- package/src/session-runtime/workspace/worktree-intents.mjs +242 -0
- package/src/session-runtime/workspace/worktree-maintenance.mjs +200 -0
- package/src/session-runtime/workspace/worktree-setup.mjs +97 -0
- package/src/session-runtime/workspace/worktree-tool.mjs +70 -54
- package/src/standalone/daemon-agent-control.mjs +25 -8
- package/src/standalone/daemon-desktop-runtime.mjs +8 -2
- package/src/standalone/daemon-lag-profiler.mjs +7 -7
- package/src/standalone/daemon-session-control.mjs +151 -136
- package/src/standalone/daemon.mjs +61 -2
- package/src/standalone/desktop-service-registry.mjs +9 -0
- package/src/standalone/local-session-runtime.mjs +21 -1
- package/src/standalone/session-client/daemon-unavailable.mjs +4 -1
- package/src/standalone/session-client.mjs +5 -1
- package/src/standalone/session-frame-stream.mjs +83 -11
- package/src/standalone/session-protocol.mjs +15 -0
- package/src/standalone/session-service/agent-route-follow.mjs +23 -20
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +18 -6
- package/src/standalone/session-service/agent-tree.mjs +45 -9
- package/src/standalone/session-service/projection/transcript-window.mjs +700 -64
- package/src/standalone/session-service/quota-resume.mjs +9 -0
- package/src/standalone/session-service/session-calls/session-action.mjs +51 -1
- package/src/standalone/session-service/session-calls/session-turns.mjs +37 -18
- package/src/standalone/session-service/session-calls/session-views.mjs +160 -104
- package/src/standalone/session-service/session-calls/turn-gate.mjs +34 -0
- package/src/standalone/session-service/session-calls.mjs +2 -1
- package/src/standalone/session-service/stored-reader.mjs +127 -40
- package/src/tui/App.jsx +162 -164
- package/src/tui/app/app-view/floating-panel.jsx +3 -0
- package/src/tui/app/app-view/text-entry-labels.mjs +1 -0
- package/src/tui/app/clipboard.mjs +22 -11
- package/src/tui/app/create-app-pickers.mjs +2 -0
- package/src/tui/app/doctor.mjs +2 -2
- package/src/tui/app/panel-signature.mjs +1 -1
- package/src/tui/app/prompt-handlers/use-prompt-history-nav.mjs +1 -1
- package/src/tui/app/prompt-handlers/use-prompt-interrupt.mjs +3 -0
- package/src/tui/app/prompt-submit/provider-prompts.mjs +3 -1
- package/src/tui/app/provider-setup/api-key-actions.mjs +31 -1
- package/src/tui/app/provider-setup/oauth-actions.mjs +25 -6
- package/src/tui/app/provider-setup-picker.mjs +20 -13
- package/src/tui/app/settings-picker.mjs +53 -8
- package/src/tui/app/shell-layout/row-budgets.mjs +14 -12
- package/src/tui/app/slash-commands.mjs +2 -1
- package/src/tui/app/slash-dispatch/route-commands.mjs +1 -1
- package/src/tui/app/slash-dispatch/session-commands.mjs +47 -1
- package/src/tui/app/slash-dispatch.mjs +1 -1
- package/src/tui/app/use-btw-panel.mjs +29 -0
- package/src/tui/app/use-context-panel-refresh.mjs +31 -0
- package/src/tui/app/use-model-catalog-cache.mjs +13 -10
- package/src/tui/app/use-mouse-input.mjs +11 -0
- package/src/tui/app/use-onboarding-start.mjs +8 -3
- package/src/tui/app/use-panel-transition.mjs +10 -1
- package/src/tui/app/use-pasted-buffers.mjs +28 -20
- package/src/tui/app/use-prompt-draft-flow.mjs +7 -0
- package/src/tui/app/use-prompt-layout-rows.mjs +3 -3
- package/src/tui/app/use-prompt-queue-history.mjs +5 -4
- package/src/tui/app/use-selection-focus.mjs +11 -2
- package/src/tui/app/use-selection-geometry.mjs +18 -9
- package/src/tui/app/use-selection-paint.mjs +4 -4
- package/src/tui/app/use-selection-stitch.mjs +3 -3
- package/src/tui/app/use-slash-palette.mjs +62 -0
- package/src/tui/app/use-terminal-chrome.mjs +2 -2
- package/src/tui/app/use-transcript-activity.mjs +48 -33
- package/src/tui/app/use-transcript-follow.mjs +12 -4
- package/src/tui/app/use-transcript-scroll.mjs +46 -17
- package/src/tui/app/use-transcript-swap-reset.mjs +27 -0
- package/src/tui/app/use-transcript-window/geometry.mjs +20 -6
- package/src/tui/app/use-transcript-window/instance-state.mjs +4 -0
- package/src/tui/app/use-transcript-window/selection-shift.mjs +5 -1
- package/src/tui/app/use-transcript-window.mjs +21 -11
- package/src/tui/app/use-tui-ready.mjs +18 -0
- package/src/tui/app/use-welcome-prompt-hint.mjs +5 -0
- package/src/tui/app/use-workflow-tab-cycle.mjs +5 -3
- package/src/tui/components/Picker.jsx +27 -3
- package/src/tui/components/SignInUrl.jsx +68 -0
- package/src/tui/components/TextEntryPanel.jsx +76 -46
- package/src/tui/components/prompt-input/escape-policy.d.mts +0 -1
- package/src/tui/components/prompt-input/escape-policy.mjs +1 -1
- package/src/tui/components/question-panel-model.mjs +4 -4
- package/src/tui/components/sign-in-url-layout.mjs +24 -0
- package/src/tui/components/tool-execution/standard-card.jsx +3 -4
- package/src/tui/components/user-message-skills.mjs +1 -1
- package/src/tui/dist/index.mjs +2205 -1371
- package/src/tui/markdown/render-ansi.mjs +1 -1
- package/src/tui/session/agent-job-feed/notification-router.mjs +10 -0
- package/src/tui/session/agent-job-feed.mjs +2 -0
- package/src/tui/session/context-state.mjs +3 -0
- package/src/tui/session/goal-turn-state.mjs +6 -2
- package/src/tui/session/quota-resume.mjs +1 -1
- package/src/tui/session/session-api/controls.mjs +4 -0
- package/src/tui/session/session-api/extensions.mjs +8 -0
- package/src/tui/session/session-api/intake/abort.mjs +2 -1
- package/src/tui/session/session-api/intake.mjs +2 -0
- package/src/tui/session/session-api/integrations.mjs +2 -2
- package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
- package/src/tui/session/session-api/lifecycle/session-reset.mjs +7 -0
- package/src/tui/session/session-api/routes-agent-definitions.mjs +6 -0
- package/src/tui/session/session-api/settings.mjs +2 -2
- package/src/tui/session/session-api/worktree.mjs +13 -1
- package/src/tui/session/session-flow/drain.mjs +11 -14
- package/src/tui/session/tool-card-results/grouped-result.mjs +2 -2
- package/src/tui/session/transcript-spill/spill-writer.mjs +15 -0
- package/src/tui/session/transcript-spill.mjs +92 -4
- package/src/tui/session/transcript-store.mjs +2 -0
- package/src/tui/session/turn-quiet.mjs +1 -1
- package/src/tui/session/turn.mjs +3 -0
- package/src/tui/session/worktree-setup-notice.mjs +1 -1
- package/src/tui/spinner-meta.mjs +3 -3
- package/src/tui/spinner-verbs.mjs +2 -2
- package/src/defaults/skills/goal-management/SKILL.md +0 -169
- package/src/defaults/skills/skill-creator/references/source-review.md +0 -94
- package/src/runtime/agent/orchestrator/runtime-core/goal-tasks.mjs +0 -156
- package/src/session-runtime/goal-tasks.mjs +0 -1
|
@@ -1,42 +1,149 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
1
3
|
import { availableParallelism } from 'node:os';
|
|
2
|
-
import { testConcurrencyArg } from './run-node-tests.mjs';
|
|
3
4
|
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
// The local gate runs every lane from one shared pool of workers: each lane's
|
|
6
|
+
// files are cut into shards, and whichever worker is free takes the longest
|
|
7
|
+
// shard left, whatever its lane. Shard sizes come from the previous run's
|
|
8
|
+
// measured per-file times, so the split stays balanced without hand-kept
|
|
9
|
+
// weights, and no worker idles while another lane still has work.
|
|
10
|
+
|
|
11
|
+
// The gate runs its whole process tree at below-normal priority, so a
|
|
12
|
+
// foreground app always wins the CPU. Priority does not bound what the
|
|
13
|
+
// desktop shell shares with every process (handles, desktop heap, work
|
|
14
|
+
// queues; explorer.exe has failed with "no work queue available" under many
|
|
15
|
+
// concurrent test processes), so the default also caps the worker count:
|
|
16
|
+
// half the cores, leaving the other half for what the budget does not count
|
|
17
|
+
// (each shard's runner, the processes tests spawn, the desktop build's own
|
|
18
|
+
// threads), and never more than DEFAULT_MAX_WORKERS. Each worker is three
|
|
19
|
+
// node processes (shard runner, its node --test batch, the test file) plus
|
|
20
|
+
// whatever the test spawns. Measured on a 20-core Windows PC with other work
|
|
21
|
+
// running: at 10 workers, test-file processes began dying within ~20 ms with
|
|
22
|
+
// no output (every remaining file of a shard, in 2 of 2 runs); at 8 and 6
|
|
23
|
+
// they did not.
|
|
24
|
+
// An explicit budget still wins: --test-concurrency (gate argument), then
|
|
25
|
+
// MIXDOG_TEST_CONCURRENCY.
|
|
26
|
+
const DEFAULT_MAX_WORKERS = 8;
|
|
27
|
+
export function gateTestBudget(argv = [], env = process.env, parallelism = availableParallelism()) {
|
|
28
|
+
const index = argv.findIndex((arg) => arg === '--test-concurrency' || arg.startsWith('--test-concurrency='));
|
|
29
|
+
let explicit = env.MIXDOG_TEST_CONCURRENCY || undefined;
|
|
30
|
+
// A flag without a value is an error, not "unspecified".
|
|
31
|
+
if (index >= 0) explicit = argv[index].includes('=') ? argv[index].split('=')[1] : (argv[index + 1] ?? '');
|
|
32
|
+
if (explicit === undefined) return Math.max(1, Math.min(DEFAULT_MAX_WORKERS, Math.floor(parallelism / 2)));
|
|
33
|
+
const budget = Number(explicit);
|
|
34
|
+
if (!Number.isInteger(budget) || budget < 1)
|
|
35
|
+
throw new Error(`gate test budget must be a positive integer (got "${explicit}")`);
|
|
36
|
+
return budget;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Estimates for work with no recorded time yet: the next run records it.
|
|
40
|
+
export const DEFAULT_ESTIMATE_MS = { fast: 2_000, slow: 25_000, command: 60_000 };
|
|
41
|
+
|
|
42
|
+
// Previous runs' measured times ({ version, ms: { key: ms } }). A missing,
|
|
43
|
+
// unreadable or malformed file counts as no history, and an entry that is
|
|
44
|
+
// not a finite positive number is dropped (its work gets the default).
|
|
45
|
+
export function readTimingHistory(file) {
|
|
46
|
+
let ms;
|
|
47
|
+
try {
|
|
48
|
+
ms = JSON.parse(readFileSync(file, 'utf8'))?.ms;
|
|
49
|
+
} catch {
|
|
50
|
+
return {};
|
|
51
|
+
}
|
|
52
|
+
if (!ms || typeof ms !== 'object' || Array.isArray(ms)) return {};
|
|
53
|
+
return Object.fromEntries(
|
|
54
|
+
Object.entries(ms).filter(([, value]) => typeof value === 'number' && Number.isFinite(value) && value > 0)
|
|
55
|
+
);
|
|
9
56
|
}
|
|
10
57
|
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
export function
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
58
|
+
// Writes the history through a temp file of its own (concurrent gates in
|
|
59
|
+
// other worktrees share the file) and renames it into place. Never throws:
|
|
60
|
+
// returns the error, or null once saved; the timings only plan the next run.
|
|
61
|
+
export function saveTimingHistory(file, ms) {
|
|
62
|
+
const temp = `${file}.${process.pid}.${randomUUID()}.tmp`;
|
|
63
|
+
try {
|
|
64
|
+
writeFileSync(temp, `${JSON.stringify({ version: 1, ms })}\n`);
|
|
65
|
+
renameSync(temp, file);
|
|
66
|
+
return null;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
try {
|
|
69
|
+
rmSync(temp, { force: true });
|
|
70
|
+
} catch {
|
|
71
|
+
// The save error is the one reported.
|
|
20
72
|
}
|
|
21
|
-
|
|
73
|
+
return error;
|
|
22
74
|
}
|
|
23
|
-
return shares;
|
|
24
75
|
}
|
|
76
|
+
// Enough shards per worker that the pool's tail (the shards still running
|
|
77
|
+
// when the queue is empty) stays short; the floor bounds the per-shard runner
|
|
78
|
+
// start-up and process sweep (~1-2 s each).
|
|
79
|
+
const SHARDS_PER_WORKER = 8;
|
|
80
|
+
const MIN_SHARD_MS = 15_000;
|
|
25
81
|
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
export
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
Array.from({ length: budget }, async () => {
|
|
35
|
-
while (next < legs.length) await runLeg(legs[next++], 1);
|
|
36
|
-
})
|
|
37
|
-
);
|
|
38
|
-
return;
|
|
82
|
+
// test.mjs selects every lane file whose path contains a given filter, so a
|
|
83
|
+
// file whose path contains another file's path has to share that file's shard
|
|
84
|
+
// or it would run twice. Returns the indivisible groups.
|
|
85
|
+
export function filterGroups(files) {
|
|
86
|
+
const parent = new Map(files.map((file) => [file, file]));
|
|
87
|
+
const find = (file) => (parent.get(file) === file ? file : find(parent.get(file)));
|
|
88
|
+
for (const inner of files) {
|
|
89
|
+
for (const outer of files) if (outer !== inner && outer.includes(inner)) parent.set(find(outer), find(inner));
|
|
39
90
|
}
|
|
40
|
-
const
|
|
41
|
-
|
|
91
|
+
const groups = new Map();
|
|
92
|
+
for (const file of files) groups.set(find(file), [...(groups.get(find(file)) ?? []), file]);
|
|
93
|
+
return [...groups.values()];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Cuts each lane ({ name, files, estimateOf }) into shards of about one
|
|
97
|
+
// target size, longest-first into the least-loaded shard, so a file longer
|
|
98
|
+
// than the target runs alone. Returns [{ lane, files, estimate }].
|
|
99
|
+
export function planShards(lanes, budget) {
|
|
100
|
+
const total = lanes.reduce((sum, lane) => sum + lane.files.reduce((s, file) => s + lane.estimateOf(file), 0), 0);
|
|
101
|
+
const target = Math.max(MIN_SHARD_MS, total / (budget * SHARDS_PER_WORKER));
|
|
102
|
+
const shards = [];
|
|
103
|
+
for (const lane of lanes) {
|
|
104
|
+
const items = filterGroups(lane.files)
|
|
105
|
+
.map((files) => ({ files, estimate: files.reduce((s, file) => s + lane.estimateOf(file), 0) }))
|
|
106
|
+
.sort((a, b) => b.estimate - a.estimate);
|
|
107
|
+
const laneTotal = items.reduce((sum, item) => sum + item.estimate, 0);
|
|
108
|
+
const bins = Array.from({ length: Math.min(items.length, Math.max(1, Math.round(laneTotal / target))) }, () => ({
|
|
109
|
+
lane: lane.name,
|
|
110
|
+
files: [],
|
|
111
|
+
estimate: 0,
|
|
112
|
+
}));
|
|
113
|
+
for (const item of items) {
|
|
114
|
+
const bin = bins.reduce((least, candidate) => (candidate.estimate < least.estimate ? candidate : least));
|
|
115
|
+
bin.files.push(...item.files);
|
|
116
|
+
bin.estimate += item.estimate;
|
|
117
|
+
}
|
|
118
|
+
shards.push(...bins);
|
|
119
|
+
}
|
|
120
|
+
return shards;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Per-file time of a finished shard: what its own tests reported plus an
|
|
124
|
+
// equal share of the rest of the shard's wall time (process start-up, module
|
|
125
|
+
// load, the runner's own work). Returns { file: ms }.
|
|
126
|
+
export function measuredFileTimes(files, reportedMs, wallMs) {
|
|
127
|
+
const reported = files.reduce((sum, file) => sum + (reportedMs[file] ?? 0), 0);
|
|
128
|
+
const overhead = Math.max(0, wallMs - reported) / files.length;
|
|
129
|
+
return Object.fromEntries(files.map((file) => [file, (reportedMs[file] ?? 0) + overhead]));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Runs every unit on `budget` workers, longest estimate first. A failed unit
|
|
133
|
+
// marked stopOnFail stops new starts (running units finish). Returns the
|
|
134
|
+
// units never started.
|
|
135
|
+
export async function runPool(units, budget, run) {
|
|
136
|
+
if (!Number.isInteger(budget) || budget < 1) throw new Error('gate test budget must be a positive integer');
|
|
137
|
+
const queue = [...units].sort((a, b) => b.estimate - a.estimate);
|
|
138
|
+
let stopped = false;
|
|
139
|
+
await Promise.all(
|
|
140
|
+
Array.from({ length: Math.min(budget, queue.length) }, async () => {
|
|
141
|
+
while (!stopped && queue.length > 0) {
|
|
142
|
+
const unit = queue.shift();
|
|
143
|
+
unit.ok = await run(unit);
|
|
144
|
+
if (!unit.ok && unit.stopOnFail) stopped = true;
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
);
|
|
148
|
+
return queue;
|
|
42
149
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// node:test reporter the local gate adds to each shard it runs: one JSON line
|
|
2
|
+
// per test file, { file, ms }, the file's top-level test time (relative to the
|
|
3
|
+
// package the run started in, like `scripts/test.mjs --list`). The gate turns
|
|
4
|
+
// these into the per-file estimates its next run plans shards from.
|
|
5
|
+
import { relative } from 'node:path';
|
|
6
|
+
|
|
7
|
+
export default async function* gateTimingReporter(source) {
|
|
8
|
+
const byFile = new Map();
|
|
9
|
+
for await (const event of source) {
|
|
10
|
+
if (event.type !== 'test:pass' && event.type !== 'test:fail') continue;
|
|
11
|
+
if (event.data.nesting !== 0 || !event.data.file) continue;
|
|
12
|
+
const file = relative(process.cwd(), String(event.data.file)).replaceAll('\\', '/');
|
|
13
|
+
byFile.set(file, (byFile.get(file) ?? 0) + (Number(event.data.details?.duration_ms) || 0));
|
|
14
|
+
}
|
|
15
|
+
for (const [file, ms] of byFile) yield `${JSON.stringify({ file, ms })}\n`;
|
|
16
|
+
}
|
|
@@ -29,6 +29,8 @@ const TEST_LOG_PREFIX = 'mixdog-test-output-';
|
|
|
29
29
|
const TEST_FAILURES_PREFIX = 'mixdog-test-failures-';
|
|
30
30
|
const TEST_SCRATCH_PREFIX = 'mixdog-test-scratch-';
|
|
31
31
|
export const RUNNER_TEMP_PREFIXES = [TEST_LOG_PREFIX, TEST_FAILURES_PREFIX, TEST_SCRATCH_PREFIX];
|
|
32
|
+
// Printed first by every run; the local gate finds a dead unit's log by it.
|
|
33
|
+
export const FULL_LOG_LABEL = 'Full test log: ';
|
|
32
34
|
|
|
33
35
|
// Batches keep the file order Node was given; a single path larger than the
|
|
34
36
|
// budget still gets its own batch rather than being dropped. An empty list
|
|
@@ -82,7 +84,8 @@ const concurrencyFlag = (arg) => arg === '--test-concurrency' || arg.startsWith(
|
|
|
82
84
|
function concurrencyOf(runArgs, fileArgs) {
|
|
83
85
|
const args = [...runArgs, ...fileArgs];
|
|
84
86
|
const index = args.findIndex(concurrencyFlag);
|
|
85
|
-
|
|
87
|
+
let value = null;
|
|
88
|
+
if (index >= 0) value = positiveInt(args[index].includes('=') ? args[index].split('=')[1] : args[index + 1]);
|
|
86
89
|
return value ?? Math.max(1, availableParallelism() - 1);
|
|
87
90
|
}
|
|
88
91
|
|
|
@@ -141,11 +144,19 @@ function capture(command, args) {
|
|
|
141
144
|
});
|
|
142
145
|
}
|
|
143
146
|
|
|
144
|
-
// Kills a
|
|
145
|
-
//
|
|
147
|
+
// Kills a live batch and every descendant still linked to it (on Windows the
|
|
148
|
+
// shared creation-checked tree kill). On POSIX a batch leads its own process
|
|
149
|
+
// group (spawned detached), so the group goes with it.
|
|
150
|
+
// Loaded only when a Windows kill runs, so importers that need just this
|
|
151
|
+
// module's constants (the local gate) do not pull in the runtime.
|
|
152
|
+
async function loadKillProcessTrees() {
|
|
153
|
+
const { killProcessTrees } = await import('../../src/runtime/agent/orchestrator/mcp/child-tree.mjs');
|
|
154
|
+
return killProcessTrees;
|
|
155
|
+
}
|
|
156
|
+
|
|
146
157
|
async function killTree(pid) {
|
|
147
158
|
if (process.platform === 'win32') {
|
|
148
|
-
await
|
|
159
|
+
await (await loadKillProcessTrees())([pid]);
|
|
149
160
|
return;
|
|
150
161
|
}
|
|
151
162
|
for (const target of [-pid, pid]) {
|
|
@@ -189,17 +200,29 @@ async function processSnapshot() {
|
|
|
189
200
|
});
|
|
190
201
|
}
|
|
191
202
|
|
|
192
|
-
|
|
203
|
+
// How far a snapshot's creation time may sit from Date.now() read around the
|
|
204
|
+
// spawn. Windows reports creation to the microsecond from the same system
|
|
205
|
+
// clock, so only a clock tick separates them; POSIX ps reports whole elapsed
|
|
206
|
+
// seconds. No wider: Windows hands a freed pid to the next process within
|
|
207
|
+
// milliseconds, and a 2 s window made a process another run had just started
|
|
208
|
+
// pass for one of this run's exited spawns, so it was killed with them.
|
|
209
|
+
const clockSlackMs = (platform) => (platform === 'win32' ? 50 : 2000);
|
|
193
210
|
|
|
194
211
|
// The processes this run owns, out of a snapshot: an anchor (the batch, or a
|
|
195
212
|
// child a test process recorded in its spawn ledger) still alive under its
|
|
196
|
-
// pid, the orphans an exited anchor left (they still name it as their parent
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
//
|
|
213
|
+
// pid, the orphans an exited anchor left (they still name it as their parent),
|
|
214
|
+
// every descendant of those, and anything executing from the run's scratch
|
|
215
|
+
// root (e.g. the postgres the memory runtime downloads into MIXDOG_DATA_DIR).
|
|
216
|
+
// An anchor is { pid, before, since, exited? }: the clock just before and just
|
|
217
|
+
// after its spawn, and when its exit was seen. A pid alone never identifies a
|
|
218
|
+
// process: an anchor seen to exit no longer holds its pid, so whatever holds
|
|
219
|
+
// it now is another process; a live holder was created within its spawn; an
|
|
220
|
+
// orphan was created while its anchor lived (a later process that reuses the
|
|
221
|
+
// pid only has younger children); a descendant is younger than its parent.
|
|
200
222
|
// Command-line text never makes a process the run's: an editor or terminal
|
|
201
223
|
// opened on the scratch root is not.
|
|
202
|
-
export function ownedProcesses(snapshot, anchors, {
|
|
224
|
+
export function ownedProcesses(snapshot, anchors, { scratchDir, platform = process.platform }) {
|
|
225
|
+
const slack = clockSlackMs(platform);
|
|
203
226
|
const byPid = new Map(snapshot.map((entry) => [entry.pid, entry]));
|
|
204
227
|
const childrenOf = new Map();
|
|
205
228
|
for (const entry of snapshot) childrenOf.set(entry.ppid, [...(childrenOf.get(entry.ppid) ?? []), entry]);
|
|
@@ -210,14 +233,19 @@ export function ownedProcesses(snapshot, anchors, { batchStart, scratchDir, plat
|
|
|
210
233
|
owned.set(entry.pid, entry);
|
|
211
234
|
queue.push(entry);
|
|
212
235
|
};
|
|
213
|
-
for (const { pid, since } of anchors) {
|
|
236
|
+
for (const { pid, before, since, exited } of anchors) {
|
|
214
237
|
const holder = byPid.get(pid);
|
|
215
|
-
if (holder && holder.created >=
|
|
238
|
+
if (holder && exited === undefined && holder.created >= before - slack && holder.created <= since + slack) {
|
|
216
239
|
claim(holder);
|
|
217
240
|
continue;
|
|
218
241
|
}
|
|
219
242
|
for (const orphan of childrenOf.get(pid) ?? []) {
|
|
220
|
-
if (
|
|
243
|
+
if (
|
|
244
|
+
orphan.created >= before - slack &&
|
|
245
|
+
orphan.created <= (exited ?? Number.POSITIVE_INFINITY) + slack &&
|
|
246
|
+
(!holder || orphan.created < holder.created)
|
|
247
|
+
)
|
|
248
|
+
claim(orphan);
|
|
221
249
|
}
|
|
222
250
|
}
|
|
223
251
|
const fold = (path) => (platform === 'win32' ? path.toLowerCase() : path);
|
|
@@ -229,26 +257,30 @@ export function ownedProcesses(snapshot, anchors, { batchStart, scratchDir, plat
|
|
|
229
257
|
while (queue.length > 0) {
|
|
230
258
|
const parent = queue.pop();
|
|
231
259
|
for (const entry of childrenOf.get(parent.pid) ?? []) {
|
|
232
|
-
if (entry.created >= parent.created -
|
|
260
|
+
if (entry.created >= parent.created - slack) claim(entry);
|
|
233
261
|
}
|
|
234
262
|
}
|
|
235
263
|
return [...owned.values()];
|
|
236
264
|
}
|
|
237
265
|
|
|
266
|
+
// One anchor per recorded spawn (scripts/lib/test-spawn-ledger.mjs): its
|
|
267
|
+
// "<pid> <before> <since>" line, with "<exited>" from the line that repeats
|
|
268
|
+
// it once the exit was seen.
|
|
238
269
|
async function readSpawnLedger(path) {
|
|
239
270
|
const text = await readFile(path, 'utf8').catch(() => '');
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
.
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
271
|
+
const spawns = new Map();
|
|
272
|
+
for (const line of text.split('\n').filter(Boolean)) {
|
|
273
|
+
const [pid, before, since, exited] = line.split(' ').map(Number);
|
|
274
|
+
const key = `${pid} ${before} ${since}`;
|
|
275
|
+
spawns.set(key, { ...(spawns.get(key) ?? { pid, before, since }), ...(exited ? { exited } : {}) });
|
|
276
|
+
}
|
|
277
|
+
return [...spawns.values()];
|
|
247
278
|
}
|
|
248
279
|
|
|
249
280
|
// A process a test leaves running must not outlive its batch: it can hold a
|
|
250
281
|
// port, a database, or files in the scratch root the run removes at the end.
|
|
251
|
-
|
|
282
|
+
// `batch` is the batch's own anchor, its exit already seen.
|
|
283
|
+
async function reapLeftovers(child, { batch, ledger, scratchDir }) {
|
|
252
284
|
// The batch itself has exited: only its process group can remain.
|
|
253
285
|
if (process.platform !== 'win32' && child.pid) {
|
|
254
286
|
try {
|
|
@@ -257,17 +289,19 @@ async function reapLeftovers(child, { batchStart, ledger, scratchDir }) {
|
|
|
257
289
|
/* the group is empty */
|
|
258
290
|
}
|
|
259
291
|
}
|
|
260
|
-
const anchors = [...(child.pid ? [
|
|
261
|
-
await killOwned(anchors, {
|
|
292
|
+
const anchors = [...(child.pid ? [batch] : []), ...(await readSpawnLedger(ledger))];
|
|
293
|
+
await killOwned(anchors, { scratchDir });
|
|
262
294
|
}
|
|
263
295
|
|
|
264
|
-
async function killOwned(anchors, {
|
|
265
|
-
|
|
296
|
+
async function killOwned(anchors, { scratchDir }) {
|
|
297
|
+
// Every owned process ran when the snapshot was taken.
|
|
298
|
+
const snapshotAt = Date.now();
|
|
299
|
+
const leftovers = ownedProcesses(await processSnapshot(), anchors, { scratchDir });
|
|
266
300
|
if (leftovers.length === 0) return;
|
|
267
301
|
const pids = leftovers.map((entry) => entry.pid);
|
|
268
302
|
console.error(`Killing ${pids.length} process(es) a test left running: ${pids.join(' ')}`);
|
|
269
303
|
if (process.platform === 'win32') {
|
|
270
|
-
await
|
|
304
|
+
await (await loadKillProcessTrees())(pids, { aliveAt: snapshotAt });
|
|
271
305
|
return;
|
|
272
306
|
}
|
|
273
307
|
for (const pid of pids) {
|
|
@@ -279,12 +313,13 @@ async function killOwned(anchors, { batchStart, scratchDir }) {
|
|
|
279
313
|
}
|
|
280
314
|
}
|
|
281
315
|
|
|
282
|
-
// Resolves { code, hung } once the batch exited and its leftovers are gone.
|
|
316
|
+
// Resolves { code, hung, pid } once the batch exited and its leftovers are gone.
|
|
283
317
|
// `scratchDir` '' = leave the scratch-root sweep to the caller (side-by-side batches).
|
|
284
318
|
function spawnBatch(args, env, { files, ledger, scratchDir, timeoutMs }) {
|
|
285
319
|
return new Promise((resolve) => {
|
|
286
|
-
const
|
|
320
|
+
const before = Date.now();
|
|
287
321
|
const child = spawn(process.execPath, args, { stdio: 'inherit', env, detached: process.platform !== 'win32' });
|
|
322
|
+
const since = Date.now();
|
|
288
323
|
let hung = '';
|
|
289
324
|
const watchdog = setTimeout(() => {
|
|
290
325
|
hung = `hung after ${minutes(timeoutMs)} min: ${files.join(' ') || '(discovered files)'}`;
|
|
@@ -302,10 +337,11 @@ function spawnBatch(args, env, { files, ledger, scratchDir, timeoutMs }) {
|
|
|
302
337
|
const signals = process.platform === 'win32' ? [] : ['SIGINT', 'SIGTERM'];
|
|
303
338
|
for (const signal of signals) process.on(signal, forward);
|
|
304
339
|
const settle = async (code) => {
|
|
340
|
+
const batch = { pid: child.pid, before, since, exited: Date.now() };
|
|
305
341
|
clearTimeout(watchdog);
|
|
306
342
|
for (const signal of signals) process.off(signal, forward);
|
|
307
|
-
await reapLeftovers(child, {
|
|
308
|
-
resolve({ code: hung ? 1 : code, hung });
|
|
343
|
+
await reapLeftovers(child, { batch, ledger, scratchDir });
|
|
344
|
+
resolve({ code: hung ? 1 : code, hung, pid: child.pid });
|
|
309
345
|
};
|
|
310
346
|
child.on('error', (error) => {
|
|
311
347
|
console.error(error);
|
|
@@ -363,7 +399,7 @@ export async function runNodeTests(nodeArgs, fileArgs, options = {}) {
|
|
|
363
399
|
pruneStaleTempDirs(RUNNER_TEMP_PREFIXES);
|
|
364
400
|
const outputDir = await mkdtemp(join(tmpdir(), TEST_LOG_PREFIX));
|
|
365
401
|
const logPath = join(outputDir, 'full.log');
|
|
366
|
-
console.error(
|
|
402
|
+
console.error(`${FULL_LOG_LABEL}${logPath}`);
|
|
367
403
|
// Failure records live outside the log directory: the log directory holds
|
|
368
404
|
// exactly one merged log, and these records are an internal artifact.
|
|
369
405
|
const failureDir = await mkdtemp(join(tmpdir(), TEST_FAILURES_PREFIX));
|
|
@@ -461,7 +497,7 @@ async function executeRun(
|
|
|
461
497
|
);
|
|
462
498
|
// Peers were still running while each batch exited, so the scratch root's
|
|
463
499
|
// own processes (e.g. a postgres a test started) are swept once, here.
|
|
464
|
-
if (overlapped) await killOwned([], {
|
|
500
|
+
if (overlapped) await killOwned([], { scratchDir });
|
|
465
501
|
// Logs and records merge in batch order, whatever order the batches finished.
|
|
466
502
|
for (const [index, outcome] of outcomes.entries()) {
|
|
467
503
|
const batchLog = batchLogOf(index);
|
|
@@ -518,6 +554,7 @@ async function executeRun(
|
|
|
518
554
|
await rm(coverageDir, { recursive: true, force: true });
|
|
519
555
|
}
|
|
520
556
|
// Last line of the run, after every reporter and the coverage report.
|
|
521
|
-
|
|
557
|
+
const pids = outcomes.filter((outcome) => outcome.code !== 0 && outcome.pid).map((outcome) => outcome.pid);
|
|
558
|
+
process.stdout.write(`\n${formatFailureSummary({ failed, flaky, rerunFailed, hung, exitCode: status, pids })}`);
|
|
522
559
|
return status;
|
|
523
560
|
}
|
|
@@ -36,7 +36,20 @@ export function failureRecord(event, stacks) {
|
|
|
36
36
|
if (event.data.todo) return null;
|
|
37
37
|
if (event.data.details?.error?.failureType === 'subtestsFailed') return null;
|
|
38
38
|
const ancestors = (stacks.get(file) ?? []).slice(0, event.data.nesting);
|
|
39
|
-
|
|
39
|
+
const exit = processExit(event.data.details?.error);
|
|
40
|
+
return { file, name: [...ancestors, event.data.name].join(' > '), ...(exit ? { exit } : {}) };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// A test file whose process ended badly fails with only "test failed"; node
|
|
44
|
+
// keeps how it ended on the error. A Windows NTSTATUS code (e.g. 0xC0000142,
|
|
45
|
+
// the process could not initialise) reads better in hex.
|
|
46
|
+
export function processExit(error) {
|
|
47
|
+
if (error?.failureType !== 'testCodeFailure') return '';
|
|
48
|
+
if (error.signal) return `test process killed by ${error.signal}`;
|
|
49
|
+
const code = error.exitCode;
|
|
50
|
+
if (!Number.isInteger(code) || code === 0) return '';
|
|
51
|
+
const hex = code > 255 || code < 0 ? ` (0x${(code >>> 0).toString(16).toUpperCase().padStart(8, '0')})` : '';
|
|
52
|
+
return `test process exited with code ${code}${hex}`;
|
|
40
53
|
}
|
|
41
54
|
|
|
42
55
|
// The runner sets FAILURE_RECORDS_ENV to a per-spawn JSONL path. Without it
|
|
@@ -46,19 +46,24 @@ export function policyLine(rerunFailed) {
|
|
|
46
46
|
// `hung` holds one "hung after N min: <files>" message per batch the runner's
|
|
47
47
|
// watchdog killed; such a batch fails the run whatever its tests reported.
|
|
48
48
|
// A non-zero `exitCode` with no failed test, flaky test or hung batch behind it
|
|
49
|
-
// (a bad node flag, a crashed runner) is its own failure,
|
|
50
|
-
|
|
49
|
+
// (a bad node flag, a crashed runner, a kill from outside) is its own failure,
|
|
50
|
+
// never "0 failed". `pids` names the node --test processes that exited
|
|
51
|
+
// non-zero, so a kill can be matched to the "Killing ... <pid>" line of
|
|
52
|
+
// whatever killed them.
|
|
53
|
+
export function formatFailureSummary({ failed = [], flaky = [], rerunFailed = 0, hung = [], exitCode = 0, pids = [] }) {
|
|
51
54
|
const hungCount = hung.length > 0 ? `, ${hung.length} hung batch(es)` : '';
|
|
52
55
|
const unexplained = exitCode !== 0 && failed.length === 0 && hung.length === 0;
|
|
56
|
+
const runner = pids.length > 0 ? `node --test pid ${pids.join(' ')}: ` : '';
|
|
53
57
|
const lines = [
|
|
54
58
|
unexplained
|
|
55
|
-
? `${SUMMARY_TAG} RUN FAILED: exit code ${exitCode} with no failed test recorded (bad argument or
|
|
59
|
+
? `${SUMMARY_TAG} RUN FAILED: exit code ${exitCode} with no failed test recorded (${runner}bad argument, runner error or killed from outside; see output above)`
|
|
56
60
|
: `${SUMMARY_TAG} ${failed.length} failed, ${flaky.length} flaky${hungCount} (every failed test is named below)`,
|
|
57
61
|
];
|
|
58
62
|
for (const message of hung) lines.push(`${SUMMARY_TAG} HUNG ${message}`);
|
|
59
63
|
for (const record of failed) {
|
|
60
64
|
const origin = record.firstSeenOnRerun ? ` (first seen on re-run ${record.firstSeenOnRerun})` : '';
|
|
61
|
-
|
|
65
|
+
const exit = record.exit ? ` (${record.exit})` : '';
|
|
66
|
+
lines.push(`${SUMMARY_TAG} FAILED ${record.file} > ${record.name}${exit}${origin}`);
|
|
62
67
|
}
|
|
63
68
|
for (const record of flaky) {
|
|
64
69
|
lines.push(
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
// Preloaded (--import) into every test process the runner starts: records
|
|
2
|
-
//
|
|
2
|
+
// each child process's pid, spawn time and exit, so a process a test leaves
|
|
3
3
|
// running can be traced back to the run after its parent exited
|
|
4
4
|
// (scripts/lib/run-node-tests.mjs). Without the runner's environment it does
|
|
5
5
|
// nothing.
|
|
6
6
|
import { ChildProcess } from 'node:child_process';
|
|
7
7
|
import { appendFileSync, readFileSync } from 'node:fs';
|
|
8
|
+
import { constants } from 'node:os';
|
|
8
9
|
import { dirname, join } from 'node:path';
|
|
9
10
|
import { pathToFileURL } from 'node:url';
|
|
11
|
+
import { getSystemErrorMap } from 'node:util';
|
|
10
12
|
import { FAILURE_RECORDS_ENV, spawnLedgerPath } from './test-failure-records.mjs';
|
|
11
13
|
|
|
12
14
|
// --test-force-exit ends a test file's process once every test registered so
|
|
@@ -54,11 +56,49 @@ if (
|
|
|
54
56
|
|
|
55
57
|
const ledger = spawnLedgerPath(process.env[FAILURE_RECORDS_ENV]);
|
|
56
58
|
if (ledger) {
|
|
57
|
-
// spawn, exec, execFile and fork all start their process here.
|
|
59
|
+
// spawn, exec, execFile and fork all start their process here. A spawn is
|
|
60
|
+
// "<pid> <before> <since>", the clock just before and just after it; the
|
|
61
|
+
// same line plus "<exited>" follows once its exit is seen. The 'exit' event
|
|
62
|
+
// comes before the child's handle is closed, so the pid is still this
|
|
63
|
+
// child's when the exit is written: from then on another process may hold it.
|
|
58
64
|
const spawn = ChildProcess.prototype.spawn;
|
|
65
|
+
const exited = new Set();
|
|
59
66
|
ChildProcess.prototype.spawn = function recordedSpawn(...args) {
|
|
67
|
+
const before = Date.now();
|
|
60
68
|
const result = spawn.apply(this, args);
|
|
61
|
-
if (this.pid)
|
|
69
|
+
if (this.pid) {
|
|
70
|
+
const { pid } = this;
|
|
71
|
+
exited.delete(pid);
|
|
72
|
+
const spawned = `${pid} ${before} ${Date.now()}`;
|
|
73
|
+
appendFileSync(ledger, `${spawned}\n`);
|
|
74
|
+
this.once('exit', () => {
|
|
75
|
+
exited.add(pid);
|
|
76
|
+
appendFileSync(ledger, `${spawned} ${Date.now()}\n`);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
62
79
|
return result;
|
|
63
80
|
};
|
|
81
|
+
// Once a child's exit is seen its pid is free, and Windows hands it to the
|
|
82
|
+
// next process started within moments — on a loaded gate often a parallel
|
|
83
|
+
// unit's test runner, node --test batch or test file process. A cleanup that
|
|
84
|
+
// signals every pid it recorded (e.g. `after(() => process.kill(pid))`, or a
|
|
85
|
+
// kill(pid, 0) liveness poll) then terminates that process instead, and the
|
|
86
|
+
// whole unit dies without output. Such a signal fails ESRCH here, as for a
|
|
87
|
+
// gone process, and never reaches whatever holds the pid now. Arguments Node
|
|
88
|
+
// rejects (a non-integer pid, an unknown signal) still go to Node, which
|
|
89
|
+
// throws before any signal is sent; this rejects only what it would send.
|
|
90
|
+
const kill = process.kill;
|
|
91
|
+
const [ESRCH] = [...getSystemErrorMap()].find(([, [name]]) => name === 'ESRCH');
|
|
92
|
+
const sendable = (signal) => signal === (signal | 0) || Boolean(constants.signals[signal || 'SIGTERM']);
|
|
93
|
+
process.kill = function killLiveChildOnly(pid, signal) {
|
|
94
|
+
// Only a primitive pid is checked: coercing an object would run its
|
|
95
|
+
// valueOf before Node's own coercion does. Number(pid) === (pid | 0) is
|
|
96
|
+
// Node's `pid == (pid | 0)` integer check.
|
|
97
|
+
const primitive = typeof pid === 'number' || typeof pid === 'string';
|
|
98
|
+
if (primitive && Number(pid) === (pid | 0) && exited.has(pid | 0) && sendable(signal)) {
|
|
99
|
+
// The shape of Node's own failed kill (an ErrnoException).
|
|
100
|
+
throw Object.assign(new Error('kill ESRCH'), { errno: ESRCH, code: 'ESRCH', syscall: 'kill' });
|
|
101
|
+
}
|
|
102
|
+
return kill.call(process, pid, signal);
|
|
103
|
+
};
|
|
64
104
|
}
|
|
@@ -87,9 +87,12 @@ export async function checkFeed({ text, label, version, assets, hashAsset, requi
|
|
|
87
87
|
for (const name of requiredFiles) {
|
|
88
88
|
if (!byUrl.has(name)) problems.push(`${label}: files[] does not list ${name}`);
|
|
89
89
|
}
|
|
90
|
+
// Every listed asset downloads at once; the problems keep files[] order.
|
|
91
|
+
const fileAssets = feed.files.map((file) => assets.find((candidate) => candidate.name === file.url));
|
|
92
|
+
const digests = await Promise.all(fileAssets.map((asset) => (asset ? hashAsset(asset) : null)));
|
|
90
93
|
const hashes = new Map();
|
|
91
|
-
for (const file of feed.files) {
|
|
92
|
-
const asset =
|
|
94
|
+
for (const [index, file] of feed.files.entries()) {
|
|
95
|
+
const asset = fileAssets[index];
|
|
93
96
|
if (!asset) {
|
|
94
97
|
problems.push(`${label}: ${file.url} is not an uploaded asset`);
|
|
95
98
|
continue;
|
|
@@ -99,7 +102,7 @@ export async function checkFeed({ text, label, version, assets, hashAsset, requi
|
|
|
99
102
|
problems.push(`${label}: ${file.url} size ${size} != uploaded ${asset.size}`);
|
|
100
103
|
}
|
|
101
104
|
const sha512 = field(file, 'sha512');
|
|
102
|
-
const actual =
|
|
105
|
+
const actual = digests[index];
|
|
103
106
|
hashes.set(file.url, actual);
|
|
104
107
|
if (sha512 !== actual) problems.push(`${label}: ${file.url} sha512 does not match the uploaded asset`);
|
|
105
108
|
}
|
|
@@ -233,6 +236,25 @@ export async function detectPublished({ fetchRelease, sleep = defaultSleep, atte
|
|
|
233
236
|
}
|
|
234
237
|
}
|
|
235
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Run independent `[name, run]` sections concurrently. Results keep the
|
|
241
|
+
* section order; a thrown section becomes a "check failed" problem.
|
|
242
|
+
*/
|
|
243
|
+
export function runSections(sections) {
|
|
244
|
+
return Promise.all(
|
|
245
|
+
sections.map(async ([name, run]) => {
|
|
246
|
+
try {
|
|
247
|
+
const outcome = await run();
|
|
248
|
+
return Array.isArray(outcome)
|
|
249
|
+
? { name, problems: outcome, warnings: [] }
|
|
250
|
+
: { name, problems: outcome.problems, warnings: outcome.warnings };
|
|
251
|
+
} catch (error) {
|
|
252
|
+
return { name, problems: [`check failed: ${error.message}`], warnings: [] };
|
|
253
|
+
}
|
|
254
|
+
})
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
236
258
|
async function sha512Base64(url) {
|
|
237
259
|
const response = await fetch(url, { redirect: 'follow' });
|
|
238
260
|
if (!response.ok) throw new Error(`download ${url} answered ${response.status}`);
|
|
@@ -290,19 +312,7 @@ async function main() {
|
|
|
290
312
|
],
|
|
291
313
|
['npm', () => waitForNpm({ name: 'mixdog', version })],
|
|
292
314
|
];
|
|
293
|
-
const results =
|
|
294
|
-
for (const [name, run] of sections) {
|
|
295
|
-
try {
|
|
296
|
-
const outcome = await run();
|
|
297
|
-
results.push(
|
|
298
|
-
Array.isArray(outcome)
|
|
299
|
-
? { name, problems: outcome, warnings: [] }
|
|
300
|
-
: { name, problems: outcome.problems, warnings: outcome.warnings }
|
|
301
|
-
);
|
|
302
|
-
} catch (error) {
|
|
303
|
-
results.push({ name, problems: [`check failed: ${error.message}`], warnings: [] });
|
|
304
|
-
}
|
|
305
|
-
}
|
|
315
|
+
const results = await runSections(sections);
|
|
306
316
|
|
|
307
317
|
const lines = [`## Post-release check for ${tag}`, ''];
|
|
308
318
|
for (const { name, problems, warnings } of results) {
|
|
@@ -21,12 +21,11 @@ const SOURCES = [
|
|
|
21
21
|
// A statement this short says its whole meaning (a constant, a one-line arrow); a longer one gives its first line.
|
|
22
22
|
const WHOLE_STATEMENT = 240;
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
property.type === 'Property'
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
: '';
|
|
24
|
+
function propertyName(property) {
|
|
25
|
+
if (property.type === 'Property') return String(property.key.name ?? property.key.value ?? '');
|
|
26
|
+
if (property.type === 'SpreadElement') return '…';
|
|
27
|
+
return '';
|
|
28
|
+
}
|
|
30
29
|
|
|
31
30
|
// The keys an object literal offers a script, one level into object-valued keys ("body: { x, y, w, h }"). A table of
|
|
32
31
|
// like entries (STYLES: an id per style, each with the same fields) reads as its ids and the fields each carries.
|