mixdog 1.4.1 → 1.4.2
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 +3 -2
- package/scripts/build-shipped-history.mjs +115 -33
- package/scripts/gate-local.mjs +172 -61
- package/scripts/lib/gate-scheduler.mjs +138 -32
- package/scripts/lib/gate-timing-reporter.mjs +16 -0
- package/scripts/lib/run-node-tests.mjs +54 -29
- package/scripts/lib/scoped-temp.mjs +1 -0
- package/scripts/lib/test-failure-records.mjs +14 -1
- package/scripts/lib/test-failure-summary.mjs +16 -4
- package/scripts/lib/test-spawn-ledger.mjs +12 -3
- package/scripts/pptx-kit-index.mjs +5 -6
- 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 +18 -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/goal-management/SKILL.md +1 -1
- package/src/defaults/skills/setup/references/actions.md +12 -6
- package/src/defaults/skills/setup/references/surfaces.md +2 -2
- package/src/defaults/skills/setup-guide/SKILL.md +5 -2
- 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/rules-builder.cjs +6 -30
- 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.mjs +10 -1
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +130 -21
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +13 -0
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +4 -1
- 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 +5 -26
- package/src/runtime/agent/orchestrator/internal-agents.mjs +3 -14
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +122 -12
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +5 -3
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +13 -2
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +152 -58
- package/src/runtime/agent/orchestrator/mcp/client.mjs +51 -18
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +83 -52
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +42 -26
- 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/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/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-oauth-login.mjs +26 -28
- 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 +567 -0
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +2 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +14 -5
- package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -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/cache-warm.mjs +94 -10
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +10 -5
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +5 -5
- 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/requests.mjs +36 -6
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +21 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +2 -0
- 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/manager/ask-session.mjs +50 -28
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +3 -4
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +25 -14
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +17 -14
- 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 +7 -3
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +47 -29
- package/src/runtime/agent/orchestrator/session/manager/turn-reap.mjs +125 -0
- package/src/runtime/agent/orchestrator/session/mixy-name.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.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 +164 -4
- package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +142 -18
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +36 -26
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +3 -5
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +58 -20
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +20 -15
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +96 -20
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +59 -10
- 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 +103 -13
- 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 +34 -10
- 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 -2
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
- package/src/runtime/channels/lib/whisper-port.mjs +19 -10
- package/src/runtime/channels/lib/whisper-server.mjs +10 -1
- package/src/runtime/connectors/connector-accounts-mcp.mjs +236 -103
- package/src/runtime/connectors/gmail-app-password.mjs +3 -3
- package/src/runtime/explore/explorer.mjs +93 -0
- package/src/runtime/explore/tool-defs.mjs +35 -0
- package/src/runtime/explore/tool.mjs +123 -0
- package/src/runtime/memory/index.mjs +4 -0
- package/src/runtime/memory/lib/embedding-provider.mjs +4 -1
- package/src/runtime/memory/lib/embedding-worker.mjs +4 -0
- 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.mjs +103 -2
- package/src/runtime/shared/background-tasks.mjs +15 -6
- package/src/runtime/shared/connector-accounts.mjs +5 -5
- package/src/runtime/shared/connector-catalog.d.mts +11 -4
- package/src/runtime/shared/connector-catalog.mjs +60 -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 +13 -15
- 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/llm/cost.mjs +6 -1
- package/src/runtime/shared/llm/http-agent.mjs +313 -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 +452 -0
- package/src/runtime/shared/provider-accounts.mjs +52 -2
- package/src/runtime/shared/staged-install-worker.mjs +5 -0
- package/src/runtime/shared/staged-update.mjs +47 -2
- package/src/runtime/shared/tool-surface.mjs +110 -36
- package/src/runtime/shared/tool-work-units.mjs +4 -0
- package/src/runtime/shared/turn-worktree-snapshot.mjs +414 -54
- 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 +9 -3
- package/src/runtime/shared/worktrees/git.mjs +57 -0
- package/src/runtime/shared/worktrees/manager.mjs +205 -137
- package/src/runtime/tidy/install.mjs +95 -15
- package/src/runtime/tidy/languages.mjs +17 -15
- package/src/runtime/tidy/process.mjs +7 -4
- 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 +1 -1
- package/src/session-runtime/agent-task-sessions.mjs +410 -353
- package/src/session-runtime/boot/tools.mjs +2 -0
- package/src/session-runtime/bot-status.mjs +103 -103
- package/src/session-runtime/connector-merge-migration.mjs +18 -15
- package/src/session-runtime/conversation-import/discovery.mjs +159 -40
- package/src/session-runtime/conversation-import/service.mjs +9 -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/feature-tools.mjs +2 -0
- package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +8 -0
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +143 -116
- package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -1
- package/src/session-runtime/lifecycle/teardown.mjs +1 -1
- package/src/session-runtime/mcp-glue/server-catalog.mjs +16 -10
- package/src/session-runtime/mcp-glue/server-input.mjs +4 -1
- package/src/session-runtime/mixy-heartbeat.mjs +1 -1
- package/src/session-runtime/provider-auth/login.mjs +2 -2
- package/src/session-runtime/resource-mcp-api.mjs +98 -35
- package/src/session-runtime/resource-mcp-connector-accounts.mjs +44 -31
- package/src/session-runtime/resource-mcp-secrets.mjs +28 -11
- package/src/session-runtime/resource-mcp-servers.mjs +5 -4
- package/src/session-runtime/services/agent-tool/send-flow.mjs +10 -2
- 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 +4 -1
- package/src/session-runtime/services/agent-watchdog-registry.mjs +32 -4
- 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/memory-runtime-proxy/daemon-spawn.mjs +4 -0
- package/src/session-runtime/services/plugin-admin.mjs +10 -6
- package/src/session-runtime/services/provider-admin.mjs +281 -10
- package/src/session-runtime/session-turn-api.mjs +2 -0
- package/src/session-runtime/settings-builtin-tools-api.mjs +4 -1
- 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 +6 -2
- package/src/session-runtime/setup-tool/tool-defs.mjs +2 -1
- package/src/session-runtime/turn/agent-ops.mjs +23 -0
- package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +49 -11
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +10 -1
- package/src/session-runtime/workspace/activity-guards.mjs +176 -0
- 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 +108 -1066
- 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 +80 -0
- package/src/session-runtime/workspace/worktree-setup.mjs +85 -0
- package/src/session-runtime/workspace/worktree-tool.mjs +54 -54
- package/src/standalone/daemon-desktop-runtime.mjs +2 -0
- package/src/standalone/daemon-session-control.mjs +151 -136
- package/src/standalone/daemon.mjs +7 -0
- package/src/standalone/desktop-service-registry.mjs +9 -0
- package/src/standalone/local-session-runtime.mjs +5 -1
- package/src/standalone/session-client/daemon-unavailable.mjs +3 -0
- package/src/standalone/session-client.mjs +5 -1
- package/src/standalone/session-frame-stream.mjs +83 -11
- package/src/standalone/session-protocol.mjs +4 -0
- 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/session-calls/session-views.mjs +160 -104
- package/src/standalone/session-service/stored-reader.mjs +127 -40
- package/src/tui/App.jsx +154 -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 +21 -10
- 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/shell-layout/row-budgets.mjs +12 -10
- package/src/tui/app/slash-commands.mjs +1 -0
- package/src/tui/app/slash-dispatch/session-commands.mjs +46 -0
- 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 +3 -2
- 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 +47 -32
- 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 +19 -5
- 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/sign-in-url-layout.mjs +24 -0
- package/src/tui/dist/index.mjs +1896 -1341
- package/src/tui/session/goal-turn-state.mjs +6 -2
- 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/routes-agent-definitions.mjs +1 -0
- package/src/tui/session/session-api/settings.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/defaults/skills/skill-creator/references/source-review.md +0 -94
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mixdog",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Open-source coding agent that matches Codex on Terminal-Bench 2.1 with 63% fewer tokens.",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"src/",
|
|
54
54
|
"!src/tui/dev",
|
|
55
55
|
"!src/**/*.test.mjs",
|
|
56
|
+
"!src/**/*.test-hook.mjs",
|
|
56
57
|
"!src/**/*.test.jsx"
|
|
57
58
|
],
|
|
58
59
|
"scripts": {
|
|
@@ -182,7 +183,7 @@
|
|
|
182
183
|
"wrap-ansi": "^10.0.0",
|
|
183
184
|
"ws": "^8.21.0",
|
|
184
185
|
"yaml": "^2.9.0",
|
|
185
|
-
"zod": "^
|
|
186
|
+
"zod": "^4.6.7"
|
|
186
187
|
},
|
|
187
188
|
"overrides": {
|
|
188
189
|
"onnxruntime-node": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* node scripts/build-shipped-history.mjs --check (release workflow)
|
|
14
14
|
* A shallow clone has no history: the committed file is left untouched.
|
|
15
15
|
*/
|
|
16
|
-
import { execFileSync } from 'node:child_process';
|
|
16
|
+
import { execFileSync, spawn } from 'node:child_process';
|
|
17
17
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
18
18
|
import { dirname, join } from 'node:path';
|
|
19
19
|
import { fileURLToPath } from 'node:url';
|
|
@@ -39,12 +39,81 @@ if (git('rev-parse', '--is-shallow-repository').toString().trim() === 'true') {
|
|
|
39
39
|
process.exit(0);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
/**
|
|
43
|
+
* One long-lived `git cat-file --batch` answers every object read, so the whole
|
|
44
|
+
* history costs a single process. `read(spec)` resolves to {sha, type, body}
|
|
45
|
+
* or null when the object is missing.
|
|
46
|
+
*/
|
|
47
|
+
const catFile = spawn('git', ['cat-file', '--batch'], { cwd: ROOT, stdio: ['pipe', 'pipe', 'inherit'] });
|
|
48
|
+
const waiting = [];
|
|
49
|
+
let pending = Buffer.alloc(0);
|
|
50
|
+
catFile.stdout.on('data', (chunk) => {
|
|
51
|
+
pending = Buffer.concat([pending, chunk]);
|
|
52
|
+
for (;;) {
|
|
53
|
+
const eol = pending.indexOf(10);
|
|
54
|
+
if (eol < 0) return;
|
|
55
|
+
const header = pending.toString('utf8', 0, eol);
|
|
56
|
+
if (header.endsWith(' missing')) {
|
|
57
|
+
pending = pending.subarray(eol + 1);
|
|
58
|
+
waiting.shift()(null);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const [sha, type, size] = header.split(' ');
|
|
62
|
+
const end = eol + 1 + Number(size);
|
|
63
|
+
if (pending.length < end + 1) return;
|
|
64
|
+
const body = pending.subarray(eol + 1, end);
|
|
65
|
+
pending = pending.subarray(end + 1);
|
|
66
|
+
waiting.shift()({ sha, type, body });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
const read = (spec) =>
|
|
70
|
+
new Promise((resolve) => {
|
|
71
|
+
waiting.push(resolve);
|
|
72
|
+
catFile.stdin.write(`${spec}\n`);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
/** Entries of a tree object body: [{ name, tree, sha }] in git (ls-tree) order. */
|
|
76
|
+
function parseTree(body) {
|
|
77
|
+
const entries = [];
|
|
78
|
+
for (let at = 0; at < body.length; ) {
|
|
79
|
+
const space = body.indexOf(32, at);
|
|
80
|
+
const nul = body.indexOf(0, space);
|
|
81
|
+
const mode = body.toString('latin1', at, space);
|
|
82
|
+
entries.push({
|
|
83
|
+
name: body.toString('utf8', space + 1, nul),
|
|
84
|
+
tree: mode === '40000',
|
|
85
|
+
sha: body.toString('hex', nul + 1, nul + 21),
|
|
86
|
+
});
|
|
87
|
+
at = nul + 21;
|
|
88
|
+
}
|
|
89
|
+
return entries;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const treeCache = new Map();
|
|
93
|
+
const blobCache = new Map();
|
|
94
|
+
const treeOf = async (sha) => {
|
|
95
|
+
if (!treeCache.has(sha)) treeCache.set(sha, read(sha).then(({ body }) => parseTree(body)));
|
|
96
|
+
return treeCache.get(sha);
|
|
97
|
+
};
|
|
98
|
+
const blobOf = async (sha) => {
|
|
99
|
+
if (!blobCache.has(sha)) blobCache.set(sha, read(sha).then(({ body }) => body));
|
|
100
|
+
return blobCache.get(sha);
|
|
46
101
|
};
|
|
47
102
|
|
|
103
|
+
/** Relative path -> blob sha for every file under a tree entry (a lone file keeps its own name). */
|
|
104
|
+
async function filesOf({ name, tree, sha }) {
|
|
105
|
+
if (!tree) return new Map([[name, sha]]);
|
|
106
|
+
const files = new Map();
|
|
107
|
+
const walk = async (treeSha, base) => {
|
|
108
|
+
for (const entry of await treeOf(treeSha)) {
|
|
109
|
+
if (entry.tree) await walk(entry.sha, `${base}${entry.name}/`);
|
|
110
|
+
else files.set(`${base}${entry.name}`, entry.sha);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
await walk(sha, '');
|
|
114
|
+
return files;
|
|
115
|
+
}
|
|
116
|
+
|
|
48
117
|
/** Names (path segment after `prefix`) that ever existed under prefix. */
|
|
49
118
|
function everNames(prefix) {
|
|
50
119
|
const names = new Set();
|
|
@@ -54,29 +123,39 @@ function everNames(prefix) {
|
|
|
54
123
|
return [...names].sort();
|
|
55
124
|
}
|
|
56
125
|
|
|
57
|
-
/**
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
126
|
+
/**
|
|
127
|
+
* Every distinct state `prefix/<name>` had in the commits the path-limited
|
|
128
|
+
* `git log -- path` lists (its history simplification decides which commits
|
|
129
|
+
* count). Only the object reads are batched through `cat-file`.
|
|
130
|
+
*/
|
|
131
|
+
async function everVersions(prefix) {
|
|
132
|
+
const out = new Map();
|
|
133
|
+
for (const name of everNames(prefix)) {
|
|
134
|
+
const path = `${prefix}/${name}`;
|
|
135
|
+
const states = new Map();
|
|
136
|
+
for (const commit of lines(git('log', '--format=%H', '--', path))) {
|
|
137
|
+
const object = await read(`${commit}:${path}`);
|
|
138
|
+
if (object) states.set(object.sha, { name, tree: object.type === 'tree', sha: object.sha });
|
|
65
139
|
}
|
|
66
|
-
|
|
140
|
+
const versions = [];
|
|
141
|
+
for (const entry of states.values()) {
|
|
142
|
+
const files = await filesOf(entry);
|
|
143
|
+
if (files.size) versions.push(files);
|
|
144
|
+
}
|
|
145
|
+
out.set(name, versions);
|
|
67
146
|
}
|
|
68
|
-
return
|
|
147
|
+
return out;
|
|
69
148
|
}
|
|
70
|
-
|
|
71
149
|
const unique = (hashes) => [...new Set(hashes)].sort();
|
|
72
150
|
|
|
73
|
-
function packHistory(dir, entry) {
|
|
151
|
+
async function packHistory(dir, entry) {
|
|
74
152
|
const out = {};
|
|
75
|
-
for (const id of
|
|
153
|
+
for (const [id, versions] of await everVersions(dir)) {
|
|
76
154
|
const hashes = [];
|
|
77
|
-
for (const files of
|
|
78
|
-
const
|
|
79
|
-
const
|
|
155
|
+
for (const files of versions) {
|
|
156
|
+
const texts = new Map();
|
|
157
|
+
for (const [file, sha] of files) texts.set(file, (await blobOf(sha)).toString('utf8'));
|
|
158
|
+
const definition = packDefinitionFrom((name) => texts.get(name) ?? null, entry, id);
|
|
80
159
|
if (definition) hashes.push(hashDefinition(definition));
|
|
81
160
|
}
|
|
82
161
|
if (hashes.length) out[id] = unique(hashes);
|
|
@@ -84,32 +163,35 @@ function packHistory(dir, entry) {
|
|
|
84
163
|
return out;
|
|
85
164
|
}
|
|
86
165
|
|
|
87
|
-
function outputStyleHistory() {
|
|
166
|
+
async function outputStyleHistory() {
|
|
88
167
|
const out = {};
|
|
89
|
-
for (const name of
|
|
90
|
-
const hashes =
|
|
168
|
+
for (const [name, versions] of await everVersions('src/output-styles')) {
|
|
169
|
+
const hashes = [];
|
|
170
|
+
for (const files of versions) hashes.push(hashText(await blobOf([...files.values()][0])));
|
|
91
171
|
if (hashes.length) out[name] = unique(hashes);
|
|
92
172
|
}
|
|
93
173
|
return out;
|
|
94
174
|
}
|
|
95
175
|
|
|
96
|
-
function skillHistory() {
|
|
176
|
+
async function skillHistory() {
|
|
97
177
|
const out = {};
|
|
98
|
-
for (const name of
|
|
99
|
-
const hashes =
|
|
100
|
-
|
|
101
|
-
|
|
178
|
+
for (const [name, versions] of await everVersions('src/defaults/skills')) {
|
|
179
|
+
const hashes = [];
|
|
180
|
+
for (const files of versions) {
|
|
181
|
+
hashes.push(hashTree(await Promise.all([...files].map(async ([rel, sha]) => [rel, await blobOf(sha)]))));
|
|
182
|
+
}
|
|
102
183
|
if (hashes.length) out[name] = unique(hashes);
|
|
103
184
|
}
|
|
104
185
|
return out;
|
|
105
186
|
}
|
|
106
187
|
|
|
107
188
|
const history = {
|
|
108
|
-
agents: packHistory('src/agents', 'AGENT.md'),
|
|
109
|
-
workflows: packHistory('src/workflows', 'WORKFLOW.md'),
|
|
110
|
-
outputStyles: outputStyleHistory(),
|
|
111
|
-
skills: skillHistory(),
|
|
189
|
+
agents: await packHistory('src/agents', 'AGENT.md'),
|
|
190
|
+
workflows: await packHistory('src/workflows', 'WORKFLOW.md'),
|
|
191
|
+
outputStyles: await outputStyleHistory(),
|
|
192
|
+
skills: await skillHistory(),
|
|
112
193
|
};
|
|
194
|
+
catFile.stdin.end();
|
|
113
195
|
const text = `${JSON.stringify(history, null, 2)}\n`;
|
|
114
196
|
if (CHECK) {
|
|
115
197
|
let committed = '';
|
package/scripts/gate-local.mjs
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Local pre-push check. Mirrors the test legs of .github/workflows/release-gate.yml
|
|
3
|
-
// (jobs: runtime, runtime-slow, desktop, desktop-tests fast/slow).
|
|
4
|
-
// updated together with that workflow whenever those commands change.
|
|
5
|
-
// Up front
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// (
|
|
3
|
+
// (jobs: runtime, runtime-slow, desktop, desktop-tests fast/slow, shipped-history).
|
|
4
|
+
// It MUST be updated together with that workflow whenever those commands change.
|
|
5
|
+
// Up front it runs the released-section changelog check the desktop fast lane
|
|
6
|
+
// asserts (changelog-locales.test.mjs; Deploy's plan adds the Unreleased
|
|
7
|
+
// section) and stops if it fails. Everything else runs from one shared pool of
|
|
8
|
+
// workers (scripts/lib/gate-scheduler.mjs): every test lane is cut into shards
|
|
9
|
+
// of its own files (each run with the lane's own npm script command), and the
|
|
10
|
+
// desktop build + daemon end-to-end test, the patch smoke and the gate's
|
|
11
|
+
// shipped-history job (build-shipped-history.mjs --check; needs a full clone, a
|
|
12
|
+
// shallow one keeps the committed file) are units of the same pool. A failed
|
|
13
|
+
// shipped-history check stops new starts, so the gate still ends early on it.
|
|
14
|
+
// The whole run is at below-normal priority so the desktop stays responsive.
|
|
10
15
|
// Not covered locally: runtime-macos, computer-backend (cargo + Linux X11),
|
|
11
16
|
// desktop-tests `computer` lane (windows-latest, src/main/computer/ only),
|
|
12
17
|
// relay (apps/relay tests), graph (cargo test), patch (cargo test of the
|
|
@@ -15,23 +20,32 @@
|
|
|
15
20
|
// unittest). The gate's path
|
|
16
21
|
// filtering is not applied: every check and leg always runs. Run `npm ci`
|
|
17
22
|
// (root and apps/desktop) first.
|
|
23
|
+
// Usage: npm run gate:local [-- --test-concurrency=<workers>]
|
|
24
|
+
// (MIXDOG_TEST_CONCURRENCY also sets the pool size; default: half the cores, at most 8).
|
|
18
25
|
import { spawn, spawnSync } from 'node:child_process';
|
|
19
26
|
import fs from 'node:fs';
|
|
20
27
|
import os from 'node:os';
|
|
21
28
|
import path from 'node:path';
|
|
22
29
|
import { fileURLToPath } from 'node:url';
|
|
23
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
DEFAULT_ESTIMATE_MS,
|
|
32
|
+
gateTestBudget,
|
|
33
|
+
measuredFileTimes,
|
|
34
|
+
planShards,
|
|
35
|
+
readTimingHistory,
|
|
36
|
+
runPool,
|
|
37
|
+
saveTimingHistory,
|
|
38
|
+
} from './lib/gate-scheduler.mjs';
|
|
24
39
|
import { pruneStaleTempDirs, removeTempDirSync } from './lib/stale-temp.mjs';
|
|
25
40
|
|
|
26
41
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
27
42
|
const started = Date.now();
|
|
43
|
+
const budget = gateTestBudget(process.argv.slice(2));
|
|
44
|
+
// Inherited by every process the gate starts (Windows and POSIX alike).
|
|
45
|
+
os.setPriority(os.constants.priority.PRIORITY_BELOW_NORMAL);
|
|
28
46
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
(cmd) => spawnSync(cmd, { cwd: root, stdio: 'inherit', shell: true }).status !== 0
|
|
32
|
-
);
|
|
33
|
-
if (failedPreChecks.length) {
|
|
34
|
-
console.error(`[gate:local] pre-check failed: ${failedPreChecks.join('; ')}`);
|
|
47
|
+
if (spawnSync('node scripts/check-changelog-locales.mjs', { cwd: root, stdio: 'inherit', shell: true }).status !== 0) {
|
|
48
|
+
console.error('[gate:local] pre-check failed: node scripts/check-changelog-locales.mjs');
|
|
35
49
|
process.exit(1);
|
|
36
50
|
}
|
|
37
51
|
|
|
@@ -57,73 +71,170 @@ const RESOURCE_PREFIX = 'mixdog-gate-resources-';
|
|
|
57
71
|
pruneStaleTempDirs([LOG_PREFIX, RESOURCE_PREFIX]);
|
|
58
72
|
const logDir = fs.mkdtempSync(path.join(os.tmpdir(), LOG_PREFIX));
|
|
59
73
|
const resourceDir = fs.mkdtempSync(path.join(os.tmpdir(), RESOURCE_PREFIX));
|
|
60
|
-
const budget = gateTestBudget();
|
|
61
|
-
// weight: relative worker share, roughly each leg's measured duration x its 3 workers
|
|
62
|
-
// (runtime ~440s, runtime-slow ~545s, desktop fast ~390s, desktop slow ~280s).
|
|
63
|
-
const legs = [
|
|
64
|
-
{ name: 'runtime', weight: 3, cmds: ['npm test', 'npm run smoke:patch'] },
|
|
65
|
-
{ name: 'desktop-tests-fast', weight: 3, cmds: ['npm test --prefix apps/desktop'] },
|
|
66
|
-
{ name: 'runtime-slow', weight: 4, cmds: ['npm run test:slow'] },
|
|
67
|
-
{ name: 'desktop-tests-slow', weight: 2, cmds: ['npm run test:slow --prefix apps/desktop'] },
|
|
68
|
-
{ name: 'desktop', cmds: ['npm run build --prefix apps/desktop', 'npm run test:daemon:e2e --prefix apps/desktop'] },
|
|
69
|
-
];
|
|
70
74
|
|
|
71
|
-
|
|
75
|
+
// Measured times live in the repository's common git dir, so every worktree
|
|
76
|
+
// and snapshot of the repo plans from the same history. Keys: a test file's
|
|
77
|
+
// repo-relative path, or `cmd:` + a command unit's commands. Timings only
|
|
78
|
+
// plan the run: without them (no git dir, unreadable or invalid history) the
|
|
79
|
+
// gate plans from default estimates.
|
|
80
|
+
const commonDir = spawnSync('git', ['rev-parse', '--path-format=absolute', '--git-common-dir'], {
|
|
81
|
+
cwd: root,
|
|
82
|
+
encoding: 'utf8',
|
|
83
|
+
});
|
|
84
|
+
const historyPath =
|
|
85
|
+
commonDir.status === 0 ? path.join(commonDir.stdout.trim(), 'mixdog-gate-timings.json') : null;
|
|
86
|
+
if (!historyPath) console.warn(`[gate:local] warning: no git common dir, timings not kept: ${commonDir.stderr}`);
|
|
87
|
+
const history = historyPath ? readTimingHistory(historyPath) : {};
|
|
88
|
+
const measured = {};
|
|
89
|
+
|
|
90
|
+
// Each test lane runs its package's own npm script command, given shard files
|
|
91
|
+
// as filters (scripts/test.mjs), without npm or a shell between: a shell's
|
|
92
|
+
// command line could not hold a shard's paths.
|
|
93
|
+
const testLanes = [
|
|
94
|
+
{ name: 'runtime', pkg: '.', script: 'test', speed: 'fast' },
|
|
95
|
+
{ name: 'runtime-slow', pkg: '.', script: 'test:slow', speed: 'slow' },
|
|
96
|
+
{ name: 'desktop-tests-fast', pkg: 'apps/desktop', script: 'test', speed: 'fast' },
|
|
97
|
+
{ name: 'desktop-tests-slow', pkg: 'apps/desktop', script: 'test:slow', speed: 'slow' },
|
|
98
|
+
].map((lane) => {
|
|
99
|
+
const dir = path.join(root, lane.pkg);
|
|
100
|
+
const command = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8')).scripts[lane.script];
|
|
101
|
+
const [program, ...args] = command.split(/\s+/);
|
|
102
|
+
if (program !== 'node') throw new Error(`${lane.pkg}/package.json "${lane.script}" must be a node command: ${command}`);
|
|
103
|
+
const list = spawnSync(process.execPath, [...args, '--list'], { cwd: dir, encoding: 'utf8' });
|
|
104
|
+
if (list.status !== 0) throw new Error(`${command} --list failed: ${list.stderr}`);
|
|
105
|
+
const files = list.stdout
|
|
106
|
+
.split(/\r?\n/)
|
|
107
|
+
.filter(Boolean)
|
|
108
|
+
.map((line) => line.replace(/^\S+\s+/, ''));
|
|
109
|
+
const key = (file) => path.posix.join(lane.pkg, file);
|
|
110
|
+
return { ...lane, dir, args, files, key, estimateOf: (file) => history[key(file)] ?? DEFAULT_ESTIMATE_MS[lane.speed] };
|
|
111
|
+
});
|
|
112
|
+
const lanesByName = new Map(testLanes.map((lane) => [lane.name, lane]));
|
|
113
|
+
const commandUnits = [
|
|
114
|
+
{ lane: 'shipped-history', cmds: ['node scripts/build-shipped-history.mjs --check'], stopOnFail: true },
|
|
115
|
+
{ lane: 'runtime', cmds: ['npm run smoke:patch'] },
|
|
116
|
+
// Only the daemon end-to-end test reads the build (apps/desktop/out); no
|
|
117
|
+
// test lane does, so the build shares the pool with them.
|
|
118
|
+
{ lane: 'desktop', cmds: ['npm run build --prefix apps/desktop', 'npm run test:daemon:e2e --prefix apps/desktop'] },
|
|
119
|
+
].map((unit) => {
|
|
120
|
+
const key = `cmd:${unit.cmds.join(' && ')}`;
|
|
121
|
+
return { ...unit, key, estimate: history[key] ?? DEFAULT_ESTIMATE_MS.command };
|
|
122
|
+
});
|
|
123
|
+
const units = [...planShards(testLanes, budget), ...commandUnits];
|
|
124
|
+
const laneCounts = new Map();
|
|
125
|
+
for (const unit of units) {
|
|
126
|
+
laneCounts.set(unit.lane, (laneCounts.get(unit.lane) ?? 0) + 1);
|
|
127
|
+
unit.id = `${unit.lane}.${laneCounts.get(unit.lane)}`;
|
|
128
|
+
}
|
|
129
|
+
const laneNames = [...laneCounts.keys()];
|
|
130
|
+
const laneLog = (name) => path.join(logDir, `${name}.log`);
|
|
131
|
+
|
|
132
|
+
// Every unit is one worker: a shard runs its files one at a time.
|
|
133
|
+
const env = { ...process.env, MIXDOG_TEST_CONCURRENCY: '1', MIXDOG_TEST_OOXML_CACHE_DIR: resourceDir };
|
|
134
|
+
function run(command, args, options, fd) {
|
|
72
135
|
return new Promise((resolve) => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
windowsHide: true,
|
|
78
|
-
stdio: ['ignore', logFd, logFd],
|
|
79
|
-
env: {
|
|
80
|
-
...process.env,
|
|
81
|
-
MIXDOG_TEST_CONCURRENCY: String(concurrency),
|
|
82
|
-
MIXDOG_TEST_OOXML_CACHE_DIR: resourceDir,
|
|
83
|
-
},
|
|
136
|
+
const child = spawn(command, args, { ...options, env, windowsHide: true, stdio: ['ignore', fd, fd] });
|
|
137
|
+
child.on('error', (error) => {
|
|
138
|
+
fs.writeSync(fd, `${error.stack}\n`);
|
|
139
|
+
resolve(1);
|
|
84
140
|
});
|
|
85
|
-
child.on('error', () => resolve(1));
|
|
86
141
|
child.on('close', (code) => resolve(code ?? 1));
|
|
87
142
|
});
|
|
88
143
|
}
|
|
89
144
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
145
|
+
const TIMING_REPORTER = new URL('./lib/gate-timing-reporter.mjs', import.meta.url).href;
|
|
146
|
+
async function runShard(unit, fd) {
|
|
147
|
+
const lane = lanesByName.get(unit.lane);
|
|
148
|
+
const timings = path.join(logDir, `${unit.id}.timings.jsonl`);
|
|
149
|
+
const args = [...lane.args, `--test-reporter=${TIMING_REPORTER}`, `--test-reporter-destination=${timings}`];
|
|
150
|
+
fs.writeSync(fd, `\n$ (${lane.pkg}) node ${[...args, ...unit.files].join(' ')}\n`);
|
|
151
|
+
const code = await run(process.execPath, [...args, ...unit.files], { cwd: lane.dir }, fd);
|
|
152
|
+
const reported = Object.fromEntries(
|
|
153
|
+
(fs.existsSync(timings) ? fs.readFileSync(timings, 'utf8') : '')
|
|
154
|
+
.split('\n')
|
|
155
|
+
.filter(Boolean)
|
|
156
|
+
.map((line) => {
|
|
157
|
+
const { file, ms } = JSON.parse(line);
|
|
158
|
+
return [file, ms];
|
|
159
|
+
})
|
|
160
|
+
);
|
|
161
|
+
fs.rmSync(timings, { force: true });
|
|
162
|
+
for (const [file, ms] of Object.entries(measuredFileTimes(unit.files, reported, Date.now() - unit.start))) {
|
|
163
|
+
measured[lane.key(file)] = Math.round(ms);
|
|
164
|
+
}
|
|
165
|
+
return code === 0;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function runCommands(unit, fd) {
|
|
169
|
+
let ok = true;
|
|
170
|
+
for (const cmd of unit.cmds) {
|
|
171
|
+
fs.writeSync(fd, `\n$ ${cmd}\n`);
|
|
172
|
+
if ((await run(cmd, [], { cwd: root, shell: true }, fd)) !== 0) {
|
|
173
|
+
ok = false;
|
|
98
174
|
break;
|
|
99
175
|
}
|
|
100
176
|
}
|
|
101
|
-
|
|
102
|
-
|
|
177
|
+
measured[unit.key] = Date.now() - unit.start;
|
|
178
|
+
return ok;
|
|
103
179
|
}
|
|
104
180
|
|
|
105
|
-
|
|
181
|
+
let finished = 0;
|
|
182
|
+
async function runUnit(unit) {
|
|
183
|
+
const part = path.join(logDir, `${unit.id}.part.log`);
|
|
184
|
+
const fd = fs.openSync(part, 'w');
|
|
185
|
+
unit.start = Date.now();
|
|
186
|
+
let ok = false;
|
|
187
|
+
try {
|
|
188
|
+
ok = await (unit.files ? runShard(unit, fd) : runCommands(unit, fd));
|
|
189
|
+
} finally {
|
|
190
|
+
fs.closeSync(fd);
|
|
191
|
+
unit.end = Date.now();
|
|
192
|
+
fs.appendFileSync(laneLog(unit.lane), fs.readFileSync(part));
|
|
193
|
+
fs.rmSync(part);
|
|
194
|
+
}
|
|
195
|
+
console.log(
|
|
196
|
+
`[gate:local] ${ok ? 'ok ' : 'FAIL'} ${unit.id} ${((unit.end - unit.start) / 1000).toFixed(1)}s (${++finished}/${units.length})`
|
|
197
|
+
);
|
|
198
|
+
return ok;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
console.log(
|
|
202
|
+
`[gate:local] ${units.length} units on ${budget} workers at below-normal priority; timings: ${historyPath}; logs: ${logDir}`
|
|
203
|
+
);
|
|
204
|
+
let notStarted = [];
|
|
106
205
|
try {
|
|
107
|
-
|
|
108
|
-
await runLeg(legs.at(-1), budget);
|
|
109
|
-
await runGateLegs(legs.slice(0, -1), budget, runLeg);
|
|
206
|
+
notStarted = await runPool(units, budget, runUnit);
|
|
110
207
|
} finally {
|
|
111
208
|
removeTempDirSync(resourceDir);
|
|
209
|
+
// The next run plans from these; work this run did not reach keeps its old time.
|
|
210
|
+
const keys = [...testLanes.flatMap((lane) => lane.files.map(lane.key)), ...commandUnits.map((unit) => unit.key)];
|
|
211
|
+
const next = Object.fromEntries(
|
|
212
|
+
keys.filter((key) => (measured[key] ?? history[key]) !== undefined).map((key) => [key, measured[key] ?? history[key]])
|
|
213
|
+
);
|
|
214
|
+
const saveError = historyPath && saveTimingHistory(historyPath, next);
|
|
215
|
+
if (saveError) console.warn(`[gate:local] warning: timings not saved (${saveError.message})`);
|
|
112
216
|
}
|
|
113
217
|
|
|
114
218
|
console.log('\n=== gate:local summary ===');
|
|
115
|
-
for (const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
219
|
+
for (const name of laneNames) {
|
|
220
|
+
const laneUnits = units.filter((unit) => unit.lane === name);
|
|
221
|
+
const ran = laneUnits.filter((unit) => unit.end);
|
|
222
|
+
const status = ran.some((unit) => !unit.ok) ? 'FAIL' : ran.length < laneUnits.length ? 'NOT RUN' : 'PASS';
|
|
223
|
+
const span = ran.length ? Math.max(...ran.map((u) => u.end)) - Math.min(...ran.map((u) => u.start)) : 0;
|
|
224
|
+
const work = ran.reduce((sum, unit) => sum + unit.end - unit.start, 0);
|
|
225
|
+
console.log(
|
|
226
|
+
`${status} ${name} ${(span / 1000).toFixed(1)}s (${(work / 1000).toFixed(1)}s worker time, ${ran.length}/${laneUnits.length} units) ${laneLog(name)}`
|
|
227
|
+
);
|
|
228
|
+
if (status !== 'FAIL') continue;
|
|
229
|
+
const lines = fs
|
|
230
|
+
.readFileSync(laneLog(name), 'utf8')
|
|
231
|
+
.split(/\r?\n/)
|
|
232
|
+
.filter((l) => /\[failure-summary\] (?:RUN FAILED|HUNG|FAILED|FLAKY)/.test(l));
|
|
233
|
+
for (const l of lines) console.log(` ${l}`);
|
|
124
234
|
}
|
|
235
|
+
if (notStarted.length) console.log(`Stopped after a failed ${units.find((u) => u.stopOnFail && u.ok === false).lane} check: ${notStarted.length} unit(s) not run`);
|
|
125
236
|
console.log(
|
|
126
237
|
'Not covered locally: runtime-macos, computer-backend, desktop-tests computer lane (Windows-only), relay, graph, patch, browser-import, mobile-scripts'
|
|
127
238
|
);
|
|
128
239
|
console.log(`Total: ${((Date.now() - started) / 1000).toFixed(1)}s`);
|
|
129
|
-
process.exit(
|
|
240
|
+
process.exit(units.every((unit) => unit.ok) ? 0 : 1);
|