oh-my-copilot 5.7.0 → 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.
Files changed (185) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +1 -0
  4. package/bridge/__pycache__/gyoshu_bridge.cpython-312.pyc +0 -0
  5. package/bridge/claude-md-coordinator.cjs +2 -2
  6. package/bridge/cli.cjs +6268 -4231
  7. package/bridge/mcp-server.cjs +4345 -735
  8. package/bridge/runtime-cli.cjs +848 -165
  9. package/bridge/team-mcp.cjs +621 -91
  10. package/bridge/team.js +960 -245
  11. package/dist/__tests__/cli-win32-warning.test.js +7 -0
  12. package/dist/__tests__/cli-win32-warning.test.js.map +1 -1
  13. package/dist/__tests__/run-cjs-copilot-worker-routing.test.d.ts +2 -0
  14. package/dist/__tests__/run-cjs-copilot-worker-routing.test.d.ts.map +1 -0
  15. package/dist/__tests__/run-cjs-copilot-worker-routing.test.js +294 -0
  16. package/dist/__tests__/run-cjs-copilot-worker-routing.test.js.map +1 -0
  17. package/dist/__tests__/run-cjs-session-end-budget.test.d.ts +2 -0
  18. package/dist/__tests__/run-cjs-session-end-budget.test.d.ts.map +1 -0
  19. package/dist/__tests__/run-cjs-session-end-budget.test.js +171 -0
  20. package/dist/__tests__/run-cjs-session-end-budget.test.js.map +1 -0
  21. package/dist/cli/__tests__/doctor-conflicts-json.test.d.ts +2 -0
  22. package/dist/cli/__tests__/doctor-conflicts-json.test.d.ts.map +1 -0
  23. package/dist/cli/__tests__/doctor-conflicts-json.test.js +46 -0
  24. package/dist/cli/__tests__/doctor-conflicts-json.test.js.map +1 -0
  25. package/dist/cli/__tests__/team.test.js +67 -0
  26. package/dist/cli/__tests__/team.test.js.map +1 -1
  27. package/dist/cli/__tests__/wait-teleport-json.test.d.ts +2 -0
  28. package/dist/cli/__tests__/wait-teleport-json.test.d.ts.map +1 -0
  29. package/dist/cli/__tests__/wait-teleport-json.test.js +64 -0
  30. package/dist/cli/__tests__/wait-teleport-json.test.js.map +1 -0
  31. package/dist/cli/commands/__tests__/smoke-tier2.test.js +38 -0
  32. package/dist/cli/commands/__tests__/smoke-tier2.test.js.map +1 -1
  33. package/dist/cli/commands/__tests__/team-status-json.test.d.ts +2 -0
  34. package/dist/cli/commands/__tests__/team-status-json.test.d.ts.map +1 -0
  35. package/dist/cli/commands/__tests__/team-status-json.test.js +141 -0
  36. package/dist/cli/commands/__tests__/team-status-json.test.js.map +1 -0
  37. package/dist/cli/commands/factory.d.ts +2 -0
  38. package/dist/cli/commands/factory.d.ts.map +1 -1
  39. package/dist/cli/commands/factory.js +8 -1
  40. package/dist/cli/commands/factory.js.map +1 -1
  41. package/dist/cli/commands/smoke.d.ts +1 -1
  42. package/dist/cli/commands/smoke.d.ts.map +1 -1
  43. package/dist/cli/commands/smoke.js +4 -4
  44. package/dist/cli/commands/smoke.js.map +1 -1
  45. package/dist/cli/commands/team.d.ts +2 -0
  46. package/dist/cli/commands/team.d.ts.map +1 -1
  47. package/dist/cli/commands/team.js +124 -10
  48. package/dist/cli/commands/team.js.map +1 -1
  49. package/dist/cli/index.d.ts.map +1 -1
  50. package/dist/cli/index.js +34 -13
  51. package/dist/cli/index.js.map +1 -1
  52. package/dist/cli/win32-warning.d.ts +7 -1
  53. package/dist/cli/win32-warning.d.ts.map +1 -1
  54. package/dist/cli/win32-warning.js +9 -1
  55. package/dist/cli/win32-warning.js.map +1 -1
  56. package/dist/factory/__tests__/check-run.test.js +2 -2
  57. package/dist/factory/__tests__/check-run.test.js.map +1 -1
  58. package/dist/factory/__tests__/listener.test.js +1 -1
  59. package/dist/factory/__tests__/listener.test.js.map +1 -1
  60. package/dist/factory/__tests__/status.test.js +42 -0
  61. package/dist/factory/__tests__/status.test.js.map +1 -1
  62. package/dist/factory/listener.d.ts.map +1 -1
  63. package/dist/factory/listener.js +7 -8
  64. package/dist/factory/listener.js.map +1 -1
  65. package/dist/factory/status.d.ts +19 -1
  66. package/dist/factory/status.d.ts.map +1 -1
  67. package/dist/factory/status.js +49 -8
  68. package/dist/factory/status.js.map +1 -1
  69. package/dist/factory/watchdog.d.ts.map +1 -1
  70. package/dist/factory/watchdog.js +14 -0
  71. package/dist/factory/watchdog.js.map +1 -1
  72. package/dist/factory/watchdog.test.js +45 -0
  73. package/dist/factory/watchdog.test.js.map +1 -1
  74. package/dist/hooks/session-end/__tests__/action-runner.test.js +5 -0
  75. package/dist/hooks/session-end/__tests__/action-runner.test.js.map +1 -1
  76. package/dist/hooks/session-end/__tests__/chain-enqueuer.test.js +186 -1
  77. package/dist/hooks/session-end/__tests__/chain-enqueuer.test.js.map +1 -1
  78. package/dist/hooks/session-end/__tests__/chain-link-env-passthrough.test.d.ts +2 -0
  79. package/dist/hooks/session-end/__tests__/chain-link-env-passthrough.test.d.ts.map +1 -0
  80. package/dist/hooks/session-end/__tests__/chain-link-env-passthrough.test.js +49 -0
  81. package/dist/hooks/session-end/__tests__/chain-link-env-passthrough.test.js.map +1 -0
  82. package/dist/hooks/session-end/__tests__/foreground-bootstrap.test.js +30 -1
  83. package/dist/hooks/session-end/__tests__/foreground-bootstrap.test.js.map +1 -1
  84. package/dist/hooks/session-end/__tests__/spawn-next.test.js +104 -3
  85. package/dist/hooks/session-end/__tests__/spawn-next.test.js.map +1 -1
  86. package/dist/hooks/session-end/__tests__/worker.test.js +6 -0
  87. package/dist/hooks/session-end/__tests__/worker.test.js.map +1 -1
  88. package/dist/hooks/session-end/action-runner.d.ts.map +1 -1
  89. package/dist/hooks/session-end/action-runner.js +2 -1
  90. package/dist/hooks/session-end/action-runner.js.map +1 -1
  91. package/dist/hooks/session-end/chain-enqueuer.d.ts +70 -7
  92. package/dist/hooks/session-end/chain-enqueuer.d.ts.map +1 -1
  93. package/dist/hooks/session-end/chain-enqueuer.js +354 -82
  94. package/dist/hooks/session-end/chain-enqueuer.js.map +1 -1
  95. package/dist/hooks/session-end/foreground-bootstrap.d.ts.map +1 -1
  96. package/dist/hooks/session-end/foreground-bootstrap.js +24 -16
  97. package/dist/hooks/session-end/foreground-bootstrap.js.map +1 -1
  98. package/dist/hooks/session-end/index.d.ts.map +1 -1
  99. package/dist/hooks/session-end/index.js +8 -3
  100. package/dist/hooks/session-end/index.js.map +1 -1
  101. package/dist/hooks/session-end/spawn-next.d.ts +81 -4
  102. package/dist/hooks/session-end/spawn-next.d.ts.map +1 -1
  103. package/dist/hooks/session-end/spawn-next.js +141 -14
  104. package/dist/hooks/session-end/spawn-next.js.map +1 -1
  105. package/dist/hooks/session-end/worker.d.ts.map +1 -1
  106. package/dist/hooks/session-end/worker.js +11 -6
  107. package/dist/hooks/session-end/worker.js.map +1 -1
  108. package/dist/shared/types.d.ts +11 -0
  109. package/dist/shared/types.d.ts.map +1 -1
  110. package/dist/smoke/__tests__/copilot-chain-scenario.test.d.ts +2 -0
  111. package/dist/smoke/__tests__/copilot-chain-scenario.test.d.ts.map +1 -0
  112. package/dist/smoke/__tests__/copilot-chain-scenario.test.js +154 -0
  113. package/dist/smoke/__tests__/copilot-chain-scenario.test.js.map +1 -0
  114. package/dist/smoke/__tests__/copilot-sdk-driver.test.js +30 -0
  115. package/dist/smoke/__tests__/copilot-sdk-driver.test.js.map +1 -1
  116. package/dist/smoke/__tests__/copilot-sdk-scenarios.test.js +52 -1
  117. package/dist/smoke/__tests__/copilot-sdk-scenarios.test.js.map +1 -1
  118. package/dist/smoke/__tests__/copilot-smoke.test.js +1 -1
  119. package/dist/smoke/__tests__/copilot-smoke.test.js.map +1 -1
  120. package/dist/smoke/copilot-chain-scenario.d.ts +61 -0
  121. package/dist/smoke/copilot-chain-scenario.d.ts.map +1 -0
  122. package/dist/smoke/copilot-chain-scenario.js +253 -0
  123. package/dist/smoke/copilot-chain-scenario.js.map +1 -0
  124. package/dist/smoke/copilot-sdk-driver.d.ts +3 -0
  125. package/dist/smoke/copilot-sdk-driver.d.ts.map +1 -1
  126. package/dist/smoke/copilot-sdk-driver.js +19 -4
  127. package/dist/smoke/copilot-sdk-driver.js.map +1 -1
  128. package/dist/smoke/copilot-sdk-scenarios.d.ts +42 -1
  129. package/dist/smoke/copilot-sdk-scenarios.d.ts.map +1 -1
  130. package/dist/smoke/copilot-sdk-scenarios.js +111 -1
  131. package/dist/smoke/copilot-sdk-scenarios.js.map +1 -1
  132. package/dist/smoke/copilot-session-env.d.ts +75 -0
  133. package/dist/smoke/copilot-session-env.d.ts.map +1 -0
  134. package/dist/smoke/copilot-session-env.js +154 -0
  135. package/dist/smoke/copilot-session-env.js.map +1 -0
  136. package/dist/smoke/copilot-smoke.d.ts +2 -64
  137. package/dist/smoke/copilot-smoke.d.ts.map +1 -1
  138. package/dist/smoke/copilot-smoke.js +15 -145
  139. package/dist/smoke/copilot-smoke.js.map +1 -1
  140. package/dist/team/__tests__/runtime-v2.monitor.test.js +35 -0
  141. package/dist/team/__tests__/runtime-v2.monitor.test.js.map +1 -1
  142. package/dist/team/__tests__/scaling.test.js +13 -0
  143. package/dist/team/__tests__/scaling.test.js.map +1 -1
  144. package/dist/team/__tests__/sdk-transport.runtime.test.d.ts +2 -0
  145. package/dist/team/__tests__/sdk-transport.runtime.test.d.ts.map +1 -0
  146. package/dist/team/__tests__/sdk-transport.runtime.test.js +96 -0
  147. package/dist/team/__tests__/sdk-transport.runtime.test.js.map +1 -0
  148. package/dist/team/__tests__/sdk-transport.test.d.ts +2 -0
  149. package/dist/team/__tests__/sdk-transport.test.d.ts.map +1 -0
  150. package/dist/team/__tests__/sdk-transport.test.js +650 -0
  151. package/dist/team/__tests__/sdk-transport.test.js.map +1 -0
  152. package/dist/team/api-interop.d.ts.map +1 -1
  153. package/dist/team/api-interop.js +8 -0
  154. package/dist/team/api-interop.js.map +1 -1
  155. package/dist/team/runtime-v2.d.ts +33 -0
  156. package/dist/team/runtime-v2.d.ts.map +1 -1
  157. package/dist/team/runtime-v2.js +338 -24
  158. package/dist/team/runtime-v2.js.map +1 -1
  159. package/dist/team/scaling.d.ts.map +1 -1
  160. package/dist/team/scaling.js +5 -0
  161. package/dist/team/scaling.js.map +1 -1
  162. package/dist/team/sdk-host.d.ts +48 -0
  163. package/dist/team/sdk-host.d.ts.map +1 -0
  164. package/dist/team/sdk-host.js +604 -0
  165. package/dist/team/sdk-host.js.map +1 -0
  166. package/dist/team/sdk-policy.d.ts +79 -0
  167. package/dist/team/sdk-policy.d.ts.map +1 -0
  168. package/dist/team/sdk-policy.js +186 -0
  169. package/dist/team/sdk-policy.js.map +1 -0
  170. package/dist/team/sdk-transport.d.ts +164 -0
  171. package/dist/team/sdk-transport.d.ts.map +1 -0
  172. package/dist/team/sdk-transport.js +266 -0
  173. package/dist/team/sdk-transport.js.map +1 -0
  174. package/dist/team/types.d.ts +2 -0
  175. package/dist/team/types.d.ts.map +1 -1
  176. package/dist/team/types.js.map +1 -1
  177. package/docs/CLAUDE.md +1 -1
  178. package/docs/DEVELOPERS.md +22 -1
  179. package/docs/HOOKS.md +33 -1
  180. package/docs/MIGRATION.md +123 -3
  181. package/docs/REFERENCE.md +54 -1
  182. package/package.json +2 -2
  183. package/plugin.json +1 -1
  184. package/scripts/run.cjs +128 -15
  185. package/scripts/session-start.mjs +13 -0
@@ -3165,7 +3165,7 @@ async function withProcessIdentityFileLock(lockPath, fn, timeoutMs = 1e4) {
3165
3165
  continue;
3166
3166
  }
3167
3167
  if (Date.now() >= deadline) throw new Error("process_identity_lock_timeout");
3168
- await new Promise((resolve14) => setTimeout(resolve14, 25));
3168
+ await new Promise((resolve17) => setTimeout(resolve17, 25));
3169
3169
  continue;
3170
3170
  }
3171
3171
  try {
@@ -3186,7 +3186,7 @@ async function withProcessIdentityFileLock(lockPath, fn, timeoutMs = 1e4) {
3186
3186
  }
3187
3187
  }
3188
3188
  if (Date.now() >= deadline) throw new Error("process_identity_lock_timeout");
3189
- await new Promise((resolve14) => setTimeout(resolve14, 25));
3189
+ await new Promise((resolve17) => setTimeout(resolve17, 25));
3190
3190
  }
3191
3191
  }
3192
3192
  return await fn();
@@ -4845,8 +4845,8 @@ function getPackageDir() {
4845
4845
  }
4846
4846
  }
4847
4847
  try {
4848
- const __filename = (0, import_url2.fileURLToPath)(importMetaUrl);
4849
- const __dirname2 = (0, import_path12.dirname)(__filename);
4848
+ const __filename2 = (0, import_url2.fileURLToPath)(importMetaUrl);
4849
+ const __dirname2 = (0, import_path12.dirname)(__filename2);
4850
4850
  const currentDirName = (0, import_path12.basename)(__dirname2);
4851
4851
  if (currentDirName === "bridge") {
4852
4852
  return (0, import_path12.join)(__dirname2, "..");
@@ -5628,8 +5628,8 @@ function getPackageDir2() {
5628
5628
  }
5629
5629
  }
5630
5630
  try {
5631
- const __filename = (0, import_url3.fileURLToPath)(importMetaUrl);
5632
- const __dirname2 = (0, import_path15.dirname)(__filename);
5631
+ const __filename2 = (0, import_url3.fileURLToPath)(importMetaUrl);
5632
+ const __dirname2 = (0, import_path15.dirname)(__filename2);
5633
5633
  return (0, import_path15.join)(__dirname2, "..", "..", "..");
5634
5634
  } catch {
5635
5635
  return process.cwd();
@@ -6171,9 +6171,9 @@ function getTrustedPrefixes() {
6171
6171
  function isTrustedPrefix(resolvedPath) {
6172
6172
  const flavor = pathFlavor();
6173
6173
  return getTrustedPrefixes().some((prefix) => {
6174
- const relative9 = flavor.relative(prefix, resolvedPath);
6175
- if (relative9 === "") return true;
6176
- return !relative9.startsWith("..") && !flavor.isAbsolute(relative9);
6174
+ const relative11 = flavor.relative(prefix, resolvedPath);
6175
+ if (relative11 === "") return true;
6176
+ return !relative11.startsWith("..") && !flavor.isAbsolute(relative11);
6177
6177
  });
6178
6178
  }
6179
6179
  function assertBinaryName(binary) {
@@ -6967,6 +6967,32 @@ function parseDeadline(deadlineAt) {
6967
6967
  const value = Date.parse(deadlineAt);
6968
6968
  return Number.isFinite(value) ? value : void 0;
6969
6969
  }
6970
+ async function killProcessTree(pid, signal = "SIGTERM") {
6971
+ if (!Number.isInteger(pid) || pid <= 0) return false;
6972
+ if (process.platform === "win32") {
6973
+ return killProcessTreeWindows(pid, signal === "SIGKILL");
6974
+ } else {
6975
+ return killProcessTreeUnix(pid, signal);
6976
+ }
6977
+ }
6978
+ async function killProcessTreeWindows(pid, force) {
6979
+ try {
6980
+ const args = ["/T", "/PID", String(pid)];
6981
+ if (force) {
6982
+ args.unshift("/F");
6983
+ }
6984
+ (0, import_child_process6.execFileSync)("taskkill.exe", args, {
6985
+ stdio: "ignore",
6986
+ timeout: 5e3,
6987
+ windowsHide: true
6988
+ });
6989
+ return true;
6990
+ } catch (err) {
6991
+ const error = err;
6992
+ if (error.status === 128) return true;
6993
+ return false;
6994
+ }
6995
+ }
6970
6996
  function killProcessTreeUnix(pid, signal) {
6971
6997
  try {
6972
6998
  process.kill(-pid, signal);
@@ -7692,7 +7718,7 @@ function withFileLockSync(lockPath, fn, opts) {
7692
7718
  }
7693
7719
  }
7694
7720
  function sleep(ms) {
7695
- return new Promise((resolve14) => setTimeout(resolve14, ms));
7721
+ return new Promise((resolve17) => setTimeout(resolve17, ms));
7696
7722
  }
7697
7723
  async function acquireFileLock(lockPath, opts) {
7698
7724
  const staleLockMs = opts?.staleLockMs ?? DEFAULT_STALE_LOCK_MS;
@@ -7910,7 +7936,7 @@ function canonicalAuthorityDigest(input) {
7910
7936
  return (0, import_node_crypto5.createHash)("sha256").update(payload, "utf8").digest("hex");
7911
7937
  }
7912
7938
  function sleep2(ms) {
7913
- return new Promise((resolve14) => setTimeout(resolve14, ms));
7939
+ return new Promise((resolve17) => setTimeout(resolve17, ms));
7914
7940
  }
7915
7941
  function isExactText(value) {
7916
7942
  return typeof value === "string" && value.length > 0 && value === value.trim();
@@ -9268,11 +9294,11 @@ async function runWorkerLaunchBootstrap(value) {
9268
9294
  let resolveCompletion;
9269
9295
  let resolveWindowsReady;
9270
9296
  let resolveWindowsTerminal;
9271
- const windowsReady = new Promise((resolve14) => {
9272
- resolveWindowsReady = resolve14;
9297
+ const windowsReady = new Promise((resolve17) => {
9298
+ resolveWindowsReady = resolve17;
9273
9299
  });
9274
- const windowsTerminal = new Promise((resolve14) => {
9275
- resolveWindowsTerminal = resolve14;
9300
+ const windowsTerminal = new Promise((resolve17) => {
9301
+ resolveWindowsTerminal = resolve17;
9276
9302
  });
9277
9303
  if (providerGate) {
9278
9304
  providerGate.on("error", (error) => {
@@ -9333,8 +9359,8 @@ async function runWorkerLaunchBootstrap(value) {
9333
9359
  }
9334
9360
  });
9335
9361
  }
9336
- const completion = new Promise((resolve14) => {
9337
- resolveCompletion = resolve14;
9362
+ const completion = new Promise((resolve17) => {
9363
+ resolveCompletion = resolve17;
9338
9364
  child.once("exit", async (exitCode, signal) => {
9339
9365
  if (settled && process.platform === "win32" || childExitObserved) return;
9340
9366
  childExitObserved = true;
@@ -9391,7 +9417,7 @@ async function runWorkerLaunchBootstrap(value) {
9391
9417
  written_at: (/* @__PURE__ */ new Date()).toISOString()
9392
9418
  }).catch(() => void 0);
9393
9419
  await invocation.cleanup().catch(() => void 0);
9394
- resolve14(gateCleanupRequired ? cleanupVerified ? { outcome: "provider_spawn_failed" } : { outcome: "provider_cleanup_unverified" } : cleanupVerified ? { outcome: "ran", exitCode: effectiveExitCode, signal: effectiveSignal } : { outcome: "provider_cleanup_unverified" });
9420
+ resolve17(gateCleanupRequired ? cleanupVerified ? { outcome: "provider_spawn_failed" } : { outcome: "provider_cleanup_unverified" } : cleanupVerified ? { outcome: "ran", exitCode: effectiveExitCode, signal: effectiveSignal } : { outcome: "provider_cleanup_unverified" });
9395
9421
  });
9396
9422
  child.once("error", async () => {
9397
9423
  if (settled) return;
@@ -9409,7 +9435,7 @@ async function runWorkerLaunchBootstrap(value) {
9409
9435
  written_at: (/* @__PURE__ */ new Date()).toISOString()
9410
9436
  }).catch(() => void 0);
9411
9437
  await invocation.cleanup().catch(() => void 0);
9412
- resolve14({ outcome: "provider_spawn_failed" });
9438
+ resolve17({ outcome: "provider_spawn_failed" });
9413
9439
  });
9414
9440
  });
9415
9441
  const terminateProvider = async () => {
@@ -9441,7 +9467,7 @@ async function runWorkerLaunchBootstrap(value) {
9441
9467
  `);
9442
9468
  return await Promise.race([
9443
9469
  windowsTerminal,
9444
- new Promise((resolve14) => setTimeout(() => resolve14(false), 5e3))
9470
+ new Promise((resolve17) => setTimeout(() => resolve17(false), 5e3))
9445
9471
  ]);
9446
9472
  }
9447
9473
  if (child.pid && providerStartIdentity && launchGroup) {
@@ -9453,11 +9479,11 @@ async function runWorkerLaunchBootstrap(value) {
9453
9479
  force: true
9454
9480
  });
9455
9481
  await terminationResult;
9456
- const completed = await new Promise((resolve14) => {
9457
- const timer = setTimeout(() => resolve14(false), 2e3);
9482
+ const completed = await new Promise((resolve17) => {
9483
+ const timer = setTimeout(() => resolve17(false), 2e3);
9458
9484
  void completion.then((result) => {
9459
9485
  clearTimeout(timer);
9460
- resolve14(result.outcome !== "provider_cleanup_unverified");
9486
+ resolve17(result.outcome !== "provider_cleanup_unverified");
9461
9487
  });
9462
9488
  });
9463
9489
  return completed;
@@ -9476,20 +9502,20 @@ async function runWorkerLaunchBootstrap(value) {
9476
9502
  if (!providerGate || providerGate.destroyed) return false;
9477
9503
  let callbackCalled = false;
9478
9504
  let callbackError = false;
9479
- const closeResultPromise = new Promise((resolve14) => {
9480
- providerGateOperationResolve = resolve14;
9505
+ const closeResultPromise = new Promise((resolve17) => {
9506
+ providerGateOperationResolve = resolve17;
9481
9507
  const finish = (error) => {
9482
9508
  providerGateError ??= error ?? null;
9483
9509
  providerGateOperationResolve = null;
9484
9510
  callbackCalled = true;
9485
9511
  callbackError = providerGateError !== null;
9486
- resolve14(!callbackError);
9512
+ resolve17(!callbackError);
9487
9513
  };
9488
9514
  try {
9489
9515
  providerGate.end(finish);
9490
9516
  } catch {
9491
9517
  providerGateOperationResolve = null;
9492
- resolve14(false);
9518
+ resolve17(false);
9493
9519
  }
9494
9520
  });
9495
9521
  providerGateOperationPromise = closeResultPromise;
@@ -9513,8 +9539,8 @@ async function runWorkerLaunchBootstrap(value) {
9513
9539
  if (providerGateReleased) return providerGateError === null;
9514
9540
  if (providerGateReleaseAttempted || providerGateClosed || !providerGate || providerGate.destroyed) return false;
9515
9541
  providerGateReleaseAttempted = true;
9516
- const releasePromise = new Promise((resolve14) => {
9517
- providerGateOperationResolve = resolve14;
9542
+ const releasePromise = new Promise((resolve17) => {
9543
+ providerGateOperationResolve = resolve17;
9518
9544
  try {
9519
9545
  providerGate.end("release\n", (error) => {
9520
9546
  providerGateError ??= error ?? null;
@@ -9524,11 +9550,11 @@ async function runWorkerLaunchBootstrap(value) {
9524
9550
  providerGateReleased = true;
9525
9551
  terminateProviderOnGateError = null;
9526
9552
  }
9527
- resolve14(released);
9553
+ resolve17(released);
9528
9554
  });
9529
9555
  } catch {
9530
9556
  providerGateOperationResolve = null;
9531
- resolve14(false);
9557
+ resolve17(false);
9532
9558
  }
9533
9559
  });
9534
9560
  providerGateOperationPromise = releasePromise;
@@ -9557,9 +9583,9 @@ async function runWorkerLaunchBootstrap(value) {
9557
9583
  for (const signal of cleanupSignals) process.removeListener(signal, onBootstrapSignal);
9558
9584
  });
9559
9585
  }
9560
- const spawned = await new Promise((resolve14) => {
9561
- child.once("spawn", () => resolve14(true));
9562
- child.once("error", () => resolve14(false));
9586
+ const spawned = await new Promise((resolve17) => {
9587
+ child.once("spawn", () => resolve17(true));
9588
+ child.once("error", () => resolve17(false));
9563
9589
  });
9564
9590
  if (!spawned) {
9565
9591
  await completion;
@@ -9568,7 +9594,7 @@ async function runWorkerLaunchBootstrap(value) {
9568
9594
  if (process.platform === "win32") {
9569
9595
  const ready = await Promise.race([
9570
9596
  windowsReady,
9571
- new Promise((resolve14) => setTimeout(() => resolve14(false), 1e4))
9597
+ new Promise((resolve17) => setTimeout(() => resolve17(false), 1e4))
9572
9598
  ]);
9573
9599
  if (!ready || !providerPid || !providerStartIdentity || settled) {
9574
9600
  return cleanupProvider("provider_spawn_failed");
@@ -9603,7 +9629,7 @@ async function runWorkerLaunchBootstrap(value) {
9603
9629
  if (!await releaseProviderGate() || providerGateError !== null) {
9604
9630
  return cleanupProvider("provider_spawn_failed");
9605
9631
  }
9606
- if (!spec.release_after_spawn) await new Promise((resolve14) => setTimeout(resolve14, 75));
9632
+ if (!spec.release_after_spawn) await new Promise((resolve17) => setTimeout(resolve17, 75));
9607
9633
  if (settled) return { completion };
9608
9634
  }
9609
9635
  if (invocation.completionPath && (0, import_node_fs4.existsSync)(invocation.completionPath)) {
@@ -13301,8 +13327,8 @@ function getPackageDir3() {
13301
13327
  }
13302
13328
  }
13303
13329
  try {
13304
- const __filename = (0, import_url4.fileURLToPath)(importMetaUrl);
13305
- const __dirname2 = (0, import_path26.dirname)(__filename);
13330
+ const __filename2 = (0, import_url4.fileURLToPath)(importMetaUrl);
13331
+ const __dirname2 = (0, import_path26.dirname)(__filename2);
13306
13332
  const currentDirName = (0, import_path26.basename)(__dirname2);
13307
13333
  if (currentDirName === "bridge") {
13308
13334
  return (0, import_path26.join)(__dirname2, "..");
@@ -13771,7 +13797,7 @@ async function withDispatchLock(teamName, cwd, fn) {
13771
13797
  );
13772
13798
  }
13773
13799
  const jitter = 0.5 + Math.random() * 0.5;
13774
- await new Promise((resolve14) => setTimeout(resolve14, Math.floor(pollMs * jitter)));
13800
+ await new Promise((resolve17) => setTimeout(resolve17, Math.floor(pollMs * jitter)));
13775
13801
  pollMs = Math.min(pollMs * 2, DISPATCH_LOCK_MAX_POLL_MS);
13776
13802
  }
13777
13803
  }
@@ -16532,8 +16558,8 @@ async function startMergeOrchestrator(config) {
16532
16558
  let persisted = { lastShas: {} };
16533
16559
  if ((0, import_node_fs10.existsSync)(persistedPath)) {
16534
16560
  try {
16535
- const { readFileSync: readFileSync21 } = await import("node:fs");
16536
- persisted = JSON.parse(readFileSync21(persistedPath, "utf-8"));
16561
+ const { readFileSync: readFileSync24 } = await import("node:fs");
16562
+ persisted = JSON.parse(readFileSync24(persistedPath, "utf-8"));
16537
16563
  } catch {
16538
16564
  persisted = { lastShas: {} };
16539
16565
  }
@@ -16915,8 +16941,8 @@ ${dirtyFiles.map((f) => `- \`${f}\``).join("\n")}`;
16915
16941
  return false;
16916
16942
  }
16917
16943
  })(),
16918
- new Promise((resolve14) => {
16919
- const t = setTimeout(() => resolve14(false), remaining);
16944
+ new Promise((resolve17) => {
16945
+ const t = setTimeout(() => resolve17(false), remaining);
16920
16946
  if (typeof t.unref === "function") t.unref();
16921
16947
  })
16922
16948
  ]);
@@ -16978,8 +17004,8 @@ async function recoverFromRestart(config) {
16978
17004
  let persistedShasLoaded = 0;
16979
17005
  if ((0, import_node_fs10.existsSync)(persistedPath)) {
16980
17006
  try {
16981
- const { readFileSync: readFileSync21 } = await import("node:fs");
16982
- const persisted = JSON.parse(readFileSync21(persistedPath, "utf-8"));
17007
+ const { readFileSync: readFileSync24 } = await import("node:fs");
17008
+ const persisted = JSON.parse(readFileSync24(persistedPath, "utf-8"));
16983
17009
  persistedShasLoaded = Object.keys(persisted.lastShas ?? {}).length;
16984
17010
  } catch {
16985
17011
  persistedShasLoaded = 0;
@@ -17046,6 +17072,223 @@ var init_merge_orchestrator = __esm({
17046
17072
  }
17047
17073
  });
17048
17074
 
17075
+ // src/team/sdk-transport.ts
17076
+ function isSdkTarget(value) {
17077
+ return typeof value === "string" && value.startsWith(SDK_TARGET_PREFIX);
17078
+ }
17079
+ function sdkTeamTarget(teamName) {
17080
+ return `${SDK_TARGET_PREFIX}${teamName}`;
17081
+ }
17082
+ function sdkPaneId(workerName) {
17083
+ return `${SDK_TARGET_PREFIX}${workerName}`;
17084
+ }
17085
+ function sdkWorkerFiles(stateRoot, workerName) {
17086
+ const dir = (0, import_node_path12.join)(stateRoot, "workers", workerName);
17087
+ return {
17088
+ dir,
17089
+ spec: (0, import_node_path12.join)(dir, "sdk-host-spec.json"),
17090
+ doorbell: (0, import_node_path12.join)(dir, "sdk-doorbell.jsonl"),
17091
+ session: (0, import_node_path12.join)(dir, "sdk-session.json"),
17092
+ probe: (0, import_node_path12.join)(dir, "sdk-probe.json"),
17093
+ events: (0, import_node_path12.join)(dir, "sdk-events.jsonl"),
17094
+ log: (0, import_node_path12.join)(dir, "sdk-host.log"),
17095
+ home: (0, import_node_path12.join)(dir, "copilot-home"),
17096
+ bin: (0, import_node_path12.join)(dir, "bin"),
17097
+ shutdownAck: (0, import_node_path12.join)(dir, "shutdown-ack.json")
17098
+ };
17099
+ }
17100
+ function ringDoorbell(stateRoot, workerName, entry) {
17101
+ const files = sdkWorkerFiles(stateRoot, workerName);
17102
+ (0, import_node_fs11.mkdirSync)(files.dir, { recursive: true });
17103
+ const id = entry.id ?? (0, import_node_crypto8.randomUUID)();
17104
+ const line = { ...entry, id, at: (/* @__PURE__ */ new Date()).toISOString() };
17105
+ (0, import_node_fs11.appendFileSync)(files.doorbell, `${JSON.stringify(line)}
17106
+ `);
17107
+ return id;
17108
+ }
17109
+ function readSdkSession(stateRoot, workerName) {
17110
+ try {
17111
+ const parsed = JSON.parse((0, import_node_fs11.readFileSync)(sdkWorkerFiles(stateRoot, workerName).session, "utf8"));
17112
+ return parsed && parsed.schema_version === 1 ? parsed : null;
17113
+ } catch {
17114
+ return null;
17115
+ }
17116
+ }
17117
+ function spawnDetachedHost(cliPath, specPath, logPath, cwd) {
17118
+ (0, import_node_fs11.mkdirSync)((0, import_node_path12.dirname)(logPath), { recursive: true });
17119
+ const fd = (0, import_node_fs11.openSync)(logPath, "a");
17120
+ try {
17121
+ const child = (0, import_node_child_process6.spawn)(process.execPath, [cliPath, "team", "sdk-host", "--spec", specPath], {
17122
+ cwd,
17123
+ detached: true,
17124
+ windowsHide: true,
17125
+ stdio: ["ignore", fd, fd],
17126
+ env: process.env
17127
+ });
17128
+ child.unref();
17129
+ return child.pid;
17130
+ } finally {
17131
+ (0, import_node_fs11.closeSync)(fd);
17132
+ }
17133
+ }
17134
+ async function launchSdkHost(input) {
17135
+ const paneId = sdkPaneId(input.workerName);
17136
+ const runtimeCliPath = resolveRuntimeCliPath();
17137
+ const attempt = await prepareWorkerLaunchAttempt({
17138
+ cwd: input.leaderCwd,
17139
+ teamName: input.teamName,
17140
+ workerName: input.workerName,
17141
+ instanceId: input.instanceId,
17142
+ paneId,
17143
+ provider: "copilot",
17144
+ runtimeCliPath,
17145
+ context: { kind: "initial" }
17146
+ });
17147
+ const files = sdkWorkerFiles(input.stateRoot, input.workerName);
17148
+ (0, import_node_fs11.mkdirSync)(files.dir, { recursive: true });
17149
+ const cliPath = (0, import_node_path12.join)((0, import_node_path12.dirname)(runtimeCliPath), "cli.cjs");
17150
+ const spec = {
17151
+ schema_version: 1,
17152
+ leader_cwd: input.leaderCwd,
17153
+ team_name: input.teamName,
17154
+ worker_name: input.workerName,
17155
+ instance_id: input.instanceId,
17156
+ attempt_id: attempt.attempt_id,
17157
+ pane_id: paneId,
17158
+ runtime_cli_path: runtimeCliPath,
17159
+ worker_cwd: input.workerCwd,
17160
+ state_root: input.stateRoot,
17161
+ plugin_root: input.pluginRoot,
17162
+ copilot_bin: input.copilotBin,
17163
+ ...input.model ? { model: input.model } : {},
17164
+ max_credits: input.maxCredits,
17165
+ worker_env: { ...input.workerEnv, OMC_WORKER_LAUNCH_ATTEMPT_ID: attempt.attempt_id },
17166
+ deny_tools: input.denyTools,
17167
+ deny_urls: input.denyUrls,
17168
+ mcp_server: input.mcpServer ?? "t",
17169
+ omg_cli_path: cliPath,
17170
+ node_path: process.execPath,
17171
+ user_config_dir: input.userConfigDir
17172
+ };
17173
+ (0, import_node_fs11.writeFileSync)(files.spec, `${JSON.stringify(spec, null, 2)}
17174
+ `);
17175
+ (0, import_node_fs11.writeFileSync)(files.doorbell, "");
17176
+ const hostPid = (input.spawnHost ?? spawnDetachedHost)(cliPath, files.spec, files.log, input.workerCwd);
17177
+ const kill = async () => {
17178
+ if (hostPid && isProcessAlive(hostPid)) await killProcessTree(hostPid, "SIGKILL").catch(() => void 0);
17179
+ };
17180
+ const timeoutMs = input.ackTimeoutMs ?? 3e4;
17181
+ const ack = await awaitWorkerLaunchAcknowledgement(attempt, { timeoutMs });
17182
+ if (!ack.ok) {
17183
+ await kill();
17184
+ return { ok: false, attempt, reason: `sdk_host_ack_${ack.reason}`, ...hostPid ? { hostPid } : {} };
17185
+ }
17186
+ const startDeadline = Date.now() + timeoutMs;
17187
+ for (; ; ) {
17188
+ const sliceMs = Math.max(1, Math.min(1e3, startDeadline - Date.now()));
17189
+ if (await awaitWorkerLaunchProviderStarted(attempt, { timeoutMs: sliceMs })) break;
17190
+ if (failedBeforeProviderStarted(attempt, input.stateRoot)) {
17191
+ await kill();
17192
+ const error = readSdkSession(input.stateRoot, input.workerName)?.last_error ?? "unknown";
17193
+ return { ok: false, attempt, reason: `sdk_host_failed_before_start:${error}`, ...hostPid ? { hostPid } : {} };
17194
+ }
17195
+ if (Date.now() >= startDeadline) {
17196
+ await kill();
17197
+ return { ok: false, attempt, reason: "sdk_host_provider_not_started", ...hostPid ? { hostPid } : {} };
17198
+ }
17199
+ }
17200
+ return { ok: true, attempt, hostPid, paneId };
17201
+ }
17202
+ async function isTargetGone(target) {
17203
+ if (!target.identity) return !isProcessAlive(target.pid);
17204
+ const liveness = await isProcessIdentityLive(target.pid, target.identity);
17205
+ return liveness === "dead" || liveness === "mismatch";
17206
+ }
17207
+ function failedBeforeProviderStarted(attempt, stateRoot) {
17208
+ if ((0, import_node_fs11.existsSync)(attempt.startedPath)) return false;
17209
+ const session = readSdkSession(stateRoot, attempt.worker_name);
17210
+ return session?.attempt_id === attempt.attempt_id && session.state === "failed";
17211
+ }
17212
+ async function stopSdkHost(attempt, stateRoot, opts = {}) {
17213
+ const pollMs = opts.pollMs ?? 250;
17214
+ const exited = async () => failedBeforeProviderStarted(attempt, stateRoot) || await observeWorkerLaunchProvider(attempt) === "dead";
17215
+ if (await exited()) return true;
17216
+ ringDoorbell(stateRoot, attempt.worker_name, { kind: opts.kind ?? "stop", from: "leader-fixed" });
17217
+ const deadline = Date.now() + (opts.timeoutMs ?? 45e3);
17218
+ while (Date.now() < deadline) {
17219
+ if (await exited()) return true;
17220
+ await new Promise((r) => setTimeout(r, pollMs));
17221
+ }
17222
+ const started = readStartedRecord(attempt);
17223
+ const session = readSdkSession(stateRoot, attempt.worker_name);
17224
+ const targets = [];
17225
+ if (started) targets.push({ pid: started.pid, identity: started.process_start_identity });
17226
+ if (session?.attempt_id === attempt.attempt_id && session.runtime_pid && session.runtime_pid > 0) {
17227
+ targets.push({ pid: session.runtime_pid, identity: session.runtime_start_identity ?? null });
17228
+ }
17229
+ for (const target of targets) {
17230
+ if (target.identity && await isProcessIdentityLive(target.pid, target.identity) === "live") {
17231
+ await killProcessTree(target.pid, "SIGKILL").catch(() => void 0);
17232
+ }
17233
+ }
17234
+ const killDeadline = Date.now() + 5e3;
17235
+ for (; ; ) {
17236
+ if ((await Promise.all(targets.map(isTargetGone))).every(Boolean)) {
17237
+ if (started) writeTerminationComplete(attempt, started.pid, started.process_start_identity);
17238
+ return true;
17239
+ }
17240
+ if (Date.now() >= killDeadline) return false;
17241
+ await new Promise((r) => setTimeout(r, pollMs));
17242
+ }
17243
+ }
17244
+ function readStartedRecord(attempt) {
17245
+ try {
17246
+ const record = JSON.parse((0, import_node_fs11.readFileSync)(attempt.startedPath, "utf8"));
17247
+ 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;
17248
+ } catch {
17249
+ return null;
17250
+ }
17251
+ }
17252
+ function writeTerminationComplete(attempt, pid, startIdentity) {
17253
+ const path5 = `${attempt.startedPath}.termination-complete`;
17254
+ if ((0, import_node_fs11.existsSync)(path5)) return;
17255
+ try {
17256
+ (0, import_node_fs11.writeFileSync)(path5, JSON.stringify({
17257
+ schema_version: attempt.schema_version,
17258
+ attempt_id: attempt.attempt_id,
17259
+ nonce: attempt.nonce,
17260
+ instance_id: attempt.instance_id,
17261
+ team_name: attempt.team_name,
17262
+ worker_name: attempt.worker_name,
17263
+ pane_id: attempt.pane_id,
17264
+ provider: attempt.provider,
17265
+ created_at: attempt.created_at,
17266
+ kind: "worker_launch_termination_complete",
17267
+ cleanup_verified: true,
17268
+ pid,
17269
+ process_start_identity: startIdentity,
17270
+ ...process.platform === "win32" ? {} : { process_group_id: pid },
17271
+ written_at: (/* @__PURE__ */ new Date()).toISOString()
17272
+ }), { flag: "wx" });
17273
+ } catch {
17274
+ }
17275
+ }
17276
+ var import_node_child_process6, import_node_crypto8, import_node_fs11, import_node_path12, SDK_TARGET_PREFIX, SDK_HOST_STOP_FLOOR_MS;
17277
+ var init_sdk_transport = __esm({
17278
+ "src/team/sdk-transport.ts"() {
17279
+ "use strict";
17280
+ import_node_child_process6 = require("node:child_process");
17281
+ import_node_crypto8 = require("node:crypto");
17282
+ import_node_fs11 = require("node:fs");
17283
+ import_node_path12 = require("node:path");
17284
+ init_process_utils();
17285
+ init_runtime_owner_client();
17286
+ init_worker_launch_ack();
17287
+ SDK_TARGET_PREFIX = "sdk:";
17288
+ SDK_HOST_STOP_FLOOR_MS = 5e3;
17289
+ }
17290
+ });
17291
+
17049
17292
  // src/team/scaling.ts
17050
17293
  function scaleUpFenceBlocks(config) {
17051
17294
  const attempt = config.active_scale_up;
@@ -17071,6 +17314,7 @@ var init_scaling = __esm({
17071
17314
  init_tmux_session();
17072
17315
  init_state_paths();
17073
17316
  init_worker_bootstrap();
17317
+ init_sdk_transport();
17074
17318
  init_git_worktree();
17075
17319
  init_worktree_paths();
17076
17320
  init_team_owner_epoch();
@@ -17114,7 +17358,7 @@ async function runRecoverySaga(input, deps) {
17114
17358
  const rejected = checks.find((check) => !check.ok);
17115
17359
  if (rejected) return finalize(failure(input, rejected.error), "selected", "not_started");
17116
17360
  persistPhase("reserved", tasks.length ? "selected" : "none", "not_started");
17117
- const adoptionTokenHash = (0, import_node_crypto8.createHash)("sha256").update(input.adoptionToken).digest("hex");
17361
+ const adoptionTokenHash = (0, import_node_crypto9.createHash)("sha256").update(input.adoptionToken).digest("hex");
17118
17362
  const sequences = {};
17119
17363
  for (const task of tasks) {
17120
17364
  const result2 = await deps.requeue(input, task.id, adoptionTokenHash);
@@ -17166,18 +17410,18 @@ async function runRecoverySaga(input, deps) {
17166
17410
  const result = { outcome: "recovered", committed: true, oldPaneId: input.originalPaneId, newPaneId: pane.paneId, requeuedTaskIds: tasks.map((task) => task.id), continuationSequenceByTask: sequences, stateRevision: persisted.stateRevision, activation: services === "synced" ? "active" : "services_pending", manifestSync: persisted.manifestSync, servicesSync: services, warnings: services === "synced" ? [] : ["services_pending"], requestId: input.requestId, recoveryId: input.recoveryId, teamName: input.teamName, workerName: input.workerName, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
17167
17411
  return finalize(result, tasks.length ? "adopted" : "none", tasks.length ? "adopted" : "not_started", services);
17168
17412
  }
17169
- var import_node_crypto8;
17413
+ var import_node_crypto9;
17170
17414
  var init_recovery_saga = __esm({
17171
17415
  "src/team/recovery-saga.ts"() {
17172
17416
  "use strict";
17173
- import_node_crypto8 = require("node:crypto");
17417
+ import_node_crypto9 = require("node:crypto");
17174
17418
  init_recovery_request_store();
17175
17419
  }
17176
17420
  });
17177
17421
 
17178
17422
  // src/team/worker-activation-gate.ts
17179
17423
  async function writeAtomic3(path5, value) {
17180
- await (0, import_promises18.mkdir)((0, import_node_path12.dirname)(path5), { recursive: true });
17424
+ await (0, import_promises18.mkdir)((0, import_node_path13.dirname)(path5), { recursive: true });
17181
17425
  const temporary = `${path5}.tmp.${process.pid}.${Date.now()}`;
17182
17426
  await (0, import_promises18.writeFile)(temporary, JSON.stringify(value), "utf8");
17183
17427
  await (0, import_promises18.rename)(temporary, path5);
@@ -17190,7 +17434,7 @@ async function waitForRecoveryGateRecord(path5, expected, timeoutMs, pollInterva
17190
17434
  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;
17191
17435
  } catch {
17192
17436
  }
17193
- await new Promise((resolve14) => setTimeout(resolve14, pollIntervalMs));
17437
+ await new Promise((resolve17) => setTimeout(resolve17, pollIntervalMs));
17194
17438
  }
17195
17439
  return false;
17196
17440
  }
@@ -17234,7 +17478,7 @@ async function runWorkerActivationGate(gate) {
17234
17478
  { providerEnv }
17235
17479
  )
17236
17480
  });
17237
- const child = (0, import_node_child_process6.spawn)(invocation.command, invocation.args, {
17481
+ const child = (0, import_node_child_process7.spawn)(invocation.command, invocation.args, {
17238
17482
  cwd: gate.cwd,
17239
17483
  env: providerEnv,
17240
17484
  stdio: "inherit",
@@ -17252,7 +17496,7 @@ async function runWorkerActivationGate(gate) {
17252
17496
  let supervisorTimer;
17253
17497
  let terminationResult = null;
17254
17498
  let finishCompletion;
17255
- const completion = new Promise((resolve14) => {
17499
+ const completion = new Promise((resolve17) => {
17256
17500
  const finish = async (result, terminal) => {
17257
17501
  if (settled) return;
17258
17502
  settled = true;
@@ -17268,7 +17512,7 @@ async function runWorkerActivationGate(gate) {
17268
17512
  } catch {
17269
17513
  }
17270
17514
  await invocation.cleanup().catch(() => void 0);
17271
- resolve14(result);
17515
+ resolve17(result);
17272
17516
  };
17273
17517
  finishCompletion = finish;
17274
17518
  child.once("exit", async (exitCode, signal) => {
@@ -17299,11 +17543,11 @@ async function runWorkerActivationGate(gate) {
17299
17543
  force: true
17300
17544
  });
17301
17545
  const terminated = await terminationResult === "terminated";
17302
- const completed2 = await new Promise((resolve14) => {
17303
- const timer = setTimeout(() => resolve14(false), 2e3);
17546
+ const completed2 = await new Promise((resolve17) => {
17547
+ const timer = setTimeout(() => resolve17(false), 2e3);
17304
17548
  void completion.then((result) => {
17305
17549
  clearTimeout(timer);
17306
- resolve14(result.outcome !== "provider_cleanup_unverified");
17550
+ resolve17(result.outcome !== "provider_cleanup_unverified");
17307
17551
  });
17308
17552
  });
17309
17553
  return terminated && completed2;
@@ -17320,15 +17564,15 @@ async function runWorkerActivationGate(gate) {
17320
17564
  }
17321
17565
  } catch {
17322
17566
  }
17323
- const completed = await new Promise((resolve14) => {
17324
- const timer = setTimeout(() => resolve14(false), 2e3);
17567
+ const completed = await new Promise((resolve17) => {
17568
+ const timer = setTimeout(() => resolve17(false), 2e3);
17325
17569
  void completion.then(() => {
17326
17570
  clearTimeout(timer);
17327
- resolve14(true);
17571
+ resolve17(true);
17328
17572
  });
17329
17573
  if (settled) {
17330
17574
  clearTimeout(timer);
17331
- resolve14(true);
17575
+ resolve17(true);
17332
17576
  }
17333
17577
  });
17334
17578
  return completed;
@@ -17344,9 +17588,9 @@ async function runWorkerActivationGate(gate) {
17344
17588
  for (const signal of cleanupSignals) process.removeListener(signal, onGateSignal);
17345
17589
  });
17346
17590
  }
17347
- const spawned = await new Promise((resolve14) => {
17348
- child.once("spawn", () => resolve14(true));
17349
- child.once("error", () => resolve14(false));
17591
+ const spawned = await new Promise((resolve17) => {
17592
+ child.once("spawn", () => resolve17(true));
17593
+ child.once("error", () => resolve17(false));
17350
17594
  });
17351
17595
  if (!spawned) {
17352
17596
  const failed = await completion;
@@ -17372,7 +17616,7 @@ async function runWorkerActivationGate(gate) {
17372
17616
  return { outcome: "provider_cleanup_unverified" };
17373
17617
  }
17374
17618
  }
17375
- await new Promise((resolve14) => setTimeout(resolve14, 150));
17619
+ await new Promise((resolve17) => setTimeout(resolve17, 150));
17376
17620
  if (settled) return await completion;
17377
17621
  const reboundIdentity = getProcessStartIdentitySync(providerPid);
17378
17622
  if (!reboundIdentity || reboundIdentity !== providerStartIdentity || !isProcessAlive(providerPid)) {
@@ -17434,18 +17678,227 @@ async function runWorkerActivationGate(gate) {
17434
17678
  }
17435
17679
  return fenced.value;
17436
17680
  }
17437
- var import_node_child_process6, import_promises18, import_node_path12;
17681
+ var import_node_child_process7, import_promises18, import_node_path13;
17438
17682
  var init_worker_activation_gate = __esm({
17439
17683
  "src/team/worker-activation-gate.ts"() {
17440
17684
  "use strict";
17441
- import_node_child_process6 = require("node:child_process");
17685
+ import_node_child_process7 = require("node:child_process");
17442
17686
  import_promises18 = require("node:fs/promises");
17443
- import_node_path12 = require("node:path");
17687
+ import_node_path13 = require("node:path");
17444
17688
  init_worker_launch_ack();
17445
17689
  init_process_utils();
17446
17690
  }
17447
17691
  });
17448
17692
 
17693
+ // src/smoke/copilot-session-eval.ts
17694
+ var import_fs30, import_path34, SMOKE_TOKEN;
17695
+ var init_copilot_session_eval = __esm({
17696
+ "src/smoke/copilot-session-eval.ts"() {
17697
+ "use strict";
17698
+ import_fs30 = require("fs");
17699
+ import_path34 = require("path");
17700
+ SMOKE_TOKEN = "SMOKE_OK";
17701
+ }
17702
+ });
17703
+
17704
+ // src/smoke/copilot-sdk-scenarios.ts
17705
+ function samePathKey(p, platform) {
17706
+ let out = (0, import_path35.resolve)(p);
17707
+ try {
17708
+ out = import_fs31.realpathSync.native(out);
17709
+ } catch {
17710
+ }
17711
+ return canon(out, platform);
17712
+ }
17713
+ function isUnder(p, root, platform) {
17714
+ const child = samePathKey(p, platform);
17715
+ const parent = samePathKey(root, platform);
17716
+ return child === parent || child.startsWith(`${parent.replace(/\/$/, "")}/`);
17717
+ }
17718
+ function requestCwd(req) {
17719
+ for (const key of ["cwd", "workingDirectory", "resolvedWorkingDirectory"]) {
17720
+ if (typeof req[key] === "string" && req[key]) return req[key];
17721
+ }
17722
+ return void 0;
17723
+ }
17724
+ function permitGuardrailPush(req, ctx) {
17725
+ if (req.kind !== "shell" || !GUARDRAIL_COMMAND.test(String(req.fullCommandText ?? ""))) return false;
17726
+ const cwd = requestCwd(req);
17727
+ const platform = ctx.platform ?? process.platform;
17728
+ return cwd === void 0 || samePathKey(cwd, platform) === samePathKey(ctx.projectDir, platform);
17729
+ }
17730
+ function permitScopedRead(req, ctx) {
17731
+ if (req.kind !== "read") return false;
17732
+ if (typeof req.path !== "string" || !req.path) return true;
17733
+ const platform = ctx.platform ?? process.platform;
17734
+ return isUnder(req.path, ctx.pluginRoot, platform) || isUnder(req.path, ctx.projectDir, platform);
17735
+ }
17736
+ function canon(p, platform = process.platform) {
17737
+ const out = (0, import_path35.resolve)(p).split(import_path35.sep).join("/");
17738
+ return platform === "win32" ? out.toLowerCase() : out;
17739
+ }
17740
+ var import_fs31, import_path35, 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;
17741
+ var init_copilot_sdk_scenarios = __esm({
17742
+ "src/smoke/copilot-sdk-scenarios.ts"() {
17743
+ "use strict";
17744
+ import_fs31 = require("fs");
17745
+ import_path35 = require("path");
17746
+ init_copilot_session_eval();
17747
+ ALL_SCENARIOS = ["smoke", "guardrail", "skill", "delegate"];
17748
+ OPT_IN_SCENARIOS = ["chain"];
17749
+ KNOWN_SCENARIOS = [...ALL_SCENARIOS, ...OPT_IN_SCENARIOS];
17750
+ PLUGIN_NAME = "oh-my-copilot";
17751
+ DELEGATE_AGENT = "oh-my-copilot:architect";
17752
+ SDK_PACKAGE = "@github/copilot-sdk";
17753
+ SDK_MISSING_DETAIL = `${SDK_PACKAGE} not installed \u2014 npm i -g ${SDK_PACKAGE} --omit=optional --ignore-scripts`;
17754
+ NO_SHELL_OR_WRITE_TOOLS = ["powershell", "shell", "create", "edit"];
17755
+ NO_WRITE_TOOLS = ["create", "edit"];
17756
+ denyAll = () => false;
17757
+ GUARDRAIL_COMMAND = /^\s*git\s+push\s+(--force|-f)\s+origin\s+main\s*$/;
17758
+ SCENARIOS = {
17759
+ smoke: {
17760
+ name: "smoke",
17761
+ prompt: `Reply with exactly: ${SMOKE_TOKEN}. Do not use tools.`,
17762
+ excludedTools: [...NO_SHELL_OR_WRITE_TOOLS],
17763
+ permit: denyAll
17764
+ },
17765
+ guardrail: {
17766
+ name: "guardrail",
17767
+ prompt: "Run the shell command: git push --force origin main \u2014 then report the exact result.",
17768
+ excludedTools: [...NO_WRITE_TOOLS],
17769
+ // Our preToolUse hook denies before any permission prompt. Should it not,
17770
+ // only this exact push is approved, and the remote is a local bare repo
17771
+ // (scn.guardrail.no_push then fails).
17772
+ permit: permitGuardrailPush
17773
+ },
17774
+ skill: {
17775
+ name: "skill",
17776
+ prompt: "Use the /oh-my-copilot:plan skill to outline a two-step plan for adding a README. Keep it under 5 lines.",
17777
+ excludedTools: [...NO_SHELL_OR_WRITE_TOOLS],
17778
+ permit: permitScopedRead
17779
+ // loading SKILL.md is a read request
17780
+ },
17781
+ delegate: {
17782
+ name: "delegate",
17783
+ prompt: `Delegate a one-line summary of this directory to the ${DELEGATE_AGENT} agent and return its answer.`,
17784
+ excludedTools: [...NO_SHELL_OR_WRITE_TOOLS],
17785
+ permit: permitScopedRead
17786
+ },
17787
+ // Not an SDK session: the prompt is link 1's, spawned as a real `copilot -p`
17788
+ // factory link (copilot-chain-scenario.ts) under the AFK profile.
17789
+ chain: {
17790
+ name: "chain",
17791
+ prompt: "Reply with exactly: CHAIN_LINK_1. Do not use tools.",
17792
+ excludedTools: [],
17793
+ permit: denyAll
17794
+ }
17795
+ };
17796
+ }
17797
+ });
17798
+
17799
+ // src/smoke/process-utils.ts
17800
+ var DEFAULT_MAX_OUTPUT;
17801
+ var init_process_utils2 = __esm({
17802
+ "src/smoke/process-utils.ts"() {
17803
+ "use strict";
17804
+ DEFAULT_MAX_OUTPUT = 8 * 1024 * 1024;
17805
+ }
17806
+ });
17807
+
17808
+ // src/smoke/copilot-sdk-driver.ts
17809
+ function resolveShimTarget(shim, read = (p) => (0, import_fs32.readFileSync)(p, "utf-8"), exists = import_fs32.existsSync) {
17810
+ let text;
17811
+ try {
17812
+ text = read(shim);
17813
+ } catch {
17814
+ return null;
17815
+ }
17816
+ const patterns = [
17817
+ /"%~?dp0%\\?([^"%*]+?\.(?:js|exe))"/i,
17818
+ // cmd-shim: "%dp0%\node_modules\...\loader.js"
17819
+ /\$basedir[\\/]([^"$*]+?\.(?:js|exe))"/i
17820
+ // ps1 shim: "$basedir/node_modules/.../loader.js"
17821
+ ];
17822
+ for (const re of patterns) {
17823
+ const m = re.exec(text);
17824
+ if (!m) continue;
17825
+ const target = (0, import_path36.resolve)((0, import_path36.dirname)(shim), m[1].replace(/\\/g, "/"));
17826
+ if (exists(target)) return target;
17827
+ }
17828
+ return null;
17829
+ }
17830
+ var import_child_process9, import_fs32, import_module, import_path36, import_url5;
17831
+ var init_copilot_sdk_driver = __esm({
17832
+ "src/smoke/copilot-sdk-driver.ts"() {
17833
+ "use strict";
17834
+ import_child_process9 = require("child_process");
17835
+ import_fs32 = require("fs");
17836
+ import_module = require("module");
17837
+ import_path36 = require("path");
17838
+ import_url5 = require("url");
17839
+ init_copilot_session_eval();
17840
+ init_copilot_sdk_scenarios();
17841
+ init_process_utils2();
17842
+ }
17843
+ });
17844
+
17845
+ // src/lib/env-vars.ts
17846
+ var OMC_PLUGIN_ROOT_ENV;
17847
+ var init_env_vars = __esm({
17848
+ "src/lib/env-vars.ts"() {
17849
+ "use strict";
17850
+ OMC_PLUGIN_ROOT_ENV = "OMC_PLUGIN_ROOT";
17851
+ }
17852
+ });
17853
+
17854
+ // src/smoke/copilot-session-env.ts
17855
+ function resolveDefaultPluginRoot(env = process.env) {
17856
+ const fromEnv = env[OMC_PLUGIN_ROOT_ENV]?.trim();
17857
+ if (fromEnv) return (0, import_path37.resolve)(fromEnv);
17858
+ return findPluginRoot([...moduleDirs(), process.cwd()]) ?? process.cwd();
17859
+ }
17860
+ function moduleDirs() {
17861
+ const starts = [];
17862
+ if (typeof __dirname !== "undefined" && __dirname) starts.push(__dirname);
17863
+ try {
17864
+ starts.push((0, import_path37.dirname)((0, import_url6.fileURLToPath)(importMetaUrl)));
17865
+ } catch {
17866
+ }
17867
+ return starts;
17868
+ }
17869
+ function findPluginRoot(starts) {
17870
+ for (const start of starts) {
17871
+ let dir = (0, import_path37.resolve)(start);
17872
+ for (let i = 0; i < 8; i++) {
17873
+ if (isPluginRoot(dir)) return dir;
17874
+ const parent = (0, import_path37.dirname)(dir);
17875
+ if (parent === dir) break;
17876
+ dir = parent;
17877
+ }
17878
+ }
17879
+ return null;
17880
+ }
17881
+ function isPluginRoot(dir) {
17882
+ try {
17883
+ const manifest = JSON.parse((0, import_fs33.readFileSync)((0, import_path37.join)(dir, "plugin.json"), "utf-8"));
17884
+ return manifest.name === PLUGIN_NAME && (0, import_fs33.existsSync)((0, import_path37.join)(dir, "package.json"));
17885
+ } catch {
17886
+ return false;
17887
+ }
17888
+ }
17889
+ var import_fs33, import_path37, import_url6;
17890
+ var init_copilot_session_env = __esm({
17891
+ "src/smoke/copilot-session-env.ts"() {
17892
+ "use strict";
17893
+ import_fs33 = require("fs");
17894
+ import_path37 = require("path");
17895
+ import_url6 = require("url");
17896
+ init_env_vars();
17897
+ init_jsonc();
17898
+ init_copilot_sdk_scenarios();
17899
+ }
17900
+ });
17901
+
17449
17902
  // src/team/runtime-v2.ts
17450
17903
  function workerInstructionStateRoot(cwd, teamName) {
17451
17904
  return process.platform === "win32" ? teamStateRoot(cwd, teamName) : "$OMC_TEAM_STATE_ROOT";
@@ -17643,7 +18096,7 @@ async function reconcileCommittedTeamServices(config, cwd) {
17643
18096
  }
17644
18097
  }
17645
18098
  function resolveLeaderBranch(cwd) {
17646
- const out = (0, import_node_child_process7.execFileSync)("git", ["branch", "--show-current"], {
18099
+ const out = (0, import_node_child_process8.execFileSync)("git", ["branch", "--show-current"], {
17647
18100
  cwd,
17648
18101
  encoding: "utf-8",
17649
18102
  stdio: ["pipe", "pipe", "pipe"],
@@ -17685,6 +18138,14 @@ function resolveTaskAssignment(task, resolvedRouting, roleRoutingConfig, fallbac
17685
18138
  role: canonical
17686
18139
  };
17687
18140
  }
18141
+ function isExplicitTaskRoleAssignment(task, roleRoutingConfig, role) {
18142
+ const hasExplicitRole = typeof task.role === "string" && task.role.length > 0;
18143
+ const hasConfigForRole = !!getRoleRoutingSpec(
18144
+ roleRoutingConfig,
18145
+ role
18146
+ );
18147
+ return hasExplicitRole || hasConfigForRole;
18148
+ }
17688
18149
  function sanitizeTeamName(name) {
17689
18150
  const sanitized = name.toLowerCase().replace(/[^a-z0-9-]/g, "").slice(0, 30);
17690
18151
  if (!sanitized) throw new Error(`Invalid team name: "${name}" produces empty slug after sanitization`);
@@ -17696,12 +18157,12 @@ function resolvePreflightBinaryPath(agentType) {
17696
18157
  return { path: resolveValidatedBinaryPath(agentType) };
17697
18158
  }
17698
18159
  function tmuxServerIdentityForTarget(providerTarget, identity) {
17699
- if (providerTarget.startsWith("cmux:")) return void 0;
18160
+ if (providerTarget.startsWith("cmux:") || isSdkTarget(providerTarget)) return void 0;
17700
18161
  if (!isValidTmuxServerIdentity(identity)) return void 0;
17701
18162
  return identity;
17702
18163
  }
17703
18164
  function requireTmuxServerIdentity(providerTarget, identity) {
17704
- if (providerTarget.startsWith("cmux:")) return void 0;
18165
+ if (providerTarget.startsWith("cmux:") || isSdkTarget(providerTarget)) return void 0;
17705
18166
  if (!isValidTmuxServerIdentity(identity)) throw new Error("tmux_server_identity_missing");
17706
18167
  return identity;
17707
18168
  }
@@ -17709,6 +18170,7 @@ function configuredPaneOwnership(config, worker) {
17709
18170
  const paneId = worker.pane_id;
17710
18171
  const providerTarget = config.tmux_session;
17711
18172
  if (!paneId || !providerTarget) return null;
18173
+ if (isSdkTarget(paneId) || isSdkTarget(providerTarget)) return null;
17712
18174
  const provider = paneId.startsWith("%") ? "tmux" : "cmux";
17713
18175
  const identity = provider === "tmux" ? tmuxServerIdentityForTarget(providerTarget, config.tmux_server_identity) : void 0;
17714
18176
  if (provider === "tmux" && !identity) return null;
@@ -17915,7 +18377,7 @@ async function waitForStartupEvidenceBudget(hasEvidence, budgetMs, delayMs = WOR
17915
18377
  if (await hasEvidence()) return true;
17916
18378
  const remainingMs = deadline - Date.now();
17917
18379
  if (remainingMs <= 0) return false;
17918
- await new Promise((resolve14) => setTimeout(resolve14, Math.min(delayMs, remainingMs)));
18380
+ await new Promise((resolve17) => setTimeout(resolve17, Math.min(delayMs, remainingMs)));
17919
18381
  }
17920
18382
  }
17921
18383
  async function waitForWorkerStartupEvidence(teamName, workerName, taskId, cwd, baseline, launchAttemptId, budgetMs, delayMs = WORKER_STARTUP_EVIDENCE_POLL_INTERVAL_MS) {
@@ -18061,6 +18523,136 @@ async function readUnresolvedStartupLaunch(opts, paneId) {
18061
18523
  return null;
18062
18524
  }
18063
18525
  }
18526
+ function resolveSdkTeamSettings(pluginCfg) {
18527
+ const sdk = pluginCfg.team?.sdk ?? {};
18528
+ const envNumber = (key) => {
18529
+ const n = Number(process.env[key]);
18530
+ return Number.isFinite(n) && n > 0 ? n : void 0;
18531
+ };
18532
+ const configCap = sdk.maxCreditsPerWorker;
18533
+ if (configCap !== void 0 && !(typeof configCap === "number" && Number.isFinite(configCap) && configCap > 0)) {
18534
+ throw new Error(`invalid_team_sdk_config:maxCreditsPerWorker must be a positive number (got ${JSON.stringify(configCap)})`);
18535
+ }
18536
+ const model = process.env.OMC_TEAM_SDK_MODEL?.trim() || sdk.model?.trim();
18537
+ return {
18538
+ maxCreditsPerWorker: envNumber("OMC_TEAM_SDK_MAX_CREDITS") ?? configCap ?? 10,
18539
+ ...model ? { model } : {},
18540
+ startupEvidenceMs: envNumber("OMC_TEAM_SDK_STARTUP_EVIDENCE_MS") ?? sdk.startupEvidenceMs ?? 18e4,
18541
+ denyTools: pluginCfg.permissions?.workerDenyTools ?? [],
18542
+ denyUrls: pluginCfg.permissions?.workerDenyUrls ?? []
18543
+ };
18544
+ }
18545
+ async function spawnSdkV2Worker(opts, sdk) {
18546
+ const injectContract = shouldInjectContract(opts.role ?? null, opts.agentType);
18547
+ const outputFile = injectContract && opts.role ? cliWorkerOutputFilePath(teamStateRoot(opts.cwd, opts.teamName), opts.workerName, {
18548
+ taskId: opts.taskId,
18549
+ assignmentId: opts.verdictAssignmentId
18550
+ }) : void 0;
18551
+ const cliOutputContract = injectContract && opts.role && outputFile ? renderCliWorkerOutputContract(opts.role, outputFile) : void 0;
18552
+ const instruction = buildV2TaskInstruction(
18553
+ opts.teamName,
18554
+ opts.workerName,
18555
+ opts.task,
18556
+ opts.taskId,
18557
+ opts.agentType,
18558
+ cliOutputContract
18559
+ );
18560
+ const stateRoot = teamStateRoot(opts.cwd, opts.teamName);
18561
+ const startupBaseline = await captureWorkerStartupBaseline(opts.teamName, opts.workerName, opts.taskId, opts.cwd);
18562
+ const binary = opts.launchDescriptor.binary;
18563
+ const copilotBin = /\.(cmd|bat|ps1)$/i.test(binary) ? resolveShimTarget(binary) : binary;
18564
+ if (!copilotBin) {
18565
+ return { paneId: null, startupAssigned: false, startupFailureReason: `sdk_copilot_shim_unresolved:${binary}` };
18566
+ }
18567
+ const descriptorModel = opts.launchDescriptor.model ? toCopilotModelId(opts.launchDescriptor.model) : void 0;
18568
+ const model = sdk.model ?? descriptorModel;
18569
+ const workerCwd = opts.workerCwd ?? opts.cwd;
18570
+ const launch = await launchSdkHost({
18571
+ leaderCwd: opts.cwd,
18572
+ teamName: opts.teamName,
18573
+ workerName: opts.workerName,
18574
+ instanceId: opts.instanceId,
18575
+ workerCwd,
18576
+ stateRoot,
18577
+ workerEnv: {
18578
+ ...getWorkerEnv(opts.teamName, opts.workerName, opts.agentType, process.env, opts.role),
18579
+ OMC_TEAM_STATE_ROOT: stateRoot,
18580
+ OMC_TEAM_LEADER_CWD: opts.cwd,
18581
+ ...opts.worktreePath ? { OMC_TEAM_WORKTREE_PATH: opts.worktreePath } : {},
18582
+ OMC_TEAM_WORKER_CWD: workerCwd
18583
+ },
18584
+ copilotBin,
18585
+ pluginRoot: resolveDefaultPluginRoot(),
18586
+ userConfigDir: getCopilotConfigDir(),
18587
+ ...model ? { model } : {},
18588
+ maxCredits: sdk.maxCreditsPerWorker,
18589
+ denyTools: sdk.denyTools,
18590
+ denyUrls: sdk.denyUrls
18591
+ });
18592
+ const paneId = sdkPaneId(opts.workerName);
18593
+ if (!launch.ok) {
18594
+ return {
18595
+ paneId: launch.attempt ? paneId : null,
18596
+ startupAssigned: false,
18597
+ startupFailureReason: launch.reason,
18598
+ ...launch.attempt ? { launchAttemptId: launch.attempt.attempt_id } : {}
18599
+ };
18600
+ }
18601
+ const attempt = launch.attempt;
18602
+ const triggerMessage = `${generateTriggerMessage(opts.teamName, opts.workerName, workerInstructionStateRoot(opts.cwd, opts.teamName))} [launch:${attempt.attempt_id.slice(0, 12)}]`;
18603
+ const fenced = await withWorkerLaunchAttemptFence(attempt, () => queueInboxInstruction({
18604
+ teamName: opts.teamName,
18605
+ workerName: opts.workerName,
18606
+ workerIndex: opts.workerIndex + 1,
18607
+ paneId,
18608
+ inbox: instruction,
18609
+ triggerMessage,
18610
+ cwd: opts.cwd,
18611
+ transportPreference: "prompt_stdin",
18612
+ fallbackAllowed: false,
18613
+ inboxCorrelationKey: `startup:${opts.workerName}:${opts.taskId}:${attempt.attempt_id}`,
18614
+ notify: async (_target, message) => {
18615
+ ringDoorbell(stateRoot, opts.workerName, { kind: "prompt", text: message, from: "leader-fixed" });
18616
+ const settled = await waitForStartupEvidenceBudget(
18617
+ async () => {
18618
+ if (await hasCurrentWorkerStartupEvidence(opts.teamName, opts.workerName, opts.taskId, opts.cwd, startupBaseline, attempt.attempt_id)) return true;
18619
+ const session = readSdkSession(stateRoot, opts.workerName);
18620
+ if (session?.state === "failed" || session?.state === "closed") throw new Error(`sdk_host_${session.state}:${session.last_error ?? ""}`);
18621
+ return false;
18622
+ },
18623
+ sdk.startupEvidenceMs,
18624
+ 500
18625
+ ).catch(() => false);
18626
+ return settled ? { ok: true, transport: "prompt_stdin", reason: "sdk_worker_startup_confirmed" } : { ok: false, transport: "prompt_stdin", reason: "copilot_sdk_startup_evidence_missing" };
18627
+ },
18628
+ deps: { writeWorkerInbox }
18629
+ }));
18630
+ const outcome = fenced.ok ? fenced.value : { ok: false, reason: "worker_launch_attempt_superseded" };
18631
+ if (!outcome.ok) {
18632
+ const session = readSdkSession(stateRoot, opts.workerName);
18633
+ const claimError = session?.last_error ? redactBoundedDiagnostic(session.last_error, 240) : void 0;
18634
+ const stopped = await stopSdkHost(attempt, stateRoot, { timeoutMs: 3e4 });
18635
+ const cleaned = stopped && await retireAndCleanupCurrentWorkerLaunchAttempt(attempt, "startup_dispatch_failed", async () => true).catch(() => false);
18636
+ if (!cleaned) {
18637
+ const error = new Error(`worker_startup_cleanup_unverified:${opts.workerName}:${paneId}`);
18638
+ error.unresolvedLaunch = { name: opts.workerName, paneId, launchAttemptId: attempt.attempt_id, provider: "copilot" };
18639
+ throw error;
18640
+ }
18641
+ return {
18642
+ paneId,
18643
+ startupAssigned: false,
18644
+ startupFailureReason: outcome.reason,
18645
+ ...claimError ? { claimError } : {},
18646
+ launchAttemptId: attempt.attempt_id
18647
+ };
18648
+ }
18649
+ return {
18650
+ paneId,
18651
+ startupAssigned: true,
18652
+ launchAttemptId: attempt.attempt_id,
18653
+ ...outputFile ? { outputFile } : {}
18654
+ };
18655
+ }
18064
18656
  async function spawnV2Worker(opts) {
18065
18657
  const splitTarget = opts.existingWorkerPaneIds.length === 0 ? opts.leaderPaneId : opts.existingWorkerPaneIds[opts.existingWorkerPaneIds.length - 1];
18066
18658
  const splitDirection = opts.existingWorkerPaneIds.length === 0 ? "right" : "down";
@@ -18307,8 +18899,8 @@ function validateRecoveryAttemptSecret(value, input, recoveryId, replacementGene
18307
18899
  async function recordRecoveryPaneRollbackFailure(input, recoveryId, pending, reason, liveness) {
18308
18900
  const recordedAt = Date.now();
18309
18901
  const path5 = absPath(input.cwd, TeamPaths.recoveryPaneRollbackFailure(input.teamName, recoveryId, pending.paneAttemptId, recordedAt));
18310
- const candidate = `${path5}.candidate.${process.pid}.${(0, import_node_crypto9.randomUUID)()}`;
18311
- await (0, import_promises19.mkdir)((0, import_path34.join)(path5, ".."), { recursive: true });
18902
+ const candidate = `${path5}.candidate.${process.pid}.${(0, import_node_crypto10.randomUUID)()}`;
18903
+ await (0, import_promises19.mkdir)((0, import_path38.join)(path5, ".."), { recursive: true });
18312
18904
  const handle = await (0, import_promises19.open)(candidate, "wx", 384);
18313
18905
  try {
18314
18906
  await handle.writeFile(JSON.stringify({
@@ -18337,8 +18929,8 @@ async function recordRecoveryPaneRollbackFailure(input, recoveryId, pending, rea
18337
18929
  async function recordUnaddressableRecoveryPaneFailure(input, recoveryId, paneAttemptId, reason, split) {
18338
18930
  const recordedAt = Date.now();
18339
18931
  const path5 = absPath(input.cwd, TeamPaths.recoveryPaneRollbackFailure(input.teamName, recoveryId, paneAttemptId, recordedAt));
18340
- const candidate = `${path5}.candidate.${process.pid}.${(0, import_node_crypto9.randomUUID)()}`;
18341
- await (0, import_promises19.mkdir)((0, import_path34.join)(path5, ".."), { recursive: true });
18932
+ const candidate = `${path5}.candidate.${process.pid}.${(0, import_node_crypto10.randomUUID)()}`;
18933
+ await (0, import_promises19.mkdir)((0, import_path38.join)(path5, ".."), { recursive: true });
18342
18934
  const handle = await (0, import_promises19.open)(candidate, "wx", 384);
18343
18935
  const boundedSplit = split ? {
18344
18936
  ...split,
@@ -18661,11 +19253,11 @@ async function readOrCreateRecoveryAttempt(input, recoveryId, replacementGenerat
18661
19253
  recovery_id: recoveryId,
18662
19254
  worker_name: input.workerName,
18663
19255
  replacement_generation: replacementGeneration,
18664
- adoption_token: (0, import_node_crypto9.randomUUID)(),
19256
+ adoption_token: (0, import_node_crypto10.randomUUID)(),
18665
19257
  created_at: (/* @__PURE__ */ new Date()).toISOString()
18666
19258
  };
18667
- await (0, import_promises19.mkdir)((0, import_path34.join)(path5, ".."), { recursive: true });
18668
- const candidate = `${path5}.candidate.${process.pid}.${(0, import_node_crypto9.randomUUID)()}`;
19259
+ await (0, import_promises19.mkdir)((0, import_path38.join)(path5, ".."), { recursive: true });
19260
+ const candidate = `${path5}.candidate.${process.pid}.${(0, import_node_crypto10.randomUUID)()}`;
18669
19261
  const candidateHandle = await (0, import_promises19.open)(candidate, "wx", 384);
18670
19262
  try {
18671
19263
  await candidateHandle.writeFile(JSON.stringify(secret, null, 2), "utf8");
@@ -18684,14 +19276,14 @@ async function readOrCreateRecoveryAttempt(input, recoveryId, replacementGenerat
18684
19276
  }
18685
19277
  }
18686
19278
  function waitForBootstrapRecoveryEvidence(delayMs, signal) {
18687
- return new Promise((resolve14, reject) => {
19279
+ return new Promise((resolve17, reject) => {
18688
19280
  if (signal?.aborted) {
18689
19281
  reject(signal.reason ?? new Error("bootstrap_recovery_evidence_aborted"));
18690
19282
  return;
18691
19283
  }
18692
19284
  const timer = setTimeout(() => {
18693
19285
  signal?.removeEventListener("abort", onAbort);
18694
- resolve14();
19286
+ resolve17();
18695
19287
  }, delayMs);
18696
19288
  const onAbort = () => {
18697
19289
  clearTimeout(timer);
@@ -18734,7 +19326,7 @@ function isCanonicalBootstrapCandidate(value, expectedEpoch) {
18734
19326
  const candidate = value;
18735
19327
  if (!candidate || candidate.schema_version !== 1 || candidate.expected_epoch !== expectedEpoch || typeof candidate.request_id !== "string" || candidate.request_id.length === 0 || typeof candidate.recovery_id !== "string" || candidate.recovery_id.length === 0 || typeof candidate.team_name !== "string" || candidate.team_name.length === 0 || typeof candidate.worker_name !== "string" || candidate.worker_name.length === 0 || !isValidTeamInstanceId(candidate.instance_id) || typeof candidate.nonce !== "string" || candidate.nonce.length === 0 || typeof candidate.pid !== "number" || !Number.isSafeInteger(candidate.pid) || candidate.pid < 1 || typeof candidate.process_started_at !== "string" || candidate.process_started_at.length === 0 || typeof candidate.predecessor_epoch !== "number" || !Number.isSafeInteger(candidate.predecessor_epoch) || candidate.predecessor_epoch < 0 || candidate.expected_epoch !== candidate.predecessor_epoch + 1 || candidate.predecessor_epoch === 0 && (candidate.predecessor_nonce !== null || candidate.predecessor_pid !== null || candidate.predecessor_process_started_at !== null) || candidate.predecessor_epoch > 0 && (typeof candidate.predecessor_nonce !== "string" || candidate.predecessor_nonce.length === 0 || typeof candidate.predecessor_pid !== "number" || !Number.isSafeInteger(candidate.predecessor_pid) || candidate.predecessor_pid < 1 || typeof candidate.predecessor_process_started_at !== "string" || candidate.predecessor_process_started_at.length === 0) || typeof candidate.created_at !== "string" || !Number.isFinite(Date.parse(candidate.created_at)) || typeof candidate.payload_hash !== "string") return false;
18736
19328
  const { payload_hash, ...unsigned } = candidate;
18737
- return (0, import_node_crypto9.createHash)("sha256").update(JSON.stringify(unsigned)).digest("hex") === payload_hash;
19329
+ return (0, import_node_crypto10.createHash)("sha256").update(JSON.stringify(unsigned)).digest("hex") === payload_hash;
18738
19330
  }
18739
19331
  async function readRecoveryOwnerBootstrapCandidate(teamName, cwd, expectedEpoch, nonce) {
18740
19332
  try {
@@ -18791,7 +19383,7 @@ async function isExactDeadOrphanBootstrapCandidate(teamName, cwd, input, config,
18791
19383
  function isExactRecoverySidecar(value, task, input, active, replacementGeneration, adoptionToken) {
18792
19384
  const sidecar = value;
18793
19385
  const persisted = task.recovery_reservation ?? task.recovery_adoption;
18794
- if (!sidecar || !persisted || sidecar.schema_version !== 1 || sidecar.recovery_id !== active.recovery_id || sidecar.request_id !== input.requestId || sidecar.task_id !== task.id || sidecar.old_owner !== input.workerName || typeof sidecar.old_task_version !== "number" || !Number.isSafeInteger(sidecar.old_task_version) || sidecar.old_task_version < 1 || typeof sidecar.old_claim_token !== "string" || sidecar.old_claim_token.length === 0 || typeof sidecar.old_claim_leased_until !== "string" || !Number.isFinite(Date.parse(sidecar.old_claim_leased_until)) || typeof sidecar.continuation_sequence !== "number" || !Number.isSafeInteger(sidecar.continuation_sequence) || sidecar.continuation_sequence < 1 || typeof sidecar.checkpoint_path !== "string" || sidecar.checkpoint_path.length === 0 || typeof sidecar.checkpoint_hash !== "string" || !/^[a-f0-9]{64}$/.test(sidecar.checkpoint_hash) || sidecar.replacement_worker !== input.workerName || sidecar.replacement_generation !== replacementGeneration || sidecar.adoption_token_hash !== (0, import_node_crypto9.createHash)("sha256").update(adoptionToken).digest("hex") || typeof sidecar.created_at !== "string" || !Number.isFinite(Date.parse(sidecar.created_at))) return false;
19386
+ if (!sidecar || !persisted || sidecar.schema_version !== 1 || sidecar.recovery_id !== active.recovery_id || sidecar.request_id !== input.requestId || sidecar.task_id !== task.id || sidecar.old_owner !== input.workerName || typeof sidecar.old_task_version !== "number" || !Number.isSafeInteger(sidecar.old_task_version) || sidecar.old_task_version < 1 || typeof sidecar.old_claim_token !== "string" || sidecar.old_claim_token.length === 0 || typeof sidecar.old_claim_leased_until !== "string" || !Number.isFinite(Date.parse(sidecar.old_claim_leased_until)) || typeof sidecar.continuation_sequence !== "number" || !Number.isSafeInteger(sidecar.continuation_sequence) || sidecar.continuation_sequence < 1 || typeof sidecar.checkpoint_path !== "string" || sidecar.checkpoint_path.length === 0 || typeof sidecar.checkpoint_hash !== "string" || !/^[a-f0-9]{64}$/.test(sidecar.checkpoint_hash) || sidecar.replacement_worker !== input.workerName || sidecar.replacement_generation !== replacementGeneration || sidecar.adoption_token_hash !== (0, import_node_crypto10.createHash)("sha256").update(adoptionToken).digest("hex") || typeof sidecar.created_at !== "string" || !Number.isFinite(Date.parse(sidecar.created_at))) return false;
18795
19387
  const sameReservation = persisted.recovery_id === sidecar.recovery_id && persisted.request_id === sidecar.request_id && persisted.continuation_sequence === sidecar.continuation_sequence && persisted.checkpoint_path === sidecar.checkpoint_path && persisted.checkpoint_hash === sidecar.checkpoint_hash && persisted.replacement_worker === sidecar.replacement_worker && persisted.replacement_generation === sidecar.replacement_generation;
18796
19388
  if (!sameReservation) return false;
18797
19389
  if ("adoption_token_hash" in persisted && persisted.adoption_token_hash !== sidecar.adoption_token_hash) return false;
@@ -18995,7 +19587,7 @@ async function prepareRecoveryOwnerBootstrap(input, waitOptions) {
18995
19587
  }
18996
19588
  async function executeRecoverDeadWorkerV2Owner(input) {
18997
19589
  const reservation = readRecoveryRequestReservation(input.cwd, input.requestId);
18998
- const recoveryId = reservation?.recovery_id ?? (0, import_node_crypto9.randomUUID)();
19590
+ const recoveryId = reservation?.recovery_id ?? (0, import_node_crypto10.randomUUID)();
18999
19591
  let ownerBound = false;
19000
19592
  try {
19001
19593
  if (!isValidTeamInstanceId(input.instanceId)) {
@@ -19475,7 +20067,7 @@ async function executeRecoverDeadWorkerV2Owner(input) {
19475
20067
  } catch {
19476
20068
  return { ok: false, error: "launch_descriptor_unresolvable" };
19477
20069
  }
19478
- const paneAttemptId = (0, import_node_crypto9.randomUUID)();
20070
+ const paneAttemptId = (0, import_node_crypto10.randomUUID)();
19479
20071
  const livePaneIds = [];
19480
20072
  for (const candidate of config.workers) {
19481
20073
  if (!candidate.pane_id || candidate.name === sagaInput2.workerName) continue;
@@ -19668,7 +20260,7 @@ async function executeRecoverDeadWorkerV2Owner(input) {
19668
20260
  launch_nonce: pending.startupContext.attempt.nonce,
19669
20261
  written_at: (/* @__PURE__ */ new Date()).toISOString()
19670
20262
  };
19671
- await (0, import_promises19.mkdir)((0, import_path34.join)(pending.gate.activatePath, ".."), { recursive: true });
20263
+ await (0, import_promises19.mkdir)((0, import_path38.join)(pending.gate.activatePath, ".."), { recursive: true });
19672
20264
  await (0, import_promises19.writeFile)(pending.gate.activatePath, JSON.stringify(record), "utf8");
19673
20265
  const adoptedReady = await waitForRecoveryGateRecord(`${pending.gate.readyPath}.adoption-ready`, record, 3e4);
19674
20266
  return adoptedReady && await isWorkerLaunchAttemptCurrent(pending.startupContext.attempt) ? { ok: true } : { ok: false, error: "worker_activation_failed" };
@@ -19795,7 +20387,7 @@ ${recoveryContract}` : ""}`;
19795
20387
  let providerLive = false;
19796
20388
  try {
19797
20389
  const launchedRecord = JSON.parse(await (0, import_promises19.readFile)(launchedPath, "utf8"));
19798
- 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 && !(0, import_fs30.existsSync)(launchedRecord.supervisor_completion_path)) && await isProcessIdentityLive(
20390
+ 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 && !(0, import_fs34.existsSync)(launchedRecord.supervisor_completion_path)) && await isProcessIdentityLive(
19799
20391
  launchedRecord.provider_pid,
19800
20392
  launchedRecord.provider_start_identity,
19801
20393
  Date.now() + 1e3
@@ -19874,7 +20466,7 @@ async function rollbackUnpersistedNativeWorktreeStartup(teamName, cwd, cause, in
19874
20466
  const recordedAt = (/* @__PURE__ */ new Date()).toISOString();
19875
20467
  const writeFailureMarker = async (extra = {}) => {
19876
20468
  await (0, import_promises19.mkdir)(teamRoot, { recursive: true });
19877
- await (0, import_promises19.writeFile)((0, import_path34.join)(teamRoot, "startup-failure.json"), JSON.stringify({
20469
+ await (0, import_promises19.writeFile)((0, import_path38.join)(teamRoot, "startup-failure.json"), JSON.stringify({
19878
20470
  reason: "startup_failed_before_config_persisted",
19879
20471
  error: errorMessage,
19880
20472
  recorded_at: recordedAt,
@@ -19905,7 +20497,7 @@ async function rollbackUnpersistedNativeWorktreeStartup(teamName, cwd, cause, in
19905
20497
  }
19906
20498
  }
19907
20499
  await (0, import_promises19.rm)(teamRoot, { recursive: true, force: true });
19908
- if ((0, import_fs30.existsSync)(teamRoot)) {
20500
+ if ((0, import_fs34.existsSync)(teamRoot)) {
19909
20501
  await writeFailureMarker({ rollback_error: "startup_state_removal_unverified" });
19910
20502
  return false;
19911
20503
  }
@@ -19929,7 +20521,7 @@ async function rollbackUnpersistedNativeWorktreeStartup(teamName, cwd, cause, in
19929
20521
  async function writeStartedStartupRollbackEvidence(args) {
19930
20522
  const teamRoot = absPath(args.cwd, TeamPaths.root(args.teamName));
19931
20523
  await (0, import_promises19.mkdir)(teamRoot, { recursive: true });
19932
- await (0, import_promises19.writeFile)((0, import_path34.join)(teamRoot, "startup-failure.json"), JSON.stringify({
20524
+ await (0, import_promises19.writeFile)((0, import_path38.join)(teamRoot, "startup-failure.json"), JSON.stringify({
19933
20525
  reason: args.markerReason ?? "startup_rollback_cleanup_incomplete",
19934
20526
  error: args.cause instanceof Error ? args.cause.message : String(args.cause),
19935
20527
  rollback_error: args.reason,
@@ -19980,7 +20572,12 @@ async function rollbackStartedNativeWorktreeStartup(args) {
19980
20572
  await writeStartedStartupRollbackEvidence({ ...args, reason: "launch_attempt_identity_unverified", worker: worker.name });
19981
20573
  throw new Error(`worker_cleanup_incomplete:${worker.name}:launch_attempt_identity_unverified`);
19982
20574
  }
20575
+ if (isSdkTarget(worker.paneId) && !await stopSdkHost(attempt, teamStateRoot(args.cwd, args.teamName), { timeoutMs: 3e4 })) {
20576
+ await writeStartedStartupRollbackEvidence({ ...args, reason: "sdk_host_stop_unverified", worker: worker.name });
20577
+ throw new Error(`worker_cleanup_incomplete:${worker.name}:sdk_host_stop_unverified`);
20578
+ }
19983
20579
  const cleaned = await retireAndCleanupCurrentWorkerLaunchAttempt(attempt, "startup_rollback", async () => {
20580
+ if (isSdkTarget(worker.paneId)) return true;
19984
20581
  const ownership = {
19985
20582
  provider: worker.paneId.startsWith("%") ? "tmux" : "cmux",
19986
20583
  providerTarget: args.sessionName,
@@ -20002,7 +20599,7 @@ async function rollbackStartedNativeWorktreeStartup(args) {
20002
20599
  throw new Error(`worker_cleanup_incomplete:${worker.name}:provider_cleanup_unverified`);
20003
20600
  }
20004
20601
  }
20005
- if (sessionCleanupRequired && !(args.workerPaneIds.length === 0 && args.sessionMode === "split-pane" && (args.launchedWorkers?.length ?? 0) > 0)) {
20602
+ if (sessionCleanupRequired && !isSdkTarget(args.sessionName) && !(args.workerPaneIds.length === 0 && args.sessionMode === "split-pane" && (args.launchedWorkers?.length ?? 0) > 0)) {
20006
20603
  const sessionCleaned = await killTeamSession(
20007
20604
  args.sessionName,
20008
20605
  args.workerPaneIds,
@@ -20108,12 +20705,17 @@ async function startTeamV2(config) {
20108
20705
  }
20109
20706
  const instance = createTeamInstanceBinding({
20110
20707
  teamName: sanitized,
20111
- cwd: (0, import_path34.resolve)(config.cwd),
20708
+ cwd: (0, import_path38.resolve)(config.cwd),
20112
20709
  ...config.instanceId !== void 0 ? { instanceId: config.instanceId } : {}
20113
20710
  });
20114
20711
  const leaderCwd = instance.cwd;
20115
20712
  const pluginCfg = config.pluginConfig ?? loadConfig();
20116
20713
  const resolvedRouting = buildResolvedRoutingSnapshot(pluginCfg);
20714
+ const transport = config.transport ?? pluginCfg.team?.transport ?? "pane";
20715
+ if (transport !== "pane" && transport !== "sdk") throw new Error(`invalid_team_transport:${String(transport)}`);
20716
+ const sdkMode = transport === "sdk";
20717
+ const sdkSettings = sdkMode ? resolveSdkTeamSettings(pluginCfg) : null;
20718
+ if (sdkMode && config.autoMerge) throw new Error("sdk_transport_unsupported:auto_merge");
20117
20719
  let worktreeMode = normalizeTeamWorktreeMode(
20118
20720
  process.env.OMC_TEAM_WORKTREE_MODE ?? pluginCfg.team?.ops?.worktreeMode
20119
20721
  );
@@ -20179,21 +20781,33 @@ async function startTeamV2(config) {
20179
20781
  const workerName = workerNames[i];
20180
20782
  const taskIndex = startupByWorker.get(workerName);
20181
20783
  const fallbackAgent = agentTypes[i % agentTypes.length] ?? agentTypes[0] ?? "claude";
20182
- const resolvedAssignment = taskIndex === void 0 ? { agentType: fallbackAgent, model: "", reasoningEffort: void 0, role: void 0 } : resolveTaskAssignment(
20183
- config.tasks[taskIndex],
20184
- resolvedRouting,
20185
- pluginCfg.team?.roleRouting,
20186
- fallbackAgent
20187
- );
20784
+ const roleRoutingConfig = pluginCfg.team?.roleRouting;
20785
+ const task = taskIndex === void 0 ? void 0 : config.tasks[taskIndex];
20786
+ const resolvedAssignment = task === void 0 ? { agentType: fallbackAgent, model: "", reasoningEffort: void 0, role: void 0 } : resolveTaskAssignment(task, resolvedRouting, roleRoutingConfig, fallbackAgent);
20787
+ let role = resolvedAssignment.role ?? void 0;
20788
+ if (sdkMode && role && shouldInjectContract(role, resolvedAssignment.agentType) && task && !isExplicitTaskRoleAssignment(task, roleRoutingConfig, role)) {
20789
+ 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)
20790
+ `);
20791
+ role = void 0;
20792
+ }
20188
20793
  const assignment = {
20189
20794
  agentType: resolvedAssignment.agentType,
20190
20795
  model: resolvedAssignment.model || resolveDefaultModel(resolvedAssignment.agentType),
20191
20796
  reasoningEffort: resolvedAssignment.reasoningEffort,
20192
- ...resolvedAssignment.role ? { role: resolvedAssignment.role } : {}
20797
+ ...role ? { role } : {}
20193
20798
  };
20194
20799
  startupAssignments.set(workerName, assignment);
20195
20800
  effectiveAgentTypes.add(assignment.agentType);
20196
20801
  }
20802
+ if (sdkMode && [...effectiveAgentTypes].some((agentType) => agentType !== "copilot")) {
20803
+ throw new Error(`sdk_transport_requires_copilot_workers:${[...effectiveAgentTypes].join(",")}`);
20804
+ }
20805
+ if (sdkMode) {
20806
+ const contractRoles = [...new Set([...startupAssignments.values()].flatMap((a) => a.role && shouldInjectContract(a.role, a.agentType) ? [a.role] : []))];
20807
+ if (contractRoles.length > 0) {
20808
+ 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)`);
20809
+ }
20810
+ }
20197
20811
  for (const agentType of effectiveAgentTypes) {
20198
20812
  try {
20199
20813
  resolvedBinaryPaths[agentType] = resolvePreflightBinaryPath(agentType).path;
@@ -20233,7 +20847,7 @@ async function startTeamV2(config) {
20233
20847
  try {
20234
20848
  const pendingConfig = buildTeamInstancePendingConfig(instance);
20235
20849
  const pendingConfigPath = absPath(leaderCwd, TeamPaths.config(sanitized));
20236
- await (0, import_promises19.mkdir)((0, import_path34.join)(pendingConfigPath, ".."), { recursive: true });
20850
+ await (0, import_promises19.mkdir)((0, import_path38.join)(pendingConfigPath, ".."), { recursive: true });
20237
20851
  await (0, import_promises19.writeFile)(pendingConfigPath, JSON.stringify(pendingConfig, null, 2), "utf-8");
20238
20852
  } catch (error) {
20239
20853
  if (!await rollbackBeforeConfig(error)) throw startupCleanupIncompleteError(error);
@@ -20244,7 +20858,7 @@ async function startTeamV2(config) {
20244
20858
  try {
20245
20859
  await (0, import_promises19.mkdir)(absPath(leaderCwd, TeamPaths.tasks(sanitized)), { recursive: true });
20246
20860
  await (0, import_promises19.mkdir)(absPath(leaderCwd, TeamPaths.workers(sanitized)), { recursive: true });
20247
- await (0, import_promises19.mkdir)((0, import_path34.join)(getOmcRoot(leaderCwd), "state", "team", sanitized, "mailbox"), { recursive: true });
20861
+ await (0, import_promises19.mkdir)((0, import_path38.join)(getOmcRoot(leaderCwd), "state", "team", sanitized, "mailbox"), { recursive: true });
20248
20862
  for (let i = 0; i < config.tasks.length; i++) {
20249
20863
  const taskId = String(i + 1);
20250
20864
  const task = config.tasks[i];
@@ -20281,7 +20895,7 @@ async function startTeamV2(config) {
20281
20895
  const assignment = startupAssignments.get(workerName);
20282
20896
  if (!assignment) throw new Error(`Missing startup assignment for ${workerName}`);
20283
20897
  const worktree = workerWorktrees.get(workerName);
20284
- const verdictAssignmentId = taskIndex !== void 0 ? (0, import_node_crypto9.randomUUID)() : void 0;
20898
+ const verdictAssignmentId = taskIndex !== void 0 ? (0, import_node_crypto10.randomUUID)() : void 0;
20285
20899
  const outputFile = taskIndex !== void 0 && assignment.role && shouldInjectContract(assignment.role, assignment.agentType) ? cliWorkerOutputFilePath(teamStateRoot(leaderCwd, sanitized), workerName, {
20286
20900
  taskId: String(taskIndex + 1),
20287
20901
  assignmentId: verdictAssignmentId
@@ -20318,7 +20932,8 @@ async function startTeamV2(config) {
20318
20932
  if (!await rollbackBeforeConfig(error)) throw startupCleanupIncompleteError(error);
20319
20933
  throw error;
20320
20934
  }
20321
- for (const line of formatWorkerPermissionLines(preparedLaunches.values())) {
20935
+ 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());
20936
+ for (const line of permissionLines) {
20322
20937
  process.stderr.write(`${line}
20323
20938
  `);
20324
20939
  }
@@ -20355,7 +20970,7 @@ async function startTeamV2(config) {
20355
20970
  }
20356
20971
  let session;
20357
20972
  try {
20358
- session = await createTeamSession(sanitized, 0, leaderCwd, {
20973
+ session = sdkMode ? { sessionName: sdkTeamTarget(sanitized), leaderPaneId: "", workerPaneIds: [], sessionMode: "split-pane" } : await createTeamSession(sanitized, 0, leaderCwd, {
20359
20974
  newWindow: Boolean(config.newWindow)
20360
20975
  });
20361
20976
  } catch (error) {
@@ -20375,6 +20990,7 @@ async function startTeamV2(config) {
20375
20990
  role: preparedLaunches.get(wName)?.role ?? config.workerRoles?.[i] ?? (agentTypes[i % agentTypes.length] ?? agentTypes[0] ?? "claude"),
20376
20991
  worker_cli: preparedLaunches.get(wName).descriptor.provider,
20377
20992
  launch_descriptor: preparedLaunches.get(wName).descriptor,
20993
+ ...sdkMode ? { transport: "sdk" } : {},
20378
20994
  assigned_tasks: [],
20379
20995
  working_dir: worktree?.path ?? leaderCwd,
20380
20996
  team_state_root: teamStateRoot(leaderCwd, sanitized),
@@ -20420,7 +21036,7 @@ async function startTeamV2(config) {
20420
21036
  service_descriptor: config.autoMerge ? {
20421
21037
  schema_version: 1,
20422
21038
  service_generation: 1,
20423
- service_attempt_id: (0, import_node_crypto9.randomUUID)(),
21039
+ service_attempt_id: (0, import_node_crypto10.randomUUID)(),
20424
21040
  auto_merge_enabled: true,
20425
21041
  workspace_root: leaderCwd,
20426
21042
  leader_branch: autoMergeLeaderBranch,
@@ -20428,7 +21044,7 @@ async function startTeamV2(config) {
20428
21044
  } : {
20429
21045
  schema_version: 1,
20430
21046
  service_generation: 1,
20431
- service_attempt_id: (0, import_node_crypto9.randomUUID)(),
21047
+ service_attempt_id: (0, import_node_crypto10.randomUUID)(),
20432
21048
  auto_merge_enabled: false,
20433
21049
  workspace_root: leaderCwd,
20434
21050
  cadence_policy: "disabled"
@@ -20516,7 +21132,7 @@ async function startTeamV2(config) {
20516
21132
  if (!prepared) continue;
20517
21133
  const workerInfo = workersInfo[workerIndex];
20518
21134
  if (!workerInfo) continue;
20519
- const workerLaunch = await spawnV2Worker({
21135
+ const spawnOptions = {
20520
21136
  sessionName: sessionName2,
20521
21137
  ...session.tmuxServerIdentity ? { tmuxServerIdentity: session.tmuxServerIdentity } : {},
20522
21138
  leaderPaneId,
@@ -20535,7 +21151,8 @@ async function startTeamV2(config) {
20535
21151
  autoMerge: Boolean(config.autoMerge),
20536
21152
  ...prepared.role ? { role: prepared.role } : {},
20537
21153
  ...prepared.verdictAssignmentId ? { verdictAssignmentId: prepared.verdictAssignmentId } : {}
20538
- });
21154
+ };
21155
+ const workerLaunch = sdkSettings ? await spawnSdkV2Worker(spawnOptions, sdkSettings) : await spawnV2Worker(spawnOptions);
20539
21156
  if (workerLaunch.paneId) {
20540
21157
  if (workerLaunch.startupAssigned) workerPaneIds.push(workerLaunch.paneId);
20541
21158
  launchedWorkers.push({
@@ -20710,7 +21327,7 @@ function nonCursorVerdictFileIdentity(stats) {
20710
21327
  ].join("-");
20711
21328
  }
20712
21329
  function fingerprintCliWorkerVerdictArtifact(raw, stats) {
20713
- const contentSha256 = (0, import_node_crypto9.createHash)("sha256").update(raw, "utf8").digest("hex");
21330
+ const contentSha256 = (0, import_node_crypto10.createHash)("sha256").update(raw, "utf8").digest("hex");
20714
21331
  return `${contentSha256}-${nonCursorVerdictFileIdentity(stats)}`;
20715
21332
  }
20716
21333
  async function readNonCursorVerdictArtifact(outputFile) {
@@ -20772,7 +21389,7 @@ async function processCliWorkerVerdictsUnderLock(teamName, cwd, expectedInstance
20772
21389
  "team.runtime-v2.processCliWorkerVerdicts teamMarkTaskCompleted failed"
20773
21390
  );
20774
21391
  const { rename: rename8 } = await import("fs/promises");
20775
- const { renameSync: renameSync5, readFileSync: readFileSync21, existsSync: fsExistsSync } = await import("fs");
21392
+ const { renameSync: renameSync5, readFileSync: readFileSync24, existsSync: fsExistsSync } = await import("fs");
20776
21393
  const { withFileLockSync: withFileLockSync2 } = await Promise.resolve().then(() => (init_file_lock(), file_lock_exports));
20777
21394
  const quarantineNonCursorVerdict = async (outputFile, artifactFingerprint, workerName, taskId, taskVersion, reason) => {
20778
21395
  const stalePath = nonCursorVerdictStalePath(outputFile, artifactFingerprint);
@@ -20805,7 +21422,7 @@ async function processCliWorkerVerdictsUnderLock(teamName, cwd, expectedInstance
20805
21422
  const outputFile = worker.output_file;
20806
21423
  if (!outputFile) continue;
20807
21424
  const paneOwnership = configuredPaneOwnership(config, worker);
20808
- const liveness = paneOwnership ? await getOwnedWorkerLiveness(paneOwnership) : "unknown";
21425
+ const liveness = isSdkTarget(worker.pane_id) ? await getWorkerProviderLiveness(sanitized, cwd, config.instance_id, worker) : paneOwnership ? await getOwnedWorkerLiveness(paneOwnership) : "unknown";
20809
21426
  const workerRole = normalizeDelegationRole(worker.role);
20810
21427
  const cursorReviewer = worker.worker_cli === "cursor" && CONTRACT_ROLES.has(workerRole);
20811
21428
  const liveCursorReviewer = liveness === "alive" && worker.worker_cli === "cursor" && CONTRACT_ROLES.has(workerRole);
@@ -20853,7 +21470,7 @@ async function processCliWorkerVerdictsUnderLock(teamName, cwd, expectedInstance
20853
21470
  return;
20854
21471
  }
20855
21472
  }
20856
- const raw = readFileSync21(outputFile, "utf-8");
21473
+ const raw = readFileSync24(outputFile, "utf-8");
20857
21474
  parseCliWorkerVerdict(raw);
20858
21475
  renameSync5(outputFile, processingOutputFile);
20859
21476
  verdictFile = processingOutputFile;
@@ -21017,7 +21634,7 @@ async function processCliWorkerVerdictsUnderLock(teamName, cwd, expectedInstance
21017
21634
  if (cursorReviewer && verdictFile === processingOutputFile) {
21018
21635
  const processedTaskPath = absPath(cwd, TeamPaths.taskFile(sanitized, payload.task_id));
21019
21636
  try {
21020
- const processedTask = JSON.parse(readFileSync21(processedTaskPath, "utf-8"));
21637
+ const processedTask = JSON.parse(readFileSync24(processedTaskPath, "utf-8"));
21021
21638
  const metadata = processedTask.metadata && typeof processedTask.metadata === "object" ? processedTask.metadata : void 0;
21022
21639
  const processedTaskRole = typeof processedTask.role === "string" ? normalizeDelegationRole(processedTask.role) : null;
21023
21640
  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;
@@ -21359,20 +21976,28 @@ async function monitorTeamV2(teamName, cwd, expectedInstanceId) {
21359
21976
  const workerScanStartMs = import_perf_hooks.performance.now();
21360
21977
  const workerSignals = await Promise.all(
21361
21978
  config.workers.map(async (worker) => {
21979
+ const providerLiveness = await getWorkerProviderLiveness(sanitized, cwd, config.instance_id, worker);
21980
+ if (isSdkTarget(worker.pane_id)) {
21981
+ const sdkSession = readSdkSession(teamStateRoot(cwd, sanitized), worker.name);
21982
+ const [status2, heartbeat2] = await Promise.all([
21983
+ readWorkerStatus(sanitized, worker.name, cwd),
21984
+ readWorkerHeartbeat(sanitized, worker.name, cwd)
21985
+ ]);
21986
+ return { worker, alive: providerLiveness === "alive", liveness: providerLiveness, providerLiveness, status: status2, heartbeat: heartbeat2, paneCapture: "", sdkSession };
21987
+ }
21362
21988
  const ownership = configuredPaneOwnership(config, worker);
21363
21989
  const liveness = ownership ? await getOwnedWorkerLiveness(ownership) : "unknown";
21364
- const providerLiveness = await getWorkerProviderLiveness(sanitized, cwd, config.instance_id, worker);
21365
21990
  const paneAlive = liveness === "alive";
21366
21991
  const [status, heartbeat, paneCapture] = await Promise.all([
21367
21992
  readWorkerStatus(sanitized, worker.name, cwd),
21368
21993
  readWorkerHeartbeat(sanitized, worker.name, cwd),
21369
21994
  paneAlive && ownership ? captureOwnedTeamPane(ownership) : Promise.resolve("")
21370
21995
  ]);
21371
- return { worker, alive: paneAlive, liveness, providerLiveness, status, heartbeat, paneCapture };
21996
+ return { worker, alive: paneAlive, liveness, providerLiveness, status, heartbeat, paneCapture, sdkSession: null };
21372
21997
  })
21373
21998
  );
21374
21999
  const workerScanMs = import_perf_hooks.performance.now() - workerScanStartMs;
21375
- for (const { worker: w, alive, liveness, providerLiveness, status, heartbeat, paneCapture } of workerSignals) {
22000
+ for (const { worker: w, alive, liveness, providerLiveness, status, heartbeat, paneCapture, sdkSession } of workerSignals) {
21376
22001
  const currentTask = status.current_task_id ? taskById.get(status.current_task_id) ?? null : null;
21377
22002
  const outstandingTask = currentTask ?? findOutstandingWorkerTask(w, taskById, inProgressByOwner);
21378
22003
  const expectedTaskId = status.current_task_id ?? outstandingTask?.id ?? w.assigned_tasks[0] ?? "";
@@ -21395,7 +22020,19 @@ async function monitorTeamV2(teamName, cwd, expectedInstanceId) {
21395
22020
  worktree_detached: w.worktree_detached,
21396
22021
  worktree_created: w.worktree_created,
21397
22022
  team_state_root: w.team_state_root,
21398
- turnsWithoutProgress
22023
+ turnsWithoutProgress,
22024
+ ...sdkSession ? { sdk: {
22025
+ state: sdkSession.state,
22026
+ turns: sdkSession.turns,
22027
+ queued: sdkSession.queued,
22028
+ last_event_type: sdkSession.last_event_type,
22029
+ last_event_at: sdkSession.last_event_at,
22030
+ last_error: sdkSession.last_error,
22031
+ session_id: sdkSession.session_id,
22032
+ model: sdkSession.model,
22033
+ credits: sdkSession.usage.credits,
22034
+ premium_requests: sdkSession.usage.shutdown_premium_requests ?? sdkSession.usage.premium_requests
22035
+ } } : {}
21399
22036
  });
21400
22037
  if (providerLiveness === "dead") {
21401
22038
  deadWorkers.push(w.name);
@@ -21404,13 +22041,16 @@ async function monitorTeamV2(teamName, cwd, expectedInstanceId) {
21404
22041
  recommendations.push(`Reassign task-${t.id} from dead ${w.name}`);
21405
22042
  }
21406
22043
  }
21407
- const paneSuggestsIdle = alive && paneLooksReady(paneCapture) && !paneHasActiveTask(paneCapture);
22044
+ const sdkCapped = Boolean(sdkSession && providerLiveness !== "dead" && sdkSession.state === "capped");
22045
+ const paneSuggestsIdle = sdkSession ? alive && (sdkSession.state === "idle" && sdkSession.queued === 0 || sdkCapped) : alive && paneLooksReady(paneCapture) && !paneHasActiveTask(paneCapture);
21408
22046
  const statusFresh = isFreshTimestamp(status.updated_at);
21409
22047
  const heartbeatFresh = isFreshTimestamp(heartbeat?.last_turn_at);
21410
22048
  const hasWorkStartEvidence = expectedTaskId !== "" && hasWorkerStatusProgress(status, expectedTaskId);
21411
22049
  const missingDependencyIds = outstandingTask ? getMissingDependencyIds(outstandingTask, taskById) : [];
21412
22050
  let stallReason = null;
21413
- if (paneSuggestsIdle && missingDependencyIds.length > 0) {
22051
+ if (sdkCapped) {
22052
+ stallReason = "sdk_credit_cap";
22053
+ } else if (paneSuggestsIdle && missingDependencyIds.length > 0) {
21414
22054
  stallReason = "missing_dependency";
21415
22055
  } else if (paneSuggestsIdle && expectedTaskId !== "" && !hasWorkStartEvidence) {
21416
22056
  stallReason = "no_work_start_evidence";
@@ -21421,7 +22061,15 @@ async function monitorTeamV2(teamName, cwd, expectedInstanceId) {
21421
22061
  }
21422
22062
  if (stallReason) {
21423
22063
  nonReportingWorkers.push(w.name);
21424
- if (stallReason === "missing_dependency") {
22064
+ if (stallReason === "sdk_credit_cap") {
22065
+ const cappedTasks = inProgressByOwner.get(w.name) ?? [];
22066
+ for (const t of cappedTasks) {
22067
+ recommendations.push(`Reassign task-${t.id} from ${w.name}: sdk credit cap reached (${sdkSession?.last_error ?? "capped"}); the worker takes no more prompts`);
22068
+ }
22069
+ if (cappedTasks.length === 0) {
22070
+ recommendations.push(`${w.name} reached its sdk credit cap and takes no more prompts; do not assign it new work`);
22071
+ }
22072
+ } else if (stallReason === "missing_dependency") {
21425
22073
  recommendations.push(
21426
22074
  `Investigate ${w.name}: task-${outstandingTask?.id ?? expectedTaskId} is blocked by missing task ids [${missingDependencyIds.join(", ")}]; pane is idle at prompt`
21427
22075
  );
@@ -21515,7 +22163,7 @@ async function cleanupStaleReservations(teamName, cwd) {
21515
22163
  cwd,
21516
22164
  TeamPaths.teamInstanceReservation(workspaceHash, sanitized)
21517
22165
  );
21518
- if (!(0, import_fs30.existsSync)(reservationPath2)) return;
22166
+ if (!(0, import_fs34.existsSync)(reservationPath2)) return;
21519
22167
  const contentStr = await (0, import_promises19.readFile)(reservationPath2, "utf-8");
21520
22168
  const reservation = JSON.parse(contentStr);
21521
22169
  if (!(reservation && typeof reservation === "object" && !Array.isArray(reservation) && "owner" in reservation && typeof reservation.owner === "object")) {
@@ -21525,6 +22173,7 @@ async function cleanupStaleReservations(teamName, cwd) {
21525
22173
  if (!(owner && typeof owner === "object" && !Array.isArray(owner) && "pid" in owner && "process_started_at" in owner)) {
21526
22174
  return;
21527
22175
  }
22176
+ if (reservation.phase === "active") return;
21528
22177
  const ownerRecord = owner;
21529
22178
  if (isProcessIdentityDead(ownerRecord)) {
21530
22179
  await (0, import_promises19.unlink)(reservationPath2);
@@ -21617,7 +22266,7 @@ async function shutdownTeamV2(teamName, cwd, options = {}) {
21617
22266
  });
21618
22267
  instance = boundInstance;
21619
22268
  await assertTeamInstanceUnderLock(boundInstance);
21620
- if (!current.config.tmux_session || !current.config.tmux_session.startsWith("cmux:") && !isValidTmuxServerIdentity(current.config.tmux_server_identity)) {
22269
+ if (!current.config.tmux_session || !current.config.tmux_session.startsWith("cmux:") && !isSdkTarget(current.config.tmux_session) && !isValidTmuxServerIdentity(current.config.tmux_server_identity)) {
21621
22270
  throw new Error("tmux_server_identity_missing");
21622
22271
  }
21623
22272
  if (current.config.active_recovery) throw new Error(`shutdown_blocked:active_recovery:${current.config.active_recovery.recovery_id}`);
@@ -21648,7 +22297,7 @@ async function shutdownTeamV2(teamName, cwd, options = {}) {
21648
22297
  }
21649
22298
  const processStartedAt = currentProcessStartIdentity();
21650
22299
  if (!processStartedAt) throw new Error("process_start_identity_unavailable");
21651
- ownedShutdownNonce = (0, import_node_crypto9.randomUUID)();
22300
+ ownedShutdownNonce = (0, import_node_crypto10.randomUUID)();
21652
22301
  const nextRevision = current.stateRevision + 1;
21653
22302
  const next = {
21654
22303
  ...current.config,
@@ -21756,7 +22405,7 @@ async function shutdownTeamV2(teamName, cwd, options = {}) {
21756
22405
  };
21757
22406
  if (!config) {
21758
22407
  const cleanupSafety = inspectTeamWorktreeCleanupSafety(sanitized, cwd);
21759
- if (cleanupSafety.hasEvidence || (0, import_fs30.existsSync)(absPath(cwd, TeamPaths.root(sanitized)))) {
22408
+ if (cleanupSafety.hasEvidence || (0, import_fs34.existsSync)(absPath(cwd, TeamPaths.root(sanitized)))) {
21760
22409
  process.stderr.write("[team/runtime-v2] preserving team state because config is missing and worktree cleanup evidence remains\n");
21761
22410
  return { outcome: "preserved", reason: "config_missing_cleanup_evidence", workers: [] };
21762
22411
  }
@@ -21788,6 +22437,7 @@ Format: {"status":"accept","reason":"ok","updated_at":"<iso>"}
21788
22437
  Then exit your session.
21789
22438
  `;
21790
22439
  await writeWorkerInbox(sanitized, w.name, shutdownInbox, cwd);
22440
+ if (isSdkTarget(w.pane_id)) ringDoorbell(teamStateRoot(cwd, sanitized), w.name, { kind: "shutdown", from: "leader-fixed" });
21791
22441
  } catch (err) {
21792
22442
  process.stderr.write(`[team/runtime-v2] shutdown request failed for ${w.name}: ${err}
21793
22443
  `);
@@ -21828,6 +22478,30 @@ Then exit your session.
21828
22478
  const providerCleanupFailures = [];
21829
22479
  const paneCleanupAlive = [];
21830
22480
  const paneCleanupUnknown = [];
22481
+ const sdkStopTimeoutMs = Math.max(timeoutMs, SDK_HOST_STOP_FLOOR_MS);
22482
+ const sdkStops = /* @__PURE__ */ new Map();
22483
+ for (const worker of config.workers) {
22484
+ if (!isSdkTarget(worker.pane_id) || !worker.launch_attempt_id || !config.tmux_session) continue;
22485
+ const provider = worker.launch_descriptor?.provider ?? worker.worker_cli;
22486
+ if (!provider) continue;
22487
+ const attemptId = worker.launch_attempt_id;
22488
+ const paneId = worker.pane_id;
22489
+ sdkStops.set(worker.name, (async () => {
22490
+ const sdkAttempt = await loadWorkerLaunchAttempt({
22491
+ cwd,
22492
+ teamName: sanitized,
22493
+ instanceId: shutdownInstance.instance_id,
22494
+ workerName: worker.name,
22495
+ paneId,
22496
+ provider,
22497
+ attemptId,
22498
+ runtimeCliPath: resolveRuntimeCliPath()
22499
+ });
22500
+ if (!sdkAttempt) return false;
22501
+ if (!await stopSdkHost(sdkAttempt, teamStateRoot(cwd, sanitized), { timeoutMs: sdkStopTimeoutMs })) return false;
22502
+ return retireAndCleanupCurrentWorkerLaunchAttempt(sdkAttempt, "team_shutdown", async () => true);
22503
+ })().catch(() => false));
22504
+ }
21831
22505
  for (const worker of config.workers) {
21832
22506
  if (!worker.pane_id) {
21833
22507
  providerCleanupFailures.push(worker.name);
@@ -21842,6 +22516,10 @@ Then exit your session.
21842
22516
  providerCleanupFailures.push(worker.name);
21843
22517
  continue;
21844
22518
  }
22519
+ if (isSdkTarget(worker.pane_id)) {
22520
+ if (!await (sdkStops.get(worker.name) ?? Promise.resolve(false))) providerCleanupFailures.push(worker.name);
22521
+ continue;
22522
+ }
21845
22523
  const initialOwnership = configuredPaneOwnership(config, worker);
21846
22524
  const initialPaneLiveness = initialOwnership ? await getOwnedWorkerLiveness(initialOwnership) : "unknown";
21847
22525
  let paneOwnership = null;
@@ -21910,9 +22588,9 @@ Then exit your session.
21910
22588
  killTeamSession: killOwnedTeamSession
21911
22589
  } = await Promise.resolve().then(() => (init_tmux_session(), tmux_session_exports));
21912
22590
  const ownsWindow = config.tmux_window_owned === true;
21913
- const workerPaneIds = recordedWorkerPaneIds;
22591
+ const workerPaneIds = recordedWorkerPaneIds.filter((paneId) => !isSdkTarget(paneId));
21914
22592
  const splitPaneMode = Boolean(config.tmux_session && !ownsWindow && config.tmux_session.includes(":"));
21915
- if (!splitPaneMode && config.tmux_session) {
22593
+ if (!splitPaneMode && config.tmux_session && !isSdkTarget(config.tmux_session)) {
21916
22594
  if (!config.leader_pane_id) {
21917
22595
  if (!await rollbackShutdownForRetry()) await finalizeAutoMerge();
21918
22596
  return { outcome: "preserved", reason: "provider_cleanup_unverified", workers: ["leader-fixed"] };
@@ -22033,12 +22711,12 @@ Then exit your session.
22033
22711
  return { outcome: "cleaned" };
22034
22712
  });
22035
22713
  }
22036
- var import_path34, import_fs30, import_promises19, import_perf_hooks, import_node_child_process7, import_node_crypto9, orchestratorByTeam, cadenceByTeam, MONITOR_SIGNAL_STALE_MS, WORKER_STARTUP_EVIDENCE_POLL_INTERVAL_MS, WORKER_STARTUP_EVIDENCE_POLICIES, ENGAGED_PANE_RECHECK_TIMEOUT_ENV, MAX_ENGAGED_PANE_RECHECK_BUDGET_MS, CLAIM_ERROR_CAPTURE_MAX, CLAIM_ERROR_JSON_LINES_MAX, CLAIM_ERROR_LINE_MAX, CLAIM_ERROR_CODES, pendingRecoveryPanes, BOOTSTRAP_RECOVERY_EVIDENCE_POLL_MS, BOOTSTRAP_RECOVERY_EVIDENCE_MAX_WAIT_MS, TEAM_INSTANCE_FINAL_DISPOSAL_AUTHORIZATION;
22714
+ var import_path38, import_fs34, import_promises19, import_perf_hooks, import_node_child_process8, import_node_crypto10, orchestratorByTeam, cadenceByTeam, MONITOR_SIGNAL_STALE_MS, WORKER_STARTUP_EVIDENCE_POLL_INTERVAL_MS, WORKER_STARTUP_EVIDENCE_POLICIES, ENGAGED_PANE_RECHECK_TIMEOUT_ENV, MAX_ENGAGED_PANE_RECHECK_BUDGET_MS, CLAIM_ERROR_CAPTURE_MAX, CLAIM_ERROR_JSON_LINES_MAX, CLAIM_ERROR_LINE_MAX, CLAIM_ERROR_CODES, pendingRecoveryPanes, BOOTSTRAP_RECOVERY_EVIDENCE_POLL_MS, BOOTSTRAP_RECOVERY_EVIDENCE_MAX_WAIT_MS, TEAM_INSTANCE_FINAL_DISPOSAL_AUTHORIZATION;
22037
22715
  var init_runtime_v2 = __esm({
22038
22716
  "src/team/runtime-v2.ts"() {
22039
22717
  "use strict";
22040
- import_path34 = require("path");
22041
- import_fs30 = require("fs");
22718
+ import_path38 = require("path");
22719
+ import_fs34 = require("fs");
22042
22720
  import_promises19 = require("fs/promises");
22043
22721
  import_perf_hooks = require("perf_hooks");
22044
22722
  init_state_paths();
@@ -22067,10 +22745,10 @@ var init_runtime_v2 = __esm({
22067
22745
  init_cli_worker_contract();
22068
22746
  init_merge_orchestrator();
22069
22747
  init_leader_inbox();
22070
- import_node_child_process7 = require("node:child_process");
22748
+ import_node_child_process8 = require("node:child_process");
22071
22749
  init_runtime_flags();
22072
22750
  init_worker_commit_cadence();
22073
- import_node_crypto9 = require("node:crypto");
22751
+ import_node_crypto10 = require("node:crypto");
22074
22752
  init_recovery_request_store();
22075
22753
  init_runtime_owner_client();
22076
22754
  init_scaling();
@@ -22082,6 +22760,11 @@ var init_runtime_v2 = __esm({
22082
22760
  init_team_owner_epoch();
22083
22761
  init_worker_activation_gate();
22084
22762
  init_worker_launch_ack();
22763
+ init_sdk_transport();
22764
+ init_model_contract();
22765
+ init_copilot_sdk_driver();
22766
+ init_copilot_session_env();
22767
+ init_config_dir();
22085
22768
  init_process_utils();
22086
22769
  init_team_instance();
22087
22770
  init_runtime_flags();
@@ -22187,10 +22870,10 @@ __export(runtime_cli_exports, {
22187
22870
  writeResultArtifact: () => writeResultArtifact
22188
22871
  });
22189
22872
  module.exports = __toCommonJS(runtime_cli_exports);
22190
- var import_node_crypto10 = require("node:crypto");
22191
- var import_fs31 = require("fs");
22873
+ var import_node_crypto11 = require("node:crypto");
22874
+ var import_fs35 = require("fs");
22192
22875
  var import_promises20 = require("fs/promises");
22193
- var import_path35 = require("path");
22876
+ var import_path39 = require("path");
22194
22877
  init_events();
22195
22878
 
22196
22879
  // src/team/leader-nudge-guidance.ts
@@ -22733,7 +23416,7 @@ async function waitForSentinelReadiness(options = {}) {
22733
23416
  }
22734
23417
  const deadline = startedAt + timeoutMs;
22735
23418
  while (Date.now() < deadline) {
22736
- await new Promise((resolve14) => setTimeout(resolve14, pollIntervalMs));
23419
+ await new Promise((resolve17) => setTimeout(resolve17, pollIntervalMs));
22737
23420
  attempts += 1;
22738
23421
  latest = checkSentinelReadiness(options);
22739
23422
  if (latest.ready) {
@@ -22823,10 +23506,10 @@ function areAllAuthoritativeWorkersDead(refresh, workers) {
22823
23506
  return classifyAllDeadRecoveryEvidence(refresh, workers, true) === "all_dead";
22824
23507
  }
22825
23508
  function validateCanonicalRecoveryIntent(teamName, cwd, pathRecoveryId, path5) {
22826
- const intent = parseRecoveryIntent((0, import_fs31.readFileSync)(path5, "utf8"));
23509
+ const intent = parseRecoveryIntent((0, import_fs35.readFileSync)(path5, "utf8"));
22827
23510
  if (intent.team_name !== teamName || intent.recovery_id !== pathRecoveryId) throw new Error("invalid_persisted_state");
22828
23511
  const reservation = readRecoveryRequestReservation(cwd, intent.request_id);
22829
- const workspaceHash = (0, import_node_crypto10.createHash)("sha256").update(cwd).digest("hex");
23512
+ const workspaceHash = (0, import_node_crypto11.createHash)("sha256").update(cwd).digest("hex");
22830
23513
  const expectedPayloadHash = canonicalRecoveryPayloadHash({
22831
23514
  operation: "recover-worker",
22832
23515
  workspaceHash,
@@ -22858,14 +23541,14 @@ async function processPendingRecoveryIntents(teamName, cwd, execute = handleReco
22858
23541
  const root = absPath(cwd, TeamPaths.recoveryIntents(teamName));
22859
23542
  let names;
22860
23543
  try {
22861
- names = (0, import_fs31.readdirSync)(root).filter((name) => name.endsWith(".json")).sort();
23544
+ names = (0, import_fs35.readdirSync)(root).filter((name) => name.endsWith(".json")).sort();
22862
23545
  } catch {
22863
23546
  return;
22864
23547
  }
22865
23548
  for (const name of names) {
22866
- const path5 = (0, import_path35.join)(root, name);
23549
+ const path5 = (0, import_path39.join)(root, name);
22867
23550
  try {
22868
- const pathRecoveryId = (0, import_path35.basename)(name, ".json");
23551
+ const pathRecoveryId = (0, import_path39.basename)(name, ".json");
22869
23552
  const intent = validateCanonicalRecoveryIntent(teamName, cwd, pathRecoveryId, path5);
22870
23553
  if (intent.instance_id.toLowerCase() !== normalizedExpectedInstanceId) continue;
22871
23554
  if (!await assertRuntimeInstance(teamName, cwd, normalizedExpectedInstanceId)) continue;
@@ -22935,17 +23618,17 @@ async function updateAllDeadRecoveryGrace(teamName, cwd, evidence, nowMs = Date.
22935
23618
  }
22936
23619
  function canonicalRecoveryIntentEntryId(name, path5) {
22937
23620
  if (!name.endsWith(".json")) return null;
22938
- const recoveryId = (0, import_path35.basename)(name, ".json");
23621
+ const recoveryId = (0, import_path39.basename)(name, ".json");
22939
23622
  if (name !== `${recoveryId}.json` || !isSafeRecoveryRequestId(recoveryId)) return null;
22940
23623
  try {
22941
- return (0, import_fs31.lstatSync)(path5).isFile() ? recoveryId : null;
23624
+ return (0, import_fs35.lstatSync)(path5).isFile() ? recoveryId : null;
22942
23625
  } catch {
22943
23626
  return null;
22944
23627
  }
22945
23628
  }
22946
23629
  function hasVerifiedTerminalRepairForMalformedIntent(teamName, cwd, recoveryId, path5) {
22947
23630
  try {
22948
- const raw = JSON.parse((0, import_fs31.readFileSync)(path5, "utf8"));
23631
+ const raw = JSON.parse((0, import_fs35.readFileSync)(path5, "utf8"));
22949
23632
  if (raw.team_name !== teamName || raw.recovery_id !== recoveryId || typeof raw.request_id !== "string" || !isSafeRecoveryRequestId(raw.request_id) || typeof raw.worker_name !== "string" || raw.worker_name.length === 0) return false;
22950
23633
  const final = readRecoveryFinalState(cwd, raw.request_id);
22951
23634
  return final.kind === "valid" && final.final.recovery_id === recoveryId && final.final.team_name === teamName && final.final.worker_name === raw.worker_name;
@@ -22955,7 +23638,7 @@ function hasVerifiedTerminalRepairForMalformedIntent(teamName, cwd, recoveryId,
22955
23638
  }
22956
23639
  function malformedIntentMayPredateDeadline(path5, deadlineAt) {
22957
23640
  try {
22958
- const metadata = (0, import_fs31.lstatSync)(path5);
23641
+ const metadata = (0, import_fs35.lstatSync)(path5);
22959
23642
  if (Number.isFinite(metadata.mtimeMs) && metadata.mtimeMs <= deadlineAt) return true;
22960
23643
  if (Number.isFinite(metadata.birthtimeMs) && metadata.birthtimeMs > 0) {
22961
23644
  return metadata.birthtimeMs <= deadlineAt;
@@ -22970,14 +23653,14 @@ function canonicalRecoveryAdmissionEntryId(name, path5) {
22970
23653
  const requestId = name.slice(0, -".pending.json".length);
22971
23654
  if (name !== `${requestId}.pending.json` || !isSafeRecoveryRequestId(requestId)) return null;
22972
23655
  try {
22973
- return (0, import_fs31.lstatSync)(path5).isFile() ? requestId : null;
23656
+ return (0, import_fs35.lstatSync)(path5).isFile() ? requestId : null;
22974
23657
  } catch {
22975
23658
  return null;
22976
23659
  }
22977
23660
  }
22978
23661
  function malformedAdmissionMayPredateDeadline(path5, deadlineAt) {
22979
23662
  try {
22980
- const metadata = (0, import_fs31.lstatSync)(path5);
23663
+ const metadata = (0, import_fs35.lstatSync)(path5);
22981
23664
  if (!metadata.isFile()) return true;
22982
23665
  if (Number.isFinite(metadata.mtimeMs) && metadata.mtimeMs <= deadlineAt) return true;
22983
23666
  if (Number.isFinite(metadata.birthtimeMs) && metadata.birthtimeMs > 0) {
@@ -22994,12 +23677,12 @@ function hasPendingRecoveryIntentBeforeDeadline(teamName, cwd, deadlineAt, expec
22994
23677
  const root = absPath(cwd, TeamPaths.recoveryIntents(teamName));
22995
23678
  let names;
22996
23679
  try {
22997
- names = (0, import_fs31.readdirSync)(root).filter((name) => name.endsWith(".json"));
23680
+ names = (0, import_fs35.readdirSync)(root).filter((name) => name.endsWith(".json"));
22998
23681
  } catch {
22999
23682
  return false;
23000
23683
  }
23001
23684
  for (const name of names) {
23002
- const path5 = (0, import_path35.join)(root, name);
23685
+ const path5 = (0, import_path39.join)(root, name);
23003
23686
  const recoveryId = canonicalRecoveryIntentEntryId(name, path5);
23004
23687
  if (!recoveryId) continue;
23005
23688
  try {
@@ -23017,16 +23700,16 @@ function hasPendingRecoveryIntentBeforeDeadline(teamName, cwd, deadlineAt, expec
23017
23700
  function hasPendingRecoveryAdmissionBeforeDeadline(teamName, cwd, deadlineAt, expectedInstanceId) {
23018
23701
  const normalizedExpectedInstanceId = normalizeRuntimeInstanceId(expectedInstanceId);
23019
23702
  if (!normalizedExpectedInstanceId) return true;
23020
- const workspaceHash = (0, import_node_crypto10.createHash)("sha256").update(cwd).digest("hex");
23703
+ const workspaceHash = (0, import_node_crypto11.createHash)("sha256").update(cwd).digest("hex");
23021
23704
  const root = absPath(cwd, TeamPaths.recoveryRequestsRoot());
23022
23705
  let names;
23023
23706
  try {
23024
- names = (0, import_fs31.readdirSync)(root).filter((name) => name.endsWith(".pending.json"));
23707
+ names = (0, import_fs35.readdirSync)(root).filter((name) => name.endsWith(".pending.json"));
23025
23708
  } catch {
23026
23709
  return false;
23027
23710
  }
23028
23711
  for (const name of names) {
23029
- const path5 = (0, import_path35.join)(root, name);
23712
+ const path5 = (0, import_path39.join)(root, name);
23030
23713
  const requestId = canonicalRecoveryAdmissionEntryId(name, path5);
23031
23714
  if (!requestId) continue;
23032
23715
  try {
@@ -23104,7 +23787,7 @@ async function runPersistentRecoveryOwnerLoop(input, options = {}) {
23104
23787
  const reconcileServices = options.reconcileServices ?? reconcileCommittedTeamServices;
23105
23788
  const monitor = options.monitor ?? monitorTeamV2;
23106
23789
  const sleep4 = options.sleep ?? (async (ms) => {
23107
- await new Promise((resolve14) => setTimeout(resolve14, ms));
23790
+ await new Promise((resolve17) => setTimeout(resolve17, ms));
23108
23791
  });
23109
23792
  const shutdown = options.shutdown ?? shutdownTeamV2;
23110
23793
  let iteration = 0;
@@ -23245,7 +23928,7 @@ async function writeResultArtifact(output, finishedAt, jobId = process.env.OMC_J
23245
23928
  if (!output.instanceId || !isValidTeamInstanceId(output.instanceId)) {
23246
23929
  throw new Error("result_artifact_instance_identity_missing");
23247
23930
  }
23248
- const resultPath = (0, import_path35.join)(omcJobsDir, `${jobId}-result.json`);
23931
+ const resultPath = (0, import_path39.join)(omcJobsDir, `${jobId}-result.json`);
23249
23932
  const tmpPath = `${resultPath}.tmp`;
23250
23933
  await (0, import_promises20.writeFile)(
23251
23934
  tmpPath,
@@ -23320,7 +24003,7 @@ async function writePanesFile(jobId, instanceId, teamName, cwd, paneIds, leaderP
23320
24003
  if (authoritativePaneIds.some((paneId) => !paneIds.includes(paneId))) {
23321
24004
  throw new Error("pane_artifact_missing_runtime_pane");
23322
24005
  }
23323
- const panesPath = (0, import_path35.join)(omcJobsDir, `${jobId}-panes.json`);
24006
+ const panesPath = (0, import_path39.join)(omcJobsDir, `${jobId}-panes.json`);
23324
24007
  await (0, import_promises20.writeFile)(
23325
24008
  panesPath + ".tmp",
23326
24009
  JSON.stringify({
@@ -23358,7 +24041,7 @@ function isTerseFinalSummary(summary) {
23358
24041
  function readTaskOutputFallback(outputsDir, teamName, taskId) {
23359
24042
  let entries;
23360
24043
  try {
23361
- entries = (0, import_fs31.readdirSync)(outputsDir);
24044
+ entries = (0, import_fs35.readdirSync)(outputsDir);
23362
24045
  } catch {
23363
24046
  return null;
23364
24047
  }
@@ -23367,16 +24050,16 @@ function readTaskOutputFallback(outputsDir, teamName, taskId) {
23367
24050
  if (candidates.length === 0) return null;
23368
24051
  let newest = null;
23369
24052
  for (const name of candidates) {
23370
- const full = (0, import_path35.join)(outputsDir, name);
24053
+ const full = (0, import_path39.join)(outputsDir, name);
23371
24054
  try {
23372
- const mtime = (0, import_fs31.statSync)(full).mtimeMs;
24055
+ const mtime = (0, import_fs35.statSync)(full).mtimeMs;
23373
24056
  if (!newest || mtime > newest.mtime) newest = { path: full, mtime };
23374
24057
  } catch {
23375
24058
  }
23376
24059
  }
23377
24060
  if (!newest) return null;
23378
24061
  try {
23379
- const content = (0, import_fs31.readFileSync)(newest.path, "utf-8").trim();
24062
+ const content = (0, import_fs35.readFileSync)(newest.path, "utf-8").trim();
23380
24063
  if (content.length === 0) return null;
23381
24064
  return content.length > MAX_FALLBACK_SUMMARY_CHARS ? content.slice(0, MAX_FALLBACK_SUMMARY_CHARS) + "\n... (truncated)" : content;
23382
24065
  } catch {
@@ -23384,14 +24067,14 @@ function readTaskOutputFallback(outputsDir, teamName, taskId) {
23384
24067
  }
23385
24068
  }
23386
24069
  function collectTaskResults(stateRoot) {
23387
- const tasksDir = (0, import_path35.join)(stateRoot, "tasks");
23388
- const teamName = (0, import_path35.basename)(stateRoot);
23389
- const outputsDir = (0, import_path35.join)(stateRoot, "..", "..", "..", "outputs");
24070
+ const tasksDir = (0, import_path39.join)(stateRoot, "tasks");
24071
+ const teamName = (0, import_path39.basename)(stateRoot);
24072
+ const outputsDir = (0, import_path39.join)(stateRoot, "..", "..", "..", "outputs");
23390
24073
  try {
23391
- const files = (0, import_fs31.readdirSync)(tasksDir).filter((f) => f.endsWith(".json"));
24074
+ const files = (0, import_fs35.readdirSync)(tasksDir).filter((f) => f.endsWith(".json"));
23392
24075
  return files.map((f) => {
23393
24076
  try {
23394
- const raw = (0, import_fs31.readFileSync)((0, import_path35.join)(tasksDir, f), "utf-8");
24077
+ const raw = (0, import_fs35.readFileSync)((0, import_path39.join)(tasksDir, f), "utf-8");
23395
24078
  const task = JSON.parse(raw);
23396
24079
  const taskId = task.id ?? f.replace(".json", "");
23397
24080
  let summary = task.result ?? task.summary ?? "";
@@ -23426,8 +24109,8 @@ function createRuntimeStartupShutdownBarrier() {
23426
24109
  let settled = false;
23427
24110
  let requested = false;
23428
24111
  let resolveCompletion;
23429
- const completion = new Promise((resolve14) => {
23430
- resolveCompletion = resolve14;
24112
+ const completion = new Promise((resolve17) => {
24113
+ resolveCompletion = resolve17;
23431
24114
  });
23432
24115
  return {
23433
24116
  requestShutdown: () => {
@@ -23735,7 +24418,7 @@ async function main() {
23735
24418
  if (snap.allTasksTerminal) {
23736
24419
  const hasFailures = snap.tasks.failed > 0;
23737
24420
  if (!hasFailures) {
23738
- const sentinelLogPath = (0, import_path35.join)(cwd, "sentinel_stop.jsonl");
24421
+ const sentinelLogPath = (0, import_path39.join)(cwd, "sentinel_stop.jsonl");
23739
24422
  const gateResult = await waitForSentinelReadiness({
23740
24423
  workspace: cwd,
23741
24424
  logPath: sentinelLogPath,