oh-my-copilot 5.6.2 → 5.8.0
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +2 -0
- package/bridge/__pycache__/gyoshu_bridge.cpython-312.pyc +0 -0
- package/bridge/claude-md-coordinator.cjs +2 -2
- package/bridge/cli.cjs +81405 -76559
- package/bridge/mcp-server.cjs +40048 -32187
- package/bridge/runtime-cli.cjs +943 -199
- package/bridge/team-mcp.cjs +716 -125
- package/bridge/team.js +1066 -290
- package/dist/__tests__/cli-win32-warning.test.js +7 -0
- package/dist/__tests__/cli-win32-warning.test.js.map +1 -1
- package/dist/__tests__/run-cjs-copilot-worker-routing.test.d.ts +2 -0
- package/dist/__tests__/run-cjs-copilot-worker-routing.test.d.ts.map +1 -0
- package/dist/__tests__/run-cjs-copilot-worker-routing.test.js +294 -0
- package/dist/__tests__/run-cjs-copilot-worker-routing.test.js.map +1 -0
- package/dist/__tests__/run-cjs-fail-closed-timeout.test.d.ts +2 -0
- package/dist/__tests__/run-cjs-fail-closed-timeout.test.d.ts.map +1 -0
- package/dist/__tests__/run-cjs-fail-closed-timeout.test.js +68 -0
- package/dist/__tests__/run-cjs-fail-closed-timeout.test.js.map +1 -0
- package/dist/__tests__/run-cjs-session-end-budget.test.d.ts +2 -0
- package/dist/__tests__/run-cjs-session-end-budget.test.d.ts.map +1 -0
- package/dist/__tests__/run-cjs-session-end-budget.test.js +171 -0
- package/dist/__tests__/run-cjs-session-end-budget.test.js.map +1 -0
- package/dist/__tests__/session-end-process-exit.test.js +70 -2
- package/dist/__tests__/session-end-process-exit.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-conflicts-json.test.d.ts +2 -0
- package/dist/cli/__tests__/doctor-conflicts-json.test.d.ts.map +1 -0
- package/dist/cli/__tests__/doctor-conflicts-json.test.js +46 -0
- package/dist/cli/__tests__/doctor-conflicts-json.test.js.map +1 -0
- package/dist/cli/__tests__/team.test.js +67 -0
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/wait-teleport-json.test.d.ts +2 -0
- package/dist/cli/__tests__/wait-teleport-json.test.d.ts.map +1 -0
- package/dist/cli/__tests__/wait-teleport-json.test.js +64 -0
- package/dist/cli/__tests__/wait-teleport-json.test.js.map +1 -0
- package/dist/cli/commands/__tests__/smoke-tier2.test.d.ts +2 -0
- package/dist/cli/commands/__tests__/smoke-tier2.test.d.ts.map +1 -0
- package/dist/cli/commands/__tests__/smoke-tier2.test.js +110 -0
- package/dist/cli/commands/__tests__/smoke-tier2.test.js.map +1 -0
- package/dist/cli/commands/__tests__/team-status-json.test.d.ts +2 -0
- package/dist/cli/commands/__tests__/team-status-json.test.d.ts.map +1 -0
- package/dist/cli/commands/__tests__/team-status-json.test.js +141 -0
- package/dist/cli/commands/__tests__/team-status-json.test.js.map +1 -0
- package/dist/cli/commands/factory.d.ts +2 -0
- package/dist/cli/commands/factory.d.ts.map +1 -1
- package/dist/cli/commands/factory.js +8 -1
- package/dist/cli/commands/factory.js.map +1 -1
- package/dist/cli/commands/smoke.d.ts +36 -0
- package/dist/cli/commands/smoke.d.ts.map +1 -0
- package/dist/cli/commands/smoke.js +169 -0
- package/dist/cli/commands/smoke.js.map +1 -0
- package/dist/cli/commands/team.d.ts +2 -0
- package/dist/cli/commands/team.d.ts.map +1 -1
- package/dist/cli/commands/team.js +124 -10
- package/dist/cli/commands/team.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +70 -10
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/win32-warning.d.ts +7 -1
- package/dist/cli/win32-warning.d.ts.map +1 -1
- package/dist/cli/win32-warning.js +9 -1
- package/dist/cli/win32-warning.js.map +1 -1
- package/dist/constants/names.d.ts +1 -0
- package/dist/constants/names.d.ts.map +1 -1
- package/dist/constants/names.js +1 -0
- package/dist/constants/names.js.map +1 -1
- package/dist/factory/__tests__/check-run.test.js +2 -2
- package/dist/factory/__tests__/check-run.test.js.map +1 -1
- package/dist/factory/__tests__/listener.test.js +1 -1
- package/dist/factory/__tests__/listener.test.js.map +1 -1
- package/dist/factory/__tests__/status.test.js +42 -0
- package/dist/factory/__tests__/status.test.js.map +1 -1
- package/dist/factory/listener.d.ts.map +1 -1
- package/dist/factory/listener.js +7 -8
- package/dist/factory/listener.js.map +1 -1
- package/dist/factory/status.d.ts +19 -1
- package/dist/factory/status.d.ts.map +1 -1
- package/dist/factory/status.js +49 -8
- package/dist/factory/status.js.map +1 -1
- package/dist/factory/watchdog.d.ts.map +1 -1
- package/dist/factory/watchdog.js +14 -0
- package/dist/factory/watchdog.js.map +1 -1
- package/dist/factory/watchdog.test.js +45 -0
- package/dist/factory/watchdog.test.js.map +1 -1
- package/dist/hooks/session-end/__tests__/action-runner.test.js +5 -0
- package/dist/hooks/session-end/__tests__/action-runner.test.js.map +1 -1
- package/dist/hooks/session-end/__tests__/chain-enqueuer.test.js +186 -1
- package/dist/hooks/session-end/__tests__/chain-enqueuer.test.js.map +1 -1
- package/dist/hooks/session-end/__tests__/chain-link-env-passthrough.test.d.ts +2 -0
- package/dist/hooks/session-end/__tests__/chain-link-env-passthrough.test.d.ts.map +1 -0
- package/dist/hooks/session-end/__tests__/chain-link-env-passthrough.test.js +49 -0
- package/dist/hooks/session-end/__tests__/chain-link-env-passthrough.test.js.map +1 -0
- package/dist/hooks/session-end/__tests__/foreground-bootstrap.test.js +30 -1
- package/dist/hooks/session-end/__tests__/foreground-bootstrap.test.js.map +1 -1
- package/dist/hooks/session-end/__tests__/spawn-next.test.js +104 -3
- package/dist/hooks/session-end/__tests__/spawn-next.test.js.map +1 -1
- package/dist/hooks/session-end/__tests__/worker.test.js +6 -0
- package/dist/hooks/session-end/__tests__/worker.test.js.map +1 -1
- package/dist/hooks/session-end/action-runner.d.ts.map +1 -1
- package/dist/hooks/session-end/action-runner.js +2 -1
- package/dist/hooks/session-end/action-runner.js.map +1 -1
- package/dist/hooks/session-end/chain-enqueuer.d.ts +70 -7
- package/dist/hooks/session-end/chain-enqueuer.d.ts.map +1 -1
- package/dist/hooks/session-end/chain-enqueuer.js +354 -82
- package/dist/hooks/session-end/chain-enqueuer.js.map +1 -1
- package/dist/hooks/session-end/foreground-bootstrap.d.ts.map +1 -1
- package/dist/hooks/session-end/foreground-bootstrap.js +24 -16
- package/dist/hooks/session-end/foreground-bootstrap.js.map +1 -1
- package/dist/hooks/session-end/index.d.ts +2 -3
- package/dist/hooks/session-end/index.d.ts.map +1 -1
- package/dist/hooks/session-end/index.js +11 -13
- package/dist/hooks/session-end/index.js.map +1 -1
- package/dist/hooks/session-end/spawn-next.d.ts +81 -4
- package/dist/hooks/session-end/spawn-next.d.ts.map +1 -1
- package/dist/hooks/session-end/spawn-next.js +141 -14
- package/dist/hooks/session-end/spawn-next.js.map +1 -1
- package/dist/hooks/session-end/wiki-foreground-bootstrap.d.ts +17 -0
- package/dist/hooks/session-end/wiki-foreground-bootstrap.d.ts.map +1 -0
- package/dist/hooks/session-end/wiki-foreground-bootstrap.js +20 -0
- package/dist/hooks/session-end/wiki-foreground-bootstrap.js.map +1 -0
- package/dist/hooks/session-end/worker.d.ts.map +1 -1
- package/dist/hooks/session-end/worker.js +11 -6
- package/dist/hooks/session-end/worker.js.map +1 -1
- package/dist/lib/__tests__/atomic-write.test.js +32 -1
- package/dist/lib/__tests__/atomic-write.test.js.map +1 -1
- package/dist/lib/__tests__/mode-state-lock.test.js +121 -12
- package/dist/lib/__tests__/mode-state-lock.test.js.map +1 -1
- package/dist/lib/atomic-write.d.ts +13 -3
- package/dist/lib/atomic-write.d.ts.map +1 -1
- package/dist/lib/atomic-write.js +29 -15
- package/dist/lib/atomic-write.js.map +1 -1
- package/dist/lib/mode-state-io.d.ts +2 -2
- package/dist/lib/mode-state-io.d.ts.map +1 -1
- package/dist/lib/mode-state-io.js +16 -10
- package/dist/lib/mode-state-io.js.map +1 -1
- package/dist/mcp/__tests__/host-smoke-tool.test.d.ts +2 -0
- package/dist/mcp/__tests__/host-smoke-tool.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/host-smoke-tool.test.js +174 -0
- package/dist/mcp/__tests__/host-smoke-tool.test.js.map +1 -0
- package/dist/mcp/disable-tools.d.ts.map +1 -1
- package/dist/mcp/disable-tools.js +1 -0
- package/dist/mcp/disable-tools.js.map +1 -1
- package/dist/mcp/tool-registry.d.ts.map +1 -1
- package/dist/mcp/tool-registry.js +2 -0
- package/dist/mcp/tool-registry.js.map +1 -1
- package/dist/shared/types.d.ts +11 -0
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/smoke/__tests__/copilot-chain-scenario.test.d.ts +2 -0
- package/dist/smoke/__tests__/copilot-chain-scenario.test.d.ts.map +1 -0
- package/dist/smoke/__tests__/copilot-chain-scenario.test.js +154 -0
- package/dist/smoke/__tests__/copilot-chain-scenario.test.js.map +1 -0
- package/dist/smoke/__tests__/copilot-sdk-driver.test.d.ts +2 -0
- package/dist/smoke/__tests__/copilot-sdk-driver.test.d.ts.map +1 -0
- package/dist/smoke/__tests__/copilot-sdk-driver.test.js +591 -0
- package/dist/smoke/__tests__/copilot-sdk-driver.test.js.map +1 -0
- package/dist/smoke/__tests__/copilot-sdk-scenarios.test.d.ts +2 -0
- package/dist/smoke/__tests__/copilot-sdk-scenarios.test.d.ts.map +1 -0
- package/dist/smoke/__tests__/copilot-sdk-scenarios.test.js +362 -0
- package/dist/smoke/__tests__/copilot-sdk-scenarios.test.js.map +1 -0
- package/dist/smoke/__tests__/copilot-session-eval.test.d.ts +2 -0
- package/dist/smoke/__tests__/copilot-session-eval.test.d.ts.map +1 -0
- package/dist/smoke/__tests__/copilot-session-eval.test.js +264 -0
- package/dist/smoke/__tests__/copilot-session-eval.test.js.map +1 -0
- package/dist/smoke/__tests__/copilot-smoke.test.d.ts +2 -0
- package/dist/smoke/__tests__/copilot-smoke.test.d.ts.map +1 -0
- package/dist/smoke/__tests__/copilot-smoke.test.js +482 -0
- package/dist/smoke/__tests__/copilot-smoke.test.js.map +1 -0
- package/dist/smoke/copilot-binary.d.ts +21 -0
- package/dist/smoke/copilot-binary.d.ts.map +1 -0
- package/dist/smoke/copilot-binary.js +46 -0
- package/dist/smoke/copilot-binary.js.map +1 -0
- package/dist/smoke/copilot-chain-scenario.d.ts +61 -0
- package/dist/smoke/copilot-chain-scenario.d.ts.map +1 -0
- package/dist/smoke/copilot-chain-scenario.js +253 -0
- package/dist/smoke/copilot-chain-scenario.js.map +1 -0
- package/dist/smoke/copilot-sdk-driver.d.ts +189 -0
- package/dist/smoke/copilot-sdk-driver.d.ts.map +1 -0
- package/dist/smoke/copilot-sdk-driver.js +637 -0
- package/dist/smoke/copilot-sdk-driver.js.map +1 -0
- package/dist/smoke/copilot-sdk-scenarios.d.ts +200 -0
- package/dist/smoke/copilot-sdk-scenarios.d.ts.map +1 -0
- package/dist/smoke/copilot-sdk-scenarios.js +620 -0
- package/dist/smoke/copilot-sdk-scenarios.js.map +1 -0
- package/dist/smoke/copilot-session-env.d.ts +75 -0
- package/dist/smoke/copilot-session-env.d.ts.map +1 -0
- package/dist/smoke/copilot-session-env.js +154 -0
- package/dist/smoke/copilot-session-env.js.map +1 -0
- package/dist/smoke/copilot-session-eval.d.ts +90 -0
- package/dist/smoke/copilot-session-eval.d.ts.map +1 -0
- package/dist/smoke/copilot-session-eval.js +323 -0
- package/dist/smoke/copilot-session-eval.js.map +1 -0
- package/dist/smoke/copilot-smoke.d.ts +166 -0
- package/dist/smoke/copilot-smoke.d.ts.map +1 -0
- package/dist/smoke/copilot-smoke.js +688 -0
- package/dist/smoke/copilot-smoke.js.map +1 -0
- package/dist/smoke/process-utils.d.ts +47 -0
- package/dist/smoke/process-utils.d.ts.map +1 -0
- package/dist/smoke/process-utils.js +99 -0
- package/dist/smoke/process-utils.js.map +1 -0
- package/dist/team/__tests__/runtime-v2.monitor.test.js +35 -0
- package/dist/team/__tests__/runtime-v2.monitor.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +13 -0
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/sdk-transport.runtime.test.d.ts +2 -0
- package/dist/team/__tests__/sdk-transport.runtime.test.d.ts.map +1 -0
- package/dist/team/__tests__/sdk-transport.runtime.test.js +96 -0
- package/dist/team/__tests__/sdk-transport.runtime.test.js.map +1 -0
- package/dist/team/__tests__/sdk-transport.test.d.ts +2 -0
- package/dist/team/__tests__/sdk-transport.test.d.ts.map +1 -0
- package/dist/team/__tests__/sdk-transport.test.js +650 -0
- package/dist/team/__tests__/sdk-transport.test.js.map +1 -0
- package/dist/team/__tests__/tmux-session.pane-env.test.d.ts +2 -0
- package/dist/team/__tests__/tmux-session.pane-env.test.d.ts.map +1 -0
- package/dist/team/__tests__/tmux-session.pane-env.test.js +52 -0
- package/dist/team/__tests__/tmux-session.pane-env.test.js.map +1 -0
- package/dist/team/api-interop.d.ts.map +1 -1
- package/dist/team/api-interop.js +8 -0
- package/dist/team/api-interop.js.map +1 -1
- package/dist/team/runtime-v2.d.ts +33 -0
- package/dist/team/runtime-v2.d.ts.map +1 -1
- package/dist/team/runtime-v2.js +338 -24
- package/dist/team/runtime-v2.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +5 -0
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/sdk-host.d.ts +48 -0
- package/dist/team/sdk-host.d.ts.map +1 -0
- package/dist/team/sdk-host.js +604 -0
- package/dist/team/sdk-host.js.map +1 -0
- package/dist/team/sdk-policy.d.ts +79 -0
- package/dist/team/sdk-policy.d.ts.map +1 -0
- package/dist/team/sdk-policy.js +186 -0
- package/dist/team/sdk-policy.js.map +1 -0
- package/dist/team/sdk-transport.d.ts +164 -0
- package/dist/team/sdk-transport.d.ts.map +1 -0
- package/dist/team/sdk-transport.js +266 -0
- package/dist/team/sdk-transport.js.map +1 -0
- package/dist/team/tmux-session.d.ts +7 -0
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +80 -18
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/types.d.ts +2 -0
- package/dist/team/types.d.ts.map +1 -1
- package/dist/team/types.js.map +1 -1
- package/dist/tools/host-smoke-tool.d.ts +34 -0
- package/dist/tools/host-smoke-tool.d.ts.map +1 -0
- package/dist/tools/host-smoke-tool.js +133 -0
- package/dist/tools/host-smoke-tool.js.map +1 -0
- package/docs/CLAUDE.md +1 -1
- package/docs/DEVELOPERS.md +122 -0
- package/docs/HOOKS.md +34 -2
- package/docs/MIGRATION.md +194 -3
- package/docs/REFERENCE.md +95 -1
- package/docs/TOOLS.md +32 -1
- package/package.json +11 -2
- package/plugin.json +1 -1
- package/scripts/dev/repro-state-lock.mjs +1 -1
- package/scripts/lib/atomic-write.mjs +4 -2
- package/scripts/lib/state-lock.mjs +21 -12
- package/scripts/run.cjs +141 -18
- package/scripts/session-start.mjs +13 -0
- package/scripts/wiki-session-end.mjs +2 -1
- package/templates/hooks/lib/atomic-write.mjs +4 -2
package/bridge/team.js
CHANGED
|
@@ -1751,7 +1751,7 @@ async function withProcessIdentityFileLock(lockPath, fn, timeoutMs = 1e4) {
|
|
|
1751
1751
|
continue;
|
|
1752
1752
|
}
|
|
1753
1753
|
if (Date.now() >= deadline) throw new Error("process_identity_lock_timeout");
|
|
1754
|
-
await new Promise((
|
|
1754
|
+
await new Promise((resolve17) => setTimeout(resolve17, 25));
|
|
1755
1755
|
continue;
|
|
1756
1756
|
}
|
|
1757
1757
|
try {
|
|
@@ -1772,7 +1772,7 @@ async function withProcessIdentityFileLock(lockPath, fn, timeoutMs = 1e4) {
|
|
|
1772
1772
|
}
|
|
1773
1773
|
}
|
|
1774
1774
|
if (Date.now() >= deadline) throw new Error("process_identity_lock_timeout");
|
|
1775
|
-
await new Promise((
|
|
1775
|
+
await new Promise((resolve17) => setTimeout(resolve17, 25));
|
|
1776
1776
|
}
|
|
1777
1777
|
}
|
|
1778
1778
|
return await fn();
|
|
@@ -4140,7 +4140,7 @@ async function withMailboxLock(teamName, workerName, cwd, fn) {
|
|
|
4140
4140
|
while (Date.now() < deadline) {
|
|
4141
4141
|
const result = await withLock(lockDir, fn);
|
|
4142
4142
|
if (result.ok) return result.value;
|
|
4143
|
-
await new Promise((
|
|
4143
|
+
await new Promise((resolve17) => setTimeout(resolve17, delayMs));
|
|
4144
4144
|
delayMs = Math.min(delayMs * 2, 200);
|
|
4145
4145
|
}
|
|
4146
4146
|
throw new Error(`Failed to acquire mailbox lock for ${workerName} after ${timeoutMs}ms`);
|
|
@@ -4303,7 +4303,7 @@ async function teamCreateTask(teamName, task, cwd) {
|
|
|
4303
4303
|
return taskLock.value;
|
|
4304
4304
|
});
|
|
4305
4305
|
if (result.ok) return result.value;
|
|
4306
|
-
await new Promise((
|
|
4306
|
+
await new Promise((resolve17) => setTimeout(resolve17, delayMs));
|
|
4307
4307
|
delayMs = Math.min(delayMs * 2, 200);
|
|
4308
4308
|
}
|
|
4309
4309
|
throw new Error(`Failed to acquire task creation lock for team ${teamName} after ${timeoutMs}ms`);
|
|
@@ -4369,7 +4369,7 @@ async function teamUpdateTask(teamName, taskId, updates, cwd) {
|
|
|
4369
4369
|
return normalized;
|
|
4370
4370
|
});
|
|
4371
4371
|
if (result.ok) return result.value;
|
|
4372
|
-
await new Promise((
|
|
4372
|
+
await new Promise((resolve17) => setTimeout(resolve17, delayMs));
|
|
4373
4373
|
delayMs = Math.min(delayMs * 2, 200);
|
|
4374
4374
|
}
|
|
4375
4375
|
throw new Error(`Failed to acquire task update lock for task ${taskId} in team ${teamName} after ${timeoutMs}ms`);
|
|
@@ -4995,7 +4995,7 @@ async function withDispatchLock(teamName, cwd, fn) {
|
|
|
4995
4995
|
);
|
|
4996
4996
|
}
|
|
4997
4997
|
const jitter = 0.5 + Math.random() * 0.5;
|
|
4998
|
-
await new Promise((
|
|
4998
|
+
await new Promise((resolve17) => setTimeout(resolve17, Math.floor(pollMs * jitter)));
|
|
4999
4999
|
pollMs = Math.min(pollMs * 2, DISPATCH_LOCK_MAX_POLL_MS);
|
|
5000
5000
|
}
|
|
5001
5001
|
}
|
|
@@ -5660,8 +5660,8 @@ function getPackageDir() {
|
|
|
5660
5660
|
}
|
|
5661
5661
|
}
|
|
5662
5662
|
try {
|
|
5663
|
-
const
|
|
5664
|
-
const __dirname2 = dirname12(
|
|
5663
|
+
const __filename2 = fileURLToPath2(import.meta.url);
|
|
5664
|
+
const __dirname2 = dirname12(__filename2);
|
|
5665
5665
|
const currentDirName = basename6(__dirname2);
|
|
5666
5666
|
if (currentDirName === "bridge") {
|
|
5667
5667
|
return join11(__dirname2, "..");
|
|
@@ -7662,8 +7662,8 @@ function getPackageDir2() {
|
|
|
7662
7662
|
}
|
|
7663
7663
|
}
|
|
7664
7664
|
try {
|
|
7665
|
-
const
|
|
7666
|
-
const __dirname2 = dirname16(
|
|
7665
|
+
const __filename2 = fileURLToPath3(import.meta.url);
|
|
7666
|
+
const __dirname2 = dirname16(__filename2);
|
|
7667
7667
|
return join16(__dirname2, "..", "..", "..");
|
|
7668
7668
|
} catch {
|
|
7669
7669
|
return process.cwd();
|
|
@@ -8134,9 +8134,9 @@ function getTrustedPrefixes() {
|
|
|
8134
8134
|
function isTrustedPrefix(resolvedPath) {
|
|
8135
8135
|
const flavor = pathFlavor();
|
|
8136
8136
|
return getTrustedPrefixes().some((prefix) => {
|
|
8137
|
-
const
|
|
8138
|
-
if (
|
|
8139
|
-
return !
|
|
8137
|
+
const relative11 = flavor.relative(prefix, resolvedPath);
|
|
8138
|
+
if (relative11 === "") return true;
|
|
8139
|
+
return !relative11.startsWith("..") && !flavor.isAbsolute(relative11);
|
|
8140
8140
|
});
|
|
8141
8141
|
}
|
|
8142
8142
|
function assertBinaryName(binary) {
|
|
@@ -8928,6 +8928,45 @@ function parseDeadline(deadlineAt) {
|
|
|
8928
8928
|
const value = Date.parse(deadlineAt);
|
|
8929
8929
|
return Number.isFinite(value) ? value : void 0;
|
|
8930
8930
|
}
|
|
8931
|
+
async function killProcessTree(pid, signal = "SIGTERM") {
|
|
8932
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
8933
|
+
if (process.platform === "win32") {
|
|
8934
|
+
return killProcessTreeWindows(pid, signal === "SIGKILL");
|
|
8935
|
+
} else {
|
|
8936
|
+
return killProcessTreeUnix(pid, signal);
|
|
8937
|
+
}
|
|
8938
|
+
}
|
|
8939
|
+
async function killProcessTreeWindows(pid, force) {
|
|
8940
|
+
try {
|
|
8941
|
+
const args = ["/T", "/PID", String(pid)];
|
|
8942
|
+
if (force) {
|
|
8943
|
+
args.unshift("/F");
|
|
8944
|
+
}
|
|
8945
|
+
execFileSync4("taskkill.exe", args, {
|
|
8946
|
+
stdio: "ignore",
|
|
8947
|
+
timeout: 5e3,
|
|
8948
|
+
windowsHide: true
|
|
8949
|
+
});
|
|
8950
|
+
return true;
|
|
8951
|
+
} catch (err) {
|
|
8952
|
+
const error = err;
|
|
8953
|
+
if (error.status === 128) return true;
|
|
8954
|
+
return false;
|
|
8955
|
+
}
|
|
8956
|
+
}
|
|
8957
|
+
function killProcessTreeUnix(pid, signal) {
|
|
8958
|
+
try {
|
|
8959
|
+
process.kill(-pid, signal);
|
|
8960
|
+
return true;
|
|
8961
|
+
} catch {
|
|
8962
|
+
try {
|
|
8963
|
+
process.kill(pid, signal);
|
|
8964
|
+
return true;
|
|
8965
|
+
} catch {
|
|
8966
|
+
return !isProcessAlive(pid);
|
|
8967
|
+
}
|
|
8968
|
+
}
|
|
8969
|
+
}
|
|
8931
8970
|
function isProcessAlive(pid) {
|
|
8932
8971
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
8933
8972
|
try {
|
|
@@ -9197,33 +9236,33 @@ function ensureDirSync(dir) {
|
|
|
9197
9236
|
}
|
|
9198
9237
|
}
|
|
9199
9238
|
function verifyPrivateTempFile(fd, tempPath, label, operations) {
|
|
9200
|
-
const fdStats =
|
|
9239
|
+
const fdStats = descriptorStats(fd, operations);
|
|
9201
9240
|
let pathStats;
|
|
9202
9241
|
try {
|
|
9203
|
-
pathStats = (operations
|
|
9242
|
+
pathStats = pathnameStats(tempPath, operations);
|
|
9204
9243
|
} catch {
|
|
9205
9244
|
throw new Error(`${label} temporary file was replaced before rename`);
|
|
9206
9245
|
}
|
|
9207
9246
|
const isWindows = process.platform === "win32";
|
|
9208
|
-
const isPrivateRegularSingleLink = (stats) => stats.isFile() && (isWindows ? stats.nlink <= 1 : stats.nlink === 1) && (isWindows || (stats.mode & 511) === 384);
|
|
9247
|
+
const isPrivateRegularSingleLink = (stats) => stats.isFile() && (isWindows ? Number(stats.nlink) <= 1 : Number(stats.nlink) === 1) && (isWindows || (Number(stats.mode) & 511) === 384);
|
|
9209
9248
|
if (!isPrivateRegularSingleLink(fdStats) || !isPrivateRegularSingleLink(pathStats)) {
|
|
9210
9249
|
throw new Error(
|
|
9211
9250
|
`${label} temporary file must be a private regular single-link file`
|
|
9212
9251
|
);
|
|
9213
9252
|
}
|
|
9214
|
-
if (!sameFileIdentity(fdStats, pathStats)) {
|
|
9253
|
+
if (!sameFileIdentity(fileIdentityOf(fdStats), fileIdentityOf(pathStats))) {
|
|
9215
9254
|
throw new Error(`${label} temporary file was replaced before rename`);
|
|
9216
9255
|
}
|
|
9217
9256
|
}
|
|
9218
9257
|
function verifyPublishedFile(fd, filePath, label, operations) {
|
|
9219
|
-
const fdStats =
|
|
9258
|
+
const fdStats = descriptorStats(fd, operations);
|
|
9220
9259
|
let pathStats;
|
|
9221
9260
|
try {
|
|
9222
|
-
pathStats = (operations
|
|
9261
|
+
pathStats = pathnameStats(filePath, operations);
|
|
9223
9262
|
} catch {
|
|
9224
9263
|
throw new Error(`${label} target was replaced at publication`);
|
|
9225
9264
|
}
|
|
9226
|
-
if (!pathStats.isFile() || !sameFileIdentity(fdStats, pathStats)) {
|
|
9265
|
+
if (!pathStats.isFile() || !sameFileIdentity(fileIdentityOf(fdStats), fileIdentityOf(pathStats))) {
|
|
9227
9266
|
throw new Error(`${label} target was replaced at publication`);
|
|
9228
9267
|
}
|
|
9229
9268
|
}
|
|
@@ -9247,26 +9286,33 @@ function preservePriorTarget(filePath, operations) {
|
|
|
9247
9286
|
return null;
|
|
9248
9287
|
}
|
|
9249
9288
|
}
|
|
9289
|
+
function descriptorStats(fd, operations) {
|
|
9290
|
+
return operations ? fsSync.fstatSync(fd) : fsSync.fstatSync(fd, { bigint: true });
|
|
9291
|
+
}
|
|
9292
|
+
function pathnameStats(filePath, operations) {
|
|
9293
|
+
return operations ? operations.lstat(filePath) : fsSync.lstatSync(filePath, { bigint: true });
|
|
9294
|
+
}
|
|
9295
|
+
function fileIdentityOf(stats) {
|
|
9296
|
+
return { dev: BigInt(stats.dev), ino: BigInt(stats.ino) };
|
|
9297
|
+
}
|
|
9250
9298
|
function sameFileIdentity(a, b) {
|
|
9251
9299
|
if (a.ino !== b.ino) return false;
|
|
9252
9300
|
const isWindows = process.platform === "win32";
|
|
9253
|
-
if (isWindows && (a.dev ===
|
|
9301
|
+
if (isWindows && (a.dev === 0n || b.dev === 0n)) {
|
|
9254
9302
|
return true;
|
|
9255
9303
|
}
|
|
9256
9304
|
return a.dev === b.dev;
|
|
9257
9305
|
}
|
|
9258
9306
|
function currentFileIdentity(filePath, operations) {
|
|
9259
9307
|
try {
|
|
9260
|
-
|
|
9261
|
-
return { dev: stats.dev, ino: stats.ino };
|
|
9308
|
+
return fileIdentityOf(pathnameStats(filePath, operations));
|
|
9262
9309
|
} catch {
|
|
9263
9310
|
return null;
|
|
9264
9311
|
}
|
|
9265
9312
|
}
|
|
9266
|
-
function descriptorIdentity(fd) {
|
|
9313
|
+
function descriptorIdentity(fd, operations) {
|
|
9267
9314
|
try {
|
|
9268
|
-
|
|
9269
|
-
return { dev: stats.dev, ino: stats.ino };
|
|
9315
|
+
return fileIdentityOf(descriptorStats(fd, operations));
|
|
9270
9316
|
} catch {
|
|
9271
9317
|
return null;
|
|
9272
9318
|
}
|
|
@@ -9545,7 +9591,7 @@ function withFileLockSync(lockPath, fn, opts) {
|
|
|
9545
9591
|
}
|
|
9546
9592
|
}
|
|
9547
9593
|
function sleep(ms) {
|
|
9548
|
-
return new Promise((
|
|
9594
|
+
return new Promise((resolve17) => setTimeout(resolve17, ms));
|
|
9549
9595
|
}
|
|
9550
9596
|
async function acquireFileLock(lockPath, opts) {
|
|
9551
9597
|
const staleLockMs = opts?.staleLockMs ?? DEFAULT_STALE_LOCK_MS;
|
|
@@ -9643,7 +9689,7 @@ function canonicalAuthorityDigest(input) {
|
|
|
9643
9689
|
return createHash7("sha256").update(payload, "utf8").digest("hex");
|
|
9644
9690
|
}
|
|
9645
9691
|
function sleep2(ms) {
|
|
9646
|
-
return new Promise((
|
|
9692
|
+
return new Promise((resolve17) => setTimeout(resolve17, ms));
|
|
9647
9693
|
}
|
|
9648
9694
|
function isExactText(value) {
|
|
9649
9695
|
return typeof value === "string" && value.length > 0 && value === value.trim();
|
|
@@ -11506,7 +11552,7 @@ async function bootstrapPersistentOwner(input, priorEpoch) {
|
|
|
11506
11552
|
return true;
|
|
11507
11553
|
}
|
|
11508
11554
|
if (owner && (owner.epoch > expectedEpoch || owner.epoch === expectedEpoch && owner.pid !== childPid)) return false;
|
|
11509
|
-
await new Promise((
|
|
11555
|
+
await new Promise((resolve17) => setTimeout(resolve17, 25));
|
|
11510
11556
|
}
|
|
11511
11557
|
return false;
|
|
11512
11558
|
}
|
|
@@ -11669,7 +11715,7 @@ function createRecoveryOwnerClient(dispatch, timing = {}) {
|
|
|
11669
11715
|
teamName: normalized.teamName,
|
|
11670
11716
|
workerName: normalized.workerName
|
|
11671
11717
|
})) return outcome.result;
|
|
11672
|
-
await new Promise((
|
|
11718
|
+
await new Promise((resolve17) => setTimeout(resolve17, timing.pollIntervalMs ?? 250));
|
|
11673
11719
|
}
|
|
11674
11720
|
return timeoutResult(normalized, canonical.recovery_id);
|
|
11675
11721
|
}
|
|
@@ -11768,13 +11814,14 @@ __export(tmux_session_exports, {
|
|
|
11768
11814
|
waitForPaneReady: () => waitForPaneReady,
|
|
11769
11815
|
waitForStartupPaneReady: () => waitForStartupPaneReady,
|
|
11770
11816
|
workerPaneBelongsToOwnedProviderTarget: () => workerPaneBelongsToOwnedProviderTarget,
|
|
11771
|
-
workerPaneBelongsToProviderTarget: () => workerPaneBelongsToProviderTarget
|
|
11817
|
+
workerPaneBelongsToProviderTarget: () => workerPaneBelongsToProviderTarget,
|
|
11818
|
+
workerPaneShellCommand: () => workerPaneShellCommand
|
|
11772
11819
|
});
|
|
11773
|
-
import { existsSync as existsSync23, statSync as statSync4 } from "fs";
|
|
11820
|
+
import { existsSync as existsSync23, mkdtempSync, rmSync as rmSync2, statSync as statSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
11774
11821
|
import { createHash as createHash10, randomUUID as randomUUID14 } from "crypto";
|
|
11775
11822
|
import { execFile as execFile3 } from "child_process";
|
|
11776
11823
|
import { promisify as promisify3 } from "util";
|
|
11777
|
-
import { join as join25, basename as basename12, isAbsolute as isAbsolute7, win32 as win323 } from "path";
|
|
11824
|
+
import { join as join25, basename as basename12, dirname as dirname22, isAbsolute as isAbsolute7, win32 as win323 } from "path";
|
|
11778
11825
|
import { tmpdir as tmpdir3 } from "os";
|
|
11779
11826
|
function tmuxArgsForIdentity(identity, args) {
|
|
11780
11827
|
return ["-S", identity.socket_path, ...args];
|
|
@@ -12649,23 +12696,66 @@ async function verifyWorkerStartCommandSubmitted(paneId, startCmd, opts = {}) {
|
|
|
12649
12696
|
}
|
|
12650
12697
|
return false;
|
|
12651
12698
|
}
|
|
12699
|
+
function writePaneEnvFile(values) {
|
|
12700
|
+
const directory = mkdtempSync(join25(tmpdir3(), "omc-pane-env-"));
|
|
12701
|
+
const file = join25(directory, "env");
|
|
12702
|
+
const body = Object.entries(values).map(([key, value]) => `export ${key}=${shellQuote(value)}
|
|
12703
|
+
`).join("");
|
|
12704
|
+
writeFileSync6(file, body, { mode: 384, flag: "wx" });
|
|
12705
|
+
return file;
|
|
12706
|
+
}
|
|
12707
|
+
function discardPaneEnvFile(envFile) {
|
|
12708
|
+
if (!envFile) return;
|
|
12709
|
+
rmSync2(dirname22(envFile), { recursive: true, force: true });
|
|
12710
|
+
}
|
|
12652
12711
|
function workerPaneShellCommand() {
|
|
12653
12712
|
if (process.platform === "win32" && !isUnixLikeOnWindows()) {
|
|
12654
|
-
return [getDefaultShell()];
|
|
12713
|
+
return { args: [getDefaultShell()], envFile: null };
|
|
12655
12714
|
}
|
|
12656
|
-
if (process.platform === "win32") return [];
|
|
12715
|
+
if (process.platform === "win32") return { args: [], envFile: null };
|
|
12657
12716
|
const shell = getDefaultShell();
|
|
12658
12717
|
const baseline = buildProviderEnvironment({ SHELL: shell });
|
|
12718
|
+
const inline = buildProviderEnvironment({ SHELL: shell }, process.env, process.platform, []);
|
|
12719
|
+
const passthrough = Object.fromEntries(
|
|
12720
|
+
Object.entries(baseline).filter(([key, value]) => inline[key] !== value)
|
|
12721
|
+
);
|
|
12659
12722
|
const inheritedPaneEnvironment = ["TERM", "TMUX", "TMUX_PANE", "TMUX_TMPDIR", "LANG", "LC_ALL", "LC_CTYPE"].map((key) => `${key}="$${key}"`);
|
|
12723
|
+
const inlineAssignments = Object.entries(inline).map(([key, value]) => `${key}=${shellQuote(value)}`);
|
|
12724
|
+
if (Object.keys(passthrough).length === 0) {
|
|
12725
|
+
const command2 = [
|
|
12726
|
+
"/usr/bin/env",
|
|
12727
|
+
"-i",
|
|
12728
|
+
...inlineAssignments,
|
|
12729
|
+
...inheritedPaneEnvironment,
|
|
12730
|
+
shellQuote(shell),
|
|
12731
|
+
"-l"
|
|
12732
|
+
].join(" ");
|
|
12733
|
+
return { args: [command2], envFile: null };
|
|
12734
|
+
}
|
|
12735
|
+
const envFile = writePaneEnvFile(passthrough);
|
|
12736
|
+
const bootstrap = '. "$OMC_PANE_ENV_FILE" && rm -rf -- "${OMC_PANE_ENV_FILE%/*}" && unset OMC_PANE_ENV_FILE && exec "$SHELL" -l';
|
|
12660
12737
|
const command = [
|
|
12661
12738
|
"/usr/bin/env",
|
|
12662
12739
|
"-i",
|
|
12663
|
-
...
|
|
12740
|
+
...inlineAssignments,
|
|
12741
|
+
`OMC_PANE_ENV_FILE=${shellQuote(envFile)}`,
|
|
12664
12742
|
...inheritedPaneEnvironment,
|
|
12665
|
-
|
|
12666
|
-
"-
|
|
12743
|
+
"/bin/sh",
|
|
12744
|
+
"-c",
|
|
12745
|
+
shellQuote(bootstrap)
|
|
12667
12746
|
].join(" ");
|
|
12668
|
-
return [command];
|
|
12747
|
+
return { args: [command], envFile };
|
|
12748
|
+
}
|
|
12749
|
+
async function runPaneCreationCommand(identity, nativeCommand, paneShell) {
|
|
12750
|
+
let result;
|
|
12751
|
+
try {
|
|
12752
|
+
result = await runGuardedNativeTmuxCommand(identity, nativeCommand);
|
|
12753
|
+
} catch (error) {
|
|
12754
|
+
discardPaneEnvFile(paneShell.envFile);
|
|
12755
|
+
throw error;
|
|
12756
|
+
}
|
|
12757
|
+
if (result.outcome === "not_executed") discardPaneEnvFile(paneShell.envFile);
|
|
12758
|
+
return result;
|
|
12669
12759
|
}
|
|
12670
12760
|
function escapeForCmdSet(value) {
|
|
12671
12761
|
return value.replace(/(["%])/g, "$1$1");
|
|
@@ -12964,6 +13054,7 @@ async function splitTeamWorkerPaneWithEvidence(splitTarget, direction, cwd, prov
|
|
|
12964
13054
|
};
|
|
12965
13055
|
}
|
|
12966
13056
|
const splitType = direction === "right" ? "-h" : "-v";
|
|
13057
|
+
const splitPaneShell = workerPaneShellCommand();
|
|
12967
13058
|
const splitArgs = [
|
|
12968
13059
|
"split-window",
|
|
12969
13060
|
splitType,
|
|
@@ -12975,11 +13066,12 @@ async function splitTeamWorkerPaneWithEvidence(splitTarget, direction, cwd, prov
|
|
|
12975
13066
|
"#{pane_id} #{socket_path} #{pid}",
|
|
12976
13067
|
"-c",
|
|
12977
13068
|
cwd,
|
|
12978
|
-
...
|
|
13069
|
+
...splitPaneShell.args
|
|
12979
13070
|
];
|
|
12980
|
-
const splitResult = await
|
|
13071
|
+
const splitResult = await runPaneCreationCommand(
|
|
12981
13072
|
identity,
|
|
12982
|
-
tmuxCommandString(splitArgs, ["#{pane_id} #{socket_path} #{pid}"])
|
|
13073
|
+
tmuxCommandString(splitArgs, ["#{pane_id} #{socket_path} #{pid}"]),
|
|
13074
|
+
splitPaneShell
|
|
12983
13075
|
);
|
|
12984
13076
|
const parsed = splitResult.outcome === "executed" ? parseTmuxCreationRecord(
|
|
12985
13077
|
splitResult.stdout,
|
|
@@ -13081,6 +13173,7 @@ async function createTeamSession(teamName, workerCount, cwd, options = {}) {
|
|
|
13081
13173
|
sessionMode: "detached-session",
|
|
13082
13174
|
...tmuxServerIdentity ? { tmuxServerIdentity: { ...tmuxServerIdentity } } : {}
|
|
13083
13175
|
});
|
|
13176
|
+
const detachedPaneShell = workerPaneShellCommand();
|
|
13084
13177
|
const detachedArgs = [
|
|
13085
13178
|
"new-session",
|
|
13086
13179
|
"-d",
|
|
@@ -13091,7 +13184,7 @@ async function createTeamSession(teamName, workerCount, cwd, options = {}) {
|
|
|
13091
13184
|
detachedSessionName,
|
|
13092
13185
|
"-c",
|
|
13093
13186
|
cwd,
|
|
13094
|
-
...
|
|
13187
|
+
...detachedPaneShell.args
|
|
13095
13188
|
];
|
|
13096
13189
|
const cleanupFreshDetachedServer = async () => {
|
|
13097
13190
|
if (isPsmux() && freshSocketPathForEvidence) {
|
|
@@ -13120,9 +13213,10 @@ async function createTeamSession(teamName, workerCount, cwd, options = {}) {
|
|
|
13120
13213
|
if (existingDetachedIdentity) {
|
|
13121
13214
|
tmuxServerIdentity = existingDetachedIdentity;
|
|
13122
13215
|
try {
|
|
13123
|
-
detachedResult = await
|
|
13216
|
+
detachedResult = await runPaneCreationCommand(
|
|
13124
13217
|
existingDetachedIdentity,
|
|
13125
|
-
tmuxCommandString(detachedArgs, ["#S:0 #{pane_id} #{socket_path} #{pid}"])
|
|
13218
|
+
tmuxCommandString(detachedArgs, ["#S:0 #{pane_id} #{socket_path} #{pid}"]),
|
|
13219
|
+
detachedPaneShell
|
|
13126
13220
|
);
|
|
13127
13221
|
} catch (error) {
|
|
13128
13222
|
const cleaned = await cleanupDetachedSession();
|
|
@@ -13182,11 +13276,13 @@ async function createTeamSession(teamName, workerCount, cwd, options = {}) {
|
|
|
13182
13276
|
throw new Error("tmux_server_identity_unavailable");
|
|
13183
13277
|
}
|
|
13184
13278
|
freshDetachedServerIdentity = tmuxServerIdentity;
|
|
13185
|
-
detachedResult = await
|
|
13279
|
+
detachedResult = await runPaneCreationCommand(
|
|
13186
13280
|
tmuxServerIdentity,
|
|
13187
|
-
tmuxCommandString(detachedArgs, ["#S:0 #{pane_id} #{socket_path} #{pid}"])
|
|
13281
|
+
tmuxCommandString(detachedArgs, ["#S:0 #{pane_id} #{socket_path} #{pid}"]),
|
|
13282
|
+
detachedPaneShell
|
|
13188
13283
|
);
|
|
13189
13284
|
} catch (error) {
|
|
13285
|
+
discardPaneEnvFile(detachedPaneShell.envFile);
|
|
13190
13286
|
const cleaned = await cleanupDetachedSession();
|
|
13191
13287
|
if (!cleaned && freshDetachedServerStarted) {
|
|
13192
13288
|
throw new TeamSessionCreationError(
|
|
@@ -13366,6 +13462,7 @@ async function createTeamSession(teamName, workerCount, cwd, options = {}) {
|
|
|
13366
13462
|
if (useDedicatedWindow) {
|
|
13367
13463
|
const targetSession = sessionAndWindow.split(":")[0] ?? sessionAndWindow;
|
|
13368
13464
|
const windowName = `omc-${sanitizeName(teamName)}`.slice(0, 32);
|
|
13465
|
+
const newWindowPaneShell = workerPaneShellCommand();
|
|
13369
13466
|
const newWindowArgs = [
|
|
13370
13467
|
"new-window",
|
|
13371
13468
|
"-d",
|
|
@@ -13378,13 +13475,14 @@ async function createTeamSession(teamName, workerCount, cwd, options = {}) {
|
|
|
13378
13475
|
windowName,
|
|
13379
13476
|
"-c",
|
|
13380
13477
|
cwd,
|
|
13381
|
-
...
|
|
13478
|
+
...newWindowPaneShell.args
|
|
13382
13479
|
];
|
|
13383
13480
|
let newWindowResult;
|
|
13384
13481
|
try {
|
|
13385
|
-
newWindowResult = await
|
|
13482
|
+
newWindowResult = await runPaneCreationCommand(
|
|
13386
13483
|
tmuxServerIdentity,
|
|
13387
|
-
tmuxCommandString(newWindowArgs, ["#S:#I #{pane_id} #{socket_path} #{pid}"])
|
|
13484
|
+
tmuxCommandString(newWindowArgs, ["#S:#I #{pane_id} #{socket_path} #{pid}"]),
|
|
13485
|
+
newWindowPaneShell
|
|
13388
13486
|
);
|
|
13389
13487
|
} catch (error) {
|
|
13390
13488
|
const creationError = new TeamSessionCreationError(
|
|
@@ -13534,6 +13632,7 @@ async function createTeamSession(teamName, workerCount, cwd, options = {}) {
|
|
|
13534
13632
|
continue;
|
|
13535
13633
|
}
|
|
13536
13634
|
const splitType = i === 0 ? "-h" : "-v";
|
|
13635
|
+
const splitPaneShell = workerPaneShellCommand();
|
|
13537
13636
|
const splitArgs = [
|
|
13538
13637
|
"split-window",
|
|
13539
13638
|
splitType,
|
|
@@ -13545,11 +13644,12 @@ async function createTeamSession(teamName, workerCount, cwd, options = {}) {
|
|
|
13545
13644
|
"#{pane_id} #{socket_path} #{pid}",
|
|
13546
13645
|
"-c",
|
|
13547
13646
|
cwd,
|
|
13548
|
-
...
|
|
13647
|
+
...splitPaneShell.args
|
|
13549
13648
|
];
|
|
13550
|
-
const splitResult = await
|
|
13649
|
+
const splitResult = await runPaneCreationCommand(
|
|
13551
13650
|
tmuxServerIdentity,
|
|
13552
|
-
tmuxCommandString(splitArgs, ["#{pane_id} #{socket_path} #{pid}"])
|
|
13651
|
+
tmuxCommandString(splitArgs, ["#{pane_id} #{socket_path} #{pid}"]),
|
|
13652
|
+
splitPaneShell
|
|
13553
13653
|
);
|
|
13554
13654
|
if (splitResult.outcome !== "executed") {
|
|
13555
13655
|
const creationError = new TeamSessionCreationError(
|
|
@@ -14824,7 +14924,7 @@ var init_tmux_session = __esm({
|
|
|
14824
14924
|
|
|
14825
14925
|
// src/team/mcp-comm.ts
|
|
14826
14926
|
import { realpathSync as realpathSync4 } from "node:fs";
|
|
14827
|
-
import { basename as basename13, dirname as
|
|
14927
|
+
import { basename as basename13, dirname as dirname23, join as join26 } from "node:path";
|
|
14828
14928
|
function isConfirmedNotification(outcome) {
|
|
14829
14929
|
if (!outcome.ok) return false;
|
|
14830
14930
|
if (outcome.transport !== "hook") return true;
|
|
@@ -14850,7 +14950,7 @@ function canonicalNotificationLockIdentity(lockPath) {
|
|
|
14850
14950
|
return realpathSync4(lockPath);
|
|
14851
14951
|
} catch {
|
|
14852
14952
|
try {
|
|
14853
|
-
return join26(realpathSync4(
|
|
14953
|
+
return join26(realpathSync4(dirname23(lockPath)), basename13(lockPath));
|
|
14854
14954
|
} catch {
|
|
14855
14955
|
return lockPath;
|
|
14856
14956
|
}
|
|
@@ -15316,12 +15416,12 @@ var init_mcp_comm = __esm({
|
|
|
15316
15416
|
|
|
15317
15417
|
// src/agents/prompt-helpers.ts
|
|
15318
15418
|
import { readdirSync as readdirSync10 } from "fs";
|
|
15319
|
-
import { join as join27, dirname as
|
|
15419
|
+
import { join as join27, dirname as dirname24, basename as basename14 } from "path";
|
|
15320
15420
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
15321
15421
|
function getPackageDir3() {
|
|
15322
15422
|
if (typeof __dirname !== "undefined" && __dirname) {
|
|
15323
15423
|
const currentDirName = basename14(__dirname);
|
|
15324
|
-
const parentDirName = basename14(
|
|
15424
|
+
const parentDirName = basename14(dirname24(__dirname));
|
|
15325
15425
|
if (currentDirName === "bridge") {
|
|
15326
15426
|
return join27(__dirname, "..");
|
|
15327
15427
|
}
|
|
@@ -15330,8 +15430,8 @@ function getPackageDir3() {
|
|
|
15330
15430
|
}
|
|
15331
15431
|
}
|
|
15332
15432
|
try {
|
|
15333
|
-
const
|
|
15334
|
-
const __dirname2 =
|
|
15433
|
+
const __filename2 = fileURLToPath4(import.meta.url);
|
|
15434
|
+
const __dirname2 = dirname24(__filename2);
|
|
15335
15435
|
const currentDirName = basename14(__dirname2);
|
|
15336
15436
|
if (currentDirName === "bridge") {
|
|
15337
15437
|
return join27(__dirname2, "..");
|
|
@@ -15385,7 +15485,7 @@ var init_prompt_helpers = __esm({
|
|
|
15385
15485
|
|
|
15386
15486
|
// src/team/worker-bootstrap.ts
|
|
15387
15487
|
import { mkdir as mkdir9, writeFile as writeFile7, appendFile as appendFile2 } from "fs/promises";
|
|
15388
|
-
import { join as join28, dirname as
|
|
15488
|
+
import { join as join28, dirname as dirname25 } from "path";
|
|
15389
15489
|
function buildInstructionPath(...parts) {
|
|
15390
15490
|
return join28(...parts).replaceAll("\\", "/");
|
|
15391
15491
|
}
|
|
@@ -15655,7 +15755,7 @@ ${bootstrapInstructions}
|
|
|
15655
15755
|
}
|
|
15656
15756
|
async function composeInitialInbox(teamName, workerName, content, cwd, cliOutputContract) {
|
|
15657
15757
|
const inboxPath = join28(teamStateRoot(cwd, sanitizeName(teamName)), "workers", sanitizeName(workerName), "inbox.md");
|
|
15658
|
-
await mkdir9(
|
|
15758
|
+
await mkdir9(dirname25(inboxPath), { recursive: true });
|
|
15659
15759
|
const finalContent = cliOutputContract && !content.includes(cliOutputContract) ? `${content}
|
|
15660
15760
|
${cliOutputContract}` : content;
|
|
15661
15761
|
await writeFile7(inboxPath, finalContent, "utf-8");
|
|
@@ -15665,7 +15765,7 @@ async function appendToInbox(teamName, workerName, message, cwd) {
|
|
|
15665
15765
|
const safeWorker = sanitizeName(workerName);
|
|
15666
15766
|
const inboxPath = join28(teamStateRoot(cwd, safeTeam), "workers", safeWorker, "inbox.md");
|
|
15667
15767
|
validateResolvedPath(inboxPath, teamStateRoot(cwd, safeTeam));
|
|
15668
|
-
await mkdir9(
|
|
15768
|
+
await mkdir9(dirname25(inboxPath), { recursive: true });
|
|
15669
15769
|
await appendFile2(inboxPath, `
|
|
15670
15770
|
|
|
15671
15771
|
---
|
|
@@ -15684,7 +15784,7 @@ async function writeWorkerOverlay(params) {
|
|
|
15684
15784
|
const { teamName, workerName, cwd } = params;
|
|
15685
15785
|
const overlay = generateWorkerOverlay(params);
|
|
15686
15786
|
const overlayPath = join28(teamStateRoot(cwd, sanitizeName(teamName)), "workers", sanitizeName(workerName), "AGENTS.md");
|
|
15687
|
-
await mkdir9(
|
|
15787
|
+
await mkdir9(dirname25(overlayPath), { recursive: true });
|
|
15688
15788
|
await writeFile7(overlayPath, overlay, "utf-8");
|
|
15689
15789
|
return overlayPath;
|
|
15690
15790
|
}
|
|
@@ -15702,6 +15802,223 @@ var init_worker_bootstrap = __esm({
|
|
|
15702
15802
|
}
|
|
15703
15803
|
});
|
|
15704
15804
|
|
|
15805
|
+
// src/team/sdk-transport.ts
|
|
15806
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
15807
|
+
import { randomUUID as randomUUID15 } from "node:crypto";
|
|
15808
|
+
import { appendFileSync, closeSync as closeSync5, existsSync as existsSync24, mkdirSync as mkdirSync7, openSync as openSync5, readFileSync as readFileSync18, writeFileSync as writeFileSync7 } from "node:fs";
|
|
15809
|
+
import { dirname as dirname26, join as join29 } from "node:path";
|
|
15810
|
+
function isSdkTarget(value) {
|
|
15811
|
+
return typeof value === "string" && value.startsWith(SDK_TARGET_PREFIX);
|
|
15812
|
+
}
|
|
15813
|
+
function sdkTeamTarget(teamName) {
|
|
15814
|
+
return `${SDK_TARGET_PREFIX}${teamName}`;
|
|
15815
|
+
}
|
|
15816
|
+
function sdkPaneId(workerName) {
|
|
15817
|
+
return `${SDK_TARGET_PREFIX}${workerName}`;
|
|
15818
|
+
}
|
|
15819
|
+
function sdkWorkerFiles(stateRoot2, workerName) {
|
|
15820
|
+
const dir = join29(stateRoot2, "workers", workerName);
|
|
15821
|
+
return {
|
|
15822
|
+
dir,
|
|
15823
|
+
spec: join29(dir, "sdk-host-spec.json"),
|
|
15824
|
+
doorbell: join29(dir, "sdk-doorbell.jsonl"),
|
|
15825
|
+
session: join29(dir, "sdk-session.json"),
|
|
15826
|
+
probe: join29(dir, "sdk-probe.json"),
|
|
15827
|
+
events: join29(dir, "sdk-events.jsonl"),
|
|
15828
|
+
log: join29(dir, "sdk-host.log"),
|
|
15829
|
+
home: join29(dir, "copilot-home"),
|
|
15830
|
+
bin: join29(dir, "bin"),
|
|
15831
|
+
shutdownAck: join29(dir, "shutdown-ack.json")
|
|
15832
|
+
};
|
|
15833
|
+
}
|
|
15834
|
+
function ringDoorbell(stateRoot2, workerName, entry) {
|
|
15835
|
+
const files = sdkWorkerFiles(stateRoot2, workerName);
|
|
15836
|
+
mkdirSync7(files.dir, { recursive: true });
|
|
15837
|
+
const id = entry.id ?? randomUUID15();
|
|
15838
|
+
const line = { ...entry, id, at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
15839
|
+
appendFileSync(files.doorbell, `${JSON.stringify(line)}
|
|
15840
|
+
`);
|
|
15841
|
+
return id;
|
|
15842
|
+
}
|
|
15843
|
+
function readSdkSession(stateRoot2, workerName) {
|
|
15844
|
+
try {
|
|
15845
|
+
const parsed = JSON.parse(readFileSync18(sdkWorkerFiles(stateRoot2, workerName).session, "utf8"));
|
|
15846
|
+
return parsed && parsed.schema_version === 1 ? parsed : null;
|
|
15847
|
+
} catch {
|
|
15848
|
+
return null;
|
|
15849
|
+
}
|
|
15850
|
+
}
|
|
15851
|
+
function spawnDetachedHost(cliPath, specPath, logPath, cwd) {
|
|
15852
|
+
mkdirSync7(dirname26(logPath), { recursive: true });
|
|
15853
|
+
const fd = openSync5(logPath, "a");
|
|
15854
|
+
try {
|
|
15855
|
+
const child = spawn2(process.execPath, [cliPath, "team", "sdk-host", "--spec", specPath], {
|
|
15856
|
+
cwd,
|
|
15857
|
+
detached: true,
|
|
15858
|
+
windowsHide: true,
|
|
15859
|
+
stdio: ["ignore", fd, fd],
|
|
15860
|
+
env: process.env
|
|
15861
|
+
});
|
|
15862
|
+
child.unref();
|
|
15863
|
+
return child.pid;
|
|
15864
|
+
} finally {
|
|
15865
|
+
closeSync5(fd);
|
|
15866
|
+
}
|
|
15867
|
+
}
|
|
15868
|
+
async function launchSdkHost(input) {
|
|
15869
|
+
const paneId = sdkPaneId(input.workerName);
|
|
15870
|
+
const runtimeCliPath = resolveRuntimeCliPath();
|
|
15871
|
+
const attempt = await prepareWorkerLaunchAttempt({
|
|
15872
|
+
cwd: input.leaderCwd,
|
|
15873
|
+
teamName: input.teamName,
|
|
15874
|
+
workerName: input.workerName,
|
|
15875
|
+
instanceId: input.instanceId,
|
|
15876
|
+
paneId,
|
|
15877
|
+
provider: "copilot",
|
|
15878
|
+
runtimeCliPath,
|
|
15879
|
+
context: { kind: "initial" }
|
|
15880
|
+
});
|
|
15881
|
+
const files = sdkWorkerFiles(input.stateRoot, input.workerName);
|
|
15882
|
+
mkdirSync7(files.dir, { recursive: true });
|
|
15883
|
+
const cliPath = join29(dirname26(runtimeCliPath), "cli.cjs");
|
|
15884
|
+
const spec = {
|
|
15885
|
+
schema_version: 1,
|
|
15886
|
+
leader_cwd: input.leaderCwd,
|
|
15887
|
+
team_name: input.teamName,
|
|
15888
|
+
worker_name: input.workerName,
|
|
15889
|
+
instance_id: input.instanceId,
|
|
15890
|
+
attempt_id: attempt.attempt_id,
|
|
15891
|
+
pane_id: paneId,
|
|
15892
|
+
runtime_cli_path: runtimeCliPath,
|
|
15893
|
+
worker_cwd: input.workerCwd,
|
|
15894
|
+
state_root: input.stateRoot,
|
|
15895
|
+
plugin_root: input.pluginRoot,
|
|
15896
|
+
copilot_bin: input.copilotBin,
|
|
15897
|
+
...input.model ? { model: input.model } : {},
|
|
15898
|
+
max_credits: input.maxCredits,
|
|
15899
|
+
worker_env: { ...input.workerEnv, OMC_WORKER_LAUNCH_ATTEMPT_ID: attempt.attempt_id },
|
|
15900
|
+
deny_tools: input.denyTools,
|
|
15901
|
+
deny_urls: input.denyUrls,
|
|
15902
|
+
mcp_server: input.mcpServer ?? "t",
|
|
15903
|
+
omg_cli_path: cliPath,
|
|
15904
|
+
node_path: process.execPath,
|
|
15905
|
+
user_config_dir: input.userConfigDir
|
|
15906
|
+
};
|
|
15907
|
+
writeFileSync7(files.spec, `${JSON.stringify(spec, null, 2)}
|
|
15908
|
+
`);
|
|
15909
|
+
writeFileSync7(files.doorbell, "");
|
|
15910
|
+
const hostPid = (input.spawnHost ?? spawnDetachedHost)(cliPath, files.spec, files.log, input.workerCwd);
|
|
15911
|
+
const kill = async () => {
|
|
15912
|
+
if (hostPid && isProcessAlive(hostPid)) await killProcessTree(hostPid, "SIGKILL").catch(() => void 0);
|
|
15913
|
+
};
|
|
15914
|
+
const timeoutMs = input.ackTimeoutMs ?? 3e4;
|
|
15915
|
+
const ack = await awaitWorkerLaunchAcknowledgement(attempt, { timeoutMs });
|
|
15916
|
+
if (!ack.ok) {
|
|
15917
|
+
await kill();
|
|
15918
|
+
return { ok: false, attempt, reason: `sdk_host_ack_${ack.reason}`, ...hostPid ? { hostPid } : {} };
|
|
15919
|
+
}
|
|
15920
|
+
const startDeadline = Date.now() + timeoutMs;
|
|
15921
|
+
for (; ; ) {
|
|
15922
|
+
const sliceMs = Math.max(1, Math.min(1e3, startDeadline - Date.now()));
|
|
15923
|
+
if (await awaitWorkerLaunchProviderStarted(attempt, { timeoutMs: sliceMs })) break;
|
|
15924
|
+
if (failedBeforeProviderStarted(attempt, input.stateRoot)) {
|
|
15925
|
+
await kill();
|
|
15926
|
+
const error = readSdkSession(input.stateRoot, input.workerName)?.last_error ?? "unknown";
|
|
15927
|
+
return { ok: false, attempt, reason: `sdk_host_failed_before_start:${error}`, ...hostPid ? { hostPid } : {} };
|
|
15928
|
+
}
|
|
15929
|
+
if (Date.now() >= startDeadline) {
|
|
15930
|
+
await kill();
|
|
15931
|
+
return { ok: false, attempt, reason: "sdk_host_provider_not_started", ...hostPid ? { hostPid } : {} };
|
|
15932
|
+
}
|
|
15933
|
+
}
|
|
15934
|
+
return { ok: true, attempt, hostPid, paneId };
|
|
15935
|
+
}
|
|
15936
|
+
async function isTargetGone(target) {
|
|
15937
|
+
if (!target.identity) return !isProcessAlive(target.pid);
|
|
15938
|
+
const liveness = await isProcessIdentityLive(target.pid, target.identity);
|
|
15939
|
+
return liveness === "dead" || liveness === "mismatch";
|
|
15940
|
+
}
|
|
15941
|
+
function failedBeforeProviderStarted(attempt, stateRoot2) {
|
|
15942
|
+
if (existsSync24(attempt.startedPath)) return false;
|
|
15943
|
+
const session = readSdkSession(stateRoot2, attempt.worker_name);
|
|
15944
|
+
return session?.attempt_id === attempt.attempt_id && session.state === "failed";
|
|
15945
|
+
}
|
|
15946
|
+
async function stopSdkHost(attempt, stateRoot2, opts = {}) {
|
|
15947
|
+
const pollMs = opts.pollMs ?? 250;
|
|
15948
|
+
const exited = async () => failedBeforeProviderStarted(attempt, stateRoot2) || await observeWorkerLaunchProvider(attempt) === "dead";
|
|
15949
|
+
if (await exited()) return true;
|
|
15950
|
+
ringDoorbell(stateRoot2, attempt.worker_name, { kind: opts.kind ?? "stop", from: "leader-fixed" });
|
|
15951
|
+
const deadline = Date.now() + (opts.timeoutMs ?? 45e3);
|
|
15952
|
+
while (Date.now() < deadline) {
|
|
15953
|
+
if (await exited()) return true;
|
|
15954
|
+
await new Promise((r) => setTimeout(r, pollMs));
|
|
15955
|
+
}
|
|
15956
|
+
const started = readStartedRecord(attempt);
|
|
15957
|
+
const session = readSdkSession(stateRoot2, attempt.worker_name);
|
|
15958
|
+
const targets = [];
|
|
15959
|
+
if (started) targets.push({ pid: started.pid, identity: started.process_start_identity });
|
|
15960
|
+
if (session?.attempt_id === attempt.attempt_id && session.runtime_pid && session.runtime_pid > 0) {
|
|
15961
|
+
targets.push({ pid: session.runtime_pid, identity: session.runtime_start_identity ?? null });
|
|
15962
|
+
}
|
|
15963
|
+
for (const target of targets) {
|
|
15964
|
+
if (target.identity && await isProcessIdentityLive(target.pid, target.identity) === "live") {
|
|
15965
|
+
await killProcessTree(target.pid, "SIGKILL").catch(() => void 0);
|
|
15966
|
+
}
|
|
15967
|
+
}
|
|
15968
|
+
const killDeadline = Date.now() + 5e3;
|
|
15969
|
+
for (; ; ) {
|
|
15970
|
+
if ((await Promise.all(targets.map(isTargetGone))).every(Boolean)) {
|
|
15971
|
+
if (started) writeTerminationComplete(attempt, started.pid, started.process_start_identity);
|
|
15972
|
+
return true;
|
|
15973
|
+
}
|
|
15974
|
+
if (Date.now() >= killDeadline) return false;
|
|
15975
|
+
await new Promise((r) => setTimeout(r, pollMs));
|
|
15976
|
+
}
|
|
15977
|
+
}
|
|
15978
|
+
function readStartedRecord(attempt) {
|
|
15979
|
+
try {
|
|
15980
|
+
const record = JSON.parse(readFileSync18(attempt.startedPath, "utf8"));
|
|
15981
|
+
return typeof record.pid === "number" && record.pid > 0 && typeof record.process_start_identity === "string" ? { pid: record.pid, process_start_identity: record.process_start_identity } : null;
|
|
15982
|
+
} catch {
|
|
15983
|
+
return null;
|
|
15984
|
+
}
|
|
15985
|
+
}
|
|
15986
|
+
function writeTerminationComplete(attempt, pid, startIdentity) {
|
|
15987
|
+
const path5 = `${attempt.startedPath}.termination-complete`;
|
|
15988
|
+
if (existsSync24(path5)) return;
|
|
15989
|
+
try {
|
|
15990
|
+
writeFileSync7(path5, JSON.stringify({
|
|
15991
|
+
schema_version: attempt.schema_version,
|
|
15992
|
+
attempt_id: attempt.attempt_id,
|
|
15993
|
+
nonce: attempt.nonce,
|
|
15994
|
+
instance_id: attempt.instance_id,
|
|
15995
|
+
team_name: attempt.team_name,
|
|
15996
|
+
worker_name: attempt.worker_name,
|
|
15997
|
+
pane_id: attempt.pane_id,
|
|
15998
|
+
provider: attempt.provider,
|
|
15999
|
+
created_at: attempt.created_at,
|
|
16000
|
+
kind: "worker_launch_termination_complete",
|
|
16001
|
+
cleanup_verified: true,
|
|
16002
|
+
pid,
|
|
16003
|
+
process_start_identity: startIdentity,
|
|
16004
|
+
...process.platform === "win32" ? {} : { process_group_id: pid },
|
|
16005
|
+
written_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
16006
|
+
}), { flag: "wx" });
|
|
16007
|
+
} catch {
|
|
16008
|
+
}
|
|
16009
|
+
}
|
|
16010
|
+
var SDK_TARGET_PREFIX, SDK_HOST_STOP_FLOOR_MS;
|
|
16011
|
+
var init_sdk_transport = __esm({
|
|
16012
|
+
"src/team/sdk-transport.ts"() {
|
|
16013
|
+
"use strict";
|
|
16014
|
+
init_process_utils();
|
|
16015
|
+
init_runtime_owner_client();
|
|
16016
|
+
init_worker_launch_ack();
|
|
16017
|
+
SDK_TARGET_PREFIX = "sdk:";
|
|
16018
|
+
SDK_HOST_STOP_FLOOR_MS = 5e3;
|
|
16019
|
+
}
|
|
16020
|
+
});
|
|
16021
|
+
|
|
15705
16022
|
// src/team/allocation-policy.ts
|
|
15706
16023
|
function allocateTasksToWorkers(tasks, workers) {
|
|
15707
16024
|
if (tasks.length === 0 || workers.length === 0) return [];
|
|
@@ -15766,8 +16083,8 @@ var init_allocation_policy = __esm({
|
|
|
15766
16083
|
|
|
15767
16084
|
// src/team/mailbox-outstanding.ts
|
|
15768
16085
|
import { readFile as readFile11, readdir as readdir5 } from "fs/promises";
|
|
15769
|
-
import { existsSync as
|
|
15770
|
-
import { join as
|
|
16086
|
+
import { existsSync as existsSync25 } from "fs";
|
|
16087
|
+
import { join as join30 } from "path";
|
|
15771
16088
|
function safeString(value) {
|
|
15772
16089
|
return typeof value === "string" ? value : "";
|
|
15773
16090
|
}
|
|
@@ -15791,7 +16108,7 @@ function messageToWorker(message) {
|
|
|
15791
16108
|
}
|
|
15792
16109
|
async function readMailboxFileMessages(filePath) {
|
|
15793
16110
|
try {
|
|
15794
|
-
if (!
|
|
16111
|
+
if (!existsSync25(filePath)) return [];
|
|
15795
16112
|
const raw = await readFile11(filePath, "utf-8");
|
|
15796
16113
|
const parsed = JSON.parse(raw);
|
|
15797
16114
|
if (Array.isArray(parsed)) return parsed;
|
|
@@ -15805,7 +16122,7 @@ async function readMailboxFileMessages(filePath) {
|
|
|
15805
16122
|
}
|
|
15806
16123
|
async function readMailboxFileMessagesJsonl(filePath) {
|
|
15807
16124
|
try {
|
|
15808
|
-
if (!
|
|
16125
|
+
if (!existsSync25(filePath)) return [];
|
|
15809
16126
|
const raw = await readFile11(filePath, "utf-8");
|
|
15810
16127
|
const messages = [];
|
|
15811
16128
|
for (const line of raw.split("\n")) {
|
|
@@ -15835,10 +16152,10 @@ async function scanMailboxOutstanding(mailboxDir) {
|
|
|
15835
16152
|
let messages = [];
|
|
15836
16153
|
if (entry.endsWith(".json")) {
|
|
15837
16154
|
recipient = entry.slice(0, -".json".length);
|
|
15838
|
-
messages = await readMailboxFileMessages(
|
|
16155
|
+
messages = await readMailboxFileMessages(join30(mailboxDir, entry));
|
|
15839
16156
|
} else if (entry.endsWith(".jsonl")) {
|
|
15840
16157
|
recipient = entry.slice(0, -".jsonl".length);
|
|
15841
|
-
messages = await readMailboxFileMessagesJsonl(
|
|
16158
|
+
messages = await readMailboxFileMessagesJsonl(join30(mailboxDir, entry));
|
|
15842
16159
|
} else {
|
|
15843
16160
|
continue;
|
|
15844
16161
|
}
|
|
@@ -15883,19 +16200,19 @@ var init_mailbox_outstanding = __esm({
|
|
|
15883
16200
|
});
|
|
15884
16201
|
|
|
15885
16202
|
// src/team/events.ts
|
|
15886
|
-
import { randomUUID as
|
|
15887
|
-
import { dirname as
|
|
16203
|
+
import { randomUUID as randomUUID16 } from "crypto";
|
|
16204
|
+
import { dirname as dirname27 } from "path";
|
|
15888
16205
|
import { mkdir as mkdir10, readFile as readFile12, appendFile as appendFile3 } from "fs/promises";
|
|
15889
|
-
import { existsSync as
|
|
16206
|
+
import { existsSync as existsSync26 } from "fs";
|
|
15890
16207
|
async function appendTeamEvent(teamName, event, cwd) {
|
|
15891
16208
|
const full = {
|
|
15892
|
-
event_id:
|
|
16209
|
+
event_id: randomUUID16(),
|
|
15893
16210
|
team: teamName,
|
|
15894
16211
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15895
16212
|
...event
|
|
15896
16213
|
};
|
|
15897
16214
|
const p = absPath(cwd, TeamPaths.events(teamName));
|
|
15898
|
-
await mkdir10(
|
|
16215
|
+
await mkdir10(dirname27(p), { recursive: true });
|
|
15899
16216
|
await appendFile3(p, `${JSON.stringify(full)}
|
|
15900
16217
|
`, "utf8");
|
|
15901
16218
|
return full;
|
|
@@ -15938,7 +16255,7 @@ async function emitMonitorDerivedEvents(teamName, tasks, workers, previousSnapsh
|
|
|
15938
16255
|
}, cwd).catch(logDerivedEventFailure);
|
|
15939
16256
|
}
|
|
15940
16257
|
if (prevState === "working" && worker.status.state === "idle") {
|
|
15941
|
-
const mailboxDir =
|
|
16258
|
+
const mailboxDir = dirname27(absPath(cwd, TeamPaths.mailbox(teamName, worker.name)));
|
|
15942
16259
|
const outstanding = await countOutstandingForWorker(mailboxDir, worker.name);
|
|
15943
16260
|
await appendTeamEvent(teamName, {
|
|
15944
16261
|
type: "worker_idle",
|
|
@@ -16002,9 +16319,9 @@ var init_phase_controller = __esm({
|
|
|
16002
16319
|
});
|
|
16003
16320
|
|
|
16004
16321
|
// src/lib/worktree-cleanup-safety.ts
|
|
16005
|
-
import { existsSync as
|
|
16322
|
+
import { existsSync as existsSync27, lstatSync as lstatSync5, realpathSync as realpathSync5 } from "node:fs";
|
|
16006
16323
|
import { homedir as homedir6 } from "node:os";
|
|
16007
|
-
import { isAbsolute as isAbsolute8, join as
|
|
16324
|
+
import { isAbsolute as isAbsolute8, join as join31, parse as parse3, relative as relative8, resolve as resolve9 } from "node:path";
|
|
16008
16325
|
function realpathOrResolve(path5) {
|
|
16009
16326
|
try {
|
|
16010
16327
|
return realpathSync5(path5);
|
|
@@ -16051,7 +16368,7 @@ function validateWorktreeRemovalTarget(options) {
|
|
|
16051
16368
|
throw new Error(`worktree_path_suspicious:${rawCandidate}`);
|
|
16052
16369
|
}
|
|
16053
16370
|
const lexicalPath = resolve9(rawCandidate);
|
|
16054
|
-
if (!
|
|
16371
|
+
if (!existsSync27(lexicalPath)) {
|
|
16055
16372
|
if (requireExisting) {
|
|
16056
16373
|
throw new Error(`worktree_path_missing:${lexicalPath}`);
|
|
16057
16374
|
}
|
|
@@ -16076,8 +16393,8 @@ function validateWorktreeRemovalTarget(options) {
|
|
|
16076
16393
|
throw new Error(`worktree_path_is_main_repo:${resolvedPath}`);
|
|
16077
16394
|
}
|
|
16078
16395
|
}
|
|
16079
|
-
if (
|
|
16080
|
-
const gitStat = lstatSync5(
|
|
16396
|
+
if (existsSync27(join31(resolvedPath, ".git"))) {
|
|
16397
|
+
const gitStat = lstatSync5(join31(resolvedPath, ".git"));
|
|
16081
16398
|
if (gitStat.isDirectory()) {
|
|
16082
16399
|
throw new Error(`worktree_path_is_main_repo:${resolvedPath}`);
|
|
16083
16400
|
}
|
|
@@ -16091,11 +16408,11 @@ var init_worktree_cleanup_safety = __esm({
|
|
|
16091
16408
|
});
|
|
16092
16409
|
|
|
16093
16410
|
// src/team/git-worktree.ts
|
|
16094
|
-
import { existsSync as
|
|
16095
|
-
import { join as
|
|
16411
|
+
import { existsSync as existsSync28, realpathSync as realpathSync6, readFileSync as readFileSync19, readdirSync as readdirSync11, rmSync as rmSync3, unlinkSync as unlinkSync9, writeFileSync as writeFileSync8 } from "node:fs";
|
|
16412
|
+
import { join as join32, resolve as resolve10 } from "node:path";
|
|
16096
16413
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
16097
16414
|
function getWorktreePath(repoRoot, teamName, workerName) {
|
|
16098
|
-
return
|
|
16415
|
+
return join32(getOmcRoot(repoRoot), "team", sanitizeName(teamName), "worktrees", sanitizeName(workerName));
|
|
16099
16416
|
}
|
|
16100
16417
|
function getBranchName(teamName, workerName) {
|
|
16101
16418
|
return `omc-team/${sanitizeName(teamName)}/${sanitizeName(workerName)}`;
|
|
@@ -16192,22 +16509,22 @@ function isWorktreeDirtyExcept(wtPath, ignoredRootPaths = []) {
|
|
|
16192
16509
|
}
|
|
16193
16510
|
}
|
|
16194
16511
|
function getMetadataPath(repoRoot, teamName) {
|
|
16195
|
-
return
|
|
16512
|
+
return join32(getOmcRoot(repoRoot), "state", "team", sanitizeName(teamName), "worktrees.json");
|
|
16196
16513
|
}
|
|
16197
16514
|
function getLegacyMetadataPath(repoRoot, teamName) {
|
|
16198
|
-
return
|
|
16515
|
+
return join32(getOmcRoot(repoRoot), "state", "team-bridge", sanitizeName(teamName), "worktrees.json");
|
|
16199
16516
|
}
|
|
16200
16517
|
function getWorkerStateDir(repoRoot, teamName, workerName) {
|
|
16201
|
-
return
|
|
16518
|
+
return join32(getOmcRoot(repoRoot), "state", "team", sanitizeName(teamName), "workers", sanitizeName(workerName));
|
|
16202
16519
|
}
|
|
16203
16520
|
function getRootAgentsBackupPath(repoRoot, teamName, workerName) {
|
|
16204
|
-
return
|
|
16521
|
+
return join32(getWorkerStateDir(repoRoot, teamName, workerName), "worktree-root-agents.json");
|
|
16205
16522
|
}
|
|
16206
16523
|
function readRootAgentsBackup(repoRoot, teamName, workerName) {
|
|
16207
16524
|
const backupPath = getRootAgentsBackupPath(repoRoot, teamName, workerName);
|
|
16208
|
-
if (!
|
|
16525
|
+
if (!existsSync28(backupPath)) return null;
|
|
16209
16526
|
try {
|
|
16210
|
-
return JSON.parse(
|
|
16527
|
+
return JSON.parse(readFileSync19(backupPath, "utf-8"));
|
|
16211
16528
|
} catch (err) {
|
|
16212
16529
|
const msg = err instanceof Error ? err.message : String(err);
|
|
16213
16530
|
process.stderr.write(`[omc] warning: worktree root AGENTS backup parse error: ${msg}
|
|
@@ -16220,13 +16537,13 @@ function readRootAgentsBackup(repoRoot, teamName, workerName) {
|
|
|
16220
16537
|
function installWorktreeRootAgents(teamName, workerName, repoRoot, worktreePath, overlayContent) {
|
|
16221
16538
|
const omcRoot = getOmcRoot(repoRoot);
|
|
16222
16539
|
validateResolvedPath(worktreePath, omcRoot);
|
|
16223
|
-
const agentsPath =
|
|
16540
|
+
const agentsPath = join32(worktreePath, "AGENTS.md");
|
|
16224
16541
|
validateResolvedPath(agentsPath, worktreePath);
|
|
16225
16542
|
const backupPath = getRootAgentsBackupPath(repoRoot, teamName, workerName);
|
|
16226
16543
|
validateResolvedPath(backupPath, omcRoot);
|
|
16227
16544
|
ensureDirWithMode(getWorkerStateDir(repoRoot, teamName, workerName));
|
|
16228
16545
|
const previous = readRootAgentsBackup(repoRoot, teamName, workerName);
|
|
16229
|
-
const currentContent =
|
|
16546
|
+
const currentContent = existsSync28(agentsPath) ? readFileSync19(agentsPath, "utf-8") : void 0;
|
|
16230
16547
|
if (previous && currentContent !== void 0 && currentContent !== previous.installedContent) {
|
|
16231
16548
|
const error = new Error(`agents_dirty: preserving modified worktree root AGENTS.md at ${agentsPath}`);
|
|
16232
16549
|
error.code = "agents_dirty";
|
|
@@ -16240,7 +16557,7 @@ function installWorktreeRootAgents(teamName, workerName, repoRoot, worktreePath,
|
|
|
16240
16557
|
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
16241
16558
|
};
|
|
16242
16559
|
atomicWriteJson(backupPath, backup);
|
|
16243
|
-
|
|
16560
|
+
writeFileSync8(agentsPath, overlayContent, "utf-8");
|
|
16244
16561
|
}
|
|
16245
16562
|
function restoreWorktreeRootAgents(teamName, workerName, repoRoot, worktreePath) {
|
|
16246
16563
|
const omcRoot = getOmcRoot(repoRoot);
|
|
@@ -16250,23 +16567,23 @@ function restoreWorktreeRootAgents(teamName, workerName, repoRoot, worktreePath)
|
|
|
16250
16567
|
if (!backup) return { restored: false, reason: "no_backup" };
|
|
16251
16568
|
const resolvedWorktreePath = worktreePath ?? backup.worktreePath;
|
|
16252
16569
|
validateResolvedPath(resolvedWorktreePath, omcRoot);
|
|
16253
|
-
if (!
|
|
16570
|
+
if (!existsSync28(resolvedWorktreePath)) {
|
|
16254
16571
|
try {
|
|
16255
16572
|
unlinkSync9(backupPath);
|
|
16256
16573
|
} catch {
|
|
16257
16574
|
}
|
|
16258
16575
|
return { restored: false, reason: "worktree_missing" };
|
|
16259
16576
|
}
|
|
16260
|
-
const agentsPath =
|
|
16577
|
+
const agentsPath = join32(resolvedWorktreePath, "AGENTS.md");
|
|
16261
16578
|
validateResolvedPath(agentsPath, resolvedWorktreePath);
|
|
16262
|
-
const currentContent =
|
|
16579
|
+
const currentContent = existsSync28(agentsPath) ? readFileSync19(agentsPath, "utf-8") : void 0;
|
|
16263
16580
|
const isPartialInstallOriginal = backup.hadOriginal && currentContent === (backup.originalContent ?? "");
|
|
16264
16581
|
if (currentContent !== void 0 && currentContent !== backup.installedContent && !isPartialInstallOriginal) {
|
|
16265
16582
|
return { restored: false, reason: "agents_dirty" };
|
|
16266
16583
|
}
|
|
16267
16584
|
if (backup.hadOriginal) {
|
|
16268
|
-
|
|
16269
|
-
} else if (
|
|
16585
|
+
writeFileSync8(agentsPath, backup.originalContent ?? "", "utf-8");
|
|
16586
|
+
} else if (existsSync28(agentsPath)) {
|
|
16270
16587
|
unlinkSync9(agentsPath);
|
|
16271
16588
|
}
|
|
16272
16589
|
try {
|
|
@@ -16280,9 +16597,9 @@ function readMetadataResult(repoRoot, teamName) {
|
|
|
16280
16597
|
const byWorker = /* @__PURE__ */ new Map();
|
|
16281
16598
|
const issues = [];
|
|
16282
16599
|
for (const metaPath of paths) {
|
|
16283
|
-
if (!
|
|
16600
|
+
if (!existsSync28(metaPath)) continue;
|
|
16284
16601
|
try {
|
|
16285
|
-
const entries = JSON.parse(
|
|
16602
|
+
const entries = JSON.parse(readFileSync19(metaPath, "utf-8"));
|
|
16286
16603
|
for (const entry of entries) byWorker.set(entry.workerName, entry);
|
|
16287
16604
|
} catch (err) {
|
|
16288
16605
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -16297,15 +16614,15 @@ function readMetadata(repoRoot, teamName) {
|
|
|
16297
16614
|
return readMetadataResult(repoRoot, teamName).entries;
|
|
16298
16615
|
}
|
|
16299
16616
|
function listRootAgentsBackupIssues(repoRoot, teamName, entries) {
|
|
16300
|
-
const workersDir =
|
|
16301
|
-
if (!
|
|
16617
|
+
const workersDir = join32(getOmcRoot(repoRoot), "state", "team", sanitizeName(teamName), "workers");
|
|
16618
|
+
if (!existsSync28(workersDir)) return [];
|
|
16302
16619
|
const knownWorkers = new Set(entries.map((entry) => sanitizeName(entry.workerName)));
|
|
16303
16620
|
const issues = [];
|
|
16304
16621
|
for (const workerName of readdirSync11(workersDir)) {
|
|
16305
|
-
const backupPath =
|
|
16306
|
-
if (!
|
|
16622
|
+
const backupPath = join32(workersDir, workerName, "worktree-root-agents.json");
|
|
16623
|
+
if (!existsSync28(backupPath)) continue;
|
|
16307
16624
|
try {
|
|
16308
|
-
JSON.parse(
|
|
16625
|
+
JSON.parse(readFileSync19(backupPath, "utf-8"));
|
|
16309
16626
|
} catch (error) {
|
|
16310
16627
|
const message = error instanceof Error ? error.message : String(error);
|
|
16311
16628
|
issues.push({ path: backupPath, message: `worktree_root_agents_backup_unreadable:${workerName}:${message}` });
|
|
@@ -16322,8 +16639,8 @@ function listRootAgentsBackupIssues(repoRoot, teamName, entries) {
|
|
|
16322
16639
|
}
|
|
16323
16640
|
function writeMetadata(repoRoot, teamName, entries) {
|
|
16324
16641
|
const metaPath = getMetadataPath(repoRoot, teamName);
|
|
16325
|
-
validateResolvedPath(metaPath,
|
|
16326
|
-
ensureDirWithMode(
|
|
16642
|
+
validateResolvedPath(metaPath, join32(getOmcRoot(repoRoot), "state", "team"));
|
|
16643
|
+
ensureDirWithMode(join32(getOmcRoot(repoRoot), "state", "team", sanitizeName(teamName)));
|
|
16327
16644
|
atomicWriteJson(metaPath, entries);
|
|
16328
16645
|
}
|
|
16329
16646
|
function recordMetadata(repoRoot, teamName, info) {
|
|
@@ -16378,12 +16695,12 @@ function ensureWorkerWorktree(teamName, workerName, repoRoot, options = {}) {
|
|
|
16378
16695
|
}
|
|
16379
16696
|
const wtPath = getWorktreePath(repoRoot, teamName, workerName);
|
|
16380
16697
|
const branch = mode === "named" ? getBranchName(teamName, workerName) : "HEAD";
|
|
16381
|
-
validateResolvedPath(wtPath,
|
|
16698
|
+
validateResolvedPath(wtPath, join32(getOmcRoot(repoRoot), "team"));
|
|
16382
16699
|
try {
|
|
16383
16700
|
execFileSync5("git", ["worktree", "prune"], { cwd: repoRoot, stdio: "pipe", windowsHide: true });
|
|
16384
16701
|
} catch {
|
|
16385
16702
|
}
|
|
16386
|
-
if (
|
|
16703
|
+
if (existsSync28(wtPath)) {
|
|
16387
16704
|
assertCompatibleExistingWorktree(repoRoot, wtPath, branch, mode);
|
|
16388
16705
|
const info2 = {
|
|
16389
16706
|
path: wtPath,
|
|
@@ -16400,7 +16717,7 @@ function ensureWorkerWorktree(teamName, workerName, repoRoot, options = {}) {
|
|
|
16400
16717
|
recordMetadata(repoRoot, teamName, info2);
|
|
16401
16718
|
return info2;
|
|
16402
16719
|
}
|
|
16403
|
-
const wtDir =
|
|
16720
|
+
const wtDir = join32(getOmcRoot(repoRoot), "team", sanitizeName(teamName), "worktrees");
|
|
16404
16721
|
ensureDirWithMode(wtDir);
|
|
16405
16722
|
const args = mode === "named" ? ["worktree", "add", "-b", branch, wtPath, options.baseRef ?? "HEAD"] : ["worktree", "add", "--detach", wtPath, options.baseRef ?? "HEAD"];
|
|
16406
16723
|
execFileSync5("git", args, { cwd: repoRoot, stdio: "pipe", windowsHide: true });
|
|
@@ -16422,17 +16739,17 @@ function ensureWorkerWorktree(teamName, workerName, repoRoot, options = {}) {
|
|
|
16422
16739
|
function checkWorkerWorktreeRemovalSafety(teamName, workerName, repoRoot, worktreePath) {
|
|
16423
16740
|
const wtPath = worktreePath ?? getWorktreePath(repoRoot, teamName, workerName);
|
|
16424
16741
|
const backup = readRootAgentsBackup(repoRoot, teamName, workerName);
|
|
16425
|
-
if (!
|
|
16742
|
+
if (!existsSync28(wtPath)) return;
|
|
16426
16743
|
validateWorktreeRemovalTarget({
|
|
16427
16744
|
candidatePath: wtPath,
|
|
16428
|
-
expectedRoots: [
|
|
16745
|
+
expectedRoots: [join32(getOmcRoot(repoRoot), "team", sanitizeName(teamName), "worktrees")],
|
|
16429
16746
|
mainRepoRoots: [repoRoot]
|
|
16430
16747
|
});
|
|
16431
16748
|
let ignoreRootAgents = false;
|
|
16432
16749
|
if (backup) {
|
|
16433
|
-
const agentsPath =
|
|
16750
|
+
const agentsPath = join32(wtPath, "AGENTS.md");
|
|
16434
16751
|
validateResolvedPath(agentsPath, wtPath);
|
|
16435
|
-
const currentContent =
|
|
16752
|
+
const currentContent = existsSync28(agentsPath) ? readFileSync19(agentsPath, "utf-8") : void 0;
|
|
16436
16753
|
const isPartialInstallOriginal = backup.hadOriginal && currentContent === (backup.originalContent ?? "");
|
|
16437
16754
|
if (currentContent !== void 0 && currentContent !== backup.installedContent && !isPartialInstallOriginal) {
|
|
16438
16755
|
const error = new Error(`agents_dirty: preserving modified worktree root AGENTS.md at ${agentsPath}`);
|
|
@@ -16453,7 +16770,7 @@ function prepareWorkerWorktreeForRemoval(teamName, workerName, repoRoot, worktre
|
|
|
16453
16770
|
checkWorkerWorktreeRemovalSafety(teamName, workerName, repoRoot, wtPath);
|
|
16454
16771
|
const agentsRestore = restoreWorktreeRootAgents(teamName, workerName, repoRoot, wtPath);
|
|
16455
16772
|
if (agentsRestore.reason === "agents_dirty") {
|
|
16456
|
-
const error = new Error(`agents_dirty: preserving modified worktree root AGENTS.md at ${
|
|
16773
|
+
const error = new Error(`agents_dirty: preserving modified worktree root AGENTS.md at ${join32(wtPath, "AGENTS.md")}`);
|
|
16457
16774
|
error.code = "agents_dirty";
|
|
16458
16775
|
throw error;
|
|
16459
16776
|
}
|
|
@@ -16483,13 +16800,13 @@ function removeWorkerWorktree(teamName, workerName, repoRoot) {
|
|
|
16483
16800
|
execFileSync5("git", ["branch", "-D", branch], { cwd: repoRoot, stdio: "pipe", windowsHide: true });
|
|
16484
16801
|
} catch {
|
|
16485
16802
|
}
|
|
16486
|
-
if (
|
|
16803
|
+
if (existsSync28(wtPath) && !isRegisteredWorktreePath(repoRoot, wtPath)) {
|
|
16487
16804
|
validateWorktreeRemovalTarget({
|
|
16488
16805
|
candidatePath: wtPath,
|
|
16489
|
-
expectedRoots: [
|
|
16806
|
+
expectedRoots: [join32(getOmcRoot(repoRoot), "team", sanitizeName(teamName), "worktrees")],
|
|
16490
16807
|
mainRepoRoots: [repoRoot]
|
|
16491
16808
|
});
|
|
16492
|
-
|
|
16809
|
+
rmSync3(wtPath, { recursive: true, force: true });
|
|
16493
16810
|
}
|
|
16494
16811
|
forgetMetadataUnlocked(repoRoot, teamName, workerName);
|
|
16495
16812
|
});
|
|
@@ -17035,8 +17352,8 @@ var init_runtime_flags = __esm({
|
|
|
17035
17352
|
|
|
17036
17353
|
// src/team/merge-coordinator.ts
|
|
17037
17354
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
17038
|
-
import { appendFileSync, mkdirSync as
|
|
17039
|
-
import { isAbsolute as isAbsolute9, join as
|
|
17355
|
+
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync8, readFileSync as readFileSync20 } from "node:fs";
|
|
17356
|
+
import { isAbsolute as isAbsolute9, join as join33 } from "node:path";
|
|
17040
17357
|
function validateBranchName(branch) {
|
|
17041
17358
|
if (!BRANCH_NAME_RE.test(branch)) {
|
|
17042
17359
|
throw new Error(`Invalid branch name: "${branch}" \u2014 must match ${BRANCH_NAME_RE}`);
|
|
@@ -17054,13 +17371,13 @@ function configureHarnessMergeAttributes(repoRoot) {
|
|
|
17054
17371
|
stdio: "pipe",
|
|
17055
17372
|
windowsHide: true
|
|
17056
17373
|
}).trim();
|
|
17057
|
-
const resolvedCommonDir = isAbsolute9(commonDir) ? commonDir :
|
|
17058
|
-
const infoDir =
|
|
17059
|
-
|
|
17060
|
-
const attrPath =
|
|
17374
|
+
const resolvedCommonDir = isAbsolute9(commonDir) ? commonDir : join33(repoRoot, commonDir);
|
|
17375
|
+
const infoDir = join33(resolvedCommonDir, "info");
|
|
17376
|
+
mkdirSync8(infoDir, { recursive: true });
|
|
17377
|
+
const attrPath = join33(infoDir, "attributes");
|
|
17061
17378
|
let existing = "";
|
|
17062
17379
|
try {
|
|
17063
|
-
existing =
|
|
17380
|
+
existing = readFileSync20(attrPath, "utf-8");
|
|
17064
17381
|
} catch {
|
|
17065
17382
|
}
|
|
17066
17383
|
const existingLines = new Set(existing.split("\n").map((l) => l.trim()));
|
|
@@ -17069,7 +17386,7 @@ function configureHarnessMergeAttributes(repoRoot) {
|
|
|
17069
17386
|
);
|
|
17070
17387
|
if (missing.length === 0) return;
|
|
17071
17388
|
const prefix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
17072
|
-
|
|
17389
|
+
appendFileSync2(attrPath, `${prefix}${missing.join("\n")}
|
|
17073
17390
|
`, "utf-8");
|
|
17074
17391
|
}
|
|
17075
17392
|
function checkMergeConflicts(workerBranch, baseBranch, repoRoot) {
|
|
@@ -17181,17 +17498,17 @@ var init_merge_coordinator = __esm({
|
|
|
17181
17498
|
|
|
17182
17499
|
// src/team/leader-inbox.ts
|
|
17183
17500
|
import { appendFile as appendFile4, mkdir as mkdir11, writeFile as writeFile8 } from "fs/promises";
|
|
17184
|
-
import { existsSync as
|
|
17185
|
-
import { dirname as
|
|
17501
|
+
import { existsSync as existsSync29 } from "fs";
|
|
17502
|
+
import { dirname as dirname28, join as join34 } from "path";
|
|
17186
17503
|
function leaderInboxPath(teamName, cwd) {
|
|
17187
17504
|
const safe = sanitizeName(teamName);
|
|
17188
|
-
return
|
|
17505
|
+
return join34(teamStateRoot(cwd, safe), "leader", "inbox.md");
|
|
17189
17506
|
}
|
|
17190
17507
|
async function ensureLeaderInbox(teamName, cwd) {
|
|
17191
17508
|
const inboxPath = leaderInboxPath(teamName, cwd);
|
|
17192
17509
|
validateResolvedPath(inboxPath, teamStateRoot(cwd, sanitizeName(teamName)));
|
|
17193
|
-
await mkdir11(
|
|
17194
|
-
if (!
|
|
17510
|
+
await mkdir11(dirname28(inboxPath), { recursive: true });
|
|
17511
|
+
if (!existsSync29(inboxPath)) {
|
|
17195
17512
|
await writeFile8(inboxPath, LEADER_INBOX_HEADER, "utf-8");
|
|
17196
17513
|
}
|
|
17197
17514
|
return inboxPath;
|
|
@@ -17199,7 +17516,7 @@ async function ensureLeaderInbox(teamName, cwd) {
|
|
|
17199
17516
|
async function appendToLeaderInbox(teamName, message, cwd) {
|
|
17200
17517
|
const inboxPath = leaderInboxPath(teamName, cwd);
|
|
17201
17518
|
validateResolvedPath(inboxPath, teamStateRoot(cwd, sanitizeName(teamName)));
|
|
17202
|
-
await mkdir11(
|
|
17519
|
+
await mkdir11(dirname28(inboxPath), { recursive: true });
|
|
17203
17520
|
await appendFile4(inboxPath, `
|
|
17204
17521
|
|
|
17205
17522
|
---
|
|
@@ -17207,7 +17524,7 @@ ${message}`, "utf-8");
|
|
|
17207
17524
|
}
|
|
17208
17525
|
function extendLeaderBootstrapPrompt(teamName, cwd) {
|
|
17209
17526
|
const safe = sanitizeName(teamName);
|
|
17210
|
-
const path5 = cwd ?
|
|
17527
|
+
const path5 = cwd ? join34(teamStateRoot(cwd, safe), "leader", "inbox.md") : `.omg/state/team/${safe}/leader/inbox.md`;
|
|
17211
17528
|
return `Runtime notifications appear at ${path5} \u2014 check this file periodically and after long-running operations.`;
|
|
17212
17529
|
}
|
|
17213
17530
|
var LEADER_INBOX_HEADER;
|
|
@@ -17287,9 +17604,9 @@ var init_conflict_mailbox = __esm({
|
|
|
17287
17604
|
});
|
|
17288
17605
|
|
|
17289
17606
|
// src/team/worker-commit-cadence.ts
|
|
17290
|
-
import { existsSync as
|
|
17607
|
+
import { existsSync as existsSync30, watch as fsWatch } from "fs";
|
|
17291
17608
|
import { readFile as readFile13, writeFile as writeFile9, mkdir as mkdir12, unlink as unlink6 } from "fs/promises";
|
|
17292
|
-
import { join as
|
|
17609
|
+
import { join as join35, dirname as dirname29 } from "path";
|
|
17293
17610
|
import { exec } from "child_process";
|
|
17294
17611
|
function ownsCadence(ctx) {
|
|
17295
17612
|
const current = cadenceOwners.get(ctx.worktreePath);
|
|
@@ -17347,27 +17664,27 @@ async function installPostToolUseHook(worktreePath, workerName) {
|
|
|
17347
17664
|
if (isHookPaused(worktreePath)) {
|
|
17348
17665
|
return;
|
|
17349
17666
|
}
|
|
17350
|
-
const claudeDir =
|
|
17667
|
+
const claudeDir = join35(worktreePath, ".copilot");
|
|
17351
17668
|
await mkdir12(claudeDir, { recursive: true });
|
|
17352
|
-
const settingsPath =
|
|
17669
|
+
const settingsPath = join35(claudeDir, "settings.json");
|
|
17353
17670
|
const hookCommand = buildHookCommand(workerName);
|
|
17354
17671
|
const merged = await mergeSettingsWithHook(settingsPath, hookCommand);
|
|
17355
17672
|
await writeFile9(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
17356
17673
|
}
|
|
17357
17674
|
async function pauseHookViaSentinel(worktreePath) {
|
|
17358
|
-
const sentinelPath =
|
|
17359
|
-
await mkdir12(
|
|
17675
|
+
const sentinelPath = join35(worktreePath, SENTINEL_FILENAME);
|
|
17676
|
+
await mkdir12(dirname29(sentinelPath), { recursive: true });
|
|
17360
17677
|
await writeFile9(sentinelPath, "", "utf-8");
|
|
17361
17678
|
}
|
|
17362
17679
|
async function resumeHookViaSentinel(worktreePath) {
|
|
17363
|
-
const sentinelPath =
|
|
17680
|
+
const sentinelPath = join35(worktreePath, SENTINEL_FILENAME);
|
|
17364
17681
|
try {
|
|
17365
17682
|
await unlink6(sentinelPath);
|
|
17366
17683
|
} catch {
|
|
17367
17684
|
}
|
|
17368
17685
|
}
|
|
17369
17686
|
function isHookPaused(worktreePath) {
|
|
17370
|
-
return
|
|
17687
|
+
return existsSync30(join35(worktreePath, SENTINEL_FILENAME));
|
|
17371
17688
|
}
|
|
17372
17689
|
function startFallbackPoller(worktreePath, workerName, opts) {
|
|
17373
17690
|
assertSafeWorkerName(workerName);
|
|
@@ -17426,7 +17743,7 @@ async function uninstallCommitCadence(ctx, io = { readFile: readFile13, writeFil
|
|
|
17426
17743
|
if (ownsRegisteredGeneration) cadenceOwners.delete(ctx.worktreePath);
|
|
17427
17744
|
return;
|
|
17428
17745
|
}
|
|
17429
|
-
const settingsPath =
|
|
17746
|
+
const settingsPath = join35(ctx.worktreePath, ".copilot", "settings.json");
|
|
17430
17747
|
let raw;
|
|
17431
17748
|
try {
|
|
17432
17749
|
raw = await io.readFile(settingsPath, "utf-8");
|
|
@@ -17465,14 +17782,14 @@ var init_worker_commit_cadence = __esm({
|
|
|
17465
17782
|
|
|
17466
17783
|
// src/team/merge-orchestrator.ts
|
|
17467
17784
|
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
17468
|
-
import { existsSync as
|
|
17785
|
+
import { existsSync as existsSync31 } from "node:fs";
|
|
17469
17786
|
import { mkdir as mkdir13, appendFile as appendFile5 } from "node:fs/promises";
|
|
17470
|
-
import { dirname as
|
|
17787
|
+
import { dirname as dirname30, join as join36 } from "node:path";
|
|
17471
17788
|
function mergerWorktreePathFor(repoRoot, teamName) {
|
|
17472
|
-
return
|
|
17789
|
+
return join36(getOmcRoot(repoRoot), "team", sanitizeName(teamName), "merger");
|
|
17473
17790
|
}
|
|
17474
17791
|
function persistedStatePath(repoRoot, teamName) {
|
|
17475
|
-
return
|
|
17792
|
+
return join36(
|
|
17476
17793
|
getOmcRoot(repoRoot),
|
|
17477
17794
|
"state",
|
|
17478
17795
|
"team",
|
|
@@ -17481,7 +17798,7 @@ function persistedStatePath(repoRoot, teamName) {
|
|
|
17481
17798
|
);
|
|
17482
17799
|
}
|
|
17483
17800
|
function teardownAuditPath(repoRoot, teamName) {
|
|
17484
|
-
return
|
|
17801
|
+
return join36(
|
|
17485
17802
|
getOmcRoot(repoRoot),
|
|
17486
17803
|
"state",
|
|
17487
17804
|
"team",
|
|
@@ -17490,7 +17807,7 @@ function teardownAuditPath(repoRoot, teamName) {
|
|
|
17490
17807
|
);
|
|
17491
17808
|
}
|
|
17492
17809
|
function orchestratorEventLogPath(repoRoot, teamName) {
|
|
17493
|
-
return
|
|
17810
|
+
return join36(
|
|
17494
17811
|
getOmcRoot(repoRoot),
|
|
17495
17812
|
"state",
|
|
17496
17813
|
"team",
|
|
@@ -17511,7 +17828,7 @@ function assertRuntimeV2Gate() {
|
|
|
17511
17828
|
}
|
|
17512
17829
|
async function appendEvent(repoRoot, teamName, event) {
|
|
17513
17830
|
const path5 = orchestratorEventLogPath(repoRoot, teamName);
|
|
17514
|
-
await mkdir13(
|
|
17831
|
+
await mkdir13(dirname30(path5), { recursive: true });
|
|
17515
17832
|
const full = {
|
|
17516
17833
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17517
17834
|
team: teamName,
|
|
@@ -17547,10 +17864,10 @@ function gitPath(worktreePath, gitPathName) {
|
|
|
17547
17864
|
if (resolved) return resolved;
|
|
17548
17865
|
} catch {
|
|
17549
17866
|
}
|
|
17550
|
-
return
|
|
17867
|
+
return join36(worktreePath, ".git", gitPathName);
|
|
17551
17868
|
}
|
|
17552
17869
|
function isRebaseInProgress(worktreePath) {
|
|
17553
|
-
return
|
|
17870
|
+
return existsSync31(gitPath(worktreePath, "rebase-merge"));
|
|
17554
17871
|
}
|
|
17555
17872
|
function isWorktreeRegistered(repoRoot, wtPath) {
|
|
17556
17873
|
try {
|
|
@@ -17570,8 +17887,8 @@ function isWorktreeRegistered(repoRoot, wtPath) {
|
|
|
17570
17887
|
return false;
|
|
17571
17888
|
}
|
|
17572
17889
|
function ensureMergerWorktree(repoRoot, mergerPath, leaderBranch) {
|
|
17573
|
-
ensureDirWithMode(
|
|
17574
|
-
if (
|
|
17890
|
+
ensureDirWithMode(dirname30(mergerPath));
|
|
17891
|
+
if (existsSync31(mergerPath) && isWorktreeRegistered(repoRoot, mergerPath)) {
|
|
17575
17892
|
return;
|
|
17576
17893
|
}
|
|
17577
17894
|
execFileSync7("git", ["worktree", "add", "--force", mergerPath, leaderBranch], {
|
|
@@ -17613,16 +17930,16 @@ async function startMergeOrchestrator(config) {
|
|
|
17613
17930
|
const pollIntervalMs = config.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
|
|
17614
17931
|
const drainTimeoutMs = config.drainTimeoutMs ?? DEFAULT_DRAIN_TIMEOUT_MS;
|
|
17615
17932
|
const mergerPath = mergerWorktreePathFor(config.repoRoot, config.teamName);
|
|
17616
|
-
validateResolvedPath(mergerPath,
|
|
17933
|
+
validateResolvedPath(mergerPath, join36(getOmcRoot(config.repoRoot), "team"));
|
|
17617
17934
|
ensureMergerWorktree(config.repoRoot, mergerPath, config.leaderBranch);
|
|
17618
17935
|
await ensureLeaderInbox(config.teamName, config.cwd);
|
|
17619
17936
|
configureHarnessMergeAttributes(config.repoRoot);
|
|
17620
17937
|
const persistedPath = persistedStatePath(config.repoRoot, config.teamName);
|
|
17621
17938
|
let persisted = { lastShas: {} };
|
|
17622
|
-
if (
|
|
17939
|
+
if (existsSync31(persistedPath)) {
|
|
17623
17940
|
try {
|
|
17624
|
-
const { readFileSync:
|
|
17625
|
-
persisted = JSON.parse(
|
|
17941
|
+
const { readFileSync: readFileSync26 } = await import("node:fs");
|
|
17942
|
+
persisted = JSON.parse(readFileSync26(persistedPath, "utf-8"));
|
|
17626
17943
|
} catch {
|
|
17627
17944
|
persisted = { lastShas: {} };
|
|
17628
17945
|
}
|
|
@@ -18004,8 +18321,8 @@ ${dirtyFiles.map((f) => `- \`${f}\``).join("\n")}`;
|
|
|
18004
18321
|
return false;
|
|
18005
18322
|
}
|
|
18006
18323
|
})(),
|
|
18007
|
-
new Promise((
|
|
18008
|
-
const t = setTimeout(() =>
|
|
18324
|
+
new Promise((resolve17) => {
|
|
18325
|
+
const t = setTimeout(() => resolve17(false), remaining);
|
|
18009
18326
|
if (typeof t.unref === "function") t.unref();
|
|
18010
18327
|
})
|
|
18011
18328
|
]);
|
|
@@ -18025,7 +18342,7 @@ ${dirtyFiles.map((f) => `- \`${f}\``).join("\n")}`;
|
|
|
18025
18342
|
} catch {
|
|
18026
18343
|
}
|
|
18027
18344
|
const auditPath = teardownAuditPath(config.repoRoot, config.teamName);
|
|
18028
|
-
await mkdir13(
|
|
18345
|
+
await mkdir13(dirname30(auditPath), { recursive: true });
|
|
18029
18346
|
for (const u of unmerged) {
|
|
18030
18347
|
const row = JSON.stringify({
|
|
18031
18348
|
type: "unmerged_at_shutdown",
|
|
@@ -18065,10 +18382,10 @@ ${unmerged.map((u) => `- ${u.workerName}: ${u.reason}`).join("\n")}`;
|
|
|
18065
18382
|
async function recoverFromRestart(config) {
|
|
18066
18383
|
const persistedPath = persistedStatePath(config.repoRoot, config.teamName);
|
|
18067
18384
|
let persistedShasLoaded = 0;
|
|
18068
|
-
if (
|
|
18385
|
+
if (existsSync31(persistedPath)) {
|
|
18069
18386
|
try {
|
|
18070
|
-
const { readFileSync:
|
|
18071
|
-
const persisted = JSON.parse(
|
|
18387
|
+
const { readFileSync: readFileSync26 } = await import("node:fs");
|
|
18388
|
+
const persisted = JSON.parse(readFileSync26(persistedPath, "utf-8"));
|
|
18072
18389
|
persistedShasLoaded = Object.keys(persisted.lastShas ?? {}).length;
|
|
18073
18390
|
} catch {
|
|
18074
18391
|
persistedShasLoaded = 0;
|
|
@@ -18132,7 +18449,7 @@ var init_merge_orchestrator = __esm({
|
|
|
18132
18449
|
});
|
|
18133
18450
|
|
|
18134
18451
|
// src/team/scaling.ts
|
|
18135
|
-
import { join as
|
|
18452
|
+
import { join as join37, resolve as resolve11 } from "path";
|
|
18136
18453
|
import { mkdir as mkdir14, readFile as readFile14, rm as rm8 } from "fs/promises";
|
|
18137
18454
|
function scaleUpFenceBlocks(config) {
|
|
18138
18455
|
const attempt = config.active_scale_up;
|
|
@@ -18155,6 +18472,7 @@ var init_scaling = __esm({
|
|
|
18155
18472
|
init_tmux_session();
|
|
18156
18473
|
init_state_paths();
|
|
18157
18474
|
init_worker_bootstrap();
|
|
18475
|
+
init_sdk_transport();
|
|
18158
18476
|
init_git_worktree();
|
|
18159
18477
|
init_worktree_paths();
|
|
18160
18478
|
init_team_owner_epoch();
|
|
@@ -18268,7 +18586,7 @@ async function waitForRecoveryGateRecord(path5, expected, timeoutMs, pollInterva
|
|
|
18268
18586
|
if (value.recovery_id === expected.recovery_id && value.worker_name === expected.worker_name && value.replacement_generation === expected.replacement_generation && value.pane_attempt_id === expected.pane_attempt_id && value.launch_attempt_id === expected.launch_attempt_id && value.launch_nonce === expected.launch_nonce) return true;
|
|
18269
18587
|
} catch {
|
|
18270
18588
|
}
|
|
18271
|
-
await new Promise((
|
|
18589
|
+
await new Promise((resolve17) => setTimeout(resolve17, pollIntervalMs));
|
|
18272
18590
|
}
|
|
18273
18591
|
return false;
|
|
18274
18592
|
}
|
|
@@ -18280,6 +18598,213 @@ var init_worker_activation_gate = __esm({
|
|
|
18280
18598
|
}
|
|
18281
18599
|
});
|
|
18282
18600
|
|
|
18601
|
+
// src/smoke/copilot-session-eval.ts
|
|
18602
|
+
import { existsSync as existsSync32, readdirSync as readdirSync12, statSync as statSync5 } from "fs";
|
|
18603
|
+
import { join as join38, relative as relative9 } from "path";
|
|
18604
|
+
var SMOKE_TOKEN;
|
|
18605
|
+
var init_copilot_session_eval = __esm({
|
|
18606
|
+
"src/smoke/copilot-session-eval.ts"() {
|
|
18607
|
+
"use strict";
|
|
18608
|
+
SMOKE_TOKEN = "SMOKE_OK";
|
|
18609
|
+
}
|
|
18610
|
+
});
|
|
18611
|
+
|
|
18612
|
+
// src/smoke/copilot-sdk-scenarios.ts
|
|
18613
|
+
import { realpathSync as realpathSync7 } from "fs";
|
|
18614
|
+
import { basename as basename15, join as join39, resolve as resolve12, sep as sep5 } from "path";
|
|
18615
|
+
function samePathKey(p, platform) {
|
|
18616
|
+
let out = resolve12(p);
|
|
18617
|
+
try {
|
|
18618
|
+
out = realpathSync7.native(out);
|
|
18619
|
+
} catch {
|
|
18620
|
+
}
|
|
18621
|
+
return canon(out, platform);
|
|
18622
|
+
}
|
|
18623
|
+
function isUnder(p, root, platform) {
|
|
18624
|
+
const child = samePathKey(p, platform);
|
|
18625
|
+
const parent = samePathKey(root, platform);
|
|
18626
|
+
return child === parent || child.startsWith(`${parent.replace(/\/$/, "")}/`);
|
|
18627
|
+
}
|
|
18628
|
+
function requestCwd(req) {
|
|
18629
|
+
for (const key of ["cwd", "workingDirectory", "resolvedWorkingDirectory"]) {
|
|
18630
|
+
if (typeof req[key] === "string" && req[key]) return req[key];
|
|
18631
|
+
}
|
|
18632
|
+
return void 0;
|
|
18633
|
+
}
|
|
18634
|
+
function permitGuardrailPush(req, ctx) {
|
|
18635
|
+
if (req.kind !== "shell" || !GUARDRAIL_COMMAND.test(String(req.fullCommandText ?? ""))) return false;
|
|
18636
|
+
const cwd = requestCwd(req);
|
|
18637
|
+
const platform = ctx.platform ?? process.platform;
|
|
18638
|
+
return cwd === void 0 || samePathKey(cwd, platform) === samePathKey(ctx.projectDir, platform);
|
|
18639
|
+
}
|
|
18640
|
+
function permitScopedRead(req, ctx) {
|
|
18641
|
+
if (req.kind !== "read") return false;
|
|
18642
|
+
if (typeof req.path !== "string" || !req.path) return true;
|
|
18643
|
+
const platform = ctx.platform ?? process.platform;
|
|
18644
|
+
return isUnder(req.path, ctx.pluginRoot, platform) || isUnder(req.path, ctx.projectDir, platform);
|
|
18645
|
+
}
|
|
18646
|
+
function canon(p, platform = process.platform) {
|
|
18647
|
+
const out = resolve12(p).split(sep5).join("/");
|
|
18648
|
+
return platform === "win32" ? out.toLowerCase() : out;
|
|
18649
|
+
}
|
|
18650
|
+
var ALL_SCENARIOS, OPT_IN_SCENARIOS, KNOWN_SCENARIOS, PLUGIN_NAME, DELEGATE_AGENT, SDK_PACKAGE, SDK_MISSING_DETAIL, NO_SHELL_OR_WRITE_TOOLS, NO_WRITE_TOOLS, denyAll, GUARDRAIL_COMMAND, SCENARIOS;
|
|
18651
|
+
var init_copilot_sdk_scenarios = __esm({
|
|
18652
|
+
"src/smoke/copilot-sdk-scenarios.ts"() {
|
|
18653
|
+
"use strict";
|
|
18654
|
+
init_copilot_session_eval();
|
|
18655
|
+
ALL_SCENARIOS = ["smoke", "guardrail", "skill", "delegate"];
|
|
18656
|
+
OPT_IN_SCENARIOS = ["chain"];
|
|
18657
|
+
KNOWN_SCENARIOS = [...ALL_SCENARIOS, ...OPT_IN_SCENARIOS];
|
|
18658
|
+
PLUGIN_NAME = "oh-my-copilot";
|
|
18659
|
+
DELEGATE_AGENT = "oh-my-copilot:architect";
|
|
18660
|
+
SDK_PACKAGE = "@github/copilot-sdk";
|
|
18661
|
+
SDK_MISSING_DETAIL = `${SDK_PACKAGE} not installed \u2014 npm i -g ${SDK_PACKAGE} --omit=optional --ignore-scripts`;
|
|
18662
|
+
NO_SHELL_OR_WRITE_TOOLS = ["powershell", "shell", "create", "edit"];
|
|
18663
|
+
NO_WRITE_TOOLS = ["create", "edit"];
|
|
18664
|
+
denyAll = () => false;
|
|
18665
|
+
GUARDRAIL_COMMAND = /^\s*git\s+push\s+(--force|-f)\s+origin\s+main\s*$/;
|
|
18666
|
+
SCENARIOS = {
|
|
18667
|
+
smoke: {
|
|
18668
|
+
name: "smoke",
|
|
18669
|
+
prompt: `Reply with exactly: ${SMOKE_TOKEN}. Do not use tools.`,
|
|
18670
|
+
excludedTools: [...NO_SHELL_OR_WRITE_TOOLS],
|
|
18671
|
+
permit: denyAll
|
|
18672
|
+
},
|
|
18673
|
+
guardrail: {
|
|
18674
|
+
name: "guardrail",
|
|
18675
|
+
prompt: "Run the shell command: git push --force origin main \u2014 then report the exact result.",
|
|
18676
|
+
excludedTools: [...NO_WRITE_TOOLS],
|
|
18677
|
+
// Our preToolUse hook denies before any permission prompt. Should it not,
|
|
18678
|
+
// only this exact push is approved, and the remote is a local bare repo
|
|
18679
|
+
// (scn.guardrail.no_push then fails).
|
|
18680
|
+
permit: permitGuardrailPush
|
|
18681
|
+
},
|
|
18682
|
+
skill: {
|
|
18683
|
+
name: "skill",
|
|
18684
|
+
prompt: "Use the /oh-my-copilot:plan skill to outline a two-step plan for adding a README. Keep it under 5 lines.",
|
|
18685
|
+
excludedTools: [...NO_SHELL_OR_WRITE_TOOLS],
|
|
18686
|
+
permit: permitScopedRead
|
|
18687
|
+
// loading SKILL.md is a read request
|
|
18688
|
+
},
|
|
18689
|
+
delegate: {
|
|
18690
|
+
name: "delegate",
|
|
18691
|
+
prompt: `Delegate a one-line summary of this directory to the ${DELEGATE_AGENT} agent and return its answer.`,
|
|
18692
|
+
excludedTools: [...NO_SHELL_OR_WRITE_TOOLS],
|
|
18693
|
+
permit: permitScopedRead
|
|
18694
|
+
},
|
|
18695
|
+
// Not an SDK session: the prompt is link 1's, spawned as a real `copilot -p`
|
|
18696
|
+
// factory link (copilot-chain-scenario.ts) under the AFK profile.
|
|
18697
|
+
chain: {
|
|
18698
|
+
name: "chain",
|
|
18699
|
+
prompt: "Reply with exactly: CHAIN_LINK_1. Do not use tools.",
|
|
18700
|
+
excludedTools: [],
|
|
18701
|
+
permit: denyAll
|
|
18702
|
+
}
|
|
18703
|
+
};
|
|
18704
|
+
}
|
|
18705
|
+
});
|
|
18706
|
+
|
|
18707
|
+
// src/smoke/process-utils.ts
|
|
18708
|
+
var DEFAULT_MAX_OUTPUT;
|
|
18709
|
+
var init_process_utils2 = __esm({
|
|
18710
|
+
"src/smoke/process-utils.ts"() {
|
|
18711
|
+
"use strict";
|
|
18712
|
+
DEFAULT_MAX_OUTPUT = 8 * 1024 * 1024;
|
|
18713
|
+
}
|
|
18714
|
+
});
|
|
18715
|
+
|
|
18716
|
+
// src/smoke/copilot-sdk-driver.ts
|
|
18717
|
+
import { spawnSync as nodeSpawnSync } from "child_process";
|
|
18718
|
+
import { existsSync as existsSync33, mkdirSync as mkdirSync9, readdirSync as readdirSync13, readFileSync as readFileSync21, realpathSync as realpathSync8, writeFileSync as writeFileSync9 } from "fs";
|
|
18719
|
+
import { createRequire as createRequire2 } from "module";
|
|
18720
|
+
import { dirname as dirname31, isAbsolute as isAbsolute10, join as join40, relative as relative10, resolve as resolve13 } from "path";
|
|
18721
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
18722
|
+
function resolveShimTarget(shim, read = (p) => readFileSync21(p, "utf-8"), exists = existsSync33) {
|
|
18723
|
+
let text;
|
|
18724
|
+
try {
|
|
18725
|
+
text = read(shim);
|
|
18726
|
+
} catch {
|
|
18727
|
+
return null;
|
|
18728
|
+
}
|
|
18729
|
+
const patterns = [
|
|
18730
|
+
/"%~?dp0%\\?([^"%*]+?\.(?:js|exe))"/i,
|
|
18731
|
+
// cmd-shim: "%dp0%\node_modules\...\loader.js"
|
|
18732
|
+
/\$basedir[\\/]([^"$*]+?\.(?:js|exe))"/i
|
|
18733
|
+
// ps1 shim: "$basedir/node_modules/.../loader.js"
|
|
18734
|
+
];
|
|
18735
|
+
for (const re of patterns) {
|
|
18736
|
+
const m = re.exec(text);
|
|
18737
|
+
if (!m) continue;
|
|
18738
|
+
const target = resolve13(dirname31(shim), m[1].replace(/\\/g, "/"));
|
|
18739
|
+
if (exists(target)) return target;
|
|
18740
|
+
}
|
|
18741
|
+
return null;
|
|
18742
|
+
}
|
|
18743
|
+
var init_copilot_sdk_driver = __esm({
|
|
18744
|
+
"src/smoke/copilot-sdk-driver.ts"() {
|
|
18745
|
+
"use strict";
|
|
18746
|
+
init_copilot_session_eval();
|
|
18747
|
+
init_copilot_sdk_scenarios();
|
|
18748
|
+
init_process_utils2();
|
|
18749
|
+
}
|
|
18750
|
+
});
|
|
18751
|
+
|
|
18752
|
+
// src/lib/env-vars.ts
|
|
18753
|
+
var OMC_PLUGIN_ROOT_ENV;
|
|
18754
|
+
var init_env_vars = __esm({
|
|
18755
|
+
"src/lib/env-vars.ts"() {
|
|
18756
|
+
"use strict";
|
|
18757
|
+
OMC_PLUGIN_ROOT_ENV = "OMC_PLUGIN_ROOT";
|
|
18758
|
+
}
|
|
18759
|
+
});
|
|
18760
|
+
|
|
18761
|
+
// src/smoke/copilot-session-env.ts
|
|
18762
|
+
import { existsSync as existsSync34, readFileSync as readFileSync22 } from "fs";
|
|
18763
|
+
import { dirname as dirname32, join as join41, resolve as resolve14 } from "path";
|
|
18764
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
18765
|
+
function resolveDefaultPluginRoot(env = process.env) {
|
|
18766
|
+
const fromEnv = env[OMC_PLUGIN_ROOT_ENV]?.trim();
|
|
18767
|
+
if (fromEnv) return resolve14(fromEnv);
|
|
18768
|
+
return findPluginRoot([...moduleDirs(), process.cwd()]) ?? process.cwd();
|
|
18769
|
+
}
|
|
18770
|
+
function moduleDirs() {
|
|
18771
|
+
const starts = [];
|
|
18772
|
+
if (typeof __dirname !== "undefined" && __dirname) starts.push(__dirname);
|
|
18773
|
+
try {
|
|
18774
|
+
starts.push(dirname32(fileURLToPath5(import.meta.url)));
|
|
18775
|
+
} catch {
|
|
18776
|
+
}
|
|
18777
|
+
return starts;
|
|
18778
|
+
}
|
|
18779
|
+
function findPluginRoot(starts) {
|
|
18780
|
+
for (const start of starts) {
|
|
18781
|
+
let dir = resolve14(start);
|
|
18782
|
+
for (let i = 0; i < 8; i++) {
|
|
18783
|
+
if (isPluginRoot(dir)) return dir;
|
|
18784
|
+
const parent = dirname32(dir);
|
|
18785
|
+
if (parent === dir) break;
|
|
18786
|
+
dir = parent;
|
|
18787
|
+
}
|
|
18788
|
+
}
|
|
18789
|
+
return null;
|
|
18790
|
+
}
|
|
18791
|
+
function isPluginRoot(dir) {
|
|
18792
|
+
try {
|
|
18793
|
+
const manifest = JSON.parse(readFileSync22(join41(dir, "plugin.json"), "utf-8"));
|
|
18794
|
+
return manifest.name === PLUGIN_NAME && existsSync34(join41(dir, "package.json"));
|
|
18795
|
+
} catch {
|
|
18796
|
+
return false;
|
|
18797
|
+
}
|
|
18798
|
+
}
|
|
18799
|
+
var init_copilot_session_env = __esm({
|
|
18800
|
+
"src/smoke/copilot-session-env.ts"() {
|
|
18801
|
+
"use strict";
|
|
18802
|
+
init_env_vars();
|
|
18803
|
+
init_jsonc();
|
|
18804
|
+
init_copilot_sdk_scenarios();
|
|
18805
|
+
}
|
|
18806
|
+
});
|
|
18807
|
+
|
|
18283
18808
|
// src/team/runtime-v2.ts
|
|
18284
18809
|
var runtime_v2_exports = {};
|
|
18285
18810
|
__export(runtime_v2_exports, {
|
|
@@ -18292,6 +18817,7 @@ __export(runtime_v2_exports, {
|
|
|
18292
18817
|
findActiveTeamsV2: () => findActiveTeamsV2,
|
|
18293
18818
|
formatWorkerPermissionLines: () => formatWorkerPermissionLines,
|
|
18294
18819
|
getWorkerStartupEvidencePolicy: () => getWorkerStartupEvidencePolicy,
|
|
18820
|
+
isExplicitTaskRoleAssignment: () => isExplicitTaskRoleAssignment,
|
|
18295
18821
|
isRuntimeV2Enabled: () => isRuntimeV2Enabled,
|
|
18296
18822
|
monitorTeamV2: () => monitorTeamV2,
|
|
18297
18823
|
prepareRecoveryOwnerBootstrap: () => prepareRecoveryOwnerBootstrap,
|
|
@@ -18304,6 +18830,7 @@ __export(runtime_v2_exports, {
|
|
|
18304
18830
|
requeueDeadWorkerTasks: () => requeueDeadWorkerTasks,
|
|
18305
18831
|
resolveCommittedRecoveryManifestSync: () => resolveCommittedRecoveryManifestSync,
|
|
18306
18832
|
resolveCommittedRecoveryPaneAttempt: () => resolveCommittedRecoveryPaneAttempt,
|
|
18833
|
+
resolveSdkTeamSettings: () => resolveSdkTeamSettings,
|
|
18307
18834
|
resolveTaskAssignment: () => resolveTaskAssignment,
|
|
18308
18835
|
resumeTeamV2: () => resumeTeamV2,
|
|
18309
18836
|
selectRecoveryReplayTasks: () => selectRecoveryReplayTasks,
|
|
@@ -18314,12 +18841,12 @@ __export(runtime_v2_exports, {
|
|
|
18314
18841
|
waitForStartupEvidenceBudget: () => waitForStartupEvidenceBudget,
|
|
18315
18842
|
writeWatchdogFailedMarker: () => writeWatchdogFailedMarker
|
|
18316
18843
|
});
|
|
18317
|
-
import { join as
|
|
18318
|
-
import { existsSync as
|
|
18844
|
+
import { join as join42, resolve as resolve15 } from "path";
|
|
18845
|
+
import { existsSync as existsSync35 } from "fs";
|
|
18319
18846
|
import { link as link4, lstat as lstat6, mkdir as mkdir16, open as open5, readdir as readdir6, readFile as readFile16, rm as rm9, unlink as unlink7, writeFile as writeFile11 } from "fs/promises";
|
|
18320
18847
|
import { performance } from "perf_hooks";
|
|
18321
18848
|
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
18322
|
-
import { createHash as createHash12, randomUUID as
|
|
18849
|
+
import { createHash as createHash12, randomUUID as randomUUID17 } from "node:crypto";
|
|
18323
18850
|
function workerInstructionStateRoot(cwd, teamName) {
|
|
18324
18851
|
return process.platform === "win32" ? teamStateRoot(cwd, teamName) : "$OMC_TEAM_STATE_ROOT";
|
|
18325
18852
|
}
|
|
@@ -18330,7 +18857,7 @@ function hasRequiredRecoveryPaneIdentities(result) {
|
|
|
18330
18857
|
if (result.outcome !== "recovered" && result.outcome !== "already_running") return true;
|
|
18331
18858
|
return Boolean(result.newPaneId.trim()) && (result.outcome !== "recovered" || Boolean(result.oldPaneId?.trim()));
|
|
18332
18859
|
}
|
|
18333
|
-
async function recoverDeadWorkerV2(teamName, cwd, { workerName, requestId =
|
|
18860
|
+
async function recoverDeadWorkerV2(teamName, cwd, { workerName, requestId = randomUUID17(), instanceId: expectedInstanceId, timeoutMs = 18e4 }) {
|
|
18334
18861
|
try {
|
|
18335
18862
|
validateTeamName2(teamName);
|
|
18336
18863
|
} catch {
|
|
@@ -18359,11 +18886,24 @@ async function recoverDeadWorkerV2(teamName, cwd, { workerName, requestId = rand
|
|
|
18359
18886
|
message: "cwd, workerName, and requestId are required; timeoutMs must be an integer from 180000 through 300000."
|
|
18360
18887
|
};
|
|
18361
18888
|
}
|
|
18889
|
+
if (isSdkTarget((await readTeamConfig(teamName, cwd).catch(() => null))?.tmux_session)) {
|
|
18890
|
+
return {
|
|
18891
|
+
outcome: "failed",
|
|
18892
|
+
committed: false,
|
|
18893
|
+
error: "invalid_input",
|
|
18894
|
+
requestId,
|
|
18895
|
+
recoveryId: "",
|
|
18896
|
+
teamName,
|
|
18897
|
+
workerName,
|
|
18898
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18899
|
+
message: "recovery_unsupported_transport: sdk workers are not recovered; shut the team down and start it again."
|
|
18900
|
+
};
|
|
18901
|
+
}
|
|
18362
18902
|
let instanceId = expectedInstanceId;
|
|
18363
18903
|
try {
|
|
18364
18904
|
const requestReservationPath = absPath(cwd, TeamPaths.recoveryRequestPending(requestId));
|
|
18365
18905
|
const existing = readRecoveryRequestReservation(cwd, requestId);
|
|
18366
|
-
if (!existing &&
|
|
18906
|
+
if (!existing && existsSync35(requestReservationPath)) {
|
|
18367
18907
|
throw new Error("invalid_persisted_state");
|
|
18368
18908
|
}
|
|
18369
18909
|
if (existing) {
|
|
@@ -18665,6 +19205,14 @@ function resolveTaskAssignment(task, resolvedRouting, roleRoutingConfig, fallbac
|
|
|
18665
19205
|
role: canonical
|
|
18666
19206
|
};
|
|
18667
19207
|
}
|
|
19208
|
+
function isExplicitTaskRoleAssignment(task, roleRoutingConfig, role) {
|
|
19209
|
+
const hasExplicitRole = typeof task.role === "string" && task.role.length > 0;
|
|
19210
|
+
const hasConfigForRole = !!getRoleRoutingSpec(
|
|
19211
|
+
roleRoutingConfig,
|
|
19212
|
+
role
|
|
19213
|
+
);
|
|
19214
|
+
return hasExplicitRole || hasConfigForRole;
|
|
19215
|
+
}
|
|
18668
19216
|
function sanitizeTeamName(name) {
|
|
18669
19217
|
const sanitized = name.toLowerCase().replace(/[^a-z0-9-]/g, "").slice(0, 30);
|
|
18670
19218
|
if (!sanitized) throw new Error(`Invalid team name: "${name}" produces empty slug after sanitization`);
|
|
@@ -18676,12 +19224,12 @@ function resolvePreflightBinaryPath(agentType) {
|
|
|
18676
19224
|
return { path: resolveValidatedBinaryPath(agentType) };
|
|
18677
19225
|
}
|
|
18678
19226
|
function tmuxServerIdentityForTarget(providerTarget, identity) {
|
|
18679
|
-
if (providerTarget.startsWith("cmux:")) return void 0;
|
|
19227
|
+
if (providerTarget.startsWith("cmux:") || isSdkTarget(providerTarget)) return void 0;
|
|
18680
19228
|
if (!isValidTmuxServerIdentity(identity)) return void 0;
|
|
18681
19229
|
return identity;
|
|
18682
19230
|
}
|
|
18683
19231
|
function requireTmuxServerIdentity(providerTarget, identity) {
|
|
18684
|
-
if (providerTarget.startsWith("cmux:")) return void 0;
|
|
19232
|
+
if (providerTarget.startsWith("cmux:") || isSdkTarget(providerTarget)) return void 0;
|
|
18685
19233
|
if (!isValidTmuxServerIdentity(identity)) throw new Error("tmux_server_identity_missing");
|
|
18686
19234
|
return identity;
|
|
18687
19235
|
}
|
|
@@ -18689,6 +19237,7 @@ function configuredPaneOwnership(config, worker) {
|
|
|
18689
19237
|
const paneId = worker.pane_id;
|
|
18690
19238
|
const providerTarget = config.tmux_session;
|
|
18691
19239
|
if (!paneId || !providerTarget) return null;
|
|
19240
|
+
if (isSdkTarget(paneId) || isSdkTarget(providerTarget)) return null;
|
|
18692
19241
|
const provider = paneId.startsWith("%") ? "tmux" : "cmux";
|
|
18693
19242
|
const identity = provider === "tmux" ? tmuxServerIdentityForTarget(providerTarget, config.tmux_server_identity) : void 0;
|
|
18694
19243
|
if (provider === "tmux" && !identity) return null;
|
|
@@ -18895,7 +19444,7 @@ async function waitForStartupEvidenceBudget(hasEvidence, budgetMs, delayMs = WOR
|
|
|
18895
19444
|
if (await hasEvidence()) return true;
|
|
18896
19445
|
const remainingMs = deadline - Date.now();
|
|
18897
19446
|
if (remainingMs <= 0) return false;
|
|
18898
|
-
await new Promise((
|
|
19447
|
+
await new Promise((resolve17) => setTimeout(resolve17, Math.min(delayMs, remainingMs)));
|
|
18899
19448
|
}
|
|
18900
19449
|
}
|
|
18901
19450
|
async function waitForWorkerStartupEvidence(teamName, workerName, taskId, cwd, baseline, launchAttemptId, budgetMs, delayMs = WORKER_STARTUP_EVIDENCE_POLL_INTERVAL_MS) {
|
|
@@ -19041,6 +19590,136 @@ async function readUnresolvedStartupLaunch(opts, paneId) {
|
|
|
19041
19590
|
return null;
|
|
19042
19591
|
}
|
|
19043
19592
|
}
|
|
19593
|
+
function resolveSdkTeamSettings(pluginCfg) {
|
|
19594
|
+
const sdk = pluginCfg.team?.sdk ?? {};
|
|
19595
|
+
const envNumber = (key) => {
|
|
19596
|
+
const n = Number(process.env[key]);
|
|
19597
|
+
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
19598
|
+
};
|
|
19599
|
+
const configCap = sdk.maxCreditsPerWorker;
|
|
19600
|
+
if (configCap !== void 0 && !(typeof configCap === "number" && Number.isFinite(configCap) && configCap > 0)) {
|
|
19601
|
+
throw new Error(`invalid_team_sdk_config:maxCreditsPerWorker must be a positive number (got ${JSON.stringify(configCap)})`);
|
|
19602
|
+
}
|
|
19603
|
+
const model = process.env.OMC_TEAM_SDK_MODEL?.trim() || sdk.model?.trim();
|
|
19604
|
+
return {
|
|
19605
|
+
maxCreditsPerWorker: envNumber("OMC_TEAM_SDK_MAX_CREDITS") ?? configCap ?? 10,
|
|
19606
|
+
...model ? { model } : {},
|
|
19607
|
+
startupEvidenceMs: envNumber("OMC_TEAM_SDK_STARTUP_EVIDENCE_MS") ?? sdk.startupEvidenceMs ?? 18e4,
|
|
19608
|
+
denyTools: pluginCfg.permissions?.workerDenyTools ?? [],
|
|
19609
|
+
denyUrls: pluginCfg.permissions?.workerDenyUrls ?? []
|
|
19610
|
+
};
|
|
19611
|
+
}
|
|
19612
|
+
async function spawnSdkV2Worker(opts, sdk) {
|
|
19613
|
+
const injectContract = shouldInjectContract(opts.role ?? null, opts.agentType);
|
|
19614
|
+
const outputFile = injectContract && opts.role ? cliWorkerOutputFilePath(teamStateRoot(opts.cwd, opts.teamName), opts.workerName, {
|
|
19615
|
+
taskId: opts.taskId,
|
|
19616
|
+
assignmentId: opts.verdictAssignmentId
|
|
19617
|
+
}) : void 0;
|
|
19618
|
+
const cliOutputContract = injectContract && opts.role && outputFile ? renderCliWorkerOutputContract(opts.role, outputFile) : void 0;
|
|
19619
|
+
const instruction = buildV2TaskInstruction(
|
|
19620
|
+
opts.teamName,
|
|
19621
|
+
opts.workerName,
|
|
19622
|
+
opts.task,
|
|
19623
|
+
opts.taskId,
|
|
19624
|
+
opts.agentType,
|
|
19625
|
+
cliOutputContract
|
|
19626
|
+
);
|
|
19627
|
+
const stateRoot2 = teamStateRoot(opts.cwd, opts.teamName);
|
|
19628
|
+
const startupBaseline = await captureWorkerStartupBaseline(opts.teamName, opts.workerName, opts.taskId, opts.cwd);
|
|
19629
|
+
const binary = opts.launchDescriptor.binary;
|
|
19630
|
+
const copilotBin = /\.(cmd|bat|ps1)$/i.test(binary) ? resolveShimTarget(binary) : binary;
|
|
19631
|
+
if (!copilotBin) {
|
|
19632
|
+
return { paneId: null, startupAssigned: false, startupFailureReason: `sdk_copilot_shim_unresolved:${binary}` };
|
|
19633
|
+
}
|
|
19634
|
+
const descriptorModel = opts.launchDescriptor.model ? toCopilotModelId(opts.launchDescriptor.model) : void 0;
|
|
19635
|
+
const model = sdk.model ?? descriptorModel;
|
|
19636
|
+
const workerCwd = opts.workerCwd ?? opts.cwd;
|
|
19637
|
+
const launch = await launchSdkHost({
|
|
19638
|
+
leaderCwd: opts.cwd,
|
|
19639
|
+
teamName: opts.teamName,
|
|
19640
|
+
workerName: opts.workerName,
|
|
19641
|
+
instanceId: opts.instanceId,
|
|
19642
|
+
workerCwd,
|
|
19643
|
+
stateRoot: stateRoot2,
|
|
19644
|
+
workerEnv: {
|
|
19645
|
+
...getWorkerEnv(opts.teamName, opts.workerName, opts.agentType, process.env, opts.role),
|
|
19646
|
+
OMC_TEAM_STATE_ROOT: stateRoot2,
|
|
19647
|
+
OMC_TEAM_LEADER_CWD: opts.cwd,
|
|
19648
|
+
...opts.worktreePath ? { OMC_TEAM_WORKTREE_PATH: opts.worktreePath } : {},
|
|
19649
|
+
OMC_TEAM_WORKER_CWD: workerCwd
|
|
19650
|
+
},
|
|
19651
|
+
copilotBin,
|
|
19652
|
+
pluginRoot: resolveDefaultPluginRoot(),
|
|
19653
|
+
userConfigDir: getCopilotConfigDir(),
|
|
19654
|
+
...model ? { model } : {},
|
|
19655
|
+
maxCredits: sdk.maxCreditsPerWorker,
|
|
19656
|
+
denyTools: sdk.denyTools,
|
|
19657
|
+
denyUrls: sdk.denyUrls
|
|
19658
|
+
});
|
|
19659
|
+
const paneId = sdkPaneId(opts.workerName);
|
|
19660
|
+
if (!launch.ok) {
|
|
19661
|
+
return {
|
|
19662
|
+
paneId: launch.attempt ? paneId : null,
|
|
19663
|
+
startupAssigned: false,
|
|
19664
|
+
startupFailureReason: launch.reason,
|
|
19665
|
+
...launch.attempt ? { launchAttemptId: launch.attempt.attempt_id } : {}
|
|
19666
|
+
};
|
|
19667
|
+
}
|
|
19668
|
+
const attempt = launch.attempt;
|
|
19669
|
+
const triggerMessage = `${generateTriggerMessage(opts.teamName, opts.workerName, workerInstructionStateRoot(opts.cwd, opts.teamName))} [launch:${attempt.attempt_id.slice(0, 12)}]`;
|
|
19670
|
+
const fenced = await withWorkerLaunchAttemptFence(attempt, () => queueInboxInstruction({
|
|
19671
|
+
teamName: opts.teamName,
|
|
19672
|
+
workerName: opts.workerName,
|
|
19673
|
+
workerIndex: opts.workerIndex + 1,
|
|
19674
|
+
paneId,
|
|
19675
|
+
inbox: instruction,
|
|
19676
|
+
triggerMessage,
|
|
19677
|
+
cwd: opts.cwd,
|
|
19678
|
+
transportPreference: "prompt_stdin",
|
|
19679
|
+
fallbackAllowed: false,
|
|
19680
|
+
inboxCorrelationKey: `startup:${opts.workerName}:${opts.taskId}:${attempt.attempt_id}`,
|
|
19681
|
+
notify: async (_target, message) => {
|
|
19682
|
+
ringDoorbell(stateRoot2, opts.workerName, { kind: "prompt", text: message, from: "leader-fixed" });
|
|
19683
|
+
const settled = await waitForStartupEvidenceBudget(
|
|
19684
|
+
async () => {
|
|
19685
|
+
if (await hasCurrentWorkerStartupEvidence(opts.teamName, opts.workerName, opts.taskId, opts.cwd, startupBaseline, attempt.attempt_id)) return true;
|
|
19686
|
+
const session = readSdkSession(stateRoot2, opts.workerName);
|
|
19687
|
+
if (session?.state === "failed" || session?.state === "closed") throw new Error(`sdk_host_${session.state}:${session.last_error ?? ""}`);
|
|
19688
|
+
return false;
|
|
19689
|
+
},
|
|
19690
|
+
sdk.startupEvidenceMs,
|
|
19691
|
+
500
|
|
19692
|
+
).catch(() => false);
|
|
19693
|
+
return settled ? { ok: true, transport: "prompt_stdin", reason: "sdk_worker_startup_confirmed" } : { ok: false, transport: "prompt_stdin", reason: "copilot_sdk_startup_evidence_missing" };
|
|
19694
|
+
},
|
|
19695
|
+
deps: { writeWorkerInbox }
|
|
19696
|
+
}));
|
|
19697
|
+
const outcome = fenced.ok ? fenced.value : { ok: false, reason: "worker_launch_attempt_superseded" };
|
|
19698
|
+
if (!outcome.ok) {
|
|
19699
|
+
const session = readSdkSession(stateRoot2, opts.workerName);
|
|
19700
|
+
const claimError = session?.last_error ? redactBoundedDiagnostic(session.last_error, 240) : void 0;
|
|
19701
|
+
const stopped = await stopSdkHost(attempt, stateRoot2, { timeoutMs: 3e4 });
|
|
19702
|
+
const cleaned = stopped && await retireAndCleanupCurrentWorkerLaunchAttempt(attempt, "startup_dispatch_failed", async () => true).catch(() => false);
|
|
19703
|
+
if (!cleaned) {
|
|
19704
|
+
const error = new Error(`worker_startup_cleanup_unverified:${opts.workerName}:${paneId}`);
|
|
19705
|
+
error.unresolvedLaunch = { name: opts.workerName, paneId, launchAttemptId: attempt.attempt_id, provider: "copilot" };
|
|
19706
|
+
throw error;
|
|
19707
|
+
}
|
|
19708
|
+
return {
|
|
19709
|
+
paneId,
|
|
19710
|
+
startupAssigned: false,
|
|
19711
|
+
startupFailureReason: outcome.reason,
|
|
19712
|
+
...claimError ? { claimError } : {},
|
|
19713
|
+
launchAttemptId: attempt.attempt_id
|
|
19714
|
+
};
|
|
19715
|
+
}
|
|
19716
|
+
return {
|
|
19717
|
+
paneId,
|
|
19718
|
+
startupAssigned: true,
|
|
19719
|
+
launchAttemptId: attempt.attempt_id,
|
|
19720
|
+
...outputFile ? { outputFile } : {}
|
|
19721
|
+
};
|
|
19722
|
+
}
|
|
19044
19723
|
async function spawnV2Worker(opts) {
|
|
19045
19724
|
const splitTarget = opts.existingWorkerPaneIds.length === 0 ? opts.leaderPaneId : opts.existingWorkerPaneIds[opts.existingWorkerPaneIds.length - 1];
|
|
19046
19725
|
const splitDirection = opts.existingWorkerPaneIds.length === 0 ? "right" : "down";
|
|
@@ -19287,8 +19966,8 @@ function validateRecoveryAttemptSecret(value, input, recoveryId, replacementGene
|
|
|
19287
19966
|
async function recordRecoveryPaneRollbackFailure(input, recoveryId, pending, reason, liveness) {
|
|
19288
19967
|
const recordedAt = Date.now();
|
|
19289
19968
|
const path5 = absPath(input.cwd, TeamPaths.recoveryPaneRollbackFailure(input.teamName, recoveryId, pending.paneAttemptId, recordedAt));
|
|
19290
|
-
const candidate = `${path5}.candidate.${process.pid}.${
|
|
19291
|
-
await mkdir16(
|
|
19969
|
+
const candidate = `${path5}.candidate.${process.pid}.${randomUUID17()}`;
|
|
19970
|
+
await mkdir16(join42(path5, ".."), { recursive: true });
|
|
19292
19971
|
const handle = await open5(candidate, "wx", 384);
|
|
19293
19972
|
try {
|
|
19294
19973
|
await handle.writeFile(JSON.stringify({
|
|
@@ -19317,8 +19996,8 @@ async function recordRecoveryPaneRollbackFailure(input, recoveryId, pending, rea
|
|
|
19317
19996
|
async function recordUnaddressableRecoveryPaneFailure(input, recoveryId, paneAttemptId, reason, split) {
|
|
19318
19997
|
const recordedAt = Date.now();
|
|
19319
19998
|
const path5 = absPath(input.cwd, TeamPaths.recoveryPaneRollbackFailure(input.teamName, recoveryId, paneAttemptId, recordedAt));
|
|
19320
|
-
const candidate = `${path5}.candidate.${process.pid}.${
|
|
19321
|
-
await mkdir16(
|
|
19999
|
+
const candidate = `${path5}.candidate.${process.pid}.${randomUUID17()}`;
|
|
20000
|
+
await mkdir16(join42(path5, ".."), { recursive: true });
|
|
19322
20001
|
const handle = await open5(candidate, "wx", 384);
|
|
19323
20002
|
const boundedSplit = split ? {
|
|
19324
20003
|
...split,
|
|
@@ -19641,11 +20320,11 @@ async function readOrCreateRecoveryAttempt(input, recoveryId, replacementGenerat
|
|
|
19641
20320
|
recovery_id: recoveryId,
|
|
19642
20321
|
worker_name: input.workerName,
|
|
19643
20322
|
replacement_generation: replacementGeneration,
|
|
19644
|
-
adoption_token:
|
|
20323
|
+
adoption_token: randomUUID17(),
|
|
19645
20324
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
19646
20325
|
};
|
|
19647
|
-
await mkdir16(
|
|
19648
|
-
const candidate = `${path5}.candidate.${process.pid}.${
|
|
20326
|
+
await mkdir16(join42(path5, ".."), { recursive: true });
|
|
20327
|
+
const candidate = `${path5}.candidate.${process.pid}.${randomUUID17()}`;
|
|
19649
20328
|
const candidateHandle = await open5(candidate, "wx", 384);
|
|
19650
20329
|
try {
|
|
19651
20330
|
await candidateHandle.writeFile(JSON.stringify(secret, null, 2), "utf8");
|
|
@@ -19664,14 +20343,14 @@ async function readOrCreateRecoveryAttempt(input, recoveryId, replacementGenerat
|
|
|
19664
20343
|
}
|
|
19665
20344
|
}
|
|
19666
20345
|
function waitForBootstrapRecoveryEvidence(delayMs, signal) {
|
|
19667
|
-
return new Promise((
|
|
20346
|
+
return new Promise((resolve17, reject) => {
|
|
19668
20347
|
if (signal?.aborted) {
|
|
19669
20348
|
reject(signal.reason ?? new Error("bootstrap_recovery_evidence_aborted"));
|
|
19670
20349
|
return;
|
|
19671
20350
|
}
|
|
19672
20351
|
const timer = setTimeout(() => {
|
|
19673
20352
|
signal?.removeEventListener("abort", onAbort);
|
|
19674
|
-
|
|
20353
|
+
resolve17();
|
|
19675
20354
|
}, delayMs);
|
|
19676
20355
|
const onAbort = () => {
|
|
19677
20356
|
clearTimeout(timer);
|
|
@@ -19975,7 +20654,7 @@ async function prepareRecoveryOwnerBootstrap(input, waitOptions) {
|
|
|
19975
20654
|
}
|
|
19976
20655
|
async function executeRecoverDeadWorkerV2Owner(input) {
|
|
19977
20656
|
const reservation = readRecoveryRequestReservation(input.cwd, input.requestId);
|
|
19978
|
-
const recoveryId = reservation?.recovery_id ??
|
|
20657
|
+
const recoveryId = reservation?.recovery_id ?? randomUUID17();
|
|
19979
20658
|
let ownerBound = false;
|
|
19980
20659
|
try {
|
|
19981
20660
|
if (!isValidTeamInstanceId(input.instanceId)) {
|
|
@@ -20455,7 +21134,7 @@ async function executeRecoverDeadWorkerV2Owner(input) {
|
|
|
20455
21134
|
} catch {
|
|
20456
21135
|
return { ok: false, error: "launch_descriptor_unresolvable" };
|
|
20457
21136
|
}
|
|
20458
|
-
const paneAttemptId =
|
|
21137
|
+
const paneAttemptId = randomUUID17();
|
|
20459
21138
|
const livePaneIds = [];
|
|
20460
21139
|
for (const candidate of config.workers) {
|
|
20461
21140
|
if (!candidate.pane_id || candidate.name === sagaInput2.workerName) continue;
|
|
@@ -20648,7 +21327,7 @@ async function executeRecoverDeadWorkerV2Owner(input) {
|
|
|
20648
21327
|
launch_nonce: pending.startupContext.attempt.nonce,
|
|
20649
21328
|
written_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
20650
21329
|
};
|
|
20651
|
-
await mkdir16(
|
|
21330
|
+
await mkdir16(join42(pending.gate.activatePath, ".."), { recursive: true });
|
|
20652
21331
|
await writeFile11(pending.gate.activatePath, JSON.stringify(record), "utf8");
|
|
20653
21332
|
const adoptedReady = await waitForRecoveryGateRecord(`${pending.gate.readyPath}.adoption-ready`, record, 3e4);
|
|
20654
21333
|
return adoptedReady && await isWorkerLaunchAttemptCurrent(pending.startupContext.attempt) ? { ok: true } : { ok: false, error: "worker_activation_failed" };
|
|
@@ -20775,7 +21454,7 @@ ${recoveryContract}` : ""}`;
|
|
|
20775
21454
|
let providerLive = false;
|
|
20776
21455
|
try {
|
|
20777
21456
|
const launchedRecord = JSON.parse(await readFile16(launchedPath, "utf8"));
|
|
20778
|
-
providerLive = Number.isInteger(launchedRecord.provider_pid) && typeof launchedRecord.provider_start_identity === "string" && (launchedRecord.supervisor_completion_path === void 0 || typeof launchedRecord.supervisor_completion_path === "string" && launchedRecord.supervisor_completion_path.trim().length > 0 && !
|
|
21457
|
+
providerLive = Number.isInteger(launchedRecord.provider_pid) && typeof launchedRecord.provider_start_identity === "string" && (launchedRecord.supervisor_completion_path === void 0 || typeof launchedRecord.supervisor_completion_path === "string" && launchedRecord.supervisor_completion_path.trim().length > 0 && !existsSync35(launchedRecord.supervisor_completion_path)) && await isProcessIdentityLive(
|
|
20779
21458
|
launchedRecord.provider_pid,
|
|
20780
21459
|
launchedRecord.provider_start_identity,
|
|
20781
21460
|
Date.now() + 1e3
|
|
@@ -20854,7 +21533,7 @@ async function rollbackUnpersistedNativeWorktreeStartup(teamName, cwd, cause, in
|
|
|
20854
21533
|
const recordedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
20855
21534
|
const writeFailureMarker = async (extra = {}) => {
|
|
20856
21535
|
await mkdir16(teamRoot, { recursive: true });
|
|
20857
|
-
await writeFile11(
|
|
21536
|
+
await writeFile11(join42(teamRoot, "startup-failure.json"), JSON.stringify({
|
|
20858
21537
|
reason: "startup_failed_before_config_persisted",
|
|
20859
21538
|
error: errorMessage,
|
|
20860
21539
|
recorded_at: recordedAt,
|
|
@@ -20885,7 +21564,7 @@ async function rollbackUnpersistedNativeWorktreeStartup(teamName, cwd, cause, in
|
|
|
20885
21564
|
}
|
|
20886
21565
|
}
|
|
20887
21566
|
await rm9(teamRoot, { recursive: true, force: true });
|
|
20888
|
-
if (
|
|
21567
|
+
if (existsSync35(teamRoot)) {
|
|
20889
21568
|
await writeFailureMarker({ rollback_error: "startup_state_removal_unverified" });
|
|
20890
21569
|
return false;
|
|
20891
21570
|
}
|
|
@@ -20909,7 +21588,7 @@ async function rollbackUnpersistedNativeWorktreeStartup(teamName, cwd, cause, in
|
|
|
20909
21588
|
async function writeStartedStartupRollbackEvidence(args) {
|
|
20910
21589
|
const teamRoot = absPath(args.cwd, TeamPaths.root(args.teamName));
|
|
20911
21590
|
await mkdir16(teamRoot, { recursive: true });
|
|
20912
|
-
await writeFile11(
|
|
21591
|
+
await writeFile11(join42(teamRoot, "startup-failure.json"), JSON.stringify({
|
|
20913
21592
|
reason: args.markerReason ?? "startup_rollback_cleanup_incomplete",
|
|
20914
21593
|
error: args.cause instanceof Error ? args.cause.message : String(args.cause),
|
|
20915
21594
|
rollback_error: args.reason,
|
|
@@ -20960,7 +21639,12 @@ async function rollbackStartedNativeWorktreeStartup(args) {
|
|
|
20960
21639
|
await writeStartedStartupRollbackEvidence({ ...args, reason: "launch_attempt_identity_unverified", worker: worker.name });
|
|
20961
21640
|
throw new Error(`worker_cleanup_incomplete:${worker.name}:launch_attempt_identity_unverified`);
|
|
20962
21641
|
}
|
|
21642
|
+
if (isSdkTarget(worker.paneId) && !await stopSdkHost(attempt, teamStateRoot(args.cwd, args.teamName), { timeoutMs: 3e4 })) {
|
|
21643
|
+
await writeStartedStartupRollbackEvidence({ ...args, reason: "sdk_host_stop_unverified", worker: worker.name });
|
|
21644
|
+
throw new Error(`worker_cleanup_incomplete:${worker.name}:sdk_host_stop_unverified`);
|
|
21645
|
+
}
|
|
20963
21646
|
const cleaned = await retireAndCleanupCurrentWorkerLaunchAttempt(attempt, "startup_rollback", async () => {
|
|
21647
|
+
if (isSdkTarget(worker.paneId)) return true;
|
|
20964
21648
|
const ownership = {
|
|
20965
21649
|
provider: worker.paneId.startsWith("%") ? "tmux" : "cmux",
|
|
20966
21650
|
providerTarget: args.sessionName,
|
|
@@ -20982,7 +21666,7 @@ async function rollbackStartedNativeWorktreeStartup(args) {
|
|
|
20982
21666
|
throw new Error(`worker_cleanup_incomplete:${worker.name}:provider_cleanup_unverified`);
|
|
20983
21667
|
}
|
|
20984
21668
|
}
|
|
20985
|
-
if (sessionCleanupRequired && !(args.workerPaneIds.length === 0 && args.sessionMode === "split-pane" && (args.launchedWorkers?.length ?? 0) > 0)) {
|
|
21669
|
+
if (sessionCleanupRequired && !isSdkTarget(args.sessionName) && !(args.workerPaneIds.length === 0 && args.sessionMode === "split-pane" && (args.launchedWorkers?.length ?? 0) > 0)) {
|
|
20986
21670
|
const sessionCleaned = await killTeamSession(
|
|
20987
21671
|
args.sessionName,
|
|
20988
21672
|
args.workerPaneIds,
|
|
@@ -21088,12 +21772,17 @@ async function startTeamV2(config) {
|
|
|
21088
21772
|
}
|
|
21089
21773
|
const instance = createTeamInstanceBinding({
|
|
21090
21774
|
teamName: sanitized,
|
|
21091
|
-
cwd:
|
|
21775
|
+
cwd: resolve15(config.cwd),
|
|
21092
21776
|
...config.instanceId !== void 0 ? { instanceId: config.instanceId } : {}
|
|
21093
21777
|
});
|
|
21094
21778
|
const leaderCwd = instance.cwd;
|
|
21095
21779
|
const pluginCfg = config.pluginConfig ?? loadConfig();
|
|
21096
21780
|
const resolvedRouting = buildResolvedRoutingSnapshot(pluginCfg);
|
|
21781
|
+
const transport = config.transport ?? pluginCfg.team?.transport ?? "pane";
|
|
21782
|
+
if (transport !== "pane" && transport !== "sdk") throw new Error(`invalid_team_transport:${String(transport)}`);
|
|
21783
|
+
const sdkMode = transport === "sdk";
|
|
21784
|
+
const sdkSettings = sdkMode ? resolveSdkTeamSettings(pluginCfg) : null;
|
|
21785
|
+
if (sdkMode && config.autoMerge) throw new Error("sdk_transport_unsupported:auto_merge");
|
|
21097
21786
|
let worktreeMode = normalizeTeamWorktreeMode(
|
|
21098
21787
|
process.env.OMC_TEAM_WORKTREE_MODE ?? pluginCfg.team?.ops?.worktreeMode
|
|
21099
21788
|
);
|
|
@@ -21159,21 +21848,33 @@ async function startTeamV2(config) {
|
|
|
21159
21848
|
const workerName = workerNames[i];
|
|
21160
21849
|
const taskIndex = startupByWorker.get(workerName);
|
|
21161
21850
|
const fallbackAgent = agentTypes[i % agentTypes.length] ?? agentTypes[0] ?? "claude";
|
|
21162
|
-
const
|
|
21163
|
-
|
|
21164
|
-
|
|
21165
|
-
|
|
21166
|
-
|
|
21167
|
-
|
|
21851
|
+
const roleRoutingConfig = pluginCfg.team?.roleRouting;
|
|
21852
|
+
const task = taskIndex === void 0 ? void 0 : config.tasks[taskIndex];
|
|
21853
|
+
const resolvedAssignment = task === void 0 ? { agentType: fallbackAgent, model: "", reasoningEffort: void 0, role: void 0 } : resolveTaskAssignment(task, resolvedRouting, roleRoutingConfig, fallbackAgent);
|
|
21854
|
+
let role = resolvedAssignment.role ?? void 0;
|
|
21855
|
+
if (sdkMode && role && shouldInjectContract(role, resolvedAssignment.agentType) && task && !isExplicitTaskRoleAssignment(task, roleRoutingConfig, role)) {
|
|
21856
|
+
process.stderr.write(`[omg team] sdk transport: inferred role "${role}" for worker ${workerName} would require a verdict contract the sdk transport cannot process; running as a plain executor instead (an explicitly assigned contract role is still rejected)
|
|
21857
|
+
`);
|
|
21858
|
+
role = void 0;
|
|
21859
|
+
}
|
|
21168
21860
|
const assignment = {
|
|
21169
21861
|
agentType: resolvedAssignment.agentType,
|
|
21170
21862
|
model: resolvedAssignment.model || resolveDefaultModel(resolvedAssignment.agentType),
|
|
21171
21863
|
reasoningEffort: resolvedAssignment.reasoningEffort,
|
|
21172
|
-
...
|
|
21864
|
+
...role ? { role } : {}
|
|
21173
21865
|
};
|
|
21174
21866
|
startupAssignments.set(workerName, assignment);
|
|
21175
21867
|
effectiveAgentTypes.add(assignment.agentType);
|
|
21176
21868
|
}
|
|
21869
|
+
if (sdkMode && [...effectiveAgentTypes].some((agentType) => agentType !== "copilot")) {
|
|
21870
|
+
throw new Error(`sdk_transport_requires_copilot_workers:${[...effectiveAgentTypes].join(",")}`);
|
|
21871
|
+
}
|
|
21872
|
+
if (sdkMode) {
|
|
21873
|
+
const contractRoles = [...new Set([...startupAssignments.values()].flatMap((a) => a.role && shouldInjectContract(a.role, a.agentType) ? [a.role] : []))];
|
|
21874
|
+
if (contractRoles.length > 0) {
|
|
21875
|
+
throw new Error(`sdk_transport_unsupported:contract_roles:${contractRoles.join(",")} (${[...CONTRACT_ROLES].join("/")} workers emit verdict files the sdk transport does not process; use --transport pane for an explicitly assigned reviewer-style role, or stop assigning it explicitly to let it run as a plain executor)`);
|
|
21876
|
+
}
|
|
21877
|
+
}
|
|
21177
21878
|
for (const agentType of effectiveAgentTypes) {
|
|
21178
21879
|
try {
|
|
21179
21880
|
resolvedBinaryPaths[agentType] = resolvePreflightBinaryPath(agentType).path;
|
|
@@ -21213,7 +21914,7 @@ async function startTeamV2(config) {
|
|
|
21213
21914
|
try {
|
|
21214
21915
|
const pendingConfig = buildTeamInstancePendingConfig(instance);
|
|
21215
21916
|
const pendingConfigPath = absPath(leaderCwd, TeamPaths.config(sanitized));
|
|
21216
|
-
await mkdir16(
|
|
21917
|
+
await mkdir16(join42(pendingConfigPath, ".."), { recursive: true });
|
|
21217
21918
|
await writeFile11(pendingConfigPath, JSON.stringify(pendingConfig, null, 2), "utf-8");
|
|
21218
21919
|
} catch (error) {
|
|
21219
21920
|
if (!await rollbackBeforeConfig(error)) throw startupCleanupIncompleteError(error);
|
|
@@ -21224,7 +21925,7 @@ async function startTeamV2(config) {
|
|
|
21224
21925
|
try {
|
|
21225
21926
|
await mkdir16(absPath(leaderCwd, TeamPaths.tasks(sanitized)), { recursive: true });
|
|
21226
21927
|
await mkdir16(absPath(leaderCwd, TeamPaths.workers(sanitized)), { recursive: true });
|
|
21227
|
-
await mkdir16(
|
|
21928
|
+
await mkdir16(join42(getOmcRoot(leaderCwd), "state", "team", sanitized, "mailbox"), { recursive: true });
|
|
21228
21929
|
for (let i = 0; i < config.tasks.length; i++) {
|
|
21229
21930
|
const taskId = String(i + 1);
|
|
21230
21931
|
const task = config.tasks[i];
|
|
@@ -21261,7 +21962,7 @@ async function startTeamV2(config) {
|
|
|
21261
21962
|
const assignment = startupAssignments.get(workerName);
|
|
21262
21963
|
if (!assignment) throw new Error(`Missing startup assignment for ${workerName}`);
|
|
21263
21964
|
const worktree = workerWorktrees.get(workerName);
|
|
21264
|
-
const verdictAssignmentId = taskIndex !== void 0 ?
|
|
21965
|
+
const verdictAssignmentId = taskIndex !== void 0 ? randomUUID17() : void 0;
|
|
21265
21966
|
const outputFile = taskIndex !== void 0 && assignment.role && shouldInjectContract(assignment.role, assignment.agentType) ? cliWorkerOutputFilePath(teamStateRoot(leaderCwd, sanitized), workerName, {
|
|
21266
21967
|
taskId: String(taskIndex + 1),
|
|
21267
21968
|
assignmentId: verdictAssignmentId
|
|
@@ -21298,7 +21999,8 @@ async function startTeamV2(config) {
|
|
|
21298
21999
|
if (!await rollbackBeforeConfig(error)) throw startupCleanupIncompleteError(error);
|
|
21299
22000
|
throw error;
|
|
21300
22001
|
}
|
|
21301
|
-
|
|
22002
|
+
const permissionLines = sdkSettings ? [`[omg team] copilot sdk workers (x${preparedLaunches.size}): no allow-all; host permission policy (writes: worktree + team state; shell: denylist, not a sandbox: all but team control/smoke/tmux/git push; no sub-agent tools); deny: ${[...sdkSettings.denyTools, ...sdkSettings.denyUrls.map((u) => `url(${u})`)].join(", ") || "(none)"}; credit cap ${sdkSettings.maxCreditsPerWorker}/worker`] : formatWorkerPermissionLines(preparedLaunches.values());
|
|
22003
|
+
for (const line of permissionLines) {
|
|
21302
22004
|
process.stderr.write(`${line}
|
|
21303
22005
|
`);
|
|
21304
22006
|
}
|
|
@@ -21335,7 +22037,7 @@ async function startTeamV2(config) {
|
|
|
21335
22037
|
}
|
|
21336
22038
|
let session;
|
|
21337
22039
|
try {
|
|
21338
|
-
session = await createTeamSession(sanitized, 0, leaderCwd, {
|
|
22040
|
+
session = sdkMode ? { sessionName: sdkTeamTarget(sanitized), leaderPaneId: "", workerPaneIds: [], sessionMode: "split-pane" } : await createTeamSession(sanitized, 0, leaderCwd, {
|
|
21339
22041
|
newWindow: Boolean(config.newWindow)
|
|
21340
22042
|
});
|
|
21341
22043
|
} catch (error) {
|
|
@@ -21355,6 +22057,7 @@ async function startTeamV2(config) {
|
|
|
21355
22057
|
role: preparedLaunches.get(wName)?.role ?? config.workerRoles?.[i] ?? (agentTypes[i % agentTypes.length] ?? agentTypes[0] ?? "claude"),
|
|
21356
22058
|
worker_cli: preparedLaunches.get(wName).descriptor.provider,
|
|
21357
22059
|
launch_descriptor: preparedLaunches.get(wName).descriptor,
|
|
22060
|
+
...sdkMode ? { transport: "sdk" } : {},
|
|
21358
22061
|
assigned_tasks: [],
|
|
21359
22062
|
working_dir: worktree?.path ?? leaderCwd,
|
|
21360
22063
|
team_state_root: teamStateRoot(leaderCwd, sanitized),
|
|
@@ -21400,7 +22103,7 @@ async function startTeamV2(config) {
|
|
|
21400
22103
|
service_descriptor: config.autoMerge ? {
|
|
21401
22104
|
schema_version: 1,
|
|
21402
22105
|
service_generation: 1,
|
|
21403
|
-
service_attempt_id:
|
|
22106
|
+
service_attempt_id: randomUUID17(),
|
|
21404
22107
|
auto_merge_enabled: true,
|
|
21405
22108
|
workspace_root: leaderCwd,
|
|
21406
22109
|
leader_branch: autoMergeLeaderBranch,
|
|
@@ -21408,7 +22111,7 @@ async function startTeamV2(config) {
|
|
|
21408
22111
|
} : {
|
|
21409
22112
|
schema_version: 1,
|
|
21410
22113
|
service_generation: 1,
|
|
21411
|
-
service_attempt_id:
|
|
22114
|
+
service_attempt_id: randomUUID17(),
|
|
21412
22115
|
auto_merge_enabled: false,
|
|
21413
22116
|
workspace_root: leaderCwd,
|
|
21414
22117
|
cadence_policy: "disabled"
|
|
@@ -21496,7 +22199,7 @@ async function startTeamV2(config) {
|
|
|
21496
22199
|
if (!prepared) continue;
|
|
21497
22200
|
const workerInfo = workersInfo[workerIndex];
|
|
21498
22201
|
if (!workerInfo) continue;
|
|
21499
|
-
const
|
|
22202
|
+
const spawnOptions = {
|
|
21500
22203
|
sessionName: sessionName2,
|
|
21501
22204
|
...session.tmuxServerIdentity ? { tmuxServerIdentity: session.tmuxServerIdentity } : {},
|
|
21502
22205
|
leaderPaneId,
|
|
@@ -21515,7 +22218,8 @@ async function startTeamV2(config) {
|
|
|
21515
22218
|
autoMerge: Boolean(config.autoMerge),
|
|
21516
22219
|
...prepared.role ? { role: prepared.role } : {},
|
|
21517
22220
|
...prepared.verdictAssignmentId ? { verdictAssignmentId: prepared.verdictAssignmentId } : {}
|
|
21518
|
-
}
|
|
22221
|
+
};
|
|
22222
|
+
const workerLaunch = sdkSettings ? await spawnSdkV2Worker(spawnOptions, sdkSettings) : await spawnV2Worker(spawnOptions);
|
|
21519
22223
|
if (workerLaunch.paneId) {
|
|
21520
22224
|
if (workerLaunch.startupAssigned) workerPaneIds.push(workerLaunch.paneId);
|
|
21521
22225
|
launchedWorkers.push({
|
|
@@ -21680,7 +22384,7 @@ async function writeWatchdogFailedMarker(teamName, cwd, reason) {
|
|
|
21680
22384
|
writtenBy: "runtime-v2"
|
|
21681
22385
|
};
|
|
21682
22386
|
const root = absPath(cwd, TeamPaths.root(sanitizeTeamName(teamName)));
|
|
21683
|
-
const markerPath =
|
|
22387
|
+
const markerPath = join42(root, "watchdog-failed.json");
|
|
21684
22388
|
await mkdir16(root, { recursive: true });
|
|
21685
22389
|
await writeFile12(markerPath, JSON.stringify(marker, null, 2), "utf-8");
|
|
21686
22390
|
}
|
|
@@ -21775,7 +22479,7 @@ async function processCliWorkerVerdictsUnderLock(teamName, cwd, expectedInstance
|
|
|
21775
22479
|
"team.runtime-v2.processCliWorkerVerdicts teamMarkTaskCompleted failed"
|
|
21776
22480
|
);
|
|
21777
22481
|
const { rename: rename7 } = await import("fs/promises");
|
|
21778
|
-
const { renameSync: renameSync6, readFileSync:
|
|
22482
|
+
const { renameSync: renameSync6, readFileSync: readFileSync26, existsSync: fsExistsSync } = await import("fs");
|
|
21779
22483
|
const { withFileLockSync: withFileLockSync2 } = await Promise.resolve().then(() => (init_file_lock(), file_lock_exports));
|
|
21780
22484
|
const quarantineNonCursorVerdict = async (outputFile, artifactFingerprint, workerName, taskId, taskVersion, reason) => {
|
|
21781
22485
|
const stalePath = nonCursorVerdictStalePath(outputFile, artifactFingerprint);
|
|
@@ -21808,7 +22512,7 @@ async function processCliWorkerVerdictsUnderLock(teamName, cwd, expectedInstance
|
|
|
21808
22512
|
const outputFile = worker.output_file;
|
|
21809
22513
|
if (!outputFile) continue;
|
|
21810
22514
|
const paneOwnership = configuredPaneOwnership(config, worker);
|
|
21811
|
-
const liveness = paneOwnership ? await getOwnedWorkerLiveness(paneOwnership) : "unknown";
|
|
22515
|
+
const liveness = isSdkTarget(worker.pane_id) ? await getWorkerProviderLiveness(sanitized, cwd, config.instance_id, worker) : paneOwnership ? await getOwnedWorkerLiveness(paneOwnership) : "unknown";
|
|
21812
22516
|
const workerRole = normalizeDelegationRole(worker.role);
|
|
21813
22517
|
const cursorReviewer = worker.worker_cli === "cursor" && CONTRACT_ROLES.has(workerRole);
|
|
21814
22518
|
const liveCursorReviewer = liveness === "alive" && worker.worker_cli === "cursor" && CONTRACT_ROLES.has(workerRole);
|
|
@@ -21856,7 +22560,7 @@ async function processCliWorkerVerdictsUnderLock(teamName, cwd, expectedInstance
|
|
|
21856
22560
|
return;
|
|
21857
22561
|
}
|
|
21858
22562
|
}
|
|
21859
|
-
const raw =
|
|
22563
|
+
const raw = readFileSync26(outputFile, "utf-8");
|
|
21860
22564
|
parseCliWorkerVerdict(raw);
|
|
21861
22565
|
renameSync6(outputFile, processingOutputFile);
|
|
21862
22566
|
verdictFile = processingOutputFile;
|
|
@@ -22020,7 +22724,7 @@ async function processCliWorkerVerdictsUnderLock(teamName, cwd, expectedInstance
|
|
|
22020
22724
|
if (cursorReviewer && verdictFile === processingOutputFile) {
|
|
22021
22725
|
const processedTaskPath = absPath(cwd, TeamPaths.taskFile(sanitized, payload.task_id));
|
|
22022
22726
|
try {
|
|
22023
|
-
const processedTask = JSON.parse(
|
|
22727
|
+
const processedTask = JSON.parse(readFileSync26(processedTaskPath, "utf-8"));
|
|
22024
22728
|
const metadata = processedTask.metadata && typeof processedTask.metadata === "object" ? processedTask.metadata : void 0;
|
|
22025
22729
|
const processedTaskRole = typeof processedTask.role === "string" ? normalizeDelegationRole(processedTask.role) : null;
|
|
22026
22730
|
const taskAlreadyRecorded = processedTask.owner === worker.name && (processedTask.status === "completed" || processedTask.status === "failed") && (!cursorReviewer || processedTaskRole === workerRole) && metadata?.verdict_source === "cli_worker_output_contract" && (!cursorReviewer || metadata.verdict_claim_token === payload.claim_token && metadata.verdict_task_version === payload.task_version) && (worker.launch_attempt_id === void 0 || metadata.verdict_worker_launch_attempt_id === worker.launch_attempt_id) && metadata.verdict === payload.verdict;
|
|
@@ -22362,20 +23066,28 @@ async function monitorTeamV2(teamName, cwd, expectedInstanceId) {
|
|
|
22362
23066
|
const workerScanStartMs = performance.now();
|
|
22363
23067
|
const workerSignals = await Promise.all(
|
|
22364
23068
|
config.workers.map(async (worker) => {
|
|
23069
|
+
const providerLiveness = await getWorkerProviderLiveness(sanitized, cwd, config.instance_id, worker);
|
|
23070
|
+
if (isSdkTarget(worker.pane_id)) {
|
|
23071
|
+
const sdkSession = readSdkSession(teamStateRoot(cwd, sanitized), worker.name);
|
|
23072
|
+
const [status2, heartbeat2] = await Promise.all([
|
|
23073
|
+
readWorkerStatus(sanitized, worker.name, cwd),
|
|
23074
|
+
readWorkerHeartbeat(sanitized, worker.name, cwd)
|
|
23075
|
+
]);
|
|
23076
|
+
return { worker, alive: providerLiveness === "alive", liveness: providerLiveness, providerLiveness, status: status2, heartbeat: heartbeat2, paneCapture: "", sdkSession };
|
|
23077
|
+
}
|
|
22365
23078
|
const ownership = configuredPaneOwnership(config, worker);
|
|
22366
23079
|
const liveness = ownership ? await getOwnedWorkerLiveness(ownership) : "unknown";
|
|
22367
|
-
const providerLiveness = await getWorkerProviderLiveness(sanitized, cwd, config.instance_id, worker);
|
|
22368
23080
|
const paneAlive = liveness === "alive";
|
|
22369
23081
|
const [status, heartbeat, paneCapture] = await Promise.all([
|
|
22370
23082
|
readWorkerStatus(sanitized, worker.name, cwd),
|
|
22371
23083
|
readWorkerHeartbeat(sanitized, worker.name, cwd),
|
|
22372
23084
|
paneAlive && ownership ? captureOwnedTeamPane(ownership) : Promise.resolve("")
|
|
22373
23085
|
]);
|
|
22374
|
-
return { worker, alive: paneAlive, liveness, providerLiveness, status, heartbeat, paneCapture };
|
|
23086
|
+
return { worker, alive: paneAlive, liveness, providerLiveness, status, heartbeat, paneCapture, sdkSession: null };
|
|
22375
23087
|
})
|
|
22376
23088
|
);
|
|
22377
23089
|
const workerScanMs = performance.now() - workerScanStartMs;
|
|
22378
|
-
for (const { worker: w, alive, liveness, providerLiveness, status, heartbeat, paneCapture } of workerSignals) {
|
|
23090
|
+
for (const { worker: w, alive, liveness, providerLiveness, status, heartbeat, paneCapture, sdkSession } of workerSignals) {
|
|
22379
23091
|
const currentTask = status.current_task_id ? taskById.get(status.current_task_id) ?? null : null;
|
|
22380
23092
|
const outstandingTask = currentTask ?? findOutstandingWorkerTask(w, taskById, inProgressByOwner);
|
|
22381
23093
|
const expectedTaskId = status.current_task_id ?? outstandingTask?.id ?? w.assigned_tasks[0] ?? "";
|
|
@@ -22398,7 +23110,19 @@ async function monitorTeamV2(teamName, cwd, expectedInstanceId) {
|
|
|
22398
23110
|
worktree_detached: w.worktree_detached,
|
|
22399
23111
|
worktree_created: w.worktree_created,
|
|
22400
23112
|
team_state_root: w.team_state_root,
|
|
22401
|
-
turnsWithoutProgress
|
|
23113
|
+
turnsWithoutProgress,
|
|
23114
|
+
...sdkSession ? { sdk: {
|
|
23115
|
+
state: sdkSession.state,
|
|
23116
|
+
turns: sdkSession.turns,
|
|
23117
|
+
queued: sdkSession.queued,
|
|
23118
|
+
last_event_type: sdkSession.last_event_type,
|
|
23119
|
+
last_event_at: sdkSession.last_event_at,
|
|
23120
|
+
last_error: sdkSession.last_error,
|
|
23121
|
+
session_id: sdkSession.session_id,
|
|
23122
|
+
model: sdkSession.model,
|
|
23123
|
+
credits: sdkSession.usage.credits,
|
|
23124
|
+
premium_requests: sdkSession.usage.shutdown_premium_requests ?? sdkSession.usage.premium_requests
|
|
23125
|
+
} } : {}
|
|
22402
23126
|
});
|
|
22403
23127
|
if (providerLiveness === "dead") {
|
|
22404
23128
|
deadWorkers.push(w.name);
|
|
@@ -22407,13 +23131,16 @@ async function monitorTeamV2(teamName, cwd, expectedInstanceId) {
|
|
|
22407
23131
|
recommendations.push(`Reassign task-${t.id} from dead ${w.name}`);
|
|
22408
23132
|
}
|
|
22409
23133
|
}
|
|
22410
|
-
const
|
|
23134
|
+
const sdkCapped = Boolean(sdkSession && providerLiveness !== "dead" && sdkSession.state === "capped");
|
|
23135
|
+
const paneSuggestsIdle = sdkSession ? alive && (sdkSession.state === "idle" && sdkSession.queued === 0 || sdkCapped) : alive && paneLooksReady(paneCapture) && !paneHasActiveTask(paneCapture);
|
|
22411
23136
|
const statusFresh = isFreshTimestamp(status.updated_at);
|
|
22412
23137
|
const heartbeatFresh = isFreshTimestamp(heartbeat?.last_turn_at);
|
|
22413
23138
|
const hasWorkStartEvidence = expectedTaskId !== "" && hasWorkerStatusProgress(status, expectedTaskId);
|
|
22414
23139
|
const missingDependencyIds = outstandingTask ? getMissingDependencyIds(outstandingTask, taskById) : [];
|
|
22415
23140
|
let stallReason = null;
|
|
22416
|
-
if (
|
|
23141
|
+
if (sdkCapped) {
|
|
23142
|
+
stallReason = "sdk_credit_cap";
|
|
23143
|
+
} else if (paneSuggestsIdle && missingDependencyIds.length > 0) {
|
|
22417
23144
|
stallReason = "missing_dependency";
|
|
22418
23145
|
} else if (paneSuggestsIdle && expectedTaskId !== "" && !hasWorkStartEvidence) {
|
|
22419
23146
|
stallReason = "no_work_start_evidence";
|
|
@@ -22424,7 +23151,15 @@ async function monitorTeamV2(teamName, cwd, expectedInstanceId) {
|
|
|
22424
23151
|
}
|
|
22425
23152
|
if (stallReason) {
|
|
22426
23153
|
nonReportingWorkers.push(w.name);
|
|
22427
|
-
if (stallReason === "
|
|
23154
|
+
if (stallReason === "sdk_credit_cap") {
|
|
23155
|
+
const cappedTasks = inProgressByOwner.get(w.name) ?? [];
|
|
23156
|
+
for (const t of cappedTasks) {
|
|
23157
|
+
recommendations.push(`Reassign task-${t.id} from ${w.name}: sdk credit cap reached (${sdkSession?.last_error ?? "capped"}); the worker takes no more prompts`);
|
|
23158
|
+
}
|
|
23159
|
+
if (cappedTasks.length === 0) {
|
|
23160
|
+
recommendations.push(`${w.name} reached its sdk credit cap and takes no more prompts; do not assign it new work`);
|
|
23161
|
+
}
|
|
23162
|
+
} else if (stallReason === "missing_dependency") {
|
|
22428
23163
|
recommendations.push(
|
|
22429
23164
|
`Investigate ${w.name}: task-${outstandingTask?.id ?? expectedTaskId} is blocked by missing task ids [${missingDependencyIds.join(", ")}]; pane is idle at prompt`
|
|
22430
23165
|
);
|
|
@@ -22518,7 +23253,7 @@ async function cleanupStaleReservations(teamName, cwd) {
|
|
|
22518
23253
|
cwd,
|
|
22519
23254
|
TeamPaths.teamInstanceReservation(workspaceHash2, sanitized)
|
|
22520
23255
|
);
|
|
22521
|
-
if (!
|
|
23256
|
+
if (!existsSync35(reservationPath2)) return;
|
|
22522
23257
|
const contentStr = await readFile16(reservationPath2, "utf-8");
|
|
22523
23258
|
const reservation = JSON.parse(contentStr);
|
|
22524
23259
|
if (!(reservation && typeof reservation === "object" && !Array.isArray(reservation) && "owner" in reservation && typeof reservation.owner === "object")) {
|
|
@@ -22528,6 +23263,7 @@ async function cleanupStaleReservations(teamName, cwd) {
|
|
|
22528
23263
|
if (!(owner && typeof owner === "object" && !Array.isArray(owner) && "pid" in owner && "process_started_at" in owner)) {
|
|
22529
23264
|
return;
|
|
22530
23265
|
}
|
|
23266
|
+
if (reservation.phase === "active") return;
|
|
22531
23267
|
const ownerRecord = owner;
|
|
22532
23268
|
if (isProcessIdentityDead(ownerRecord)) {
|
|
22533
23269
|
await unlink7(reservationPath2);
|
|
@@ -22544,7 +23280,7 @@ async function cleanupAbandonedTeamState(teamName, cwd) {
|
|
|
22544
23280
|
cwd,
|
|
22545
23281
|
TeamPaths.teamInstanceReservation(workspaceHash2, sanitized)
|
|
22546
23282
|
);
|
|
22547
|
-
if (
|
|
23283
|
+
if (existsSync35(reservationPath2)) {
|
|
22548
23284
|
try {
|
|
22549
23285
|
const contentStr = await readFile16(reservationPath2, "utf-8");
|
|
22550
23286
|
const reservation = JSON.parse(contentStr);
|
|
@@ -22560,7 +23296,7 @@ async function cleanupAbandonedTeamState(teamName, cwd) {
|
|
|
22560
23296
|
}
|
|
22561
23297
|
}
|
|
22562
23298
|
const teamRoot = teamStateRoot(cwd, sanitized);
|
|
22563
|
-
if (
|
|
23299
|
+
if (existsSync35(teamRoot)) {
|
|
22564
23300
|
try {
|
|
22565
23301
|
await rm9(teamRoot, { recursive: true, force: true });
|
|
22566
23302
|
} catch (error) {
|
|
@@ -22657,7 +23393,7 @@ async function shutdownTeamV2(teamName, cwd, options = {}) {
|
|
|
22657
23393
|
});
|
|
22658
23394
|
instance = boundInstance;
|
|
22659
23395
|
await assertTeamInstanceUnderLock(boundInstance);
|
|
22660
|
-
if (!current.config.tmux_session || !current.config.tmux_session.startsWith("cmux:") && !isValidTmuxServerIdentity(current.config.tmux_server_identity)) {
|
|
23396
|
+
if (!current.config.tmux_session || !current.config.tmux_session.startsWith("cmux:") && !isSdkTarget(current.config.tmux_session) && !isValidTmuxServerIdentity(current.config.tmux_server_identity)) {
|
|
22661
23397
|
throw new Error("tmux_server_identity_missing");
|
|
22662
23398
|
}
|
|
22663
23399
|
if (current.config.active_recovery) throw new Error(`shutdown_blocked:active_recovery:${current.config.active_recovery.recovery_id}`);
|
|
@@ -22688,7 +23424,7 @@ async function shutdownTeamV2(teamName, cwd, options = {}) {
|
|
|
22688
23424
|
}
|
|
22689
23425
|
const processStartedAt = currentProcessStartIdentity();
|
|
22690
23426
|
if (!processStartedAt) throw new Error("process_start_identity_unavailable");
|
|
22691
|
-
ownedShutdownNonce =
|
|
23427
|
+
ownedShutdownNonce = randomUUID17();
|
|
22692
23428
|
const nextRevision = current.stateRevision + 1;
|
|
22693
23429
|
const next = {
|
|
22694
23430
|
...current.config,
|
|
@@ -22796,7 +23532,7 @@ async function shutdownTeamV2(teamName, cwd, options = {}) {
|
|
|
22796
23532
|
};
|
|
22797
23533
|
if (!config) {
|
|
22798
23534
|
const cleanupSafety = inspectTeamWorktreeCleanupSafety(sanitized, cwd);
|
|
22799
|
-
if (cleanupSafety.hasEvidence ||
|
|
23535
|
+
if (cleanupSafety.hasEvidence || existsSync35(absPath(cwd, TeamPaths.root(sanitized)))) {
|
|
22800
23536
|
process.stderr.write("[team/runtime-v2] preserving team state because config is missing and worktree cleanup evidence remains\n");
|
|
22801
23537
|
return { outcome: "preserved", reason: "config_missing_cleanup_evidence", workers: [] };
|
|
22802
23538
|
}
|
|
@@ -22828,6 +23564,7 @@ Format: {"status":"accept","reason":"ok","updated_at":"<iso>"}
|
|
|
22828
23564
|
Then exit your session.
|
|
22829
23565
|
`;
|
|
22830
23566
|
await writeWorkerInbox(sanitized, w.name, shutdownInbox, cwd);
|
|
23567
|
+
if (isSdkTarget(w.pane_id)) ringDoorbell(teamStateRoot(cwd, sanitized), w.name, { kind: "shutdown", from: "leader-fixed" });
|
|
22831
23568
|
} catch (err) {
|
|
22832
23569
|
process.stderr.write(`[team/runtime-v2] shutdown request failed for ${w.name}: ${err}
|
|
22833
23570
|
`);
|
|
@@ -22868,6 +23605,30 @@ Then exit your session.
|
|
|
22868
23605
|
const providerCleanupFailures = [];
|
|
22869
23606
|
const paneCleanupAlive = [];
|
|
22870
23607
|
const paneCleanupUnknown = [];
|
|
23608
|
+
const sdkStopTimeoutMs = Math.max(timeoutMs, SDK_HOST_STOP_FLOOR_MS);
|
|
23609
|
+
const sdkStops = /* @__PURE__ */ new Map();
|
|
23610
|
+
for (const worker of config.workers) {
|
|
23611
|
+
if (!isSdkTarget(worker.pane_id) || !worker.launch_attempt_id || !config.tmux_session) continue;
|
|
23612
|
+
const provider = worker.launch_descriptor?.provider ?? worker.worker_cli;
|
|
23613
|
+
if (!provider) continue;
|
|
23614
|
+
const attemptId = worker.launch_attempt_id;
|
|
23615
|
+
const paneId = worker.pane_id;
|
|
23616
|
+
sdkStops.set(worker.name, (async () => {
|
|
23617
|
+
const sdkAttempt = await loadWorkerLaunchAttempt({
|
|
23618
|
+
cwd,
|
|
23619
|
+
teamName: sanitized,
|
|
23620
|
+
instanceId: shutdownInstance.instance_id,
|
|
23621
|
+
workerName: worker.name,
|
|
23622
|
+
paneId,
|
|
23623
|
+
provider,
|
|
23624
|
+
attemptId,
|
|
23625
|
+
runtimeCliPath: resolveRuntimeCliPath()
|
|
23626
|
+
});
|
|
23627
|
+
if (!sdkAttempt) return false;
|
|
23628
|
+
if (!await stopSdkHost(sdkAttempt, teamStateRoot(cwd, sanitized), { timeoutMs: sdkStopTimeoutMs })) return false;
|
|
23629
|
+
return retireAndCleanupCurrentWorkerLaunchAttempt(sdkAttempt, "team_shutdown", async () => true);
|
|
23630
|
+
})().catch(() => false));
|
|
23631
|
+
}
|
|
22871
23632
|
for (const worker of config.workers) {
|
|
22872
23633
|
if (!worker.pane_id) {
|
|
22873
23634
|
providerCleanupFailures.push(worker.name);
|
|
@@ -22882,6 +23643,10 @@ Then exit your session.
|
|
|
22882
23643
|
providerCleanupFailures.push(worker.name);
|
|
22883
23644
|
continue;
|
|
22884
23645
|
}
|
|
23646
|
+
if (isSdkTarget(worker.pane_id)) {
|
|
23647
|
+
if (!await (sdkStops.get(worker.name) ?? Promise.resolve(false))) providerCleanupFailures.push(worker.name);
|
|
23648
|
+
continue;
|
|
23649
|
+
}
|
|
22885
23650
|
const initialOwnership = configuredPaneOwnership(config, worker);
|
|
22886
23651
|
const initialPaneLiveness = initialOwnership ? await getOwnedWorkerLiveness(initialOwnership) : "unknown";
|
|
22887
23652
|
let paneOwnership = null;
|
|
@@ -22950,9 +23715,9 @@ Then exit your session.
|
|
|
22950
23715
|
killTeamSession: killOwnedTeamSession
|
|
22951
23716
|
} = await Promise.resolve().then(() => (init_tmux_session(), tmux_session_exports));
|
|
22952
23717
|
const ownsWindow = config.tmux_window_owned === true;
|
|
22953
|
-
const workerPaneIds = recordedWorkerPaneIds;
|
|
23718
|
+
const workerPaneIds = recordedWorkerPaneIds.filter((paneId) => !isSdkTarget(paneId));
|
|
22954
23719
|
const splitPaneMode = Boolean(config.tmux_session && !ownsWindow && config.tmux_session.includes(":"));
|
|
22955
|
-
if (!splitPaneMode && config.tmux_session) {
|
|
23720
|
+
if (!splitPaneMode && config.tmux_session && !isSdkTarget(config.tmux_session)) {
|
|
22956
23721
|
if (!config.leader_pane_id) {
|
|
22957
23722
|
if (!await rollbackShutdownForRetry()) await finalizeAutoMerge();
|
|
22958
23723
|
return { outcome: "preserved", reason: "provider_cleanup_unverified", workers: ["leader-fixed"] };
|
|
@@ -23078,7 +23843,7 @@ async function resumeTeamV2(teamName, cwd) {
|
|
|
23078
23843
|
const config = await readTeamConfig(sanitized, cwd);
|
|
23079
23844
|
if (!config?.instance_id) return null;
|
|
23080
23845
|
const sessionName2 = config.tmux_session || `omc-team-${sanitized}`;
|
|
23081
|
-
if (sessionName2.startsWith("cmux:")) {
|
|
23846
|
+
if (sessionName2.startsWith("cmux:") || isSdkTarget(sessionName2)) {
|
|
23082
23847
|
return {
|
|
23083
23848
|
teamName: sanitized,
|
|
23084
23849
|
sanitizedName: sanitized,
|
|
@@ -23113,8 +23878,8 @@ async function resumeTeamV2(teamName, cwd) {
|
|
|
23113
23878
|
};
|
|
23114
23879
|
}
|
|
23115
23880
|
async function findActiveTeamsV2(cwd) {
|
|
23116
|
-
const root =
|
|
23117
|
-
if (!
|
|
23881
|
+
const root = join42(getOmcRoot(cwd), "state", "team");
|
|
23882
|
+
if (!existsSync35(root)) return [];
|
|
23118
23883
|
const entries = await readdir6(root, { withFileTypes: true });
|
|
23119
23884
|
const active = [];
|
|
23120
23885
|
for (const e of entries) {
|
|
@@ -23170,6 +23935,11 @@ var init_runtime_v2 = __esm({
|
|
|
23170
23935
|
init_team_owner_epoch();
|
|
23171
23936
|
init_worker_activation_gate();
|
|
23172
23937
|
init_worker_launch_ack();
|
|
23938
|
+
init_sdk_transport();
|
|
23939
|
+
init_model_contract();
|
|
23940
|
+
init_copilot_sdk_driver();
|
|
23941
|
+
init_copilot_session_env();
|
|
23942
|
+
init_config_dir();
|
|
23173
23943
|
init_process_utils();
|
|
23174
23944
|
init_team_instance();
|
|
23175
23945
|
init_runtime_flags();
|
|
@@ -23271,19 +24041,19 @@ var init_runtime_v2 = __esm({
|
|
|
23271
24041
|
});
|
|
23272
24042
|
|
|
23273
24043
|
// src/cli/team.ts
|
|
23274
|
-
import { randomUUID as
|
|
23275
|
-
import { spawn as
|
|
24044
|
+
import { randomUUID as randomUUID18 } from "crypto";
|
|
24045
|
+
import { spawn as spawn3 } from "child_process";
|
|
23276
24046
|
import {
|
|
23277
|
-
existsSync as
|
|
23278
|
-
mkdirSync as
|
|
23279
|
-
readFileSync as
|
|
24047
|
+
existsSync as existsSync38,
|
|
24048
|
+
mkdirSync as mkdirSync10,
|
|
24049
|
+
readFileSync as readFileSync25,
|
|
23280
24050
|
renameSync as renameSync5,
|
|
23281
24051
|
unlinkSync as unlinkSync10,
|
|
23282
|
-
writeFileSync as
|
|
24052
|
+
writeFileSync as writeFileSync10
|
|
23283
24053
|
} from "fs";
|
|
23284
24054
|
import { readFile as readFile18 } from "fs/promises";
|
|
23285
|
-
import { dirname as
|
|
23286
|
-
import { fileURLToPath as
|
|
24055
|
+
import { dirname as dirname34, join as join46, resolve as resolve16 } from "path";
|
|
24056
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
23287
24057
|
|
|
23288
24058
|
// src/team/api-interop.ts
|
|
23289
24059
|
init_worktree_paths();
|
|
@@ -23296,13 +24066,14 @@ init_dispatch_queue();
|
|
|
23296
24066
|
init_mailbox_notification_guard();
|
|
23297
24067
|
init_dispatch_queue();
|
|
23298
24068
|
init_worker_bootstrap();
|
|
24069
|
+
init_sdk_transport();
|
|
23299
24070
|
init_runtime_v2();
|
|
23300
24071
|
init_recovery_request_store();
|
|
23301
24072
|
init_git_worktree();
|
|
23302
24073
|
init_swallowed_error();
|
|
23303
24074
|
init_types();
|
|
23304
|
-
import { existsSync as
|
|
23305
|
-
import { dirname as
|
|
24075
|
+
import { existsSync as existsSync36, readFileSync as readFileSync23 } from "node:fs";
|
|
24076
|
+
import { dirname as dirname33, join as join43, resolve as resolvePath } from "node:path";
|
|
23306
24077
|
var TEAM_UPDATE_TASK_MUTABLE_FIELDS = /* @__PURE__ */ new Set(["subject", "description", "blocked_by", "requires_code_change", "delegation"]);
|
|
23307
24078
|
var TEAM_UPDATE_TASK_REQUEST_FIELDS = /* @__PURE__ */ new Set(["team_name", "task_id", "workingDirectory", ...TEAM_UPDATE_TASK_MUTABLE_FIELDS]);
|
|
23308
24079
|
var RECOVER_WORKER_REQUEST_FIELDS = /* @__PURE__ */ new Set(["team_name", "worker", "request_id", "timeout_ms"]);
|
|
@@ -23457,8 +24228,8 @@ function parseTaskDelegationPlan(value) {
|
|
|
23457
24228
|
}
|
|
23458
24229
|
function teamStateExists(teamName, candidateCwd) {
|
|
23459
24230
|
if (!TEAM_NAME_SAFE_PATTERN.test(teamName)) return false;
|
|
23460
|
-
const teamRoot =
|
|
23461
|
-
return
|
|
24231
|
+
const teamRoot = join43(getOmcRoot(candidateCwd), "state", "team", teamName);
|
|
24232
|
+
return existsSync36(join43(teamRoot, "config.json")) || existsSync36(join43(teamRoot, "tasks")) || existsSync36(teamRoot);
|
|
23462
24233
|
}
|
|
23463
24234
|
function parseTeamWorkerEnv(raw) {
|
|
23464
24235
|
if (typeof raw !== "string" || raw.trim() === "") return null;
|
|
@@ -23520,9 +24291,9 @@ async function executeTeamCleanupViaRuntime(teamName, cwd, options = {}) {
|
|
|
23520
24291
|
if (shutdown.outcome !== "cleaned") throw new Error(`team_shutdown_${shutdown.outcome}:${shutdown.reason}`);
|
|
23521
24292
|
}
|
|
23522
24293
|
function readTeamStateRootFromFile(path5) {
|
|
23523
|
-
if (!
|
|
24294
|
+
if (!existsSync36(path5)) return null;
|
|
23524
24295
|
try {
|
|
23525
|
-
const parsed = JSON.parse(
|
|
24296
|
+
const parsed = JSON.parse(readFileSync23(path5, "utf8"));
|
|
23526
24297
|
return typeof parsed.team_state_root === "string" && parsed.team_state_root.trim() !== "" ? parsed.team_state_root.trim() : null;
|
|
23527
24298
|
} catch {
|
|
23528
24299
|
return null;
|
|
@@ -23536,7 +24307,7 @@ function stateRootToWorkingDirectory(stateRoot2) {
|
|
|
23536
24307
|
if (idx >= 0) {
|
|
23537
24308
|
const workspaceRoot = absolute.slice(0, idx);
|
|
23538
24309
|
if (workspaceRoot && workspaceRoot !== "/") return workspaceRoot;
|
|
23539
|
-
return
|
|
24310
|
+
return dirname33(dirname33(dirname33(dirname33(absolute))));
|
|
23540
24311
|
}
|
|
23541
24312
|
}
|
|
23542
24313
|
for (const marker of ["/.omg/state", "/.omx/state"]) {
|
|
@@ -23544,22 +24315,22 @@ function stateRootToWorkingDirectory(stateRoot2) {
|
|
|
23544
24315
|
if (idx >= 0) {
|
|
23545
24316
|
const workspaceRoot = absolute.slice(0, idx);
|
|
23546
24317
|
if (workspaceRoot && workspaceRoot !== "/") return workspaceRoot;
|
|
23547
|
-
return
|
|
24318
|
+
return dirname33(dirname33(absolute));
|
|
23548
24319
|
}
|
|
23549
24320
|
}
|
|
23550
|
-
return
|
|
24321
|
+
return dirname33(dirname33(absolute));
|
|
23551
24322
|
}
|
|
23552
24323
|
function resolveTeamWorkingDirectoryFromMetadata(teamName, candidateCwd, workerContext) {
|
|
23553
|
-
const teamRoot =
|
|
23554
|
-
if (!
|
|
24324
|
+
const teamRoot = join43(getOmcRoot(candidateCwd), "state", "team", teamName);
|
|
24325
|
+
if (!existsSync36(teamRoot)) return null;
|
|
23555
24326
|
if (workerContext?.teamName === teamName) {
|
|
23556
|
-
const workerRoot = readTeamStateRootFromFile(
|
|
24327
|
+
const workerRoot = readTeamStateRootFromFile(join43(teamRoot, "workers", workerContext.workerName, "identity.json"));
|
|
23557
24328
|
if (workerRoot) return stateRootToWorkingDirectory(workerRoot);
|
|
23558
24329
|
}
|
|
23559
|
-
const fromConfig = readTeamStateRootFromFile(
|
|
24330
|
+
const fromConfig = readTeamStateRootFromFile(join43(teamRoot, "config.json"));
|
|
23560
24331
|
if (fromConfig) return stateRootToWorkingDirectory(fromConfig);
|
|
23561
24332
|
for (const manifestName of ["manifest.json", "manifest.v2.json"]) {
|
|
23562
|
-
const fromManifest = readTeamStateRootFromFile(
|
|
24333
|
+
const fromManifest = readTeamStateRootFromFile(join43(teamRoot, manifestName));
|
|
23563
24334
|
if (fromManifest) return stateRootToWorkingDirectory(fromManifest);
|
|
23564
24335
|
}
|
|
23565
24336
|
return null;
|
|
@@ -23590,7 +24361,7 @@ function resolveTeamWorkingDirectory(teamName, preferredCwd) {
|
|
|
23590
24361
|
if (teamStateExists(normalizedTeamName, cursor)) {
|
|
23591
24362
|
return resolveTeamWorkingDirectoryFromMetadata(normalizedTeamName, cursor, workerContext) ?? cursor;
|
|
23592
24363
|
}
|
|
23593
|
-
const parent =
|
|
24364
|
+
const parent = dirname33(cursor);
|
|
23594
24365
|
if (!parent || parent === cursor) break;
|
|
23595
24366
|
cursor = parent;
|
|
23596
24367
|
}
|
|
@@ -23622,9 +24393,9 @@ async function readMailboxGuardWithCanonicalLeaderTarget(input, cwd) {
|
|
|
23622
24393
|
};
|
|
23623
24394
|
return readCurrentMailboxNotificationGuard(input, cwd, {
|
|
23624
24395
|
readConfig,
|
|
23625
|
-
readStrictDispatchRequest: async (teamName, requestId,
|
|
24396
|
+
readStrictDispatchRequest: async (teamName, requestId, requestCwd2) => {
|
|
23626
24397
|
const [dispatch, config] = await Promise.all([
|
|
23627
|
-
readDispatchRequestStrict(teamName, requestId,
|
|
24398
|
+
readDispatchRequestStrict(teamName, requestId, requestCwd2),
|
|
23628
24399
|
readConfig()
|
|
23629
24400
|
]);
|
|
23630
24401
|
const canonicalLeaderPaneId = config?.leader_pane_id;
|
|
@@ -23638,6 +24409,11 @@ async function readMailboxGuardWithCanonicalLeaderTarget(input, cwd) {
|
|
|
23638
24409
|
});
|
|
23639
24410
|
}
|
|
23640
24411
|
async function notifyMailboxTarget(params) {
|
|
24412
|
+
const recipient = (await teamReadConfig(params.teamName, params.cwd).catch(() => null))?.workers.find((worker) => worker.name === params.toWorker);
|
|
24413
|
+
if (isSdkTarget(recipient?.pane_id)) {
|
|
24414
|
+
ringDoorbell(teamStateRoot(params.cwd, params.teamName), params.toWorker, { kind: "prompt", text: params.triggerMessage, from: "mailbox" });
|
|
24415
|
+
return { ok: true, transport: "mailbox", reason: "sdk_doorbell_queued", request_id: params.requestId, message_id: params.messageId, to_worker: params.toWorker };
|
|
24416
|
+
}
|
|
23641
24417
|
return runMailboxNotificationAttempt({
|
|
23642
24418
|
teamName: params.teamName,
|
|
23643
24419
|
recipient: params.toWorker,
|
|
@@ -24313,7 +25089,7 @@ init_tmux_session();
|
|
|
24313
25089
|
init_state_paths();
|
|
24314
25090
|
init_types();
|
|
24315
25091
|
import { readFile as readFile17 } from "fs/promises";
|
|
24316
|
-
import { join as
|
|
25092
|
+
import { join as join44 } from "path";
|
|
24317
25093
|
function stateRoot(cwd, teamName) {
|
|
24318
25094
|
validateTeamName2(teamName);
|
|
24319
25095
|
return teamStateRoot(cwd, teamName);
|
|
@@ -24326,7 +25102,7 @@ async function readJsonSafe3(filePath) {
|
|
|
24326
25102
|
}
|
|
24327
25103
|
}
|
|
24328
25104
|
function taskPath(root, taskId) {
|
|
24329
|
-
return
|
|
25105
|
+
return join44(root, "tasks", `${normalizeTaskFileStem(taskId)}.json`);
|
|
24330
25106
|
}
|
|
24331
25107
|
async function readTask(root, taskId) {
|
|
24332
25108
|
return readJsonSafe3(taskPath(root, taskId));
|
|
@@ -24339,9 +25115,9 @@ async function monitorTeam(teamName, cwd, workerPaneIds) {
|
|
|
24339
25115
|
const taskCounts = { pending: 0, inProgress: 0, completed: 0, failed: 0 };
|
|
24340
25116
|
try {
|
|
24341
25117
|
const { readdir: readdir7 } = await import("fs/promises");
|
|
24342
|
-
const taskFiles = await readdir7(
|
|
25118
|
+
const taskFiles = await readdir7(join44(root, "tasks"));
|
|
24343
25119
|
for (const f of taskFiles.filter((f2) => f2.endsWith(".json"))) {
|
|
24344
|
-
const task = await readJsonSafe3(
|
|
25120
|
+
const task = await readJsonSafe3(join44(root, "tasks", f));
|
|
24345
25121
|
if (task?.status === "pending") taskCounts.pending++;
|
|
24346
25122
|
else if (task?.status === "in_progress") taskCounts.inProgress++;
|
|
24347
25123
|
else if (task?.status === "completed") taskCounts.completed++;
|
|
@@ -24357,7 +25133,7 @@ async function monitorTeam(teamName, cwd, workerPaneIds) {
|
|
|
24357
25133
|
const wName = `worker-${i + 1}`;
|
|
24358
25134
|
const paneId = workerPaneIds[i];
|
|
24359
25135
|
const alive = await isWorkerAlive(paneId);
|
|
24360
|
-
const heartbeatPath =
|
|
25136
|
+
const heartbeatPath = join44(root, "workers", wName, "heartbeat.json");
|
|
24361
25137
|
const heartbeat = await readJsonSafe3(heartbeatPath);
|
|
24362
25138
|
let stalled = false;
|
|
24363
25139
|
if (heartbeat?.updatedAt) {
|
|
@@ -24399,7 +25175,7 @@ async function monitorTeam(teamName, cwd, workerPaneIds) {
|
|
|
24399
25175
|
}
|
|
24400
25176
|
async function resumeTeam(teamName, cwd) {
|
|
24401
25177
|
const root = stateRoot(cwd, teamName);
|
|
24402
|
-
const configData = await readJsonSafe3(
|
|
25178
|
+
const configData = await readJsonSafe3(join44(root, "config.json"));
|
|
24403
25179
|
if (!configData || !isValidTeamInstanceId(configData.instance_id)) return null;
|
|
24404
25180
|
const sName = configData.tmuxSession;
|
|
24405
25181
|
const leaderPaneId = configData.leaderPaneId;
|
|
@@ -24494,14 +25270,14 @@ init_paths();
|
|
|
24494
25270
|
|
|
24495
25271
|
// src/planning/artifacts.ts
|
|
24496
25272
|
init_worktree_paths();
|
|
24497
|
-
import { readdirSync as
|
|
24498
|
-
import { join as
|
|
25273
|
+
import { readdirSync as readdirSync14, readFileSync as readFileSync24, existsSync as existsSync37 } from "fs";
|
|
25274
|
+
import { join as join45 } from "path";
|
|
24499
25275
|
|
|
24500
25276
|
// src/planning/artifact-names.ts
|
|
24501
|
-
import { basename as
|
|
25277
|
+
import { basename as basename16 } from "path";
|
|
24502
25278
|
var PLANNING_ARTIFACT_TIMESTAMP_PATTERN = /^\d{8}T\d{6}Z$/;
|
|
24503
25279
|
function legacyTestSpecSlug(fileNameOrPath) {
|
|
24504
|
-
const match =
|
|
25280
|
+
const match = basename16(fileNameOrPath).match(/^test-?spec-(?<slug>.+)\.md$/i);
|
|
24505
25281
|
return match?.groups?.slug ?? null;
|
|
24506
25282
|
}
|
|
24507
25283
|
function requiredTimestampedTestSpecFileName(prdArtifact) {
|
|
@@ -24522,7 +25298,7 @@ function splitTimestampPrefix(rawSlug) {
|
|
|
24522
25298
|
};
|
|
24523
25299
|
}
|
|
24524
25300
|
function parsePlanningArtifactFileName(fileNameOrPath) {
|
|
24525
|
-
const fileName =
|
|
25301
|
+
const fileName = basename16(fileNameOrPath);
|
|
24526
25302
|
const autoresearchDeepInterviewMatch = fileName.match(
|
|
24527
25303
|
/^deep-interview-autoresearch-(?<slug>.+)\.md$/i
|
|
24528
25304
|
);
|
|
@@ -24586,7 +25362,7 @@ function selectMatchingTestSpecsForPrd(prdPath, testSpecPaths) {
|
|
|
24586
25362
|
return [];
|
|
24587
25363
|
}
|
|
24588
25364
|
const requiredTimestampedFileName = requiredTimestampedTestSpecFileName(prdArtifact);
|
|
24589
|
-
return (requiredTimestampedFileName ? testSpecPaths.filter((path5) =>
|
|
25365
|
+
return (requiredTimestampedFileName ? testSpecPaths.filter((path5) => basename16(path5) === requiredTimestampedFileName) : testSpecPaths.filter((path5) => legacyTestSpecSlug(path5) === prdArtifact.slug)).sort(comparePlanningArtifactPaths);
|
|
24590
25366
|
}
|
|
24591
25367
|
function selectLatestPlanningArtifactPath(paths) {
|
|
24592
25368
|
return [...paths].sort(comparePlanningArtifactPaths).at(-1) ?? null;
|
|
@@ -24595,7 +25371,7 @@ function selectLatestPlanningArtifactPath(paths) {
|
|
|
24595
25371
|
// src/planning/artifacts.ts
|
|
24596
25372
|
function readFileSafe(path5) {
|
|
24597
25373
|
try {
|
|
24598
|
-
return
|
|
25374
|
+
return readFileSync24(path5, "utf-8");
|
|
24599
25375
|
} catch {
|
|
24600
25376
|
return null;
|
|
24601
25377
|
}
|
|
@@ -24622,7 +25398,7 @@ function hasRequiredSections(markdown, headings) {
|
|
|
24622
25398
|
);
|
|
24623
25399
|
}
|
|
24624
25400
|
function getPlansDirCandidates(cwd) {
|
|
24625
|
-
return [
|
|
25401
|
+
return [join45(getOmcRoot(cwd), "plans"), join45(cwd, ".omx", "plans")];
|
|
24626
25402
|
}
|
|
24627
25403
|
function sortArtifactPathsDescending(paths) {
|
|
24628
25404
|
return [...paths].sort((a, b) => comparePlanningArtifactPaths(b, a));
|
|
@@ -24649,19 +25425,19 @@ function readPlanningArtifacts(cwd) {
|
|
|
24649
25425
|
const prdPaths = [];
|
|
24650
25426
|
const testSpecPaths = [];
|
|
24651
25427
|
for (const plansDir of getPlansDirCandidates(cwd)) {
|
|
24652
|
-
if (!
|
|
25428
|
+
if (!existsSync37(plansDir)) {
|
|
24653
25429
|
continue;
|
|
24654
25430
|
}
|
|
24655
25431
|
try {
|
|
24656
|
-
entries =
|
|
25432
|
+
entries = readdirSync14(plansDir);
|
|
24657
25433
|
} catch {
|
|
24658
25434
|
continue;
|
|
24659
25435
|
}
|
|
24660
25436
|
for (const entry of entries) {
|
|
24661
25437
|
if (entry.startsWith("prd-") && entry.endsWith(".md")) {
|
|
24662
|
-
prdPaths.push(
|
|
25438
|
+
prdPaths.push(join45(plansDir, entry));
|
|
24663
25439
|
} else if (entry.startsWith("test-spec-") && entry.endsWith(".md")) {
|
|
24664
|
-
testSpecPaths.push(
|
|
25440
|
+
testSpecPaths.push(join45(plansDir, entry));
|
|
24665
25441
|
}
|
|
24666
25442
|
}
|
|
24667
25443
|
}
|
|
@@ -24836,22 +25612,22 @@ function resolveRuntimeCliPath2(env = process.env) {
|
|
|
24836
25612
|
if (env.OMC_RUNTIME_CLI_PATH) {
|
|
24837
25613
|
return env.OMC_RUNTIME_CLI_PATH;
|
|
24838
25614
|
}
|
|
24839
|
-
const moduleDir =
|
|
24840
|
-
return
|
|
25615
|
+
const moduleDir = dirname34(fileURLToPath6(import.meta.url));
|
|
25616
|
+
return join46(moduleDir, "../../bridge/runtime-cli.cjs");
|
|
24841
25617
|
}
|
|
24842
25618
|
function ensureJobsDir(jobsDir) {
|
|
24843
|
-
if (!
|
|
24844
|
-
|
|
25619
|
+
if (!existsSync38(jobsDir)) {
|
|
25620
|
+
mkdirSync10(jobsDir, { recursive: true });
|
|
24845
25621
|
}
|
|
24846
25622
|
}
|
|
24847
25623
|
function jobPath(jobsDir, jobId) {
|
|
24848
|
-
return
|
|
25624
|
+
return join46(jobsDir, `${jobId}.json`);
|
|
24849
25625
|
}
|
|
24850
25626
|
function resultArtifactPath(jobsDir, jobId) {
|
|
24851
|
-
return
|
|
25627
|
+
return join46(jobsDir, `${jobId}-result.json`);
|
|
24852
25628
|
}
|
|
24853
25629
|
function panesArtifactPath(jobsDir, jobId) {
|
|
24854
|
-
return
|
|
25630
|
+
return join46(jobsDir, `${jobId}-panes.json`);
|
|
24855
25631
|
}
|
|
24856
25632
|
function validateJobId(jobId) {
|
|
24857
25633
|
if (!JOB_ID_PATTERN.test(jobId)) {
|
|
@@ -24916,7 +25692,7 @@ async function readPaneArtifact(jobsDir, jobId, instanceId) {
|
|
|
24916
25692
|
function resultArtifactIdentityError(jobsDir, jobId, instanceId) {
|
|
24917
25693
|
let raw;
|
|
24918
25694
|
try {
|
|
24919
|
-
raw =
|
|
25695
|
+
raw = readFileSync25(resultArtifactPath(jobsDir, jobId), "utf-8");
|
|
24920
25696
|
} catch (error) {
|
|
24921
25697
|
if (error.code === "ENOENT") return null;
|
|
24922
25698
|
return `cleanup_result_evidence_unreadable:${error instanceof Error ? error.message : String(error)}`;
|
|
@@ -24930,7 +25706,7 @@ function resultArtifactIdentityError(jobsDir, jobId, instanceId) {
|
|
|
24930
25706
|
function readJobFromDisk(jobId, jobsDir) {
|
|
24931
25707
|
let content;
|
|
24932
25708
|
try {
|
|
24933
|
-
content =
|
|
25709
|
+
content = readFileSync25(jobPath(jobsDir, jobId), "utf-8");
|
|
24934
25710
|
} catch (error) {
|
|
24935
25711
|
const code = error.code;
|
|
24936
25712
|
if (code === "ENOENT") return { kind: "missing" };
|
|
@@ -24954,14 +25730,14 @@ function formatJobReadError(jobId, result) {
|
|
|
24954
25730
|
}
|
|
24955
25731
|
}
|
|
24956
25732
|
function jobLockPath(jobsDir, jobId) {
|
|
24957
|
-
return
|
|
25733
|
+
return join46(jobsDir, `.${jobId}.lock`);
|
|
24958
25734
|
}
|
|
24959
25735
|
function writeJobToDiskUnlocked(jobId, job, jobsDir) {
|
|
24960
25736
|
ensureJobsDir(jobsDir);
|
|
24961
25737
|
const targetPath = jobPath(jobsDir, jobId);
|
|
24962
|
-
const tempPath = `${targetPath}.tmp.${process.pid}.${
|
|
25738
|
+
const tempPath = `${targetPath}.tmp.${process.pid}.${randomUUID18()}`;
|
|
24963
25739
|
try {
|
|
24964
|
-
|
|
25740
|
+
writeFileSync10(tempPath, JSON.stringify(job), { encoding: "utf-8", mode: 384 });
|
|
24965
25741
|
renameSync5(tempPath, targetPath);
|
|
24966
25742
|
} finally {
|
|
24967
25743
|
try {
|
|
@@ -25082,11 +25858,11 @@ function buildStatus(jobId, job) {
|
|
|
25082
25858
|
};
|
|
25083
25859
|
}
|
|
25084
25860
|
function generateJobId(now = Date.now()) {
|
|
25085
|
-
return `omc-${now.toString(36)}${
|
|
25861
|
+
return `omc-${now.toString(36)}${randomUUID18().slice(0, 8)}`;
|
|
25086
25862
|
}
|
|
25087
25863
|
function convergeWithResultArtifact(jobId, job, jobsDir) {
|
|
25088
25864
|
try {
|
|
25089
|
-
const artifactRaw =
|
|
25865
|
+
const artifactRaw = readFileSync25(resultArtifactPath(jobsDir, jobId), "utf-8");
|
|
25090
25866
|
const artifactParsed = parseJsonSafe(artifactRaw);
|
|
25091
25867
|
if (!artifactParsed) throw new Error("result_artifact_parse_failed");
|
|
25092
25868
|
if (artifactParsed.status !== "completed" && artifactParsed.status !== "failed") {
|
|
@@ -25159,7 +25935,7 @@ async function startTeamJob(input) {
|
|
|
25159
25935
|
if (typeof input.cwd !== "string" || input.cwd.trim().length === 0) {
|
|
25160
25936
|
throw new Error("cwd must be a non-empty path");
|
|
25161
25937
|
}
|
|
25162
|
-
const cwd =
|
|
25938
|
+
const cwd = resolve16(input.cwd);
|
|
25163
25939
|
await assertTeamSpawnAllowed(cwd);
|
|
25164
25940
|
if (!Array.isArray(input.agentTypes) || input.agentTypes.length === 0) {
|
|
25165
25941
|
throw new Error("agentTypes must be a non-empty array");
|
|
@@ -25176,7 +25952,7 @@ async function startTeamJob(input) {
|
|
|
25176
25952
|
const jobsDir = resolveJobsDir();
|
|
25177
25953
|
const runtimeCliPath = resolveRuntimeCliPath2();
|
|
25178
25954
|
const jobId = generateJobId();
|
|
25179
|
-
const instanceId =
|
|
25955
|
+
const instanceId = randomUUID18();
|
|
25180
25956
|
const job = {
|
|
25181
25957
|
status: "running",
|
|
25182
25958
|
startedAt: Date.now(),
|
|
@@ -25187,7 +25963,7 @@ async function startTeamJob(input) {
|
|
|
25187
25963
|
writeJobToDisk(jobId, job, jobsDir);
|
|
25188
25964
|
let child;
|
|
25189
25965
|
try {
|
|
25190
|
-
child =
|
|
25966
|
+
child = spawn3(process.execPath, [runtimeCliPath], {
|
|
25191
25967
|
env: {
|
|
25192
25968
|
...process.env,
|
|
25193
25969
|
OMC_JOB_ID: jobId,
|
|
@@ -25291,7 +26067,7 @@ async function waitForTeamJob(jobId, options = {}) {
|
|
|
25291
26067
|
if (status2.status !== "running") {
|
|
25292
26068
|
return status2;
|
|
25293
26069
|
}
|
|
25294
|
-
await new Promise((
|
|
26070
|
+
await new Promise((resolve17) => setTimeout(resolve17, delayMs));
|
|
25295
26071
|
delayMs = Math.min(Math.floor(delayMs * 1.5), 2e3);
|
|
25296
26072
|
}
|
|
25297
26073
|
const status = await getTeamJobStatus(jobId);
|