@yemi33/minions 0.1.2448 → 0.1.2449
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/bin/cli-api-client.js +1 -1
- package/bin/install-internal-minions.js +1382 -44
- package/bin/install-layout.js +150 -0
- package/bin/minions.js +460 -167
- package/dashboard/docs/typography.md +65 -12
- package/dashboard/js/command-center.js +58 -4
- package/dashboard/js/detail-panel.js +36 -0
- package/dashboard/js/memory-panel.js +59 -12
- package/dashboard/js/qa.js +179 -20
- package/dashboard/js/refresh.js +148 -12
- package/dashboard/js/render-dispatch.js +3 -4
- package/dashboard/js/render-inbox.js +2 -2
- package/dashboard/js/render-other.js +3 -3
- package/dashboard/js/render-pipelines.js +14 -0
- package/dashboard/js/render-plans.js +57 -9
- package/dashboard/js/render-prd.js +132 -23
- package/dashboard/js/render-prs.js +195 -166
- package/dashboard/js/render-schedules.js +63 -3
- package/dashboard/js/render-utils.js +3 -3
- package/dashboard/js/render-watches.js +19 -3
- package/dashboard/js/render-work-items.js +238 -30
- package/dashboard/js/settings.js +205 -54
- package/dashboard/js/utils.js +51 -1
- package/dashboard/pages/home.html +1 -1
- package/dashboard/pages/qa.html +1 -16
- package/dashboard/pages/work.html +40 -0
- package/dashboard/shared/cc-limits.js +79 -0
- package/dashboard/shared/pr-filters.js +21 -38
- package/dashboard/shared/project-git-summary.js +1 -1
- package/dashboard/shared/record-filters.js +169 -0
- package/dashboard/shared/watches-source.js +1 -1
- package/dashboard/shared/welcome-popup.js +1 -1
- package/dashboard/shared/wi-filters.js +302 -0
- package/dashboard/slim/body.html +1 -0
- package/dashboard/slim/js/command-send.js +26 -0
- package/dashboard/slim/js/modals-tiles.js +380 -39
- package/dashboard/slim/js/status.js +13 -21
- package/dashboard/slim/layout.html +1 -0
- package/dashboard/slim/panel-bootstrap.js +6 -2
- package/dashboard/slim/styles.css +38 -0
- package/dashboard/styles.css +159 -55
- package/dashboard-build.js +13 -2
- package/dashboard.js +956 -423
- package/docs/README.md +11 -6
- package/docs/api-errors.md +2 -2
- package/docs/architecture-review-2026-07-09.md +1 -1
- package/docs/architecture.excalidraw +2 -2
- package/docs/auto-discovery.md +18 -9
- package/docs/branch-derivation.md +4 -4
- package/docs/capture-demos.js +39 -2
- package/docs/ci-runner-canary.md +123 -0
- package/docs/claude-md-propagation.md +2 -2
- package/docs/cloud-agent-dispatch.md +204 -0
- package/docs/command-center.md +7 -7
- package/docs/completion-reports.md +43 -20
- package/docs/constants.md +10 -3
- package/docs/constellation-bridge.md +134 -6
- package/docs/constellation-style-telemetry.md +4 -4
- package/docs/contracts/capability-protocol.v1.json +165 -0
- package/docs/cooldown-merge-semantics.md +12 -12
- package/docs/copilot-cli-schema.md +7 -7
- package/docs/cross-repo-plans.md +10 -10
- package/docs/dead-code-audit-retractions.md +5 -5
- package/docs/default-branch-ci.md +173 -0
- package/docs/deprecated.json +31 -31
- package/docs/design-inbox-entries-schema.md +3 -3
- package/docs/design-language.md +1051 -0
- package/docs/design-state-storage.md +11 -11
- package/docs/diagnostics-crash-reports.md +9 -9
- package/docs/diagnostics-memory.md +5 -5
- package/docs/documentation-audit-2026-07-09.md +7 -7
- package/docs/engine-restart.md +90 -5
- package/docs/harness-mode.md +1 -1
- package/docs/internal-install.md +338 -39
- package/docs/kb-dedup-duplicate-pair-investigation.md +5 -5
- package/docs/kb-pr3223-cascade-archiving.md +1 -1
- package/docs/kb-pr696-merge-conflict-docs.md +6 -6
- package/docs/kb-sweep.md +35 -35
- package/docs/keep-processes.md +1 -1
- package/docs/live-checkout-mode.md +30 -30
- package/docs/managed-spawn.md +18 -14
- package/docs/named-agents.md +7 -7
- package/docs/plan-lifecycle.md +69 -2
- package/docs/pr-author-identity.md +114 -0
- package/docs/pr-auto-fix-dispatch.md +19 -4
- package/docs/pr-comment-followup.md +6 -6
- package/docs/pr-review-fix-loop.md +59 -10
- package/docs/process-termination.md +40 -0
- package/docs/proposals/repo-pool-for-live-checkout.md +13 -13
- package/docs/qa-runbook-lifecycle.md +367 -17
- package/docs/qa-runbooks.md +3 -3
- package/docs/rfc-completion-json.md +18 -18
- package/docs/runtime-adapters.md +26 -21
- package/docs/security.md +6 -6
- package/docs/self-improvement.md +4 -4
- package/docs/shared-lifecycle-module-map.md +473 -472
- package/docs/skills.md +52 -3
- package/docs/slim-ux/concepts.md +121 -116
- package/docs/specs/agent-configurability.md +18 -18
- package/docs/specs/agent-rename.md +18 -18
- package/docs/team-memory.md +38 -21
- package/docs/timeouts-and-liveness.md +118 -10
- package/docs/tutorials/01-install-and-connect.md +1 -1
- package/docs/watches.md +40 -39
- package/docs/workspace-manifests.md +4 -4
- package/docs/worktree-lifecycle.md +293 -14
- package/engine/README.md +46 -0
- package/engine/{ado-comment.js → ado/comment.js} +8 -8
- package/engine/{ado-git-auth.js → ado/git-auth.js} +4 -4
- package/engine/{ado.js → ado/index.js} +417 -63
- package/engine/{ado-status.js → ado/status.js} +6 -8
- package/engine/{ado-token.js → ado/token.js} +1 -1
- package/engine/{acp-transport.js → agents/acp-transport.js} +62 -22
- package/engine/{agent-worker-pool.js → agents/agent-worker-pool.js} +17 -8
- package/engine/{cc-worker-pool.js → agents/cc-worker-pool.js} +16 -6
- package/engine/{claude-md-context.js → agents/claude-md-context.js} +5 -5
- package/engine/{harness-context.js → agents/harness-context.js} +5 -5
- package/engine/{harness.js → agents/harness.js} +3 -3
- package/engine/{llm.js → agents/llm.js} +18 -14
- package/engine/{model-discovery.js → agents/model-discovery.js} +2 -2
- package/engine/{playbook.js → agents/playbook.js} +155 -22
- package/engine/{pooled-agent-process.js → agents/pooled-agent-process.js} +14 -12
- package/engine/{preflight.js → agents/preflight.js} +29 -10
- package/engine/{spawn-agent.js → agents/spawn-agent.js} +25 -14
- package/engine/{spawn-phase-watchdog.js → agents/spawn-phase-watchdog.js} +16 -7
- package/engine/{steering.js → agents/steering.js} +5 -5
- package/engine/{tools-inventory.js → agents/tools-inventory.js} +2 -2
- package/engine/{agent-api-validation.js → api/agent-api-validation.js} +2 -2
- package/engine/{api-validation.js → api/api-validation.js} +1 -1
- package/engine/api/bridge.js +787 -0
- package/engine/{cc-api-validation.js → api/cc-api-validation.js} +1 -1
- package/engine/api/companion.js +560 -0
- package/engine/{content-api-validation.js → api/content-api-validation.js} +2 -2
- package/engine/{pr-issue-validation.js → api/pr-issue-validation.js} +33 -6
- package/engine/{settings-validation.js → api/settings-validation.js} +32 -4
- package/engine/api-contracts/agent-content.js +4 -4
- package/engine/api-contracts/capability-manifest.js +236 -0
- package/engine/api-contracts/capability-protocol.js +333 -0
- package/engine/api-contracts/cc-ops.js +1 -1
- package/engine/api-contracts/config-runtime.js +5 -0
- package/engine/api-contracts/core.js +28 -1
- package/engine/api-contracts/index.js +100 -0
- package/engine/api-contracts/orchestration.js +18 -5
- package/engine/api-contracts/pull-requests.js +37 -6
- package/engine/api-contracts/qa-process.js +29 -6
- package/engine/api-contracts/work-plan-prd.js +21 -1
- package/engine/cloud/contract.js +212 -0
- package/engine/cloud/index.js +159 -0
- package/engine/{execution-model.js → core/execution-model.js} +1 -1
- package/engine/{features.js → core/features.js} +4 -4
- package/engine/{operator-identity.js → core/operator-identity.js} +1 -1
- package/engine/{queries.js → core/queries.js} +201 -36
- package/engine/{safe-expr.js → core/safe-expr.js} +1 -1
- package/engine/{shared.js → core/shared.js} +1637 -175
- package/engine/{stdio-timestamps.js → core/stdio-timestamps.js} +1 -1
- package/engine/{untrusted-fence.js → core/untrusted-fence.js} +3 -3
- package/engine/db/index.js +11 -2
- package/engine/db/migrations/002-dispatches.js +3 -3
- package/engine/db/migrations/003-work-items.js +1 -1
- package/engine/db/migrations/004-pull-requests.js +1 -1
- package/engine/db/migrations/006-metrics.js +1 -1
- package/engine/db/migrations/007-watches.js +2 -2
- package/engine/db/migrations/008-small-state.js +1 -1
- package/engine/db/migrations/009-qa.js +1 -1
- package/engine/db/migrations/010-pr-links.js +1 -1
- package/engine/db/migrations/011-remaining-state.js +1 -1
- package/engine/db/migrations/012-steering-deliveries.js +2 -2
- package/engine/db/migrations/013-backfill-broken-note-links.js +1 -1
- package/engine/db/migrations/014-pr-fix-target-prefs.js +2 -2
- package/engine/db/migrations/015-plans-prds.js +0 -0
- package/engine/db/migrations/018-sql-only-cutover.js +2 -2
- package/engine/db/migrations/021-archived-work-items.js +1 -1
- package/engine/db/migrations/022-global-cc-session.js +1 -1
- package/engine/db/migrations/023-engine-state.js +1 -1
- package/engine/db/migrations/025-malformed-work-item-phantoms.js +1 -1
- package/engine/db/migrations/027-review-learning-lifecycle.js +1 -1
- package/engine/db/migrations/029-repair-reused-versions.js +20 -0
- package/engine/db/migrations/031-pr-author-identity.js +137 -0
- package/engine/{consolidation.js → memory/consolidation.js} +6 -6
- package/engine/{kb-sweep-runner.js → memory/kb-sweep-runner.js} +2 -2
- package/engine/{kb-sweep.js → memory/kb-sweep.js} +9 -7
- package/engine/{memory-retrieval.js → memory/memory-retrieval.js} +46 -4
- package/engine/{memory-store.js → memory/memory-store.js} +3 -3
- package/engine/{promotion.js → memory/promotion.js} +3 -3
- package/engine/{review-learning-backfill.js → memory/review-learning-backfill.js} +6 -6
- package/engine/{review-learning.js → memory/review-learning.js} +10 -5
- package/engine/{diagnostics-memory.js → observability/diagnostics-memory.js} +1 -1
- package/engine/{logs-store.js → observability/logs-store.js} +5 -5
- package/engine/{metrics-store.js → observability/metrics-store.js} +4 -4
- package/engine/{check-status.js → operations/check-status.js} +3 -3
- package/engine/{cli.js → operations/cli.js} +271 -113
- package/engine/{distribution.js → operations/distribution.js} +5 -6
- package/engine/{cleanup.js → orchestration/cleanup.js} +72 -45
- package/engine/{cooldown.js → orchestration/cooldown.js} +5 -5
- package/engine/{dispatch-events.js → orchestration/dispatch-events.js} +2 -2
- package/engine/{dispatch.js → orchestration/dispatch.js} +129 -36
- package/engine/orchestration/failed-scheduled-cleanup.js +274 -0
- package/engine/{lifecycle.js → orchestration/lifecycle.js} +198 -90
- package/engine/{meeting.js → orchestration/meeting.js} +6 -16
- package/engine/{pipeline.js → orchestration/pipeline.js} +12 -12
- package/engine/{pre-dispatch-eval.js → orchestration/pre-dispatch-eval.js} +10 -9
- package/engine/{routing.js → orchestration/routing.js} +3 -3
- package/engine/{schedule-bootstrap.js → orchestration/schedule-bootstrap.js} +4 -4
- package/engine/{scheduler.js → orchestration/scheduler.js} +38 -8
- package/engine/{timeout.js → orchestration/timeout.js} +158 -109
- package/engine/{db-events.js → persistence/db-events.js} +2 -2
- package/engine/{dispatch-store.js → persistence/dispatch-store.js} +7 -7
- package/engine/{inbox-store.js → persistence/inbox-store.js} +2 -2
- package/engine/{note-link-backfill.js → persistence/note-link-backfill.js} +4 -4
- package/engine/{pr-fix-target-store.js → persistence/pr-fix-target-store.js} +8 -8
- package/engine/{pull-requests-store.js → persistence/pull-requests-store.js} +21 -7
- package/engine/{small-state-store.js → persistence/small-state-store.js} +31 -31
- package/engine/persistence/state-operations.js +350 -0
- package/engine/{steering-store.js → persistence/steering-store.js} +6 -6
- package/engine/{issues.js → planning/issues.js} +2 -2
- package/engine/{plan-prd-validation.js → planning/plan-prd-validation.js} +8 -2
- package/engine/planning/prd-result-sidecar.js +190 -0
- package/engine/{prd-store.js → planning/prd-store.js} +17 -17
- package/engine/{project-discovery.js → planning/project-discovery.js} +5 -5
- package/engine/{projects.js → planning/projects.js} +10 -10
- package/engine/{resolve-area.js → planning/resolve-area.js} +1 -1
- package/engine/{work-item-validation.js → planning/work-item-validation.js} +39 -3
- package/engine/{work-items-store.js → planning/work-items-store.js} +29 -21
- package/engine/{keep-process-sweep.js → processes/keep-process-sweep.js} +57 -17
- package/engine/{managed-spawn-launcher.js → processes/managed-spawn-launcher.js} +3 -3
- package/engine/{managed-spawn.js → processes/managed-spawn.js} +97 -46
- package/engine/{process-utils.js → processes/process-utils.js} +599 -55
- package/engine/{abandoned-pr-reconciliation.js → providers/abandoned-pr-reconciliation.js} +17 -7
- package/engine/{comment-classifier.js → providers/comment-classifier.js} +85 -17
- package/engine/{comment-format.js → providers/comment-format.js} +5 -5
- package/engine/{gh-comment.js → providers/gh-comment.js} +15 -15
- package/engine/{gh-token.js → providers/gh-token.js} +4 -4
- package/engine/{github.js → providers/github.js} +131 -54
- package/engine/{pr-action.js → providers/pr-action.js} +13 -12
- package/engine/{pr-clone-keep.js → providers/pr-clone-keep.js} +7 -7
- package/engine/{pr-devbox.js → providers/pr-devbox.js} +6 -6
- package/engine/{pr-fix-target.js → providers/pr-fix-target.js} +13 -13
- package/engine/{pr-remote-patch.js → providers/pr-remote-patch.js} +4 -4
- package/engine/{pr-resolve.js → providers/pr-resolve.js} +7 -7
- package/engine/{pr-temp-clone.js → providers/pr-temp-clone.js} +5 -5
- package/engine/{pr-track.js → providers/pr-track.js} +11 -13
- package/engine/{shared-branch-pr-reconcile.js → providers/shared-branch-pr-reconcile.js} +4 -4
- package/engine/qa/auto-prd-qa.js +313 -0
- package/engine/{qa-from-prd.js → qa/from-prd.js} +42 -12
- package/engine/qa/prd-session.js +240 -0
- package/engine/{qa-process-validation.js → qa/process-validation.js} +14 -9
- package/engine/{qa-runbooks.js → qa/runbooks.js} +1 -1
- package/engine/{qa-runs.js → qa/runs.js} +286 -15
- package/engine/{qa-sessions.js → qa/sessions.js} +595 -49
- package/engine/qa/visual-journey.js +654 -0
- package/engine/{qa-runners.js → qa-runners/index.js} +7 -7
- package/engine/qa-runners/maestro.js +3 -3
- package/engine/qa-runners/playwright.js +2 -2
- package/engine/{restart-health.js → recovery/restart-health.js} +48 -4
- package/engine/recovery/stop-stack.js +607 -0
- package/engine/{supervisor.js → recovery/supervisor.js} +105 -175
- package/engine/{watchdog.js → recovery/watchdog.js} +136 -13
- package/engine/runtimes/claude.js +14 -12
- package/engine/runtimes/codex.js +8 -6
- package/engine/runtimes/copilot.js +17 -16
- package/engine/{watch-actions.js → watches/actions.js} +13 -13
- package/engine/{watches.js → watches/index.js} +43 -32
- package/engine/{watches-store.js → watches/store.js} +4 -4
- package/engine/{create-pr-worktree.js → worktrees/create-pr.js} +1 -1
- package/engine/{worktree-gc.js → worktrees/gc.js} +70 -22
- package/engine/worktrees/inventory.js +671 -0
- package/engine/{live-checkout.js → worktrees/live-checkout.js} +4 -4
- package/engine/{worktree-pool.js → worktrees/pool.js} +2 -2
- package/engine/{worktree-preflight.js → worktrees/preflight.js} +1 -0
- package/engine/worktrees/quarantine-refs.js +173 -0
- package/engine.js +1137 -208
- package/minions.js +147 -77
- package/package.json +10 -6
- package/playbooks/_pr-description-audit.md +110 -78
- package/playbooks/build-fix-complex.md +2 -0
- package/playbooks/fix.md +16 -12
- package/playbooks/implement-shared.md +2 -0
- package/playbooks/implement.md +19 -20
- package/playbooks/plan-to-prd.md +18 -3
- package/playbooks/qa-session-draft.md +136 -1
- package/playbooks/qa-session-execute.md +80 -2
- package/playbooks/qa-session-setup.md +17 -1
- package/playbooks/qa-validate.md +1 -1
- package/playbooks/setup.md +2 -0
- package/playbooks/shared-rules.md +25 -32
- package/playbooks/templates/followup-dispatch.md +4 -3
- package/playbooks/verify.md +1 -1
- package/prompts/cc-system.md +19 -27
- package/watch-plugins/README.md +92 -0
- package/watch-plugins/ado-author-prs.js +336 -0
- package/watch-plugins/gh-author-prs.js +375 -0
- package/watch-plugins/http.js +474 -0
- package/watch-plugins/teams-channel.js +869 -0
- package/docs/dev-composite-workflow.md +0 -101
- package/docs/pr-screenshots/pr-886/after-single-header.png +0 -0
- package/docs/pr-screenshots/pr-886/before-duplicate-header.png +0 -0
- package/docs/pr-screenshots/pr-895/01-cancellation-reason-detail.png +0 -0
- package/docs/pr-screenshots/pr-899/worker-pool-worktrees-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-899/worker-pool-worktrees-BEFORE.png +0 -0
- package/docs/pr-screenshots/pr-901/projects-tab-default.png +0 -0
- package/docs/pr-screenshots/pr-901/projects-tab-fmf-selected.png +0 -0
- package/docs/pr-screenshots/pr-916/model-picker-AFTER-crop.png +0 -0
- package/docs/pr-screenshots/pr-916/model-picker-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-916/model-picker-BEFORE-crop.png +0 -0
- package/docs/pr-screenshots/pr-916/model-picker-BEFORE.png +0 -0
- package/docs/pr-screenshots/pr-916/model-picker-dropdown-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-979/auto-fix-pane-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-979/auto-fix-pane-BEFORE.png +0 -0
- package/docs/pr-screenshots/pr-985/pr-column-em-dash-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-985/pr-column-em-dash-BEFORE.png +0 -0
- package/docs/visual-evidence-ci.md +0 -103
- package/engine/bridge.js +0 -379
- package/engine/quarantine-refs.js +0 -103
- package/engine/state-operations.js +0 -178
- /package/engine/{steering-constraints.js → agents/steering-constraints.js} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
// engine/watchdog.js — out-of-process recovery scheduled by the OS.
|
|
1
|
+
// engine/recovery/watchdog.js — out-of-process recovery scheduled by the OS.
|
|
2
2
|
//
|
|
3
3
|
// PROBLEM (cluster-kill):
|
|
4
|
-
// The in-process supervisor (engine/supervisor.js) respawns engine/dashboard
|
|
4
|
+
// The in-process supervisor (engine/recovery/supervisor.js) respawns engine/dashboard
|
|
5
5
|
// when one of them dies in isolation, but it can't respawn ITSELF. When
|
|
6
6
|
// something kills all three at once — Windows job-object teardown when a
|
|
7
7
|
// parent terminal closes (RDP disconnect, Windows Terminal auto-update,
|
|
@@ -41,6 +41,7 @@ const WIN_TASK_NAME = 'MinionsWatchdog';
|
|
|
41
41
|
const MAC_PLIST_LABEL = 'io.minions.watchdog';
|
|
42
42
|
const LINUX_UNIT_NAME = 'minions-watchdog';
|
|
43
43
|
const TICK_SPAWN_TIMEOUT_MS = 120000;
|
|
44
|
+
const WIN_TASK_EXECUTION_LIMIT_MIN = 2;
|
|
44
45
|
const WATCHDOG_LOG_NAME = 'watchdog-stdio.log';
|
|
45
46
|
// Cap the watchdog log so a misbehaving scheduler can't fill the disk
|
|
46
47
|
// over months. Truncates to half-size on overflow (oldest lines dropped).
|
|
@@ -53,9 +54,13 @@ const WATCHDOG_LOG_MAX_BYTES = 2 * 1024 * 1024;
|
|
|
53
54
|
// "dead", and fire a SECOND `minions start` before the first finished binding
|
|
54
55
|
// — re-introducing the documented duplicate-engine/dashboard storm class. The
|
|
55
56
|
// in-process supervisor avoids this with an in-memory POST_SPAWN_GRACE_MS
|
|
56
|
-
// (engine/supervisor.js:76); the watchdog can't keep state across ticks, so it
|
|
57
|
+
// (engine/recovery/supervisor.js:76); the watchdog can't keep state across ticks, so it
|
|
57
58
|
// persists a tiny timestamp beacon to disk and stands down inside the window.
|
|
58
59
|
const WATCHDOG_SPAWN_BEACON_NAME = 'watchdog-spawn.json';
|
|
60
|
+
const WATCHDOG_PARTIAL_BEACON_NAME = 'watchdog-partial.json';
|
|
61
|
+
const PARTIAL_RECOVERY_GRACE_MS = 90 * 1000;
|
|
62
|
+
const FRESH_ENGINE_HEARTBEAT_MS = Number(process.env.MINIONS_WATCHDOG_FRESH_HEARTBEAT_MS) || 60 * 1000;
|
|
63
|
+
const STALE_ENGINE_HEARTBEAT_MS = Number(process.env.MINIONS_WATCHDOG_STALE_HEARTBEAT_MS) || 180 * 1000;
|
|
59
64
|
// Floor for the grace window. The effective window is max(2 × interval, floor)
|
|
60
65
|
// so a fast cadence (e.g. --interval=1) still leaves a slow cold-start time to
|
|
61
66
|
// finish, while a long cadence scales the window up with the interval.
|
|
@@ -65,6 +70,10 @@ function watchdogSpawnBeaconPath(minionsHome) {
|
|
|
65
70
|
return path.join(minionsHome, 'engine', WATCHDOG_SPAWN_BEACON_NAME);
|
|
66
71
|
}
|
|
67
72
|
|
|
73
|
+
function watchdogPartialBeaconPath(minionsHome) {
|
|
74
|
+
return path.join(minionsHome, 'engine', WATCHDOG_PARTIAL_BEACON_NAME);
|
|
75
|
+
}
|
|
76
|
+
|
|
68
77
|
// Effective grace window: max(2 × interval, 90s). intervalMin defaults to the
|
|
69
78
|
// scheduler's default cadence when the caller doesn't thread one through.
|
|
70
79
|
function resolvePostSpawnGraceMs(intervalMin) {
|
|
@@ -94,6 +103,28 @@ function writeSpawnBeacon(minionsHome, nowMs, action) {
|
|
|
94
103
|
} catch { /* fail-open — recovery still proceeds */ }
|
|
95
104
|
}
|
|
96
105
|
|
|
106
|
+
function readPartialBeacon(minionsHome) {
|
|
107
|
+
try {
|
|
108
|
+
const obj = JSON.parse(fs.readFileSync(watchdogPartialBeaconPath(minionsHome), 'utf8'));
|
|
109
|
+
const ts = Number(obj?.ts);
|
|
110
|
+
const state = typeof obj?.state === 'string' ? obj.state : '';
|
|
111
|
+
return Number.isFinite(ts) && state ? { ts, state } : null;
|
|
112
|
+
} catch { return null; }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function writePartialBeacon(minionsHome, nowMs, state) {
|
|
116
|
+
try {
|
|
117
|
+
const dir = path.join(minionsHome, 'engine');
|
|
118
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
119
|
+
fs.writeFileSync(watchdogPartialBeaconPath(minionsHome), JSON.stringify({ ts: nowMs, state }));
|
|
120
|
+
return true;
|
|
121
|
+
} catch { return false; }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function clearPartialBeacon(minionsHome) {
|
|
125
|
+
try { fs.unlinkSync(watchdogPartialBeaconPath(minionsHome)); } catch { /* absent/unremovable: best effort */ }
|
|
126
|
+
}
|
|
127
|
+
|
|
97
128
|
function logLine(minionsHome, msg) {
|
|
98
129
|
try {
|
|
99
130
|
const dir = path.join(minionsHome, 'engine');
|
|
@@ -118,6 +149,16 @@ function isPidAlive(pid) {
|
|
|
118
149
|
try { process.kill(pid, 0); return true; } catch { return false; }
|
|
119
150
|
}
|
|
120
151
|
|
|
152
|
+
function readEngineHeartbeat(minionsHome) {
|
|
153
|
+
try {
|
|
154
|
+
const control = JSON.parse(fs.readFileSync(path.join(minionsHome, 'engine', 'control.json'), 'utf8'));
|
|
155
|
+
const heartbeat = Number(control?.heartbeat);
|
|
156
|
+
return Number.isFinite(heartbeat) && heartbeat > 0 ? heartbeat : null;
|
|
157
|
+
} catch {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
121
162
|
/**
|
|
122
163
|
* Probe engine + dashboard and recover if needed. Always resolves (never
|
|
123
164
|
* throws). Returns { healthy, action, exitCode? } describing what was done.
|
|
@@ -127,9 +168,8 @@ function isPidAlive(pid) {
|
|
|
127
168
|
* respawning would undo `minions stop`)
|
|
128
169
|
* - both alive → no-op
|
|
129
170
|
* - both dead → `minions start` (idempotent fast path)
|
|
130
|
-
* -
|
|
131
|
-
*
|
|
132
|
-
* by design)
|
|
171
|
+
* - engine dead, dashboard up → start only engine after recovery grace
|
|
172
|
+
* - engine up, dashboard dead → start only dashboard after recovery grace
|
|
133
173
|
*/
|
|
134
174
|
async function tick(opts) {
|
|
135
175
|
opts = opts || {};
|
|
@@ -149,6 +189,7 @@ async function tick(opts) {
|
|
|
149
189
|
// loop is briefly blocked, so it stays accurate exactly when netstat fails.
|
|
150
190
|
const confirmPortUp = opts.confirmPortUp;
|
|
151
191
|
const isStopIntentSet = opts.isStopIntentSet;
|
|
192
|
+
const readHeartbeat = opts.readEngineHeartbeat || readEngineHeartbeat;
|
|
152
193
|
const spawner = opts.spawner || spawn;
|
|
153
194
|
const now = opts.now || (() => new Date());
|
|
154
195
|
|
|
@@ -173,13 +214,34 @@ async function tick(opts) {
|
|
|
173
214
|
try { stopWanted = !!isStopIntentSet(); } catch { stopWanted = false; }
|
|
174
215
|
}
|
|
175
216
|
if (stopWanted) {
|
|
217
|
+
clearPartialBeacon(minionsHome);
|
|
176
218
|
logLine(minionsHome, `stop-intent set → standing down (no probe, no recovery)`);
|
|
177
219
|
return { healthy: false, action: 'stand-down' };
|
|
178
220
|
}
|
|
179
221
|
|
|
180
222
|
let enginePid = null;
|
|
181
223
|
try { enginePid = readEnginePid(minionsHome); } catch { enginePid = null; }
|
|
182
|
-
|
|
224
|
+
let engineAlive = isPidAlive(enginePid);
|
|
225
|
+
if (enginePid) {
|
|
226
|
+
let heartbeat = null;
|
|
227
|
+
try { heartbeat = readHeartbeat(minionsHome); } catch { heartbeat = null; }
|
|
228
|
+
const heartbeatAgeMs = heartbeat ? now().getTime() - heartbeat : null;
|
|
229
|
+
if (!engineAlive && heartbeatAgeMs !== null && heartbeatAgeMs < FRESH_ENGINE_HEARTBEAT_MS) {
|
|
230
|
+
logLine(
|
|
231
|
+
minionsHome,
|
|
232
|
+
`engine=${enginePid} reported dead but heartbeat is ${Math.round(heartbeatAgeMs / 1000)}s old ` +
|
|
233
|
+
'— treating as alive (false-negative suppressed)',
|
|
234
|
+
);
|
|
235
|
+
engineAlive = true;
|
|
236
|
+
} else if (engineAlive && heartbeatAgeMs !== null && heartbeatAgeMs >= STALE_ENGINE_HEARTBEAT_MS) {
|
|
237
|
+
logLine(
|
|
238
|
+
minionsHome,
|
|
239
|
+
`engine=${enginePid} PID exists but heartbeat is ${Math.round(heartbeatAgeMs / 1000)}s old ` +
|
|
240
|
+
'— treating as dead (stale identity)',
|
|
241
|
+
);
|
|
242
|
+
engineAlive = false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
183
245
|
let dashUp = false;
|
|
184
246
|
try { dashUp = !!isPortListening(dashPort); } catch { dashUp = false; }
|
|
185
247
|
|
|
@@ -197,12 +259,38 @@ async function tick(opts) {
|
|
|
197
259
|
}
|
|
198
260
|
|
|
199
261
|
if (engineAlive && dashUp) {
|
|
262
|
+
clearPartialBeacon(minionsHome);
|
|
200
263
|
logLine(minionsHome, `ok engine=${enginePid} port=${dashPort} ts=${now().toISOString()}`);
|
|
201
264
|
return { healthy: true, action: 'none' };
|
|
202
265
|
}
|
|
203
266
|
|
|
204
267
|
const partial = engineAlive || dashUp;
|
|
205
|
-
const action = partial ? '
|
|
268
|
+
const action = !partial ? 'start' : (engineAlive ? 'dashboard' : 'engine');
|
|
269
|
+
const nowMs = now().getTime();
|
|
270
|
+
|
|
271
|
+
// The dashboard watchdog and supervisor both repair partial state. Give
|
|
272
|
+
// those in-process mechanisms time to converge before the external watchdog
|
|
273
|
+
// launches a destructive full restart. The first observation is persisted
|
|
274
|
+
// because every OS-scheduled tick is a fresh process.
|
|
275
|
+
if (partial) {
|
|
276
|
+
const partialState = `${engineAlive ? 'engine-up' : 'engine-down'}:${dashUp ? 'dashboard-up' : 'dashboard-down'}`;
|
|
277
|
+
const observed = readPartialBeacon(minionsHome);
|
|
278
|
+
if (!observed || observed.state !== partialState) {
|
|
279
|
+
if (writePartialBeacon(minionsHome, nowMs, partialState)) {
|
|
280
|
+
logLine(minionsHome, `partial state ${partialState} first observed → standing down for ${PARTIAL_RECOVERY_GRACE_MS}ms in-process recovery grace`);
|
|
281
|
+
return { healthy: false, action: 'skip', reason: 'partial-recovery-grace' };
|
|
282
|
+
}
|
|
283
|
+
} else {
|
|
284
|
+
const ageMs = nowMs - observed.ts;
|
|
285
|
+
if (ageMs >= 0 && ageMs < PARTIAL_RECOVERY_GRACE_MS) {
|
|
286
|
+
logLine(minionsHome, `partial state ${partialState} persists ${ageMs}ms (< ${PARTIAL_RECOVERY_GRACE_MS}ms) → standing down for in-process recovery`);
|
|
287
|
+
return { healthy: false, action: 'skip', reason: 'partial-recovery-grace' };
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
clearPartialBeacon(minionsHome);
|
|
291
|
+
} else {
|
|
292
|
+
clearPartialBeacon(minionsHome);
|
|
293
|
+
}
|
|
206
294
|
|
|
207
295
|
// Post-spawn grace: if a previous tick already issued a spawn within the
|
|
208
296
|
// grace window, stand down instead of double-firing. Each tick is a fresh
|
|
@@ -210,7 +298,6 @@ async function tick(opts) {
|
|
|
210
298
|
// the slow-cold-start race where control.json's late PID write lets the next
|
|
211
299
|
// scheduled tick spawn a second daemon stack.
|
|
212
300
|
const graceMs = resolvePostSpawnGraceMs(opts.intervalMin);
|
|
213
|
-
const nowMs = now().getTime();
|
|
214
301
|
const lastSpawnTs = readSpawnBeaconTs(minionsHome);
|
|
215
302
|
if (lastSpawnTs != null && nowMs - lastSpawnTs < graceMs) {
|
|
216
303
|
logLine(
|
|
@@ -224,7 +311,7 @@ async function tick(opts) {
|
|
|
224
311
|
logLine(
|
|
225
312
|
minionsHome,
|
|
226
313
|
`unhealthy engine=${enginePid || '-'}(${engineAlive ? 'alive' : 'dead'}) ` +
|
|
227
|
-
`port=${dashPort}(${dashUp ? 'up' : 'down'}) → invoking minions ${action}`
|
|
314
|
+
`port=${dashPort}(${dashUp ? 'up' : 'down'}) → invoking ${action === 'start' ? 'minions start' : `targeted ${action} recovery`}`
|
|
228
315
|
);
|
|
229
316
|
|
|
230
317
|
try {
|
|
@@ -236,7 +323,15 @@ async function tick(opts) {
|
|
|
236
323
|
// Detach the child so its lifetime is independent of this tick process.
|
|
237
324
|
// The OS scheduler closes our stdio shortly after exit; we don't want
|
|
238
325
|
// that closure to cascade into the new daemon stack.
|
|
239
|
-
|
|
326
|
+
let spawnArgs;
|
|
327
|
+
if (action === 'engine') {
|
|
328
|
+
spawnArgs = [path.join(minionsHome, 'engine.js'), 'start'];
|
|
329
|
+
} else if (action === 'dashboard') {
|
|
330
|
+
spawnArgs = [path.join(minionsHome, 'dashboard.js'), String(dashPort)];
|
|
331
|
+
} else {
|
|
332
|
+
spawnArgs = [minionsBin, 'start'];
|
|
333
|
+
}
|
|
334
|
+
const child = spawner(process.execPath, spawnArgs, {
|
|
240
335
|
detached: true,
|
|
241
336
|
stdio: 'ignore',
|
|
242
337
|
windowsHide: true,
|
|
@@ -245,10 +340,14 @@ async function tick(opts) {
|
|
|
245
340
|
// user action), so the post-restart hook MUST NOT pop a browser tab.
|
|
246
341
|
// The CLI's spawnFullStackAndVerify reads this env var as a hard
|
|
247
342
|
// kill-switch via MINIONS_NO_AUTO_OPEN=1.
|
|
248
|
-
env: {
|
|
343
|
+
env: {
|
|
344
|
+
...process.env,
|
|
345
|
+
MINIONS_NO_AUTO_OPEN: '1',
|
|
346
|
+
...(action === 'dashboard' ? { MINIONS_REQUIRE_DASHBOARD_PORT: '1' } : {}),
|
|
347
|
+
},
|
|
249
348
|
});
|
|
250
349
|
if (child && typeof child.unref === 'function') child.unref();
|
|
251
|
-
logLine(minionsHome, `spawned minions ${action} pid=${child && child.pid} (detached)`);
|
|
350
|
+
logLine(minionsHome, `spawned ${action === 'start' ? 'minions start' : `targeted ${action}`} pid=${child && child.pid} (detached)`);
|
|
252
351
|
// Crash-loop counter (W-mr2c46590003e3ee) — best-effort, injected so this
|
|
253
352
|
// module stays dependency-free of shared.js (matches isStopIntentSet /
|
|
254
353
|
// confirmPortUp above). Caller (bin/minions.js) wires opts.recordRespawn
|
|
@@ -324,6 +423,7 @@ REM Minions watchdog launcher — auto-generated by 'minions watchdog install'.\
|
|
|
324
423
|
REM Re-run 'minions watchdog install' to regenerate after moving MINIONS_HOME.\r
|
|
325
424
|
set "MINIONS_HOME=${opts.minionsHome}"\r
|
|
326
425
|
"${opts.nodeBin}" "${opts.minionsBin}" watchdog tick\r
|
|
426
|
+
exit /b %ERRORLEVEL%\r
|
|
327
427
|
`;
|
|
328
428
|
}
|
|
329
429
|
|
|
@@ -350,6 +450,24 @@ function installWindows(opts) {
|
|
|
350
450
|
if (r.status !== 0) {
|
|
351
451
|
throw new Error(`schtasks /Create failed (exit ${r.status}): ${(r.stderr || r.stdout || '').trim()}`);
|
|
352
452
|
}
|
|
453
|
+
// schtasks defaults one-shot tasks to IgnoreNew + a 72-hour execution
|
|
454
|
+
// limit. A stranded cmd.exe then blocks every later watchdog tick. Allow a
|
|
455
|
+
// later tick to run in parallel and bound every instance to two minutes.
|
|
456
|
+
const settingsScript = [
|
|
457
|
+
'$settings = New-ScheduledTaskSettingsSet -MultipleInstances Parallel ' +
|
|
458
|
+
`-ExecutionTimeLimit (New-TimeSpan -Minutes ${WIN_TASK_EXECUTION_LIMIT_MIN}) ` +
|
|
459
|
+
'-StartWhenAvailable',
|
|
460
|
+
`Set-ScheduledTask -TaskName '${WIN_TASK_NAME}' -Settings $settings | Out-Null`,
|
|
461
|
+
].join('; ');
|
|
462
|
+
const settingsResult = sp('powershell.exe', [
|
|
463
|
+
'-NoProfile', '-NonInteractive', '-Command', settingsScript,
|
|
464
|
+
], { encoding: 'utf8', windowsHide: true });
|
|
465
|
+
if (settingsResult.status !== 0) {
|
|
466
|
+
throw new Error(
|
|
467
|
+
`Set-ScheduledTask watchdog settings failed (exit ${settingsResult.status}): ` +
|
|
468
|
+
`${(settingsResult.stderr || settingsResult.stdout || '').trim()}`
|
|
469
|
+
);
|
|
470
|
+
}
|
|
353
471
|
return { ok: true, scheduler: 'Task Scheduler', taskName: WIN_TASK_NAME, intervalMin: opts.intervalMin, launcherPath };
|
|
354
472
|
}
|
|
355
473
|
|
|
@@ -626,8 +744,13 @@ module.exports = {
|
|
|
626
744
|
LINUX_UNIT_NAME,
|
|
627
745
|
WATCHDOG_LOG_NAME,
|
|
628
746
|
WATCHDOG_SPAWN_BEACON_NAME,
|
|
747
|
+
WATCHDOG_PARTIAL_BEACON_NAME,
|
|
748
|
+
PARTIAL_RECOVERY_GRACE_MS,
|
|
749
|
+
FRESH_ENGINE_HEARTBEAT_MS,
|
|
750
|
+
STALE_ENGINE_HEARTBEAT_MS,
|
|
629
751
|
POST_SPAWN_GRACE_FLOOR_MS,
|
|
630
752
|
watchdogSpawnBeaconPath,
|
|
753
|
+
watchdogPartialBeaconPath,
|
|
631
754
|
resolvePostSpawnGraceMs,
|
|
632
755
|
buildMacPlist,
|
|
633
756
|
buildLinuxService,
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
const fs = require('fs');
|
|
15
15
|
const os = require('os');
|
|
16
16
|
const path = require('path');
|
|
17
|
-
const { FAILURE_CLASS, safeWrite, ts, resolveEngineCacheDir, staleResumeSessionCwdReason } = require('../shared');
|
|
18
|
-
const { wrapUntrusted, buildSource } = require('../untrusted-fence');
|
|
17
|
+
const { FAILURE_CLASS, safeWrite, ts, resolveEngineCacheDir, staleResumeSessionCwdReason } = require('../core/shared');
|
|
18
|
+
const { wrapUntrusted, buildSource } = require('../core/untrusted-fence');
|
|
19
19
|
|
|
20
20
|
const ENGINE_DIR = __dirname.replace(/[\\/]runtimes$/, '');
|
|
21
21
|
const MINIONS_DIR = path.resolve(ENGINE_DIR, '..');
|
|
@@ -26,7 +26,7 @@ const RUNTIME_NAME = 'claude';
|
|
|
26
26
|
const SPAWN_STARTUP_SUBTYPES = new Set(['init', 'hook_started', 'hook_response']);
|
|
27
27
|
|
|
28
28
|
// ── Binary Resolution ────────────────────────────────────────────────────────
|
|
29
|
-
// Mirrors engine/spawn-agent.js:26-91. Cached at engine/claude-caps.json so the
|
|
29
|
+
// Mirrors engine/agents/spawn-agent.js:26-91. Cached at engine/claude-caps.json so the
|
|
30
30
|
// repeated path-probe (PATH / npm-global / npm-root-g) only happens once per
|
|
31
31
|
// install.
|
|
32
32
|
|
|
@@ -184,7 +184,7 @@ const MODELS_CACHE = path.join(_CACHE_DIR, 'claude-models.json');
|
|
|
184
184
|
*
|
|
185
185
|
* Mirrors the union of:
|
|
186
186
|
* - engine.js:817-844 (agent dispatch)
|
|
187
|
-
* - engine/llm.js:68-76 (CC / doc-chat direct spawn)
|
|
187
|
+
* - engine/agents/llm.js:68-76 (CC / doc-chat direct spawn)
|
|
188
188
|
* - spawn-agent.js:118-128 (--system-prompt-file injection on first turn)
|
|
189
189
|
*
|
|
190
190
|
* Per the plan: emits `--dangerously-skip-permissions` (the modern Claude flag)
|
|
@@ -203,6 +203,7 @@ function buildArgs(opts = {}) {
|
|
|
203
203
|
model,
|
|
204
204
|
maxTurns,
|
|
205
205
|
allowedTools,
|
|
206
|
+
disableTools,
|
|
206
207
|
effort,
|
|
207
208
|
sessionId,
|
|
208
209
|
sysPromptFile,
|
|
@@ -231,6 +232,7 @@ function buildArgs(opts = {}) {
|
|
|
231
232
|
}
|
|
232
233
|
}
|
|
233
234
|
if (allowedTools) args.push('--allowedTools', allowedTools);
|
|
235
|
+
if (disableTools === true) args.push('--tools', '');
|
|
234
236
|
if (effort) args.push('--effort', String(effort));
|
|
235
237
|
args.push('--dangerously-skip-permissions');
|
|
236
238
|
if (maxBudget != null) args.push('--max-budget-usd', String(maxBudget));
|
|
@@ -535,7 +537,7 @@ function parseOutput(raw, { maxTextLength = 0 } = {}) {
|
|
|
535
537
|
* Parse a single line from the stream-json stdout. Returns the parsed event
|
|
536
538
|
* object, or null when the line is empty / non-JSON.
|
|
537
539
|
*
|
|
538
|
-
* Used by the streaming accumulator in engine/llm.js to react to assistant
|
|
540
|
+
* Used by the streaming accumulator in engine/agents/llm.js to react to assistant
|
|
539
541
|
* text blocks, tool-use blocks, and the terminal `result` event without
|
|
540
542
|
* waiting for the whole process to exit.
|
|
541
543
|
*/
|
|
@@ -672,7 +674,7 @@ function parseError(rawOutput) {
|
|
|
672
674
|
|
|
673
675
|
// ── Stream Consumer ─────────────────────────────────────────────────────────
|
|
674
676
|
//
|
|
675
|
-
// Per-stream consumer factory invoked by engine/llm.js's accumulator. The
|
|
677
|
+
// Per-stream consumer factory invoked by engine/agents/llm.js's accumulator. The
|
|
676
678
|
// accumulator owns global stream state (stdout/stderr/text dedup/tool dedup)
|
|
677
679
|
// and exposes the `ctx` API below; the consumer owns Claude-specific per-stream
|
|
678
680
|
// state (joined-text accumulator, content-block Map for tool/thinking
|
|
@@ -738,7 +740,7 @@ function createStreamConsumer(ctx) {
|
|
|
738
740
|
// reset the hot-path accumulator so the NEXT text block starts fresh
|
|
739
741
|
// instead of welding onto text from before this block. `ctx.pushText`
|
|
740
742
|
// already opens a new segment when the pushed value doesn't extend
|
|
741
|
-
// `lastTextSent` (see engine/llm.js), so this reset is sufficient to
|
|
743
|
+
// `lastTextSent` (see engine/agents/llm.js), so this reset is sufficient to
|
|
742
744
|
// both stop the weld and trigger the segment boundary.
|
|
743
745
|
if (block.type !== 'text') claudeJoinedText = '';
|
|
744
746
|
// Out-of-order block landing: rebuild from the Map. Common case is
|
|
@@ -831,7 +833,7 @@ function createStreamConsumer(ctx) {
|
|
|
831
833
|
// each block is {type:'tool_result', tool_use_id, is_error, content}.
|
|
832
834
|
// Flip the previously-pushed chip to completed (success) or failed
|
|
833
835
|
// (is_error). This is the Claude-direct analogue of Copilot's ACP
|
|
834
|
-
// tool_call_update; engine/llm.js stays runtime-agnostic — the chip
|
|
836
|
+
// tool_call_update; engine/agents/llm.js stays runtime-agnostic — the chip
|
|
835
837
|
// turns green/red only because this adapter calls ctx.updateToolUse.
|
|
836
838
|
for (const block of obj.message.content) {
|
|
837
839
|
if (block?.type === 'tool_result' && block.tool_use_id) {
|
|
@@ -884,7 +886,7 @@ const capabilities = {
|
|
|
884
886
|
bareMode: true,
|
|
885
887
|
// The Claude Code CLI reads repo-authored CLAUDE.md files itself at startup
|
|
886
888
|
// (unless `--bare`). TRUE here tells the engine's CLAUDE.md-propagation layer
|
|
887
|
-
// (engine/claude-md-context.js, injected in engine/playbook.js) to SKIP this
|
|
889
|
+
// (engine/agents/claude-md-context.js, injected in engine/agents/playbook.js) to SKIP this
|
|
888
890
|
// runtime — re-injecting CLAUDE.md content into the prompt would double it.
|
|
889
891
|
// Copilot/Codex set this false because they have no native CLAUDE.md auto-load.
|
|
890
892
|
claudeMdNativeDiscovery: true,
|
|
@@ -908,7 +910,7 @@ const capabilities = {
|
|
|
908
910
|
};
|
|
909
911
|
|
|
910
912
|
// Install hint surfaced when `resolveBinary()` returns null. Consumed by
|
|
911
|
-
// `engine/preflight.js` (per-runtime binary check) and `engine/spawn-agent.js`
|
|
913
|
+
// `engine/agents/preflight.js` (per-runtime binary check) and `engine/agents/spawn-agent.js`
|
|
912
914
|
// (fatal error message). Multi-line so all platforms see actionable guidance.
|
|
913
915
|
const INSTALL_HINT = 'install from https://claude.ai/download or: npm install -g @anthropic-ai/claude-code';
|
|
914
916
|
|
|
@@ -949,7 +951,7 @@ function getSkillWriteTargets({ homeDir = os.homedir(), project = null } = {}) {
|
|
|
949
951
|
// Slash-command roots. Mirrors getSkillRoots' shape so consumers can iterate
|
|
950
952
|
// runtimes generically. Plugin-installed command dirs are NOT enumerated here
|
|
951
953
|
// — those are discovered dynamically from `~/.claude/plugins/installed_plugins.json`
|
|
952
|
-
// in engine/queries.js#collectCommandFiles and stay there because they require
|
|
954
|
+
// in engine/core/queries.js#collectCommandFiles and stay there because they require
|
|
953
955
|
// a registry read, not a fixed dir.
|
|
954
956
|
function getCommandRoots({ homeDir = os.homedir(), project = null } = {}) {
|
|
955
957
|
const roots = [
|
|
@@ -1076,7 +1078,7 @@ module.exports = {
|
|
|
1076
1078
|
capsFile: CAPS_FILE,
|
|
1077
1079
|
listModels,
|
|
1078
1080
|
modelsCache: MODELS_CACHE,
|
|
1079
|
-
spawnScript: path.join(ENGINE_DIR, 'spawn-agent.js'),
|
|
1081
|
+
spawnScript: path.join(ENGINE_DIR, 'agents', 'spawn-agent.js'),
|
|
1080
1082
|
installHint: INSTALL_HINT,
|
|
1081
1083
|
resolveInvocationOptions,
|
|
1082
1084
|
buildSpawnFlags,
|
package/engine/runtimes/codex.js
CHANGED
|
@@ -18,7 +18,7 @@ const {
|
|
|
18
18
|
resolveEngineCacheDir,
|
|
19
19
|
staleResumeSessionCwdReason,
|
|
20
20
|
resolveCodexCcSandbox,
|
|
21
|
-
} = require('../shared');
|
|
21
|
+
} = require('../core/shared');
|
|
22
22
|
|
|
23
23
|
const ENGINE_DIR = __dirname.replace(/[\\/]runtimes$/, '');
|
|
24
24
|
const MINIONS_DIR = path.resolve(ENGINE_DIR, '..');
|
|
@@ -345,6 +345,7 @@ function buildArgs(opts = {}) {
|
|
|
345
345
|
sessionId,
|
|
346
346
|
outputLastMessage,
|
|
347
347
|
sandbox = 'workspace-write',
|
|
348
|
+
disableTools,
|
|
348
349
|
skipGitRepoCheck = true,
|
|
349
350
|
configOverrides,
|
|
350
351
|
images,
|
|
@@ -356,7 +357,8 @@ function buildArgs(opts = {}) {
|
|
|
356
357
|
// parser rejects flags such as --color and --sandbox after the subcommand.
|
|
357
358
|
args.push('--json', '--color', 'never');
|
|
358
359
|
if (model) args.push('--model', String(model));
|
|
359
|
-
|
|
360
|
+
const effectiveSandbox = disableTools === true ? 'read-only' : sandbox;
|
|
361
|
+
if (effectiveSandbox) args.push('--sandbox', String(effectiveSandbox));
|
|
360
362
|
if (skipGitRepoCheck === true) args.push('--skip-git-repo-check');
|
|
361
363
|
if (outputLastMessage) args.push('--output-last-message', String(outputLastMessage));
|
|
362
364
|
|
|
@@ -370,7 +372,7 @@ function buildArgs(opts = {}) {
|
|
|
370
372
|
if (sessionId) args.push('resume', String(sessionId));
|
|
371
373
|
|
|
372
374
|
// Codex accepts repeatable --image paths on fresh and resumed turns. The
|
|
373
|
-
// call-scoped tmpDir is removed by engine/llm.js after the process exits.
|
|
375
|
+
// call-scoped tmpDir is removed by engine/agents/llm.js after the process exits.
|
|
374
376
|
if (Array.isArray(images) && images.length && tmpDir) {
|
|
375
377
|
for (let i = 0; i < images.length; i++) {
|
|
376
378
|
const img = images[i];
|
|
@@ -469,7 +471,7 @@ function getResumeSessionId({ agentId, branchName, agentsDir, cwd = null, maxAge
|
|
|
469
471
|
try { fs.unlinkSync(sessionPath); } catch {}
|
|
470
472
|
return null;
|
|
471
473
|
}
|
|
472
|
-
// Stale-workdir invalidation (W-ms0vk9nv000z985b) — see engine/shared.js
|
|
474
|
+
// Stale-workdir invalidation (W-ms0vk9nv000z985b) — see engine/core/shared.js
|
|
473
475
|
// staleResumeSessionCwdReason. A session anchored to a worktree the engine
|
|
474
476
|
// has since removed/recycled cannot be resumed.
|
|
475
477
|
const staleCwdReason = staleResumeSessionCwdReason(sessionFile.cwd, cwd);
|
|
@@ -1001,7 +1003,7 @@ const capabilities = {
|
|
|
1001
1003
|
bareMode: false,
|
|
1002
1004
|
fallbackModel: false,
|
|
1003
1005
|
// Codex has no native CLAUDE.md auto-load — FALSE opts it into the engine's
|
|
1004
|
-
// CLAUDE.md-propagation layer (engine/claude-md-context.js) so repo-authored
|
|
1006
|
+
// CLAUDE.md-propagation layer (engine/agents/claude-md-context.js) so repo-authored
|
|
1005
1007
|
// CLAUDE.md guidance reaches the agent prompt.
|
|
1006
1008
|
claudeMdNativeDiscovery: false,
|
|
1007
1009
|
sessionPersistenceControl: false,
|
|
@@ -1022,7 +1024,7 @@ module.exports = {
|
|
|
1022
1024
|
listModels,
|
|
1023
1025
|
getModelCacheKey,
|
|
1024
1026
|
modelsCache: MODELS_CACHE,
|
|
1025
|
-
spawnScript: path.join(ENGINE_DIR, 'spawn-agent.js'),
|
|
1027
|
+
spawnScript: path.join(ENGINE_DIR, 'agents', 'spawn-agent.js'),
|
|
1026
1028
|
installHint: INSTALL_HINT,
|
|
1027
1029
|
buildSpawnFlags,
|
|
1028
1030
|
resolveInvocationOptions,
|
|
@@ -32,7 +32,7 @@ const https = require('https');
|
|
|
32
32
|
const os = require('os');
|
|
33
33
|
const path = require('path');
|
|
34
34
|
const { execSync } = require('child_process');
|
|
35
|
-
const { ENGINE_DEFAULTS, FAILURE_CLASS, safeWrite, ts, resolveEngineCacheDir, staleResumeSessionCwdReason } = require('../shared');
|
|
35
|
+
const { ENGINE_DEFAULTS, FAILURE_CLASS, safeWrite, ts, resolveEngineCacheDir, staleResumeSessionCwdReason } = require('../core/shared');
|
|
36
36
|
|
|
37
37
|
const ENGINE_DIR = __dirname.replace(/[\\/]runtimes$/, '');
|
|
38
38
|
const _CACHE_DIR = resolveEngineCacheDir(ENGINE_DIR);
|
|
@@ -52,7 +52,7 @@ const SPAWN_STARTUP_TYPES = new Set([
|
|
|
52
52
|
// 1. Standalone `copilot` (preferred) — WinGet, scoop, or manual install. PATH
|
|
53
53
|
// probe finds it; we cache the resolved path with `leadingArgs: []`.
|
|
54
54
|
// 2. `gh copilot` extension fallback — invoked as `gh copilot ...`. We return
|
|
55
|
-
// `leadingArgs: ['copilot']` so engine/spawn-agent.js prepends "copilot"
|
|
55
|
+
// `leadingArgs: ['copilot']` so engine/agents/spawn-agent.js prepends "copilot"
|
|
56
56
|
// to the gh binary invocation. NOTE: the older gh-copilot extension is
|
|
57
57
|
// the explain/suggest UX, NOT the v1.0.36 agent CLI; flag support varies.
|
|
58
58
|
// We surface it as best-effort and let preflight warn.
|
|
@@ -93,7 +93,7 @@ function _execSyncCapture(cmd, env, timeoutMs = 10000) {
|
|
|
93
93
|
* C:\Users\<u>\AppData\Roaming\npm\copilot.cmd (Windows cmd shim)
|
|
94
94
|
* C:\Users\<u>\AppData\Roaming\npm\copilot.ps1 (PowerShell shim)
|
|
95
95
|
* The legacy resolver took `.split('\n')[0]` (the extensionless shim) and
|
|
96
|
-
* marked it `native: false`, which made engine/llm.js spawn `node.exe <shim>`.
|
|
96
|
+
* marked it `native: false`, which made engine/agents/llm.js spawn `node.exe <shim>`.
|
|
97
97
|
* Node parsed the bash script as JS and exit-1'd in <1s, silently breaking
|
|
98
98
|
* every Copilot dispatch.
|
|
99
99
|
*
|
|
@@ -120,7 +120,7 @@ function _pickStandaloneCopilotFromOutput(rawOutput, { existsSync = fs.existsSyn
|
|
|
120
120
|
if (!_isWin) {
|
|
121
121
|
// POSIX `which` returns one line; first existing entry wins. Mark native
|
|
122
122
|
// when there's no extension or an executable extension — but since
|
|
123
|
-
// engine/llm.js only checks `.exe` for native on Windows and otherwise
|
|
123
|
+
// engine/agents/llm.js only checks `.exe` for native on Windows and otherwise
|
|
124
124
|
// routes through process.execPath on non-native, we keep native:true on
|
|
125
125
|
// POSIX (matches legacy behavior — spawn(<bin>) works directly).
|
|
126
126
|
return { bin: existing[0], native: true };
|
|
@@ -398,7 +398,7 @@ function resolveModel(input) {
|
|
|
398
398
|
}
|
|
399
399
|
|
|
400
400
|
// Heuristic: does `model` look like a Copilot-namespace identifier? Mirrors
|
|
401
|
-
// engine/runtimes/claude.js#modelLooksFamiliar so engine/cli.js#_modelLooksIncompatible
|
|
401
|
+
// engine/runtimes/claude.js#modelLooksFamiliar so engine/operations/cli.js#_modelLooksIncompatible
|
|
402
402
|
// can warn when a fleet/CC switch strands an incompatible model. Accepts the
|
|
403
403
|
// three Minions short aliases (`sonnet`/`opus`/`haiku`) since the adapter
|
|
404
404
|
// translates them to valid Copilot IDs at argv-build time. Returning false
|
|
@@ -440,7 +440,7 @@ function _mapEffort(level) {
|
|
|
440
440
|
// Copilot has no --verbose flag — never emit it. The `bare` / `maxBudget` /
|
|
441
441
|
// `fallbackModel` opts are silently ignored (their capability flags are false
|
|
442
442
|
// so engine code shouldn't pass them, but we tolerate them gracefully).
|
|
443
|
-
// Do not emit --log-level: Copilot CLI 1.0.76-1
|
|
443
|
+
// Do not emit --log-level: Copilot CLI 1.0.76-1 and 1.0.76-2 exit with code 1
|
|
444
444
|
// when given an explicit non-default level. Omitting it preserves the CLI's
|
|
445
445
|
// compatible default while --output-format json keeps stdout machine-readable.
|
|
446
446
|
|
|
@@ -466,6 +466,7 @@ function buildArgs(opts = {}) {
|
|
|
466
466
|
disableBuiltinMcps,
|
|
467
467
|
reasoningSummaries,
|
|
468
468
|
disabledMcpServers,
|
|
469
|
+
disableTools,
|
|
469
470
|
images,
|
|
470
471
|
tmpDir,
|
|
471
472
|
} = opts;
|
|
@@ -475,10 +476,9 @@ function buildArgs(opts = {}) {
|
|
|
475
476
|
'-s',
|
|
476
477
|
'--no-color',
|
|
477
478
|
'--plain-diff',
|
|
478
|
-
'--autopilot',
|
|
479
|
-
'--allow-all',
|
|
480
479
|
'--no-ask-user',
|
|
481
480
|
];
|
|
481
|
+
if (disableTools !== true) args.push('--autopilot', '--allow-all');
|
|
482
482
|
|
|
483
483
|
if (Array.isArray(addDirs)) {
|
|
484
484
|
for (const d of addDirs) {
|
|
@@ -491,6 +491,7 @@ function buildArgs(opts = {}) {
|
|
|
491
491
|
}
|
|
492
492
|
|
|
493
493
|
if (model) args.push('--model', String(model));
|
|
494
|
+
if (disableTools === true) args.push('--available-tools=');
|
|
494
495
|
|
|
495
496
|
const mappedEffort = _mapEffort(effort);
|
|
496
497
|
if (mappedEffort) args.push('--effort', mappedEffort);
|
|
@@ -785,7 +786,7 @@ const KNOWN_EVENT_TYPES = new Set([
|
|
|
785
786
|
/**
|
|
786
787
|
* Parse the full JSONL output of a Copilot CLI invocation.
|
|
787
788
|
* Returns { text, usage, sessionId, model } — same shape as the Claude adapter
|
|
788
|
-
* so engine/lifecycle.js can consume both transparently.
|
|
789
|
+
* so engine/orchestration/lifecycle.js can consume both transparently.
|
|
789
790
|
*
|
|
790
791
|
* - text: the TRAILING text segment of the turn — answer
|
|
791
792
|
* `assistant.message.data.content` values plus trailing deltas,
|
|
@@ -796,7 +797,7 @@ const KNOWN_EVENT_TYPES = new Set([
|
|
|
796
797
|
* tool text never welds onto the post-tool answer. This mirrors
|
|
797
798
|
* the stream consumer's `_resetTrailingSegment` and Claude's
|
|
798
799
|
* result-carries-last-message shape, so the terminal
|
|
799
|
-
* reconciliation in engine/llm.js#finalize agrees with the live
|
|
800
|
+
* reconciliation in engine/agents/llm.js#finalize agrees with the live
|
|
800
801
|
* stream instead of re-welding split blocks (W-mrw87290000b2965).
|
|
801
802
|
* - usage: mapped from the terminal `result` event. Copilot doesn't
|
|
802
803
|
* report cost/tokens — those fields are NULL, not 0, so the
|
|
@@ -859,7 +860,7 @@ function parseOutput(raw, { maxTextLength = 0 } = {}) {
|
|
|
859
860
|
// assistant.message or as raw deltas — does not weld onto the post-tool
|
|
860
861
|
// answer. Without this, `text = messageContents.join('') + pendingDeltaContent`
|
|
861
862
|
// re-welds `<pre-tool><post-tool>` in the terminal reconciliation
|
|
862
|
-
// (engine/llm.js#finalize -> runtime.parseOutput), and the CC SSE `done`
|
|
863
|
+
// (engine/agents/llm.js#finalize -> runtime.parseOutput), and the CC SSE `done`
|
|
863
864
|
// payload re-introduces the weld the stream consumer already split apart
|
|
864
865
|
// (W-mrw87290000b2965). Mirrors the stream consumer's _resetTrailingSegment
|
|
865
866
|
// and Claude, whose result event carries only the last assistant message.
|
|
@@ -1088,7 +1089,7 @@ function _resolveCachedModelIds() {
|
|
|
1088
1089
|
// In-memory cache (seeded by `_warmModelCache()` or test `_resetModelCache`)
|
|
1089
1090
|
// wins over disk so unit tests stay hermetic. Production code that hasn't
|
|
1090
1091
|
// warmed the in-memory cache still gets the catalog via the disk file that
|
|
1091
|
-
// `engine/model-discovery.js#getRuntimeModels` populates during preflight.
|
|
1092
|
+
// `engine/agents/model-discovery.js#getRuntimeModels` populates during preflight.
|
|
1092
1093
|
if (Array.isArray(_modelDiscoveryResults) && _modelDiscoveryResults.length > 0) {
|
|
1093
1094
|
return _modelDiscoveryResults.map(m => m && m.id ? String(m.id) : null).filter(Boolean);
|
|
1094
1095
|
}
|
|
@@ -1230,7 +1231,7 @@ async function listModels({ env = process.env, timeoutMs = 10000 } = {}) {
|
|
|
1230
1231
|
|
|
1231
1232
|
// ── Stream Consumer ─────────────────────────────────────────────────────────
|
|
1232
1233
|
//
|
|
1233
|
-
// Per-stream consumer factory invoked by engine/llm.js's accumulator. Owns
|
|
1234
|
+
// Per-stream consumer factory invoked by engine/agents/llm.js's accumulator. Owns
|
|
1234
1235
|
// Copilot-specific per-stream state (delta-content buffer, task_complete
|
|
1235
1236
|
// signal). Translates Copilot event shapes into ctx callbacks.
|
|
1236
1237
|
//
|
|
@@ -1451,7 +1452,7 @@ const capabilities = {
|
|
|
1451
1452
|
// Copilot has NO native CLAUDE.md auto-load — it only reads its own AGENTS.md
|
|
1452
1453
|
// (native custom-instruction discovery owned by the CLI itself). FALSE
|
|
1453
1454
|
// here opts this runtime INTO the engine's CLAUDE.md-propagation layer
|
|
1454
|
-
// (engine/claude-md-context.js) so repo-authored CLAUDE.md guidance is
|
|
1455
|
+
// (engine/agents/claude-md-context.js) so repo-authored CLAUDE.md guidance is
|
|
1455
1456
|
// surfaced in the prompt. This is orthogonal to AGENTS.md discovery —
|
|
1456
1457
|
// CLAUDE.md carries no split-brain risk because Copilot never reads it itself.
|
|
1457
1458
|
claudeMdNativeDiscovery: false,
|
|
@@ -1469,7 +1470,7 @@ const capabilities = {
|
|
|
1469
1470
|
imageInput: true,
|
|
1470
1471
|
// P-9b3d5f61: `copilot --acp` speaks the Agent Client Protocol, so per-agent
|
|
1471
1472
|
// dispatches can route through a persistent ACP worker pool
|
|
1472
|
-
// (engine/agent-worker-pool.js) instead of cold-spawning per dispatch.
|
|
1473
|
+
// (engine/agents/agent-worker-pool.js) instead of cold-spawning per dispatch.
|
|
1473
1474
|
// Claude/Codex do not implement ACP — this flag is absent (falsy) there.
|
|
1474
1475
|
acpWorkerPool: true,
|
|
1475
1476
|
};
|
|
@@ -1553,7 +1554,7 @@ module.exports = {
|
|
|
1553
1554
|
listModels,
|
|
1554
1555
|
modelsCache: MODELS_CACHE,
|
|
1555
1556
|
// Use the same wrapper as Claude — spawn-agent.js is runtime-agnostic per P-9c4f2d6a
|
|
1556
|
-
spawnScript: path.join(ENGINE_DIR, 'spawn-agent.js'),
|
|
1557
|
+
spawnScript: path.join(ENGINE_DIR, 'agents', 'spawn-agent.js'),
|
|
1557
1558
|
installHint: INSTALL_HINT,
|
|
1558
1559
|
workerErrorHint: 'copilot --acp failed -- ensure copilot CLI >=1.0.46 and copilot login is complete',
|
|
1559
1560
|
resolveInvocationOptions,
|