@chibitek/chibi 0.1.169 → 0.1.171

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 (99) hide show
  1. package/dist/assets/{abnfDiagram-O67JEVCF-aQkRN0ee.js → abnfDiagram-O67JEVCF-DrPBKmVh.js} +1 -1
  2. package/dist/assets/{arc-BT7qPVAa.js → arc-BEIChVSB.js} +1 -1
  3. package/dist/assets/{architectureDiagram-NJMV4G6O-oMlARqKW.js → architectureDiagram-NJMV4G6O-Da60nTzs.js} +1 -1
  4. package/dist/assets/{blockDiagram-BEXU5L5S-CKUrb4js.js → blockDiagram-BEXU5L5S-_NelwIQj.js} +1 -1
  5. package/dist/assets/{c4Diagram-YGBWAQC7-BkL5cRIA.js → c4Diagram-YGBWAQC7-DUdkQ3dy.js} +1 -1
  6. package/dist/assets/channel-DEJSSlVQ.js +1 -0
  7. package/dist/assets/{chunk-2Q5K7J3B-BMitkm2s.js → chunk-2Q5K7J3B-BQgYAptM.js} +1 -1
  8. package/dist/assets/{chunk-3FUC2YCW-DOhVN2ed.js → chunk-3FUC2YCW-XXOXwcoW.js} +2 -2
  9. package/dist/assets/{chunk-5VM5RSS4-BFkFo_ng.js → chunk-5VM5RSS4-CUyMXCSt.js} +1 -1
  10. package/dist/assets/{chunk-GTNCS2PH-QbxYUf3Q.js → chunk-GTNCS2PH-BwctbINm.js} +1 -1
  11. package/dist/assets/{chunk-GWA4HPMP-BZChfTYw.js → chunk-GWA4HPMP-DQ0bZK59.js} +1 -1
  12. package/dist/assets/{chunk-JWPE2WC7-BXpmYglA.js → chunk-JWPE2WC7-BADCk9AK.js} +1 -1
  13. package/dist/assets/{chunk-NETBCI7D-CIZTrz1i.js → chunk-NETBCI7D-CoJxabSR.js} +1 -1
  14. package/dist/assets/{chunk-WEXAMYUT-FhqQ0sO0.js → chunk-WEXAMYUT-fupByVJn.js} +1 -1
  15. package/dist/assets/{chunk-XXDRQBXY-5ThtmFbP.js → chunk-XXDRQBXY-BavbGHGi.js} +1 -1
  16. package/dist/assets/{classDiagram-v2-NBCMYWYE-BzObC5Qw.js → classDiagram-v2-NBCMYWYE-B5wfUNKJ.js} +1 -1
  17. package/dist/assets/{cose-bilkent-JH36ORCC-BASV9Okv.js → cose-bilkent-JH36ORCC-DlYFmKDe.js} +1 -1
  18. package/dist/assets/{cynefin-OW5HDTMX-B0zQYMFh.js → cynefin-OW5HDTMX-Y0U-21MH.js} +1 -1
  19. package/dist/assets/{cynefinDiagram-VND7K2PF-DMEO1cHA.js → cynefinDiagram-VND7K2PF-D1Fh1sU0.js} +1 -1
  20. package/dist/assets/{dagre-6A5THRUB-BG04TSi_.js → dagre-6A5THRUB-BNU-Trog.js} +1 -1
  21. package/dist/assets/{diagram-22UHCM2B-CfpVNMhg.js → diagram-22UHCM2B-Bjie4nDQ.js} +1 -1
  22. package/dist/assets/{diagram-3UASUU5V-DG9L4I1L.js → diagram-3UASUU5V-Qnbrq1T5.js} +1 -1
  23. package/dist/assets/{diagram-ATOU4E4O-Db7Bf63S.js → diagram-ATOU4E4O-CNJasPCN.js} +1 -1
  24. package/dist/assets/{diagram-CDSNMT55-Cdzv1tJn.js → diagram-CDSNMT55-BAzGZBKX.js} +1 -1
  25. package/dist/assets/{diagram-MLGK6HIB-BBMeXGKE.js → diagram-MLGK6HIB-I8ACRaw-.js} +1 -1
  26. package/dist/assets/{diagram-MPIPVDR6-BAQ22zk3.js → diagram-MPIPVDR6-nLlrAEPz.js} +1 -1
  27. package/dist/assets/{ebnfDiagram-ZINNZB2B-BDZgSFSF.js → ebnfDiagram-ZINNZB2B-DF9BG3pH.js} +1 -1
  28. package/dist/assets/{elk-276RUBZZ-Bx4-nN8N.js → elk-276RUBZZ-C1AJYKMV.js} +1 -1
  29. package/dist/assets/{erDiagram-OPXOYQCR-D2Pn-cqm.js → erDiagram-OPXOYQCR-MNNcRrPy.js} +1 -1
  30. package/dist/assets/{flowDiagram-KWPJA3E3-hsbvL3Ow.js → flowDiagram-KWPJA3E3-C7mfBfgt.js} +1 -1
  31. package/dist/assets/{ganttDiagram-FUAMR5RP-DBUgYe4p.js → ganttDiagram-FUAMR5RP-a9zNyDbb.js} +1 -1
  32. package/dist/assets/{gitGraphDiagram-X574FWY7-ChGeSINg.js → gitGraphDiagram-X574FWY7-OL8NhUsO.js} +1 -1
  33. package/dist/assets/{index-ByXr4FRl.js → index-DojGBgbN.js} +167 -164
  34. package/dist/assets/{index-Ch93Ygtq.js → index-xypOVpVT.js} +1 -1
  35. package/dist/assets/{infoDiagram-VRGFBTTK-DY2EeC-A.js → infoDiagram-VRGFBTTK-CF-3pzUZ.js} +1 -1
  36. package/dist/assets/{ishikawaDiagram-OU5B5YK6-BznYuY04.js → ishikawaDiagram-OU5B5YK6-B2HqD-sv.js} +1 -1
  37. package/dist/assets/{journeyDiagram-ZHPQQLJL-CX74ZLbW.js → journeyDiagram-ZHPQQLJL-84gzOTb9.js} +1 -1
  38. package/dist/assets/{kanban-definition-PNTS6WVX-0DPavB-t.js → kanban-definition-PNTS6WVX-D7Ee23QC.js} +1 -1
  39. package/dist/assets/{linear-BHeqbvM8.js → linear-CdAXsmfc.js} +1 -1
  40. package/dist/assets/{mermaid.core-Dm-Ny6a0.js → mermaid.core-CE_M1v1e.js} +5 -5
  41. package/dist/assets/{mindmap-definition-NLK3R4M7-BPrMElav.js → mindmap-definition-NLK3R4M7-KSTIET9y.js} +1 -1
  42. package/dist/assets/{pegDiagram-GJSIUBJH-CaD7wyyX.js → pegDiagram-GJSIUBJH-C7ogvddb.js} +1 -1
  43. package/dist/assets/{pieDiagram-5QR66LMP-lbSp8R4K.js → pieDiagram-5QR66LMP-Bp1CgCrM.js} +1 -1
  44. package/dist/assets/{quadrantDiagram-O4NWA36T-Bs3Sn2of.js → quadrantDiagram-O4NWA36T-Bw7aN9Lh.js} +1 -1
  45. package/dist/assets/{railroadDiagram-XR7U4H2S-C88ykXCV.js → railroadDiagram-XR7U4H2S-Dij4U4NY.js} +1 -1
  46. package/dist/assets/{requirementDiagram-PLB6GJNP-mgIQnORW.js → requirementDiagram-PLB6GJNP-DOxTQvCl.js} +1 -1
  47. package/dist/assets/{sankeyDiagram-IPEJSGJF-K5esEw-m.js → sankeyDiagram-IPEJSGJF-DPCUmNbV.js} +1 -1
  48. package/dist/assets/{sequenceDiagram-PO4LG4MO-D5v8PJqQ.js → sequenceDiagram-PO4LG4MO-DPjC8zGe.js} +1 -1
  49. package/dist/assets/{sizeCapture-INFHLROL-BHBBDTEI.js → sizeCapture-INFHLROL-WlmsNlh-.js} +1 -1
  50. package/dist/assets/{stateDiagram-v2-GCMORJYK-CeRdNmKQ.js → stateDiagram-v2-GCMORJYK-BwMPiapI.js} +1 -1
  51. package/dist/assets/{swimlanes-2SLR337P-D8CbMCI8.js → swimlanes-2SLR337P-Cyja7ZPb.js} +1 -1
  52. package/dist/assets/swimlanesDiagram-TC7HE7FX-Dh25zxzq.js +8 -0
  53. package/dist/assets/{timeline-definition-EJHVYXUP-CMG02UV1.js → timeline-definition-EJHVYXUP-BehkSauz.js} +1 -1
  54. package/dist/assets/{usecaseDiagram-POWQR4AR-Bm8h6ijK.js → usecaseDiagram-POWQR4AR-koUjcmpW.js} +1 -1
  55. package/dist/assets/{vennDiagram-UO4OBE2U-Dk9tFjlM.js → vennDiagram-UO4OBE2U-Ch8TXIpy.js} +1 -1
  56. package/dist/assets/{wardleyDiagram-VNRHLVJA-61vrb2Ft.js → wardleyDiagram-VNRHLVJA-Dm5kC4c2.js} +1 -1
  57. package/dist/assets/{xychartDiagram-PMCCYNJV-C7uO44ng.js → xychartDiagram-PMCCYNJV-YA-1zyQz.js} +1 -1
  58. package/dist/handbook/atomic-handbook.html +1 -1
  59. package/dist/index.html +1 -1
  60. package/dist-server/companion/src/routes.js +28 -1
  61. package/dist-server/container-mcp.js +1 -0
  62. package/dist-server/hooks/plan-hook.js +22 -0
  63. package/dist-server/index.js +38538 -37905
  64. package/dist-server/local-computer.js +1 -0
  65. package/dist-server/openmausbot.js +389 -303
  66. package/dist-server/pair-cli.js +389 -303
  67. package/dist-server/proxy-paths.js +1 -0
  68. package/dist-server/server/atomic-engine-release.js +13 -13
  69. package/dist-server/server/bot-work-modes.js +212 -0
  70. package/dist-server/server/config.js +6 -5
  71. package/dist-server/server/drivers/acp/core.js +6 -4
  72. package/dist-server/server/drivers/acp/custom.js +51 -37
  73. package/dist-server/server/drivers/builtIn.js +2 -1
  74. package/dist-server/server/drivers/chat-mcp-tools.js +1 -0
  75. package/dist-server/server/drivers/claude.js +28 -25
  76. package/dist-server/server/drivers/codex-approval-presets.js +34 -0
  77. package/dist-server/server/drivers/codex.js +11 -35
  78. package/dist-server/server/drivers/openai-chat.js +4 -3
  79. package/dist-server/server/hooks/plan-hook.js +17 -0
  80. package/dist-server/server/index.js +92 -175
  81. package/dist-server/server/mcp-interactive-probe.js +60 -0
  82. package/dist-server/server/mcp-remote-bridge.js +13 -6
  83. package/dist-server/server/message-file.js +28 -11
  84. package/dist-server/server/plan-policy.js +14 -0
  85. package/dist-server/server/proxy-paths.js +1 -0
  86. package/dist-server/server/remote-desktop.js +142 -0
  87. package/dist-server/server/routes/message-file.js +115 -0
  88. package/dist-server/server/shared-deliverables.js +109 -0
  89. package/dist-server/server/skill-learn.js +4 -0
  90. package/dist-server/server/store.js +4 -4
  91. package/dist-server/server/subscription-bots.js +3 -2
  92. package/dist-server/shared/approval-mode.js +3 -1
  93. package/dist-server/shared/engine-choices.js +1 -1
  94. package/dist-server/shared/provider-accounts.js +5 -5
  95. package/dist-server/shared/skill-command.js +16 -0
  96. package/dist-server/vps-container-mcp.js +1 -0
  97. package/package.json +1 -1
  98. package/dist/assets/channel-BZDQCBTd.js +0 -1
  99. package/dist/assets/swimlanesDiagram-TC7HE7FX-CQHcTGnI.js +0 -8
@@ -16312,6 +16312,7 @@ var init_proxy_paths = __esm({
16312
16312
  hook: resolveProxy("hooks/omb-hook"),
16313
16313
  // the RTK add-on's Claude PreToolUse hook (bypassPermissions turns only)
16314
16314
  rtkHook: resolveProxy("hooks/rtk-hook"),
16315
+ planHook: resolveProxy("hooks/plan-hook"),
16315
16316
  // Loaded by the external `pi` process via `-e`, not by this server — but
16316
16317
  // resolved through the same single source of truth so the packaged layout
16317
16318
  // check can assert it ships.
@@ -16436,8 +16437,8 @@ function pathExts(host) {
16436
16437
  }
16437
16438
  function whichWindows(name, host) {
16438
16439
  const exts = pathExts(host);
16439
- const probe = (base3) => {
16440
- const order = extname(base3) ? [base3, ...exts.map((e) => base3 + e)] : [...exts.map((e) => base3 + e), base3];
16440
+ const probe = (base2) => {
16441
+ const order = extname(base2) ? [base2, ...exts.map((e) => base2 + e)] : [...exts.map((e) => base2 + e), base2];
16441
16442
  return order.find((p3) => host.isFile(p3)) ?? null;
16442
16443
  };
16443
16444
  if (isPathLike(name)) return probe(name);
@@ -16584,8 +16585,8 @@ import { homedir as homedir4 } from "node:os";
16584
16585
  import { delimiter as delimiter3, join as join9, posix as posix4, win32 as win322 } from "node:path";
16585
16586
  function nvmBinDirs() {
16586
16587
  try {
16587
- const base3 = join9(homedir4(), ".nvm", "versions", "node");
16588
- return readdirSync2(base3).filter((v3) => v3.startsWith("v")).sort((a2, b3) => b3.localeCompare(a2, void 0, { numeric: true })).map((v3) => join9(base3, v3, "bin"));
16588
+ const base2 = join9(homedir4(), ".nvm", "versions", "node");
16589
+ return readdirSync2(base2).filter((v3) => v3.startsWith("v")).sort((a2, b3) => b3.localeCompare(a2, void 0, { numeric: true })).map((v3) => join9(base2, v3, "bin"));
16589
16590
  } catch {
16590
16591
  return [];
16591
16592
  }
@@ -19555,7 +19556,7 @@ var init_provider_accounts = __esm({
19555
19556
  // Kimi Code reads its whole data root (login included) from KIMI_CODE_HOME.
19556
19557
  { id: "kimi", kind: "signIn", label: "Kimi Code", billing: "subscription", driver: "kimiAgent", homeEnv: "KIMI_CODE_HOME", loginArgs: ["login"], signupUrl: "https://www.kimi.com/code" },
19557
19558
  // Droid resolves <FACTORY_HOME_OVERRIDE>/.factory, so the folder is a home, not the data root.
19558
- { id: "droid", kind: "signIn", label: "Factory Droid", billing: "subscription", driver: "droidAgent", homeEnv: "FACTORY_HOME_OVERRIDE", loginArgs: [], signupUrl: "https://factory.ai/pricing" },
19559
+ { id: "droid", kind: "signIn", label: "Factory Droid", billing: "subscription", driver: "droidAgent", homeEnv: "FACTORY_HOME_OVERRIDE", loginArgs: [], signupUrl: "https://factory.com/pricing" },
19559
19560
  {
19560
19561
  id: "anthropic",
19561
19562
  kind: "apiKey",
@@ -19566,7 +19567,7 @@ var init_provider_accounts = __esm({
19566
19567
  icon: "anthropic",
19567
19568
  config: {},
19568
19569
  keyTest: { provider: "anthropic" },
19569
- keyUrl: "https://console.anthropic.com/settings/keys"
19570
+ keyUrl: "https://platform.claude.com/settings/keys"
19570
19571
  },
19571
19572
  {
19572
19573
  id: "openai",
@@ -19604,7 +19605,7 @@ var init_provider_accounts = __esm({
19604
19605
  envVar: "PERPLEXITY_API_KEY",
19605
19606
  icon: "perplexity",
19606
19607
  config: { url: "https://api.perplexity.ai", apiKeyEnv: "PERPLEXITY_API_KEY", model: "sonar-pro", provider: "" },
19607
- keyUrl: "https://www.perplexity.ai/account/api/keys"
19608
+ keyUrl: "https://console.perplexity.ai/project/keys"
19608
19609
  },
19609
19610
  {
19610
19611
  // Alibaba's flat-price Qwen Coding Plan: one plan key serves Qwen, GLM, Kimi and MiniMax models.
@@ -19617,7 +19618,7 @@ var init_provider_accounts = __esm({
19617
19618
  icon: "qwen",
19618
19619
  config: { url: "https://coding-intl.dashscope.aliyuncs.com/v1", apiKeyEnv: "QWEN_CODING_PLAN_API_KEY", model: "qwen3.7-plus", provider: "" },
19619
19620
  keyTest: { provider: "openaiCompat", url: "https://coding-intl.dashscope.aliyuncs.com/v1" },
19620
- keyUrl: "https://modelstudio.console.alibabacloud.com/",
19621
+ keyUrl: "https://modelstudio.console.alibabacloud.com/ap-southeast-1/subscription/coding-plan",
19621
19622
  signupUrl: "https://www.alibabacloud.com/help/en/model-studio/coding-plan"
19622
19623
  },
19623
19624
  {
@@ -19682,7 +19683,7 @@ var init_provider_accounts = __esm({
19682
19683
  driver: "opencodeGo",
19683
19684
  envVar: "OPENCODE_API_KEY",
19684
19685
  config: {},
19685
- keyUrl: "https://opencode.ai/docs/providers/",
19686
+ keyUrl: "https://opencode.ai/go",
19686
19687
  signupUrl: "https://opencode.ai/go"
19687
19688
  }
19688
19689
  ];
@@ -20130,10 +20131,10 @@ function normalizeVpsConfig(raw) {
20130
20131
  }
20131
20132
  return { sshAlias: alias };
20132
20133
  }
20133
- function suffixedBrowserProfileId(base3, unavailable) {
20134
+ function suffixedBrowserProfileId(base2, unavailable) {
20134
20135
  for (let suffix = 2; ; suffix += 1) {
20135
20136
  const ending = `-${suffix}`;
20136
- const candidate = `${base3.slice(0, 40 - ending.length)}${ending}`;
20137
+ const candidate = `${base2.slice(0, 40 - ending.length)}${ending}`;
20137
20138
  if (candidate !== "guest" && !unavailable.has(candidate)) return candidate;
20138
20139
  }
20139
20140
  }
@@ -20143,25 +20144,25 @@ function migrateStoredBrowserProfiles(profiles) {
20143
20144
  const canonicalIds = Array(profiles.length).fill(void 0);
20144
20145
  const used = /* @__PURE__ */ new Set();
20145
20146
  const baseOwner = /* @__PURE__ */ new Map();
20146
- rawBases.forEach((base3, index) => {
20147
- if (base3 === "guest") return;
20148
- const current = baseOwner.get(base3);
20149
- if (current === void 0 || profiles[index].id === base3 && profiles[current].id !== base3) {
20150
- baseOwner.set(base3, index);
20147
+ rawBases.forEach((base2, index) => {
20148
+ if (base2 === "guest") return;
20149
+ const current = baseOwner.get(base2);
20150
+ if (current === void 0 || profiles[index].id === base2 && profiles[current].id !== base2) {
20151
+ baseOwner.set(base2, index);
20151
20152
  }
20152
20153
  });
20153
- for (const [base3, index] of baseOwner) {
20154
- canonicalIds[index] = base3;
20155
- used.add(base3);
20154
+ for (const [base2, index] of baseOwner) {
20155
+ canonicalIds[index] = base2;
20156
+ used.add(base2);
20156
20157
  }
20157
20158
  const reserved = /* @__PURE__ */ new Set([
20158
20159
  "guest",
20159
20160
  ...rawBases,
20160
20161
  ...requestedPartitions.map((partitionId) => partitionId.toLowerCase())
20161
20162
  ]);
20162
- rawBases.forEach((base3, index) => {
20163
+ rawBases.forEach((base2, index) => {
20163
20164
  if (canonicalIds[index] !== void 0) return;
20164
- const id = suffixedBrowserProfileId(base3, /* @__PURE__ */ new Set([...reserved, ...used]));
20165
+ const id = suffixedBrowserProfileId(base2, /* @__PURE__ */ new Set([...reserved, ...used]));
20165
20166
  canonicalIds[index] = id;
20166
20167
  used.add(id);
20167
20168
  });
@@ -20497,6 +20498,8 @@ function instanceConfigs(cfg, onDevice = onDeviceFleet(), accountKeys = instance
20497
20498
  }
20498
20499
  }
20499
20500
  for (const [id, fallback] of Object.entries(ATOMIC_FLEET)) {
20501
+ const savedCli = map3[id]?.config?.cli;
20502
+ if (id === "atomic-engine" && map3[id]?.driver === "customAcp" && (savedCli === void 0 || savedCli === "atomic acp")) map3[id] = { ...map3[id], driver: "atomicEngine" };
20500
20503
  if (Object.hasOwn(map3, id)) map3[id] = withFleetDefaults(map3[id], fallback);
20501
20504
  }
20502
20505
  dropEnginesOffPlatform(map3, platform);
@@ -20978,7 +20981,7 @@ var init_config = __esm({
20978
20981
  ];
20979
20982
  configSaveListeners = /* @__PURE__ */ new Set();
20980
20983
  ATOMIC_FLEET = {
20981
- "atomic-engine": { driver: "customAcp", displayName: "Atomic Engine", config: { cli: "atomic acp" } },
20984
+ "atomic-engine": { driver: "atomicEngine", displayName: "Atomic Engine", config: { cli: "atomic acp" } },
20982
20985
  ollamaCloud: { driver: "ollamaCloud", displayName: "Ollama Cloud" }
20983
20986
  };
20984
20987
  }
@@ -25050,11 +25053,11 @@ function loadedIdsFromPayloads(_host, catalog2, extra) {
25050
25053
  const loaded = /* @__PURE__ */ new Set();
25051
25054
  const catalogIds = new Set(idsFromModelsPayload(catalog2));
25052
25055
  const add = (id) => {
25053
- const base3 = id.split(":")[0];
25054
- if (catalogIds.size && !catalogIds.has(id) && !catalogIds.has(base3)) return;
25056
+ const base2 = id.split(":")[0];
25057
+ if (catalogIds.size && !catalogIds.has(id) && !catalogIds.has(base2)) return;
25055
25058
  if (!MODEL_ID.test(id)) return;
25056
25059
  loaded.add(id);
25057
- if (catalogIds.has(base3)) loaded.add(base3);
25060
+ if (catalogIds.has(base2)) loaded.add(base2);
25058
25061
  };
25059
25062
  if (extra && typeof extra === "object") {
25060
25063
  const rec = extra;
@@ -25238,6 +25241,7 @@ var init_prompt_shape = __esm({
25238
25241
 
25239
25242
  // shared/approval-mode.ts
25240
25243
  function supportsApprovalMode(driverKind, mode) {
25244
+ if (mode === "plan") return ["atomicEngine", "codex", "claudeAgent", "grok", "openai-compat", "minimax", "mistral", "ollamaCloud"].includes(driverKind ?? "");
25241
25245
  if (mode === "custom") return driverKind === "codex";
25242
25246
  if (mode === "edits") return ["claudeAgent", "grokAgent", "antigravityAgent", "qwenAgent", "geminiAgent", "vibeAgent"].includes(driverKind ?? "");
25243
25247
  if (mode !== "full") return true;
@@ -25967,9 +25971,9 @@ var init_engine_failure = __esm({
25967
25971
 
25968
25972
  // server/drivers/retry.ts
25969
25973
  function computeCredentialRefreshBackoff(attempt, random = Math.random) {
25970
- const base3 = CREDENTIAL_REFRESH_BACKOFF_MS[Math.min(Math.max(attempt, 0), CREDENTIAL_REFRESH_BACKOFF_MS.length - 1)];
25971
- const jitter = base3 * 0.5;
25972
- return Math.round(base3 - jitter + random() * jitter * 2);
25974
+ const base2 = CREDENTIAL_REFRESH_BACKOFF_MS[Math.min(Math.max(attempt, 0), CREDENTIAL_REFRESH_BACKOFF_MS.length - 1)];
25975
+ const jitter = base2 * 0.5;
25976
+ return Math.round(base2 - jitter + random() * jitter * 2);
25973
25977
  }
25974
25978
  function isModelRejection(error51, requested) {
25975
25979
  const message2 = error51 instanceof Error ? error51.message : String(error51 ?? "");
@@ -26008,9 +26012,9 @@ function classifyError(err) {
26008
26012
  return { transient: false, reason: "unknown" };
26009
26013
  }
26010
26014
  function computeBackoff(attempt, random = Math.random) {
26011
- const base3 = BACKOFF_BASE_MS[Math.min(Math.max(attempt, 0), BACKOFF_BASE_MS.length - 1)];
26012
- const jitter = base3 * 0.25;
26013
- return Math.round(base3 - jitter + random() * jitter * 2);
26015
+ const base2 = BACKOFF_BASE_MS[Math.min(Math.max(attempt, 0), BACKOFF_BASE_MS.length - 1)];
26016
+ const jitter = base2 * 0.25;
26017
+ return Math.round(base2 - jitter + random() * jitter * 2);
26014
26018
  }
26015
26019
  function interruptibleDelay(ms2, signal) {
26016
26020
  let timer = null;
@@ -26455,7 +26459,7 @@ function createAcpDriver(support12) {
26455
26459
  };
26456
26460
  return support12.sanitizeToolPayload ? sanitizeAcpToolMessage(redacted) : redacted;
26457
26461
  };
26458
- const base3 = (threadId, turnId) => ({
26462
+ const base2 = (threadId, turnId) => ({
26459
26463
  eventId: newEventId(),
26460
26464
  provider: DRIVER_KIND8,
26461
26465
  threadId,
@@ -26478,7 +26482,7 @@ function createAcpDriver(support12) {
26478
26482
  if (isAntigravityCheckpointReply(DRIVER_KIND8, current.state.text)) {
26479
26483
  ok = false;
26480
26484
  stopReason = "invalid_checkpoint";
26481
- emit({ ...base3(threadId, current.turnId), type: "runtime.error", message: current.state.text.trim() });
26485
+ emit({ ...base2(threadId, current.turnId), type: "runtime.error", message: current.state.text.trim() });
26482
26486
  closeSession(threadId, "invalid-checkpoint");
26483
26487
  }
26484
26488
  current.flushAssistantText();
@@ -26488,12 +26492,12 @@ function createAcpDriver(support12) {
26488
26492
  finalOk = false;
26489
26493
  finalStopReason = "empty_turn";
26490
26494
  emit({
26491
- ...base3(threadId, current.turnId),
26495
+ ...base2(threadId, current.turnId),
26492
26496
  type: "runtime.error",
26493
26497
  message: `${DRIVER_KIND8} ended the turn with no reply, image, or tool result`
26494
26498
  });
26495
26499
  }
26496
- emit({ ...base3(threadId, current.turnId), type: "turn.completed", ok: finalOk, stopReason: finalStopReason, cost: null });
26500
+ emit({ ...base2(threadId, current.turnId), type: "turn.completed", ok: finalOk, stopReason: finalStopReason, cost: null });
26497
26501
  if (session.child.exitCode === null && !session.closing && !session.dead) {
26498
26502
  armIdle(threadId);
26499
26503
  } else if (session.dead && sessions.get(threadId) === session) {
@@ -26660,6 +26664,7 @@ function createAcpDriver(support12) {
26660
26664
  if (msg.method !== "session/request_permission") {
26661
26665
  return send({ jsonrpc: "2.0", id: msg.id, error: { code: -32601, message: "method not found" } });
26662
26666
  }
26667
+ if (current.turn.approvalMode === "plan") return send({ jsonrpc: "2.0", id: msg.id, result: { outcome: { outcome: "cancelled" } } });
26663
26668
  const params = msg.params ?? {};
26664
26669
  current.flushAssistantText();
26665
26670
  const options = Array.isArray(params.options) ? params.options : [];
@@ -26667,7 +26672,7 @@ function createAcpDriver(support12) {
26667
26672
  const optionAlways = options.find((o) => o.kind === "allow_always" && typeof o.optionId === "string")?.optionId ?? null;
26668
26673
  const cancelled = { outcome: { outcome: "cancelled" } };
26669
26674
  const missing = (want) => emit({
26670
- ...base3(threadId, current.turnId),
26675
+ ...base2(threadId, current.turnId),
26671
26676
  type: "runtime.error",
26672
26677
  message: `${DRIVER_KIND8} offered no "${want}" permission option \u2014 cancelling the request instead of guessing`
26673
26678
  });
@@ -26713,7 +26718,7 @@ function createAcpDriver(support12) {
26713
26718
  result: optionId ? { outcome: { outcome: "selected", optionId } } : cancelled
26714
26719
  });
26715
26720
  emit({
26716
- ...base3(threadId, current.turnId),
26721
+ ...base2(threadId, current.turnId),
26717
26722
  type: "request.resolved",
26718
26723
  requestId,
26719
26724
  behavior: optionId && isQuestion ? "answer" : optionId && behavior === "allow" ? "allow" : "deny",
@@ -26722,13 +26727,13 @@ function createAcpDriver(support12) {
26722
26727
  });
26723
26728
  };
26724
26729
  const timer = setTimeout(() => {
26725
- emit({ ...base3(threadId, current.turnId), type: "runtime.error", message: DENY_TIMEOUT_NOTE3 });
26730
+ emit({ ...base2(threadId, current.turnId), type: "runtime.error", message: DENY_TIMEOUT_NOTE3 });
26726
26731
  finish("deny", "timeout");
26727
26732
  }, 15 * 6e4);
26728
26733
  timer.unref?.();
26729
26734
  current.asks.set(requestId, finish);
26730
26735
  emit({
26731
- ...base3(threadId, current.turnId),
26736
+ ...base2(threadId, current.turnId),
26732
26737
  type: "request.opened",
26733
26738
  requestId,
26734
26739
  requestType: isQuestion ? "question" : "permission",
@@ -26760,7 +26765,7 @@ function createAcpDriver(support12) {
26760
26765
  current.flushAssistantText();
26761
26766
  current.state.producedItem = true;
26762
26767
  emit({
26763
- ...base3(threadId, current.turnId),
26768
+ ...base2(threadId, current.turnId),
26764
26769
  type: "item.completed",
26765
26770
  itemType: "assistant_image",
26766
26771
  data: content.data,
@@ -26768,14 +26773,14 @@ function createAcpDriver(support12) {
26768
26773
  });
26769
26774
  } else if (typeof delta === "string" && delta) {
26770
26775
  current.state.text += delta;
26771
- emit({ ...base3(threadId, current.turnId), type: "content.delta", streamKind: "assistant_text", delta });
26776
+ emit({ ...base2(threadId, current.turnId), type: "content.delta", streamKind: "assistant_text", delta });
26772
26777
  }
26773
26778
  break;
26774
26779
  }
26775
26780
  case "agent_thought_chunk": {
26776
26781
  const delta = u5.content?.text;
26777
26782
  if (typeof delta === "string" && delta) {
26778
- emit({ ...base3(threadId, current.turnId), type: "content.delta", streamKind: "reasoning_text", delta });
26783
+ emit({ ...base2(threadId, current.turnId), type: "content.delta", streamKind: "reasoning_text", delta });
26779
26784
  }
26780
26785
  break;
26781
26786
  }
@@ -26783,7 +26788,7 @@ function createAcpDriver(support12) {
26783
26788
  current.flushAssistantText();
26784
26789
  if (u5.status !== "completed" && u5.status !== "failed" && typeof u5.toolCallId === "string") current.runningTools.set(u5.toolCallId, Date.now());
26785
26790
  emit({
26786
- ...base3(threadId, current.turnId),
26791
+ ...base2(threadId, current.turnId),
26787
26792
  type: "item.started",
26788
26793
  itemType: "tool",
26789
26794
  itemId: u5.toolCallId,
@@ -26798,7 +26803,7 @@ function createAcpDriver(support12) {
26798
26803
  current.runningTools.delete(u5.toolCallId);
26799
26804
  current.state.producedItem = true;
26800
26805
  emit({
26801
- ...base3(threadId, current.turnId),
26806
+ ...base2(threadId, current.turnId),
26802
26807
  type: "item.completed",
26803
26808
  itemType: "tool",
26804
26809
  itemId: u5.toolCallId,
@@ -26806,7 +26811,7 @@ function createAcpDriver(support12) {
26806
26811
  output: toolDetailPreview(u5.rawOutput ?? u5.content)
26807
26812
  });
26808
26813
  for (const img of extractMcpImages(u5.content ?? u5.rawOutput)) {
26809
- emit({ ...base3(threadId, current.turnId), type: "item.completed", itemType: "assistant_image", data: img.data });
26814
+ emit({ ...base2(threadId, current.turnId), type: "item.completed", itemType: "assistant_image", data: img.data });
26810
26815
  }
26811
26816
  }
26812
26817
  break;
@@ -26902,7 +26907,7 @@ function createAcpDriver(support12) {
26902
26907
  session.dead = true;
26903
26908
  const current = session.current;
26904
26909
  if (!current) return;
26905
- emit({ ...base3(threadId, current.turnId), type: "runtime.error", ...describeSpawnFailure(e, launch.command) });
26910
+ emit({ ...base2(threadId, current.turnId), type: "runtime.error", ...describeSpawnFailure(e, launch.command) });
26906
26911
  settle(threadId, session, false, "spawn_error");
26907
26912
  });
26908
26913
  child.on("close", (code) => {
@@ -26910,7 +26915,7 @@ function createAcpDriver(support12) {
26910
26915
  const current = session.current;
26911
26916
  if (current) {
26912
26917
  emit({
26913
- ...base3(threadId, current.turnId),
26918
+ ...base2(threadId, current.turnId),
26914
26919
  type: "runtime.error",
26915
26920
  message: `${DRIVER_KIND8} exited ${code} before the prompt result${session.stderr ? `: ${session.stderr.trim().slice(-300)}` : ""}`
26916
26921
  });
@@ -26923,6 +26928,7 @@ function createAcpDriver(support12) {
26923
26928
  };
26924
26929
  const sendTurn = async (turn) => {
26925
26930
  const { threadId } = turn;
26931
+ if (turn.approvalMode === "plan" && !supportsApprovalMode(DRIVER_KIND8, "plan")) throw new Error("Plan is not available for this engine yet");
26926
26932
  if (active.has(threadId)) throw new Error("a turn is already running on this thread");
26927
26933
  const turnConfig = turn.approvalMode === void 0 ? config2 : { ...config2, fullAuto: turn.approvalMode === "full" && supportsApprovalMode(DRIVER_KIND8, "full") };
26928
26934
  const controlsHost = turn.integrations?.localComputer?.scope === "local-computer";
@@ -26933,27 +26939,27 @@ function createAcpDriver(support12) {
26933
26939
  const cwd = turn.cwd ?? turnConfig.workspace ?? homedir7();
26934
26940
  const env = childEnv(turnConfig);
26935
26941
  if (support12.requireAuthenticationBeforeSpawn && !skipSubscriptionAuthForLocalInject(turn.model) && !await support12.isAuthenticated(env, turnConfig, instanceId)) {
26936
- emit({ ...base3(threadId, turnId), type: "turn.started" });
26937
- emit({ ...base3(threadId, turnId), type: "runtime.error", message: support12.loginNote, setup: true, failure: "auth_failed" });
26938
- emit({ ...base3(threadId, turnId), type: "turn.completed", ok: false, stopReason: "auth_required", cost: null });
26942
+ emit({ ...base2(threadId, turnId), type: "turn.started" });
26943
+ emit({ ...base2(threadId, turnId), type: "runtime.error", message: support12.loginNote, setup: true, failure: "auth_failed" });
26944
+ emit({ ...base2(threadId, turnId), type: "turn.completed", ok: false, stopReason: "auth_required", cost: null });
26939
26945
  return { turnId };
26940
26946
  }
26941
26947
  const resolvedModel = support12.resolveTurnModel?.(turn.model, env);
26942
26948
  support12.applyTurnEnv?.(env, { model: resolvedModel, requestedModel: turn.model, fullAuto: turnConfig.fullAuto === true });
26943
26949
  let cliTurn = resolvedModel !== void 0 && resolvedModel !== turn.model ? { ...turn, model: resolvedModel } : turn;
26944
- const mcpServers = acpMcpServers(turn);
26950
+ const mcpServers = turn.approvalMode === "plan" ? [] : acpMcpServers(turn);
26945
26951
  let launch;
26946
26952
  try {
26947
26953
  launch = support12.resolveCommand ? await support12.resolveCommand(env, turnConfig, instanceId) : { command: turnConfig.cli };
26948
26954
  } catch (error51) {
26949
- emit({ ...base3(threadId, turnId), type: "turn.started" });
26955
+ emit({ ...base2(threadId, turnId), type: "turn.started" });
26950
26956
  emit({
26951
- ...base3(threadId, turnId),
26957
+ ...base2(threadId, turnId),
26952
26958
  type: "runtime.error",
26953
26959
  message: error51 instanceof Error ? error51.message : String(error51),
26954
26960
  setup: true
26955
26961
  });
26956
- emit({ ...base3(threadId, turnId), type: "turn.completed", ok: false, stopReason: "setup_required", cost: null });
26962
+ emit({ ...base2(threadId, turnId), type: "turn.completed", ok: false, stopReason: "setup_required", cost: null });
26957
26963
  return { turnId };
26958
26964
  }
26959
26965
  const spawnArgs = support12.spawnArgs(turnConfig, cliTurn);
@@ -26995,7 +27001,7 @@ function createAcpDriver(support12) {
26995
27001
  if (!nativeModel) return;
26996
27002
  const option = acpVariantOption(result);
26997
27003
  emit({
26998
- ...base3(threadId, turnId),
27004
+ ...base2(threadId, turnId),
26999
27005
  type: "session.model-variants",
27000
27006
  model: nativeModel === cliTurn.model ? turn.model ?? nativeModel : nativeModel,
27001
27007
  variants: {
@@ -27017,7 +27023,7 @@ function createAcpDriver(support12) {
27017
27023
  state2.text = "";
27018
27024
  if (!text4.trim()) return;
27019
27025
  state2.producedItem = true;
27020
- emit({ ...base3(threadId, turnId), type: "item.completed", itemType: "assistant_text", text: text4 });
27026
+ emit({ ...base2(threadId, turnId), type: "item.completed", itemType: "assistant_text", text: text4 });
27021
27027
  };
27022
27028
  const current = {
27023
27029
  turnId,
@@ -27045,7 +27051,7 @@ function createAcpDriver(support12) {
27045
27051
  }
27046
27052
  };
27047
27053
  active.set(threadId, { stop: () => closeSession(threadId, "stop"), interrupt, turnId, asks });
27048
- emit({ ...base3(threadId, turnId), type: "turn.started" });
27054
+ emit({ ...base2(threadId, turnId), type: "turn.started" });
27049
27055
  session.current = current;
27050
27056
  (async () => {
27051
27057
  try {
@@ -27163,7 +27169,7 @@ function createAcpDriver(support12) {
27163
27169
  if (sessionStarted) return;
27164
27170
  sessionStarted = true;
27165
27171
  emit({
27166
- ...base3(threadId, turnId),
27172
+ ...base2(threadId, turnId),
27167
27173
  type: "session.started",
27168
27174
  sessionId,
27169
27175
  model: selectedModel ?? init?._meta?.modelState?.currentModelId ?? cliTurn.model ?? null,
@@ -27218,7 +27224,7 @@ function createAcpDriver(support12) {
27218
27224
  cliTurn = { ...cliTurn, model: fallback };
27219
27225
  selectedModel = fallback;
27220
27226
  emit({
27221
- ...base3(threadId, turnId),
27227
+ ...base2(threadId, turnId),
27222
27228
  type: "session.model-fallback",
27223
27229
  requested,
27224
27230
  model: fallback,
@@ -27271,7 +27277,7 @@ ${instructed.text}` : instructed.text;
27271
27277
  const usage = result?.usage ?? result?._meta ?? {};
27272
27278
  if (typeof usage.inputTokens === "number" || typeof usage.outputTokens === "number") {
27273
27279
  emit({
27274
- ...base3(threadId, turnId),
27280
+ ...base2(threadId, turnId),
27275
27281
  type: "thread.token-usage.updated",
27276
27282
  input: usage.inputTokens ?? 0,
27277
27283
  output: usage.outputTokens ?? 0
@@ -27283,7 +27289,7 @@ ${instructed.text}` : instructed.text;
27283
27289
  else {
27284
27290
  const errorMessage = typeof result?.error === "string" && result.error ? result.error : typeof result?.message === "string" && result.message ? result.message : `Model turn failed: ${reason ?? "unknown error"}`;
27285
27291
  emit({
27286
- ...base3(threadId, turnId),
27292
+ ...base2(threadId, turnId),
27287
27293
  type: "runtime.error",
27288
27294
  message: errorMessage
27289
27295
  });
@@ -27299,7 +27305,7 @@ ${instructed.text}` : instructed.text;
27299
27305
  const needsAuth = code === "invalid_credentials" || code === "inactive_subscription" || message2 === support12.loginNote || logAuth;
27300
27306
  const failure = needsAuth ? "auth_failed" : stalled ? "stalled" : engineFailureFromCode(code) ?? (isOpaqueRequestRejection(message2) ? "request_rejected" : void 0);
27301
27307
  emit({
27302
- ...base3(threadId, turnId),
27308
+ ...base2(threadId, turnId),
27303
27309
  type: "runtime.error",
27304
27310
  message: message2,
27305
27311
  ...needsAuth ? { setup: true } : {},
@@ -28543,7 +28549,7 @@ var init_antigravity = __esm({
28543
28549
  AntigravityDriver = {
28544
28550
  ...AcpAntigravityDriver,
28545
28551
  async create(input) {
28546
- const base3 = await AcpAntigravityDriver.create(input);
28552
+ const base2 = await AcpAntigravityDriver.create(input);
28547
28553
  const auth = new AntigravityAuthController();
28548
28554
  let installFailure;
28549
28555
  const runtimeAndProfile = async () => {
@@ -28557,12 +28563,12 @@ var init_antigravity = __esm({
28557
28563
  return { runtime, profile };
28558
28564
  };
28559
28565
  return {
28560
- ...base3,
28566
+ ...base2,
28561
28567
  get models() {
28562
- return base3.models;
28568
+ return base2.models;
28563
28569
  },
28564
28570
  snapshot: async () => {
28565
- const snapshot = await base3.snapshot();
28571
+ const snapshot = await base2.snapshot();
28566
28572
  if (snapshot.state === "available") installFailure = void 0;
28567
28573
  return snapshot.state === "unavailable" && installFailure ? { ...snapshot, reason: installFailure } : snapshot;
28568
28574
  },
@@ -28585,7 +28591,7 @@ var init_antigravity = __esm({
28585
28591
  cancelAuthentication: async () => auth.cancel(),
28586
28592
  dispose: async () => {
28587
28593
  auth.cancel();
28588
- await base3.dispose();
28594
+ await base2.dispose();
28589
28595
  }
28590
28596
  };
28591
28597
  }
@@ -28641,7 +28647,7 @@ var init_boxagent = __esm({
28641
28647
  const emit = (event) => {
28642
28648
  for (const l of Array.from(listeners)) l(event);
28643
28649
  };
28644
- const base3 = (threadId, turnId) => ({
28650
+ const base2 = (threadId, turnId) => ({
28645
28651
  eventId: newEventId(),
28646
28652
  provider: DRIVER_KIND,
28647
28653
  threadId,
@@ -28694,8 +28700,8 @@ var init_boxagent = __esm({
28694
28700
  });
28695
28701
  }
28696
28702
  });
28697
- emit({ ...base3(threadId, turnId), type: "turn.started" });
28698
- emit({ ...base3(threadId, turnId), type: "session.started", sessionId: promptId, model });
28703
+ emit({ ...base2(threadId, turnId), type: "turn.started" });
28704
+ emit({ ...base2(threadId, turnId), type: "session.started", sessionId: promptId, model });
28699
28705
  (async () => {
28700
28706
  const seen = /* @__PURE__ */ new Set();
28701
28707
  const startedAt = Date.now();
@@ -28706,14 +28712,14 @@ var init_boxagent = __esm({
28706
28712
  const text4 = pendingText;
28707
28713
  pendingText = "";
28708
28714
  if (!text4.trim()) return;
28709
- emit({ ...base3(threadId, turnId), type: "item.completed", itemType: "assistant_text", text: text4 });
28715
+ emit({ ...base2(threadId, turnId), type: "item.completed", itemType: "assistant_text", text: text4 });
28710
28716
  };
28711
28717
  const ingest = (text4) => {
28712
28718
  const delta = text4.startsWith(lastText) ? text4.slice(lastText.length) : text4;
28713
28719
  lastText = text4;
28714
28720
  if (!delta) return;
28715
28721
  pendingText += delta;
28716
- emit({ ...base3(threadId, turnId), type: "content.delta", streamKind: "assistant_text", delta });
28722
+ emit({ ...base2(threadId, turnId), type: "content.delta", streamKind: "assistant_text", delta });
28717
28723
  };
28718
28724
  try {
28719
28725
  for (; ; ) {
@@ -28737,7 +28743,7 @@ var init_boxagent = __esm({
28737
28743
  } else if (/tool|command|exec|browse/i.test(kind)) {
28738
28744
  flushAssistantText();
28739
28745
  emit({
28740
- ...base3(threadId, turnId),
28746
+ ...base2(threadId, turnId),
28741
28747
  type: "item.started",
28742
28748
  itemType: "tool",
28743
28749
  itemId: id,
@@ -28748,7 +28754,7 @@ var init_boxagent = __esm({
28748
28754
  active.delete(threadId);
28749
28755
  flushAssistantText();
28750
28756
  const failed2 = /fail|error/i.test(kind);
28751
- emit({ ...base3(threadId, turnId), type: "turn.completed", ok: !failed2, stopReason: failed2 ? kind : null, cost: null });
28757
+ emit({ ...base2(threadId, turnId), type: "turn.completed", ok: !failed2, stopReason: failed2 ? kind : null, cost: null });
28752
28758
  return;
28753
28759
  }
28754
28760
  }
@@ -28768,7 +28774,7 @@ var init_boxagent = __esm({
28768
28774
  }
28769
28775
  flushAssistantText();
28770
28776
  active.delete(threadId);
28771
- emit({ ...base3(threadId, turnId), type: "turn.completed", ok: true, stopReason: null, cost: null });
28777
+ emit({ ...base2(threadId, turnId), type: "turn.completed", ok: true, stopReason: null, cost: null });
28772
28778
  return;
28773
28779
  }
28774
28780
  if (/failed|error|cancelled|interrupted/i.test(state2)) {
@@ -28776,7 +28782,7 @@ var init_boxagent = __esm({
28776
28782
  if (problem || runError || /failed|error/i.test(state2)) throw new Error(problem ?? runError ?? `the box run ${state2}`);
28777
28783
  flushAssistantText();
28778
28784
  active.delete(threadId);
28779
- emit({ ...base3(threadId, turnId), type: "turn.completed", ok: false, stopReason: state2, cost: null });
28785
+ emit({ ...base2(threadId, turnId), type: "turn.completed", ok: false, stopReason: state2, cost: null });
28780
28786
  return;
28781
28787
  }
28782
28788
  }
@@ -28786,12 +28792,12 @@ var init_boxagent = __esm({
28786
28792
  }
28787
28793
  flushAssistantText();
28788
28794
  active.delete(threadId);
28789
- emit({ ...base3(threadId, turnId), type: "turn.completed", ok: false, stopReason: "interrupted", cost: null });
28795
+ emit({ ...base2(threadId, turnId), type: "turn.completed", ok: false, stopReason: "interrupted", cost: null });
28790
28796
  } catch (e) {
28791
28797
  flushAssistantText();
28792
28798
  active.delete(threadId);
28793
- emit({ ...base3(threadId, turnId), type: "runtime.error", message: e.message });
28794
- emit({ ...base3(threadId, turnId), type: "turn.completed", ok: false, stopReason: "error", cost: null });
28799
+ emit({ ...base2(threadId, turnId), type: "runtime.error", message: e.message });
28800
+ emit({ ...base2(threadId, turnId), type: "turn.completed", ok: false, stopReason: "error", cost: null });
28795
28801
  }
28796
28802
  })();
28797
28803
  return { turnId };
@@ -28840,6 +28846,24 @@ var init_boxagent = __esm({
28840
28846
  }
28841
28847
  });
28842
28848
 
28849
+ // server/plan-policy.ts
28850
+ function planToolAllowed(tool2) {
28851
+ return typeof tool2 === "string" && PLAN_READ_TOOLS.includes(tool2);
28852
+ }
28853
+ function claudePlanHook(node, helper) {
28854
+ const quote2 = (s4) => "'" + s4.replace(/'/g, "'\\''") + "'";
28855
+ const command = process.platform === "win32" ? '"%OMB_PLAN_NODE%" "%OMB_PLAN_HOOK%" || exit /b 2' : `${quote2(node)} ${quote2(helper)} || exit 2`;
28856
+ return [{ matcher: "", hooks: [{ type: "command", command, timeout: 5 }] }];
28857
+ }
28858
+ var PLAN_READ_TOOLS, PLAN_REFUSAL;
28859
+ var init_plan_policy = __esm({
28860
+ "server/plan-policy.ts"() {
28861
+ "use strict";
28862
+ PLAN_READ_TOOLS = ["Read", "Glob", "Grep"];
28863
+ PLAN_REFUSAL = "Plan mode blocked this action. Review the plan and approve it in Atomic before making changes.";
28864
+ }
28865
+ });
28866
+
28843
28867
  // server/drivers/claude-message.ts
28844
28868
  function firstText(content) {
28845
28869
  if (typeof content === "string") return content;
@@ -29829,15 +29853,15 @@ function permissionSocketPath(threadId, botId) {
29829
29853
  return brokerSocketPath(DATA_DIR, `${prefix}${digest2}`);
29830
29854
  }
29831
29855
  function brokerSocketCandidates(threadId, botId) {
29832
- const base3 = permissionSocketPath(threadId, botId);
29856
+ const base2 = permissionSocketPath(threadId, botId);
29833
29857
  if (process.platform !== "win32") {
29834
29858
  const scope = createHash7("sha256").update(`${DATA_DIR}\0${process.pid}\0${botId ?? ""}\0${threadId}`).digest("hex").slice(0, 16);
29835
- return [base3, join23(tmpdir3(), `omb-perm-${scope}.sock`)];
29859
+ return [base2, join23(tmpdir3(), `omb-perm-${scope}.sock`)];
29836
29860
  }
29837
29861
  return [
29838
- base3,
29839
- `${base3}-${randomBytes2(3).toString("hex")}`,
29840
- `${base3}-${randomBytes2(3).toString("hex")}`
29862
+ base2,
29863
+ `${base2}-${randomBytes2(3).toString("hex")}`,
29864
+ `${base2}-${randomBytes2(3).toString("hex")}`
29841
29865
  ];
29842
29866
  }
29843
29867
  async function createPermissionBroker(opts) {
@@ -29887,6 +29911,12 @@ async function createPermissionBroker(opts) {
29887
29911
  continue;
29888
29912
  }
29889
29913
  const ask = { id: askId, kind, tool: msg.tool ?? "tool", input: msg.input ?? {}, at: Date.now() };
29914
+ if (opts.plan && kind === "permission" && !planToolAllowed(ask.tool)) {
29915
+ const behavior = "deny";
29916
+ conn.write(JSON.stringify({ t: "answer", id: askId, behavior, source: "system", message: PLAN_REFUSAL }) + "\n");
29917
+ opts.onResolve({ ...ask, behavior, source: "system" });
29918
+ continue;
29919
+ }
29890
29920
  const sessionKey = kind === "permission" && opts.sessionAllows && (opts.canAllowForSession?.(ask) ?? true) ? sessionAllowKey(ask) : null;
29891
29921
  if (sessionKey && opts.sessionAllows?.has(sessionKey)) {
29892
29922
  const updatedInput = opts.inputOnAllow?.(ask);
@@ -30045,6 +30075,7 @@ var CONNECTOR_CACHE_MS, CLAUDE_ACCOUNT_ENV_KEYS, DEFAULT_AUTOCOMPACT_TOKENS, CLA
30045
30075
  var init_claude = __esm({
30046
30076
  "server/drivers/claude.ts"() {
30047
30077
  "use strict";
30078
+ init_plan_policy();
30048
30079
  init_claude_message();
30049
30080
  init_config();
30050
30081
  init_atomic();
@@ -30182,7 +30213,7 @@ var init_claude = __esm({
30182
30213
  const sumCost = (a2, b3) => a2 === null ? b3 : b3 === null ? a2 : a2 + b3;
30183
30214
  const STEER_FOLLOWUP_MS = 1500;
30184
30215
  const sessions = /* @__PURE__ */ new Map();
30185
- const elicitations = createClaudeElicitations({ emit: (event) => emit(event), base: (threadId, turnId) => base3(threadId, turnId) });
30216
+ const elicitations = createClaudeElicitations({ emit: (event) => emit(event), base: (threadId, turnId) => base2(threadId, turnId) });
30186
30217
  const sessionAllows = /* @__PURE__ */ new Map();
30187
30218
  const deliveredVolatile = /* @__PURE__ */ new Map();
30188
30219
  const volatileNoteFor = (threadId, sessionId, turn) => {
@@ -30250,7 +30281,7 @@ var init_claude = __esm({
30250
30281
  const emit = (event) => {
30251
30282
  for (const l of Array.from(listeners)) l(event);
30252
30283
  };
30253
- const base3 = (threadId, turnId) => ({
30284
+ const base2 = (threadId, turnId) => ({
30254
30285
  eventId: newEventId(),
30255
30286
  provider: DRIVER_KIND2,
30256
30287
  threadId,
@@ -30266,6 +30297,8 @@ var init_claude = __esm({
30266
30297
  const { threadId, botId } = turn;
30267
30298
  const relaunch = logicalTurnId !== void 0;
30268
30299
  if (active.has(threadId) && !relaunch) throw new Error("a turn is already running on this thread");
30300
+ const plan = turn.approvalMode === "plan";
30301
+ if (plan) turn = { ...turn, integrations: void 0, mcpFromUserConfig: false, pluginDirs: [] };
30269
30302
  const permissionMode = turn.approvalMode === void 0 ? config2.permissionMode : turn.approvalMode === "full" ? "bypassPermissions" : turn.approvalMode === "auto" ? "auto" : turn.approvalMode === "edits" ? "acceptEdits" : "default";
30270
30303
  const controlsHost = turn.integrations?.localComputer?.scope === "local-computer";
30271
30304
  if (controlsHost && permissionMode === "bypassPermissions" && turn.approvalMode !== "full") {
@@ -30297,7 +30330,8 @@ var init_claude = __esm({
30297
30330
  "--permission-mode",
30298
30331
  permissionMode
30299
30332
  ];
30300
- if (turn.builtinTools === false) args.push("--tools", "");
30333
+ if (plan) args.push("--tools", "Read,Glob,Grep", "--strict-mcp-config", "--setting-sources", "");
30334
+ else if (turn.builtinTools === false) args.push("--tools", "");
30301
30335
  else if (config2.tools !== void 0) args.push("--tools", config2.tools.join(","));
30302
30336
  const denied = [.../* @__PURE__ */ new Set([
30303
30337
  ...config2.tools === void 0 ? HARNESS_REPLACED_BUILTINS : [],
@@ -30318,8 +30352,8 @@ var init_claude = __esm({
30318
30352
  cliVersionChecked = true;
30319
30353
  }
30320
30354
  }
30321
- const isolated = !inheritsUserConfig(turnEnvironment);
30322
- if (isolated) {
30355
+ const isolated = plan || !inheritsUserConfig(turnEnvironment);
30356
+ if (isolated && !plan) {
30323
30357
  if (!turn.mcpFromUserConfig && claudeCliSupports(cliVersion, "--strict-mcp-config")) args.push("--strict-mcp-config");
30324
30358
  if (claudeCliSupports(cliVersion, "--setting-sources")) args.push("--setting-sources", "project");
30325
30359
  }
@@ -30384,7 +30418,7 @@ var init_claude = __esm({
30384
30418
  mcpServers[name] = { ...server };
30385
30419
  botOwned.add(name);
30386
30420
  }
30387
- if (isolated && turn.cwd) {
30421
+ if (isolated && turn.cwd && !plan) {
30388
30422
  for (const [name, server] of Object.entries(projectMcpServers(turn.cwd))) {
30389
30423
  if (Object.hasOwn(mcpServers, name)) continue;
30390
30424
  mcpServers[name] = server;
@@ -30410,6 +30444,11 @@ var init_claude = __esm({
30410
30444
  args.push("--allowedTools", allowed.join(","));
30411
30445
  }
30412
30446
  const env = environment(turnModel);
30447
+ if (plan) {
30448
+ env.OMB_PLAN_NODE = process.execPath;
30449
+ env.OMB_PLAN_HOOK = SPAWNED_PROXIES.planHook;
30450
+ if (process.versions.electron) env.ELECTRON_RUN_AS_NODE = "1";
30451
+ }
30413
30452
  const authSettings = isolated && !injected.injected ? readClaudeAuthSettings(env, input.environment) : {};
30414
30453
  const hooks = turn.integrations?.hooks;
30415
30454
  const hookTokenPath = hooks ? hookTokenFile(threadId, botId) : null;
@@ -30432,7 +30471,8 @@ var init_claude = __esm({
30432
30471
  const settings2 = { ...authSettings };
30433
30472
  const hookBlock = {
30434
30473
  ...hooks ? claudeHookSettings(HOOK_HELPER_PATH) : {},
30435
- ...rtkHook ? { PreToolUse: rtkHookSettings(RTK_HOOK_PATH) } : {}
30474
+ ...rtkHook ? { PreToolUse: rtkHookSettings(RTK_HOOK_PATH) } : {},
30475
+ ...plan ? { PreToolUse: claudePlanHook(process.execPath, SPAWNED_PROXIES.planHook) } : {}
30436
30476
  };
30437
30477
  if (Object.keys(hookBlock).length) settings2.hooks = hookBlock;
30438
30478
  const authSettingsPath = mcpConfigPath && Object.keys(settings2).length ? join23(dirname9(mcpConfigPath), "auth-settings.json") : null;
@@ -30471,7 +30511,7 @@ var init_claude = __esm({
30471
30511
  retryAbort.abort();
30472
30512
  stopSession(live2);
30473
30513
  }, turnId, broker: live2.broker });
30474
- emit({ ...base3(threadId, turnId), type: "turn.started" });
30514
+ emit({ ...base2(threadId, turnId), type: "turn.started" });
30475
30515
  const note = volatileNoteFor(threadId, live2.sessionId, turn);
30476
30516
  const message2 = note ? claudeUserMessage(withNote(turn.text, note), turn.images) : promptMsg;
30477
30517
  const running3 = live2.turn;
@@ -30526,6 +30566,7 @@ var init_claude = __esm({
30526
30566
  const askTools = /* @__PURE__ */ new Map();
30527
30567
  broker = await createPermissionBroker({
30528
30568
  socketPaths: brokerSocketCandidates(threadId, botId),
30569
+ plan,
30529
30570
  isActive: () => Boolean(sessions.get(threadId)?.turn),
30530
30571
  inputOnAllow: (ask) => {
30531
30572
  const current = sessions.get(threadId)?.turn?.input ?? turn;
@@ -30548,7 +30589,7 @@ var init_claude = __esm({
30548
30589
  const nativeReview = permissionMode === "auto" && nativeMode !== null ? nativeMode === "auto" ? "active" : "inactive" : void 0;
30549
30590
  const questions = askQuestions(ask);
30550
30591
  emit({
30551
- ...base3(threadId, eventTurnId),
30592
+ ...base2(threadId, eventTurnId),
30552
30593
  type: "request.opened",
30553
30594
  requestId: ask.id,
30554
30595
  requestType: ask.kind,
@@ -30566,9 +30607,13 @@ var init_claude = __esm({
30566
30607
  });
30567
30608
  },
30568
30609
  onResolve: (resolved) => {
30610
+ if (plan && resolved.behavior === "deny") {
30611
+ emit({ ...base2(threadId, turnId), type: "item.started", itemType: "tool", itemId: resolved.id, title: "Plan blocked an action" });
30612
+ emit({ ...base2(threadId, turnId), type: "item.completed", itemType: "tool", itemId: resolved.id, ok: false, output: PLAN_REFUSAL });
30613
+ }
30569
30614
  const eventTurnId = sessions.get(threadId)?.turn?.turnId ?? turnId;
30570
30615
  emit({
30571
- ...base3(threadId, eventTurnId),
30616
+ ...base2(threadId, eventTurnId),
30572
30617
  type: "request.resolved",
30573
30618
  requestId: resolved.id,
30574
30619
  behavior: resolved.behavior,
@@ -30597,7 +30642,7 @@ var init_claude = __esm({
30597
30642
  if (relaunch && retry.cancelled) {
30598
30643
  cleanupUnownedLaunch();
30599
30644
  if (active.get(threadId)?.turnId === turnId) active.delete(threadId);
30600
- emit({ ...base3(threadId, turnId), type: "turn.completed", ok: false, stopReason: "interrupted", cost: null });
30645
+ emit({ ...base2(threadId, turnId), type: "turn.completed", ok: false, stopReason: "interrupted", cost: null });
30601
30646
  return { turnId };
30602
30647
  }
30603
30648
  let targetExisted = false;
@@ -30652,7 +30697,7 @@ var init_claude = __esm({
30652
30697
  session.turn = null;
30653
30698
  retryState.delete(threadId);
30654
30699
  if (t2.authFailed) closeSession(threadId, "sign-in required");
30655
- emit({ ...base3(threadId, t2.turnId), type: "turn.completed", ok, stopReason, cost, ...usage ? { usage } : {} });
30700
+ emit({ ...base2(threadId, t2.turnId), type: "turn.completed", ok, stopReason, cost, ...usage ? { usage } : {} });
30656
30701
  if (session.child.exitCode === null && !session.closing) armIdle(threadId);
30657
30702
  };
30658
30703
  const currentTurnId = () => session.turn?.turnId ?? turnId;
@@ -30681,7 +30726,7 @@ var init_claude = __esm({
30681
30726
  retry2.attempt++;
30682
30727
  const delayMs = backoff(retry2.attempt - 1);
30683
30728
  emit({
30684
- ...base3(threadId, turnId2),
30729
+ ...base2(threadId, turnId2),
30685
30730
  type: "turn.retrying",
30686
30731
  attempt: retry2.attempt,
30687
30732
  delayMs,
@@ -30694,7 +30739,7 @@ var init_claude = __esm({
30694
30739
  active.delete(threadId);
30695
30740
  retryState.delete(threadId);
30696
30741
  emit({
30697
- ...base3(threadId, turnId2),
30742
+ ...base2(threadId, turnId2),
30698
30743
  type: "turn.completed",
30699
30744
  ok: false,
30700
30745
  stopReason: "interrupted",
@@ -30714,12 +30759,12 @@ var init_claude = __esm({
30714
30759
  if (active.get(threadId)?.turnId === turnId2) active.delete(threadId);
30715
30760
  retryState.delete(threadId);
30716
30761
  emit({
30717
- ...base3(threadId, turnId2),
30762
+ ...base2(threadId, turnId2),
30718
30763
  type: "runtime.error",
30719
30764
  message: e instanceof Error ? e.message : String(e)
30720
30765
  });
30721
30766
  emit({
30722
- ...base3(threadId, turnId2),
30767
+ ...base2(threadId, turnId2),
30723
30768
  type: "turn.completed",
30724
30769
  ok: false,
30725
30770
  stopReason: "exit_before_result",
@@ -30755,11 +30800,11 @@ var init_claude = __esm({
30755
30800
  if (delivered && delivered.sessionId === session.sessionId) delivered.sessionId = o.session_id;
30756
30801
  session.sessionId = o.session_id;
30757
30802
  }
30758
- emit({ ...base3(threadId, currentTurnId()), type: "session.started", sessionId: o.session_id, model: o.model, modelEvidenceLevel: "session-reported", ...retry.rebuilt ? { rebuilt: true } : {} });
30803
+ emit({ ...base2(threadId, currentTurnId()), type: "session.started", sessionId: o.session_id, model: o.model, modelEvidenceLevel: "session-reported", ...retry.rebuilt ? { rebuilt: true } : {} });
30759
30804
  } else if (o.subtype === "compact_boundary") {
30760
30805
  deliveredVolatile.delete(threadId);
30761
30806
  } else if (o.subtype === "thinking_tokens") {
30762
- emit({ ...base3(threadId, currentTurnId()), type: "item.updated", itemType: "reasoning", tokens: o.estimated_tokens });
30807
+ emit({ ...base2(threadId, currentTurnId()), type: "item.updated", itemType: "reasoning", tokens: o.estimated_tokens });
30763
30808
  }
30764
30809
  break;
30765
30810
  case "stream_event": {
@@ -30772,9 +30817,9 @@ var init_claude = __esm({
30772
30817
  session.turn.sawStreamDelta = true;
30773
30818
  session.turn.produced = true;
30774
30819
  }
30775
- emit({ ...base3(threadId, currentTurnId()), type: "content.delta", streamKind: "assistant_text", delta: d.text });
30820
+ emit({ ...base2(threadId, currentTurnId()), type: "content.delta", streamKind: "assistant_text", delta: d.text });
30776
30821
  } else if (d.type === "thinking_delta" && typeof d.thinking === "string" && d.thinking) {
30777
- emit({ ...base3(threadId, currentTurnId()), type: "content.delta", streamKind: "reasoning_text", delta: d.thinking });
30822
+ emit({ ...base2(threadId, currentTurnId()), type: "content.delta", streamKind: "reasoning_text", delta: d.thinking });
30778
30823
  }
30779
30824
  break;
30780
30825
  }
@@ -30782,7 +30827,7 @@ var init_claude = __esm({
30782
30827
  const msg = o.message ?? {};
30783
30828
  if (o.is_api_error_message !== true && typeof o.error !== "string") {
30784
30829
  emit({
30785
- ...base3(threadId, currentTurnId()),
30830
+ ...base2(threadId, currentTurnId()),
30786
30831
  type: "model.evidence",
30787
30832
  callId: typeof msg.id === "string" ? msg.id : newId(),
30788
30833
  responseId: typeof msg.id === "string" ? msg.id : void 0,
@@ -30793,7 +30838,7 @@ var init_claude = __esm({
30793
30838
  const text4 = firstText(msg.content);
30794
30839
  if (claudeAuthFailure(o, text4)) {
30795
30840
  if (session.turn) session.turn.authFailed = true;
30796
- emit({ ...base3(threadId, currentTurnId()), type: "runtime.error", message: text4, setup: true, failure: "auth_failed" });
30841
+ emit({ ...base2(threadId, currentTurnId()), type: "runtime.error", message: text4, setup: true, failure: "auth_failed" });
30797
30842
  break;
30798
30843
  }
30799
30844
  if (claudeCredentialRefreshConflict(o, text4)) {
@@ -30804,16 +30849,16 @@ var init_claude = __esm({
30804
30849
  if (text4.trim()) {
30805
30850
  const synthetic = o.is_api_error_message === true || typeof o.error === "string" ? { synthetic: true } : {};
30806
30851
  if (!session.turn?.sawStreamDelta) {
30807
- emit({ ...base3(threadId, currentTurnId()), ...synthetic, type: "content.delta", streamKind: "assistant_text", delta: text4 });
30852
+ emit({ ...base2(threadId, currentTurnId()), ...synthetic, type: "content.delta", streamKind: "assistant_text", delta: text4 });
30808
30853
  }
30809
30854
  if (session.turn) session.turn.sawStreamDelta = false;
30810
- emit({ ...base3(threadId, currentTurnId()), ...synthetic, type: "item.completed", itemType: "assistant_text", text: text4 });
30855
+ emit({ ...base2(threadId, currentTurnId()), ...synthetic, type: "item.completed", itemType: "assistant_text", text: text4 });
30811
30856
  }
30812
30857
  for (const b3 of Array.isArray(msg.content) ? msg.content : []) {
30813
30858
  if (b3.type === "tool_use") {
30814
30859
  if (session.turn) session.turn.produced = true;
30815
30860
  emit({
30816
- ...base3(threadId, currentTurnId()),
30861
+ ...base2(threadId, currentTurnId()),
30817
30862
  type: "item.started",
30818
30863
  itemType: "tool",
30819
30864
  itemId: b3.id,
@@ -30825,7 +30870,7 @@ var init_claude = __esm({
30825
30870
  }
30826
30871
  if (msg.usage) {
30827
30872
  emit({
30828
- ...base3(threadId, currentTurnId()),
30873
+ ...base2(threadId, currentTurnId()),
30829
30874
  type: "thread.token-usage.updated",
30830
30875
  input: (msg.usage.input_tokens || 0) + (msg.usage.cache_read_input_tokens || 0),
30831
30876
  output: msg.usage.output_tokens || 0,
@@ -30854,9 +30899,9 @@ var init_claude = __esm({
30854
30899
  case "user":
30855
30900
  for (const b3 of Array.isArray(o.message?.content) ? o.message.content : []) {
30856
30901
  if (b3.type === "tool_result") {
30857
- emit({ ...base3(threadId, currentTurnId()), type: "item.completed", itemType: "tool", itemId: b3.tool_use_id, ok: !b3.is_error, output: toolDetailPreview(b3.content) });
30902
+ emit({ ...base2(threadId, currentTurnId()), type: "item.completed", itemType: "tool", itemId: b3.tool_use_id, ok: !b3.is_error, output: toolDetailPreview(b3.content) });
30858
30903
  for (const img of extractMcpImages(b3.content)) {
30859
- emit({ ...base3(threadId, currentTurnId()), type: "item.completed", itemType: "assistant_image", data: img.data });
30904
+ emit({ ...base2(threadId, currentTurnId()), type: "item.completed", itemType: "assistant_image", data: img.data });
30860
30905
  }
30861
30906
  }
30862
30907
  }
@@ -30868,7 +30913,7 @@ var init_claude = __esm({
30868
30913
  const resultError = [o.result, ...Array.isArray(o.errors) ? o.errors : []].filter((value) => typeof value === "string").join("\n");
30869
30914
  if (o.is_error === true && !t2?.authFailed && claudeAuthFailure({ error: o.error ?? "api_error" }, resultError)) {
30870
30915
  if (t2) t2.authFailed = true;
30871
- emit({ ...base3(threadId, currentTurnId()), type: "runtime.error", message: resultError, setup: true, failure: "auth_failed" });
30916
+ emit({ ...base2(threadId, currentTurnId()), type: "runtime.error", message: resultError, setup: true, failure: "auth_failed" });
30872
30917
  }
30873
30918
  const usage = o.usage ? {
30874
30919
  input: (o.usage.input_tokens || 0) + (o.usage.cache_read_input_tokens || 0) + (o.usage.cache_creation_input_tokens || 0),
@@ -30892,7 +30937,7 @@ var init_claude = __esm({
30892
30937
  break;
30893
30938
  }
30894
30939
  emit({
30895
- ...base3(threadId, t2.turnId),
30940
+ ...base2(threadId, t2.turnId),
30896
30941
  type: "runtime.error",
30897
30942
  message: "Claude could not renew its sign-in because another Claude process on this account was renewing it at the same moment. Try again in a minute; if it keeps happening, sign in to Claude again in Settings."
30898
30943
  });
@@ -30936,7 +30981,7 @@ var init_claude = __esm({
30936
30981
  relaunchTurn(`could not start (${e.code ?? "error"})`, computeBackoff);
30937
30982
  return;
30938
30983
  }
30939
- emit({ ...base3(threadId, currentTurnId()), type: "runtime.error", ...describeSpawnFailure(e, config2.cli) });
30984
+ emit({ ...base2(threadId, currentTurnId()), type: "runtime.error", ...describeSpawnFailure(e, config2.cli) });
30940
30985
  settle(false, "spawn_error");
30941
30986
  });
30942
30987
  let closeFinalized = false;
@@ -30945,7 +30990,7 @@ var init_claude = __esm({
30945
30990
  if (!await killCliTree(child, 0)) {
30946
30991
  session.broker?.close();
30947
30992
  session.broker = void 0;
30948
- emit({ ...base3(threadId, currentTurnId()), type: "runtime.error", message: "Claude could not be confirmed stopped; its helper processes may still be running." });
30993
+ emit({ ...base2(threadId, currentTurnId()), type: "runtime.error", message: "Claude could not be confirmed stopped; its helper processes may still be running." });
30949
30994
  return;
30950
30995
  }
30951
30996
  if (closeFinalized) return;
@@ -30995,7 +31040,7 @@ var init_claude = __esm({
30995
31040
  retryAbort2.abort();
30996
31041
  }, turnId: turnId2 });
30997
31042
  emit({
30998
- ...base3(threadId, turnId2),
31043
+ ...base2(threadId, turnId2),
30999
31044
  type: "turn.retrying",
31000
31045
  attempt: retry2.attempt + 1,
31001
31046
  delayMs: 0,
@@ -31008,18 +31053,18 @@ var init_claude = __esm({
31008
31053
  if (active.get(threadId)?.turnId === turnId2) active.delete(threadId);
31009
31054
  retryState.delete(threadId);
31010
31055
  emit({
31011
- ...base3(threadId, turnId2),
31056
+ ...base2(threadId, turnId2),
31012
31057
  type: "runtime.error",
31013
31058
  message: e instanceof Error ? e.message : String(e)
31014
31059
  });
31015
- emit({ ...base3(threadId, turnId2), type: "turn.completed", ok: false, stopReason: "exit_before_result", cost: null });
31060
+ emit({ ...base2(threadId, turnId2), type: "turn.completed", ok: false, stopReason: "exit_before_result", cost: null });
31016
31061
  }
31017
31062
  })();
31018
31063
  return;
31019
31064
  }
31020
31065
  retryState.delete(threadId);
31021
31066
  emit({
31022
- ...base3(threadId, currentTurnId()),
31067
+ ...base2(threadId, currentTurnId()),
31023
31068
  type: "runtime.error",
31024
31069
  message: message2
31025
31070
  });
@@ -31048,7 +31093,7 @@ var init_claude = __esm({
31048
31093
  stopSession(session);
31049
31094
  };
31050
31095
  active.set(threadId, { stop, turnId, broker });
31051
- emit({ ...base3(threadId, turnId), type: "turn.started" });
31096
+ emit({ ...base2(threadId, turnId), type: "turn.started" });
31052
31097
  if (relaunch) deliveredVolatile.delete(threadId);
31053
31098
  const launchNote = turn.systemStable !== void 0 ? volatileNoteFor(threadId, sessionId ?? newSessionId, turn) : "";
31054
31099
  const launchMsg = launchNote ? claudeUserMessage(withNote(turn.text, launchNote), turn.images) : promptMsg;
@@ -31234,6 +31279,45 @@ var init_claude = __esm({
31234
31279
  }
31235
31280
  });
31236
31281
 
31282
+ // server/drivers/codex-approval-presets.ts
31283
+ function namedApprovalParams(mode) {
31284
+ if (mode === "plan") return {
31285
+ thread: { approvalPolicy: "never", approvalsReviewer: "user", sandbox: "read-only" },
31286
+ turn: { approvalPolicy: "never", approvalsReviewer: "user", sandboxPolicy: { type: "readOnly" } }
31287
+ };
31288
+ if (mode === "full") {
31289
+ return {
31290
+ thread: {
31291
+ approvalPolicy: "never",
31292
+ approvalsReviewer: "user",
31293
+ sandbox: "danger-full-access"
31294
+ },
31295
+ turn: {
31296
+ approvalPolicy: "never",
31297
+ approvalsReviewer: "user",
31298
+ sandboxPolicy: { type: "dangerFullAccess" }
31299
+ }
31300
+ };
31301
+ }
31302
+ return {
31303
+ thread: {
31304
+ approvalPolicy: "on-request",
31305
+ approvalsReviewer: mode === "auto" ? "auto_review" : "user",
31306
+ sandbox: "workspace-write"
31307
+ },
31308
+ turn: {
31309
+ approvalPolicy: "on-request",
31310
+ approvalsReviewer: mode === "auto" ? "auto_review" : "user",
31311
+ sandboxPolicy: { type: "workspaceWrite" }
31312
+ }
31313
+ };
31314
+ }
31315
+ var init_codex_approval_presets = __esm({
31316
+ "server/drivers/codex-approval-presets.ts"() {
31317
+ "use strict";
31318
+ }
31319
+ });
31320
+
31237
31321
  // server/drivers/codex-tool-surface.ts
31238
31322
  function codexToolSurfaceArgs() {
31239
31323
  return [
@@ -31393,8 +31477,8 @@ function parseTomlLite(text4) {
31393
31477
  if (raw) return raw;
31394
31478
  throw new TomlError("expected a value");
31395
31479
  };
31396
- const tableAt = (base3, path4) => {
31397
- let table = base3;
31480
+ const tableAt = (base2, path4) => {
31481
+ let table = base2;
31398
31482
  for (const part of path4) {
31399
31483
  const next = table[part];
31400
31484
  if (next && typeof next === "object" && !Array.isArray(next)) table = next;
@@ -31406,8 +31490,8 @@ function parseTomlLite(text4) {
31406
31490
  }
31407
31491
  return table;
31408
31492
  };
31409
- function assign(base3, path4, item) {
31410
- const parent = tableAt(base3, path4.slice(0, -1));
31493
+ function assign(base2, path4, item) {
31494
+ const parent = tableAt(base2, path4.slice(0, -1));
31411
31495
  parent[path4[path4.length - 1]] = item;
31412
31496
  }
31413
31497
  while (pos < text4.length) {
@@ -32775,34 +32859,6 @@ function additionalPermissionSummary(permissions, reason) {
32775
32859
  const prefix = typeof reason === "string" && reason.trim() ? `${reason.trim()} \u2014 ` : "";
32776
32860
  return `${prefix}Requested permissions: ${exact}`;
32777
32861
  }
32778
- function namedApprovalParams(mode) {
32779
- if (mode === "full") {
32780
- return {
32781
- thread: {
32782
- approvalPolicy: "never",
32783
- approvalsReviewer: "user",
32784
- sandbox: "danger-full-access"
32785
- },
32786
- turn: {
32787
- approvalPolicy: "never",
32788
- approvalsReviewer: "user",
32789
- sandboxPolicy: { type: "dangerFullAccess" }
32790
- }
32791
- };
32792
- }
32793
- return {
32794
- thread: {
32795
- approvalPolicy: "on-request",
32796
- approvalsReviewer: mode === "auto" ? "auto_review" : "user",
32797
- sandbox: "workspace-write"
32798
- },
32799
- turn: {
32800
- approvalPolicy: "on-request",
32801
- approvalsReviewer: mode === "auto" ? "auto_review" : "user",
32802
- sandboxPolicy: { type: "workspaceWrite" }
32803
- }
32804
- };
32805
- }
32806
32862
  function withWritableRoots(turnParams, dirs) {
32807
32863
  if (!dirs?.length) return turnParams;
32808
32864
  const policy = plainRecord2(turnParams.sandboxPolicy);
@@ -32975,6 +33031,7 @@ var DRIVER_KIND3, ASTRA_MODEL_ID, ASTRA_MIN_CODEX_VERSION, CodexRpcError, QUESTI
32975
33031
  var init_codex = __esm({
32976
33032
  "server/drivers/codex.ts"() {
32977
33033
  "use strict";
33034
+ init_codex_approval_presets();
32978
33035
  init_codex_tool_surface();
32979
33036
  init_codex_image();
32980
33037
  init_codex_personal();
@@ -33078,7 +33135,7 @@ var init_codex = __esm({
33078
33135
  const emit = (event) => {
33079
33136
  for (const l of Array.from(listeners)) l(event);
33080
33137
  };
33081
- const base3 = (threadId, turnId) => ({
33138
+ const base2 = (threadId, turnId) => ({
33082
33139
  eventId: newEventId(),
33083
33140
  provider: DRIVER_KIND3,
33084
33141
  threadId,
@@ -33106,6 +33163,7 @@ var init_codex = __esm({
33106
33163
  const stopSignal = new AbortController();
33107
33164
  const { threadId } = turn;
33108
33165
  const approvalMode = turn.approvalMode ?? (config2.fullAuto ? "full" : "ask");
33166
+ if (approvalMode === "plan") turn = { ...turn, integrations: void 0, mcpFromUserConfig: false };
33109
33167
  const customServers = {};
33110
33168
  for (const [name, server] of Object.entries(turn.integrations?.custom ?? {})) {
33111
33169
  const reserved = "url" in server ? void 0 : Object.entries(server.env).find(([key, value]) => isHarnessOwnedMcpEnvName(key) && !(key === "ELECTRON_RUN_AS_NODE" && value === "1"))?.[0];
@@ -33143,7 +33201,7 @@ var init_codex = __esm({
33143
33201
  const declaredInCodexConfig = codexConfigMcpServers(env, childCwd);
33144
33202
  if (!turn.mcpFromUserConfig) {
33145
33203
  const harnessMounts = /* @__PURE__ */ new Set(["openmausbot_connectors", "atomic_accounts", "agents", "computer", "browser", "openmausbot_phone"]);
33146
- appServerArgs.push(...codexUserMcpOffArgs(new Map([...declaredInCodexConfig].filter(([name]) => !harnessMounts.has(name)))));
33204
+ appServerArgs.push(...codexUserMcpOffArgs(new Map([...declaredInCodexConfig].filter(([name]) => approvalMode === "plan" || !harnessMounts.has(name)))));
33147
33205
  appServerArgs.push(...codexPersonalSetupOffArgs(env, childCwd));
33148
33206
  }
33149
33207
  const customByMountName = /* @__PURE__ */ new Map();
@@ -33172,6 +33230,7 @@ var init_codex = __esm({
33172
33230
  `${prefix}.default_tools_approval_mode="auto"`
33173
33231
  );
33174
33232
  }
33233
+ if (approvalMode === "plan") appServerArgs.push("-c", 'web_search="disabled"', "-c", "features.apps=false", "-c", "features.multi_agent=false");
33175
33234
  const child = spawnCli(config2.cli, appServerArgs, {
33176
33235
  cwd: childCwd,
33177
33236
  env,
@@ -33282,11 +33341,11 @@ var init_codex = __esm({
33282
33341
  const complete = () => {
33283
33342
  if (active.get(threadId)?.stop !== stop) return;
33284
33343
  active.delete(threadId);
33285
- emit({ ...base3(threadId, turnId), type: "turn.completed", ok, stopReason, cost: null, ...state2.usage ? { usage: state2.usage } : {} });
33344
+ emit({ ...base2(threadId, turnId), type: "turn.completed", ok, stopReason, cost: null, ...state2.usage ? { usage: state2.usage } : {} });
33286
33345
  };
33287
33346
  completeStoppedTurn = complete;
33288
33347
  if (!await stop()) {
33289
- emit({ ...base3(threadId, turnId), type: "runtime.error", message: "codex did not shut down after termination was requested" });
33348
+ emit({ ...base2(threadId, turnId), type: "runtime.error", message: "codex did not shut down after termination was requested" });
33290
33349
  }
33291
33350
  };
33292
33351
  const steerActiveTurn = async (text4) => {
@@ -33348,6 +33407,10 @@ var init_codex = __esm({
33348
33407
  const mcpToolRef = customServer && mcpTool ? { server: customServer, tool: mcpTool, ...toolAnnotations(params?._meta?.annotations) ? { annotations: toolAnnotations(params._meta.annotations) } : {} } : void 0;
33349
33408
  const tool2 = serverConfirm ? elicitationTool(customByMountName.get(params.serverName)) : mcpAppApproval ? mcpAppApproval.tool : isLegacyMcpPermission ? mcpTool ?? "mcp" : isAdditionalPermission ? "permissions" : method === "item/fileChange/requestApproval" || method === "applyPatchApproval" ? "edit" : isQuestion ? "ask_user" : "shell";
33350
33409
  const permissionResult = (allow) => serverConfirm ? elicitationAnswer(serverConfirm, allow) : isMcpPermission ? allow ? mcpAppApproval?.allowResult ?? { action: "accept", content: {} } : { action: "decline" } : isAdditionalPermission ? { permissions: allow ? grantedPermissions(params.permissions) : {}, scope: "turn" } : { decision: allow ? legacy ? "approved" : "accept" : legacy ? "denied" : "decline" };
33410
+ if (approvalMode === "plan" && isPermission) {
33411
+ send({ jsonrpc: "2.0", id: msg.id, result: permissionResult(false) });
33412
+ return;
33413
+ }
33351
33414
  if (autoAcceptPermissions && isPermission && !serverConfirm) {
33352
33415
  return send({
33353
33416
  jsonrpc: "2.0",
@@ -33375,7 +33438,7 @@ var init_codex = __esm({
33375
33438
  result: serverConfirm && source2 !== "user" ? { action: "cancel" } : permissionResult(behavior === "allow")
33376
33439
  });
33377
33440
  }
33378
- emit({ ...base3(threadId, turnId), type: "request.resolved", requestId, behavior, source: source2 });
33441
+ emit({ ...base2(threadId, turnId), type: "request.resolved", requestId, behavior, source: source2 });
33379
33442
  };
33380
33443
  const timer = setTimeout(
33381
33444
  () => isQuestion ? finish("answer", QUESTION_TIMEOUT_NOTE2, "timeout") : finish("deny", DENY_TIMEOUT_NOTE2, "timeout"),
@@ -33384,7 +33447,7 @@ var init_codex = __esm({
33384
33447
  timer.unref?.();
33385
33448
  asks.set(requestId, finish);
33386
33449
  emit({
33387
- ...base3(threadId, turnId),
33450
+ ...base2(threadId, turnId),
33388
33451
  type: "request.opened",
33389
33452
  requestId,
33390
33453
  requestType: isQuestion ? "question" : "permission",
@@ -33427,14 +33490,14 @@ var init_codex = __esm({
33427
33490
  const delta = typeof p3.delta === "string" ? p3.delta : "";
33428
33491
  if (delta) {
33429
33492
  state2.sawStreamDelta = true;
33430
- emit({ ...base3(threadId, turnId), type: "content.delta", streamKind: "assistant_text", delta });
33493
+ emit({ ...base2(threadId, turnId), type: "content.delta", streamKind: "assistant_text", delta });
33431
33494
  }
33432
33495
  break;
33433
33496
  }
33434
33497
  case "item/reasoning/textDelta":
33435
33498
  case "item/reasoning/summaryTextDelta": {
33436
33499
  const delta = typeof p3.delta === "string" ? p3.delta : "";
33437
- if (delta) emit({ ...base3(threadId, turnId), type: "content.delta", streamKind: "reasoning_text", delta });
33500
+ if (delta) emit({ ...base2(threadId, turnId), type: "content.delta", streamKind: "reasoning_text", delta });
33438
33501
  break;
33439
33502
  }
33440
33503
  case "item/started": {
@@ -33442,7 +33505,7 @@ var init_codex = __esm({
33442
33505
  const title = item.type === "commandExecution" ? String(item.command ?? "shell") : item.type === "fileChange" ? "edit" : item.type === "mcpToolCall" ? typeof item.server === "string" && item.server && typeof item.tool === "string" && item.tool ? `mcp__${item.server}__${item.tool}` : item.tool ?? item.name ?? "mcp" : item.type === "webSearch" ? "web_search" : null;
33443
33506
  if (title) {
33444
33507
  emit({
33445
- ...base3(threadId, turnId),
33508
+ ...base2(threadId, turnId),
33446
33509
  type: "item.started",
33447
33510
  itemType: "tool",
33448
33511
  itemId: item.id,
@@ -33459,15 +33522,15 @@ var init_codex = __esm({
33459
33522
  if (item.text?.trim()) {
33460
33523
  state2.lastText = item.text;
33461
33524
  if (!state2.sawStreamDelta) {
33462
- emit({ ...base3(threadId, turnId), type: "content.delta", streamKind: "assistant_text", delta: item.text });
33525
+ emit({ ...base2(threadId, turnId), type: "content.delta", streamKind: "assistant_text", delta: item.text });
33463
33526
  }
33464
33527
  state2.sawStreamDelta = false;
33465
- emit({ ...base3(threadId, turnId), type: "item.completed", itemType: "assistant_text", text: item.text });
33528
+ emit({ ...base2(threadId, turnId), type: "item.completed", itemType: "assistant_text", text: item.text });
33466
33529
  }
33467
33530
  } else if (item.type === "imageGeneration" && item.status !== "failed") {
33468
33531
  if (typeof item.result === "string" && item.result.trim()) {
33469
33532
  emit({
33470
- ...base3(threadId, turnId),
33533
+ ...base2(threadId, turnId),
33471
33534
  type: "item.completed",
33472
33535
  itemType: "assistant_image",
33473
33536
  itemId: item.id,
@@ -33477,7 +33540,7 @@ var init_codex = __esm({
33477
33540
  }
33478
33541
  } else if (["commandExecution", "fileChange", "mcpToolCall", "webSearch"].includes(item.type)) {
33479
33542
  emit({
33480
- ...base3(threadId, turnId),
33543
+ ...base2(threadId, turnId),
33481
33544
  type: "item.completed",
33482
33545
  itemType: "tool",
33483
33546
  itemId: item.id,
@@ -33486,11 +33549,11 @@ var init_codex = __esm({
33486
33549
  });
33487
33550
  if (item.type === "mcpToolCall") {
33488
33551
  for (const img of extractMcpImages(item.result)) {
33489
- emit({ ...base3(threadId, turnId), type: "item.completed", itemType: "assistant_image", data: img.data });
33552
+ emit({ ...base2(threadId, turnId), type: "item.completed", itemType: "assistant_image", data: img.data });
33490
33553
  }
33491
33554
  }
33492
33555
  } else if (item.type === "reasoning") {
33493
- emit({ ...base3(threadId, turnId), type: "item.updated", itemType: "reasoning", tokens: null });
33556
+ emit({ ...base2(threadId, turnId), type: "item.updated", itemType: "reasoning", tokens: null });
33494
33557
  }
33495
33558
  break;
33496
33559
  }
@@ -33516,7 +33579,7 @@ var init_codex = __esm({
33516
33579
  if (t2) {
33517
33580
  const window = p3.tokenUsage?.modelContextWindow;
33518
33581
  emit({
33519
- ...base3(threadId, turnId),
33582
+ ...base2(threadId, turnId),
33520
33583
  type: "thread.token-usage.updated",
33521
33584
  input: t2.inputTokens ?? 0,
33522
33585
  output: t2.outputTokens ?? 0,
@@ -33535,7 +33598,7 @@ var init_codex = __esm({
33535
33598
  if (t2.status !== "completed" && message2 && message2 !== state2.lastError) {
33536
33599
  state2.lastError = message2;
33537
33600
  emit({
33538
- ...base3(threadId, turnId),
33601
+ ...base2(threadId, turnId),
33539
33602
  type: "runtime.error",
33540
33603
  message: message2,
33541
33604
  ...failure === "auth_failed" || classifyError({ text: message2 }).reason === "auth_failed" ? { setup: true } : {},
@@ -33554,7 +33617,7 @@ var init_codex = __esm({
33554
33617
  if (status === 401 || status === 403) {
33555
33618
  const said = typeof p3.error?.additionalDetails === "string" && p3.error.additionalDetails ? p3.error.additionalDetails : `unexpected status ${status} Unauthorized`;
33556
33619
  state2.lastError = said;
33557
- emit({ ...base3(threadId, turnId), type: "runtime.error", message: said, failure: "auth_failed", setup: true });
33620
+ emit({ ...base2(threadId, turnId), type: "runtime.error", message: said, failure: "auth_failed", setup: true });
33558
33621
  void settle(false, "auth_failed");
33559
33622
  }
33560
33623
  break;
@@ -33562,7 +33625,7 @@ var init_codex = __esm({
33562
33625
  if (message2) {
33563
33626
  state2.lastError = withCodexReset(String(message2), p3.error?.codexErrorInfo ?? p3.codexErrorInfo);
33564
33627
  const failure = engineFailureFromCodexInfo(p3.error?.codexErrorInfo ?? p3.codexErrorInfo);
33565
- emit({ ...base3(threadId, turnId), type: "runtime.error", message: state2.lastError, ...failure ? { failure } : {} });
33628
+ emit({ ...base2(threadId, turnId), type: "runtime.error", message: state2.lastError, ...failure ? { failure } : {} });
33566
33629
  }
33567
33630
  }
33568
33631
  break;
@@ -33611,7 +33674,7 @@ var init_codex = __esm({
33611
33674
  });
33612
33675
  child.on("error", (e) => {
33613
33676
  if (abandoned) return;
33614
- emit({ ...base3(threadId, turnId), type: "runtime.error", ...describeSpawnFailure(e, config2.cli) });
33677
+ emit({ ...base2(threadId, turnId), type: "runtime.error", ...describeSpawnFailure(e, config2.cli) });
33615
33678
  void settle(false, "spawn_error");
33616
33679
  });
33617
33680
  child.on("close", (code, signal) => {
@@ -33631,7 +33694,7 @@ var init_codex = __esm({
33631
33694
  const delayMs = computeBackoff(attempt);
33632
33695
  attempt++;
33633
33696
  emit({
33634
- ...base3(threadId, turnId),
33697
+ ...base2(threadId, turnId),
33635
33698
  type: "turn.retrying",
33636
33699
  attempt,
33637
33700
  delayMs,
@@ -33656,14 +33719,14 @@ var init_codex = __esm({
33656
33719
  return;
33657
33720
  }
33658
33721
  emit({
33659
- ...base3(threadId, turnId),
33722
+ ...base2(threadId, turnId),
33660
33723
  type: "runtime.error",
33661
33724
  message: `codex exited ${code}${signal ? ` (signal ${signal})` : ""} before turn/completed${recentStderr ? `: ${recentStderr.slice(-300)}` : hadStreamedOutput && stderr.trim() ? "; no stderr after the last app-server output" : ""}`
33662
33725
  });
33663
33726
  void settle(false, "exit_before_result");
33664
33727
  });
33665
33728
  active.set(threadId, { stop, turnId, asks, steer: steerActiveTurn });
33666
- if (attempt === 0) emit({ ...base3(threadId, turnId), type: "turn.started" });
33729
+ if (attempt === 0) emit({ ...base2(threadId, turnId), type: "turn.started" });
33667
33730
  try {
33668
33731
  await request("initialize", {
33669
33732
  clientInfo: { name: "openmausbot", version: "1" },
@@ -33755,7 +33818,7 @@ var init_codex = __esm({
33755
33818
  }
33756
33819
  if (!codexThreadId) throw new Error("Codex did not return a native thread id");
33757
33820
  await syncCodexInstructions(threadId, codexThreadId, developerInstructions, resumedNativeThread, request);
33758
- emit({ ...base3(threadId, turnId), type: "session.started", sessionId: codexThreadId, model: startedModel ?? turn.model ?? null, modelEvidenceLevel: startedModel ? "session-reported" : "requested-only", ...rebuiltFromReplay ? { rebuilt: true } : {} });
33821
+ emit({ ...base2(threadId, turnId), type: "session.started", sessionId: codexThreadId, model: startedModel ?? turn.model ?? null, modelEvidenceLevel: startedModel ? "session-reported" : "requested-only", ...rebuiltFromReplay ? { rebuilt: true } : {} });
33759
33822
  const note = turn.systemStable !== void 0 ? volatileNoteFor(threadId, codexThreadId, !resumedNativeThread || rebuiltFromReplay, turn) : "";
33760
33823
  const inputText = withNote(promptText, note);
33761
33824
  const turnInput = [
@@ -33802,7 +33865,7 @@ var init_codex = __esm({
33802
33865
  const delayMs = computeBackoff(attempt);
33803
33866
  attempt++;
33804
33867
  emit({
33805
- ...base3(threadId, turnId),
33868
+ ...base2(threadId, turnId),
33806
33869
  type: "turn.retrying",
33807
33870
  attempt,
33808
33871
  delayMs,
@@ -33824,7 +33887,7 @@ var init_codex = __esm({
33824
33887
  }
33825
33888
  if (!state2.settled && !abandoned) {
33826
33889
  emit({
33827
- ...base3(threadId, turnId),
33890
+ ...base2(threadId, turnId),
33828
33891
  type: "runtime.error",
33829
33892
  message: message2,
33830
33893
  ...needsAuth ? { setup: true } : {}
@@ -37771,10 +37834,10 @@ var require_fast_uri = __commonJS({
37771
37834
  schemelessOptions.skipEscape = true;
37772
37835
  return serialize(resolved, schemelessOptions);
37773
37836
  }
37774
- function resolveComponent(base3, relative3, options, skipNormalization) {
37837
+ function resolveComponent(base2, relative3, options, skipNormalization) {
37775
37838
  const target = {};
37776
37839
  if (!skipNormalization) {
37777
- base3 = parse4(serialize(base3, options), options);
37840
+ base2 = parse4(serialize(base2, options), options);
37778
37841
  relative3 = parse4(serialize(relative3, options), options);
37779
37842
  }
37780
37843
  options = options || {};
@@ -37794,32 +37857,32 @@ var require_fast_uri = __commonJS({
37794
37857
  target.query = relative3.query;
37795
37858
  } else {
37796
37859
  if (!relative3.path) {
37797
- target.path = base3.path;
37860
+ target.path = base2.path;
37798
37861
  if (relative3.query !== void 0) {
37799
37862
  target.query = relative3.query;
37800
37863
  } else {
37801
- target.query = base3.query;
37864
+ target.query = base2.query;
37802
37865
  }
37803
37866
  } else {
37804
37867
  if (relative3.path[0] === "/") {
37805
37868
  target.path = removeDotSegments(relative3.path);
37806
37869
  } else {
37807
- if ((base3.userinfo !== void 0 || base3.host !== void 0 || base3.port !== void 0) && !base3.path) {
37870
+ if ((base2.userinfo !== void 0 || base2.host !== void 0 || base2.port !== void 0) && !base2.path) {
37808
37871
  target.path = "/" + relative3.path;
37809
- } else if (!base3.path) {
37872
+ } else if (!base2.path) {
37810
37873
  target.path = relative3.path;
37811
37874
  } else {
37812
- target.path = base3.path.slice(0, base3.path.lastIndexOf("/") + 1) + relative3.path;
37875
+ target.path = base2.path.slice(0, base2.path.lastIndexOf("/") + 1) + relative3.path;
37813
37876
  }
37814
37877
  target.path = removeDotSegments(target.path);
37815
37878
  }
37816
37879
  target.query = relative3.query;
37817
37880
  }
37818
- target.userinfo = base3.userinfo;
37819
- target.host = base3.host;
37820
- target.port = base3.port;
37881
+ target.userinfo = base2.userinfo;
37882
+ target.host = base2.host;
37883
+ target.port = base2.port;
37821
37884
  }
37822
- target.scheme = base3.scheme;
37885
+ target.scheme = base2.scheme;
37823
37886
  }
37824
37887
  target.fragment = relative3.fragment;
37825
37888
  return target;
@@ -43142,11 +43205,11 @@ async function mountChatTools(integrations, signal, computerUse = false, report
43142
43205
  }
43143
43206
  }
43144
43207
  const description = (typeof tool2.description === "string" ? tool2.description : "Configured MCP tool") + (Object.keys(constraints).length ? " Additional argument constraints (validated before execution): " + JSON.stringify(constraints) : "");
43145
- const base3 = `${server}_${tool2.name}`.toLowerCase().replace(/[^a-z0-9_]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 64) || "mcp_tool";
43146
- let name = base3;
43208
+ const base2 = `${server}_${tool2.name}`.toLowerCase().replace(/[^a-z0-9_]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 64) || "mcp_tool";
43209
+ let name = base2;
43147
43210
  for (let index = 2; registered.has(name); index += 1) {
43148
43211
  const suffix = `_${index}`;
43149
- name = base3.slice(0, 64 - suffix.length) + suffix;
43212
+ name = base2.slice(0, 64 - suffix.length) + suffix;
43150
43213
  }
43151
43214
  registered.set(name, { client, server, name: tool2.name, schema: schema4 });
43152
43215
  if (customNames.has(server)) customTools.set(name, { server, tool: tool2.name, annotations: toolAnnotations(tool2.annotations) ?? {} });
@@ -43166,6 +43229,10 @@ async function mountChatTools(integrations, signal, computerUse = false, report
43166
43229
  validate,
43167
43230
  close,
43168
43231
  customTool: (name) => customTools.get(name),
43232
+ planReadOnly: (name) => {
43233
+ const tool2 = registered.get(name);
43234
+ return tool2?.client instanceof ChatBoxClient && ["screenshot", "get_screen_size"].includes(tool2.name);
43235
+ },
43169
43236
  async execute(name, args, callSignal) {
43170
43237
  validate(name, args);
43171
43238
  if (callSignal.aborted) {
@@ -43556,7 +43623,7 @@ function createOpenAIChatRuntime(options) {
43556
43623
  const emit = (event) => {
43557
43624
  for (const listener of Array.from(listeners)) listener(event);
43558
43625
  };
43559
- const base3 = (threadId, turnId) => ({
43626
+ const base2 = (threadId, turnId) => ({
43560
43627
  eventId: newEventId(),
43561
43628
  provider: options.driverKind,
43562
43629
  threadId,
@@ -43750,7 +43817,7 @@ function createOpenAIChatRuntime(options) {
43750
43817
  const approval = createChatToolApproval({
43751
43818
  signal: abort.signal,
43752
43819
  open: (ask) => emit({
43753
- ...base3(turn.threadId, turnId),
43820
+ ...base2(turn.threadId, turnId),
43754
43821
  type: "request.opened",
43755
43822
  requestType: "permission",
43756
43823
  requestId: ask.id,
@@ -43760,7 +43827,7 @@ function createOpenAIChatRuntime(options) {
43760
43827
  ...ask.mcpTool ? { mcpTool: ask.mcpTool } : {}
43761
43828
  }),
43762
43829
  resolved: (ask, allowed, source2) => emit({
43763
- ...base3(turn.threadId, turnId),
43830
+ ...base2(turn.threadId, turnId),
43764
43831
  type: "request.resolved",
43765
43832
  requestId: ask.id,
43766
43833
  behavior: allowed ? "allow" : "deny",
@@ -43772,8 +43839,8 @@ function createOpenAIChatRuntime(options) {
43772
43839
  resolveDone = resolve14;
43773
43840
  });
43774
43841
  active.set(turn.threadId, { abort, turnId, done, approval });
43775
- emit({ ...base3(turn.threadId, turnId), type: "turn.started" });
43776
- emit({ ...base3(turn.threadId, turnId), type: "session.started", sessionId: null, model });
43842
+ emit({ ...base2(turn.threadId, turnId), type: "turn.started" });
43843
+ emit({ ...base2(turn.threadId, turnId), type: "session.started", sessionId: null, model });
43777
43844
  void (async () => {
43778
43845
  let tools;
43779
43846
  const usage = { input: 0, output: 0 };
@@ -43808,11 +43875,11 @@ function createOpenAIChatRuntime(options) {
43808
43875
  }
43809
43876
  pending[streamKind] = hold ? combined.slice(-hold) : "";
43810
43877
  const visible = safeText(hold ? combined.slice(0, -hold) : combined);
43811
- if (visible) emit({ ...base3(turn.threadId, turnId), type: "content.delta", streamKind, delta: visible });
43878
+ if (visible) emit({ ...base2(turn.threadId, turnId), type: "content.delta", streamKind, delta: visible });
43812
43879
  };
43813
43880
  try {
43814
43881
  const callId = newId();
43815
- emit({ ...base3(turn.threadId, turnId), type: "model.call", callId });
43882
+ emit({ ...base2(turn.threadId, turnId), type: "model.call", callId });
43816
43883
  let previousIdentity = "";
43817
43884
  completion = await complete(messages, model, true, abort.signal, (text4, streamKind) => {
43818
43885
  streamed = true;
@@ -43821,7 +43888,7 @@ function createOpenAIChatRuntime(options) {
43821
43888
  const identity = JSON.stringify([reportedModel, responseId]);
43822
43889
  if (identity === previousIdentity) return;
43823
43890
  previousIdentity = identity;
43824
- emit({ ...base3(turn.threadId, turnId), type: "model.evidence", callId, model: reportedModel, responseId, level: reportedModel ? "response-reported" : "unknown" });
43891
+ emit({ ...base2(turn.threadId, turnId), type: "model.evidence", callId, model: reportedModel, responseId, level: reportedModel ? "response-reported" : "unknown" });
43825
43892
  });
43826
43893
  delta("", "assistant_text", true);
43827
43894
  delta("", "reasoning_text", true);
@@ -43831,7 +43898,7 @@ function createOpenAIChatRuntime(options) {
43831
43898
  const verdict = classifyError(error51);
43832
43899
  if (options.retryScale === void 0 || abort.signal.aborted || streamed || seenCalls.size || error51 instanceof ChatProtocolError || !verdict.transient || attempt >= RETRY_MAX_ATTEMPTS - 1) throw error51;
43833
43900
  const delayMs = computeBackoff(attempt++);
43834
- emit({ ...base3(turn.threadId, turnId), type: "turn.retrying", attempt, delayMs, reason: verdict.reason });
43901
+ emit({ ...base2(turn.threadId, turnId), type: "turn.retrying", attempt, delayMs, reason: verdict.reason });
43835
43902
  await interruptibleDelay(delayMs * options.retryScale, abort.signal).promise;
43836
43903
  abort.signal.throwIfAborted();
43837
43904
  }
@@ -43841,10 +43908,10 @@ function createOpenAIChatRuntime(options) {
43841
43908
  usage.input += completion.usage.input;
43842
43909
  usage.output += completion.usage.output;
43843
43910
  hasUsage = true;
43844
- emit({ ...base3(turn.threadId, turnId), type: "thread.token-usage.updated", ...usage });
43911
+ emit({ ...base2(turn.threadId, turnId), type: "thread.token-usage.updated", ...usage });
43845
43912
  }
43846
43913
  const reply = completion.text.trim() ? completion.text : completion.toolCalls.length ? "" : completion.reasoning;
43847
- if (reply.trim()) emit({ ...base3(turn.threadId, turnId), type: "item.completed", itemType: "assistant_text", text: safeText(reply) });
43914
+ if (reply.trim()) emit({ ...base2(turn.threadId, turnId), type: "item.completed", itemType: "assistant_text", text: safeText(reply) });
43848
43915
  abort.signal.throwIfAborted();
43849
43916
  if (!completion.toolCalls.length) {
43850
43917
  if (!reply.trim()) throw new ChatProtocolError("provider returned an empty response");
@@ -43887,10 +43954,10 @@ function createOpenAIChatRuntime(options) {
43887
43954
  if (!object3(args)) throw new ChatProtocolError("tool arguments must be a JSON object");
43888
43955
  tools.validate(call.function.name, args);
43889
43956
  const inputPreview = preview(args);
43890
- const allowed = turn.approvalMode === "full" || await approval.ask(call.function.name, inputPreview ?? "This tool has no arguments.", tools.customTool?.(call.function.name));
43957
+ const allowed = (turn.approvalMode !== "plan" || tools.planReadOnly?.(call.function.name) === true) && (turn.approvalMode === "full" || await approval.ask(call.function.name, inputPreview ?? "This tool has no arguments.", tools.customTool?.(call.function.name)));
43891
43958
  abort.signal.throwIfAborted();
43892
43959
  emit({
43893
- ...base3(turn.threadId, turnId),
43960
+ ...base2(turn.threadId, turnId),
43894
43961
  type: "item.started",
43895
43962
  itemType: "tool",
43896
43963
  itemId: call.id,
@@ -43910,16 +43977,16 @@ function createOpenAIChatRuntime(options) {
43910
43977
  }
43911
43978
  } else {
43912
43979
  denials.push(call.function.name);
43913
- result = { ok: false, text: "Permission denied or expired; the tool was not executed." };
43980
+ result = { ok: false, text: turn.approvalMode === "plan" && tools.planReadOnly?.(call.function.name) !== true ? PLAN_REFUSAL : "Permission denied or expired; the tool was not executed." };
43914
43981
  }
43915
43982
  } catch (error51) {
43916
43983
  if (error51 instanceof ChatToolSessionError) fatal = error51;
43917
43984
  result = { ok: false, text: abort.signal.aborted ? "Tool interrupted; an operation already dispatched may have taken effect. Verify its state before retrying." : safeText(asError(error51).message).slice(0, 2e3) };
43918
43985
  }
43919
- if (!started) emit({ ...base3(turn.threadId, turnId), type: "item.started", itemType: "tool", itemId: call.id, title: call.function.name });
43986
+ if (!started) emit({ ...base2(turn.threadId, turnId), type: "item.started", itemType: "tool", itemId: call.id, title: call.function.name });
43920
43987
  const text4 = safeText(result.text);
43921
43988
  const output = preview({ ok: result.ok, result: text4 });
43922
- emit({ ...base3(turn.threadId, turnId), type: "item.completed", itemType: "tool", itemId: call.id, ok: result.ok, output });
43989
+ emit({ ...base2(turn.threadId, turnId), type: "item.completed", itemType: "tool", itemId: call.id, ok: result.ok, output });
43923
43990
  if (!result.ok) toolFailed = true;
43924
43991
  messages.push({ role: "tool", tool_call_id: call.id, content: JSON.stringify({ ok: result.ok, result: text4 }) });
43925
43992
  screenshotParts.push(...chatToolImages(call.id, result.images));
@@ -43948,11 +44015,11 @@ function createOpenAIChatRuntime(options) {
43948
44015
  stopReason = "interrupted";
43949
44016
  }
43950
44017
  if (failure && (!abort.signal.aborted || cleanupFailed)) {
43951
- emit({ ...base3(turn.threadId, turnId), type: "runtime.error", message: failure, terminal: !abort.signal.aborted });
44018
+ emit({ ...base2(turn.threadId, turnId), type: "runtime.error", message: failure, terminal: !abort.signal.aborted });
43952
44019
  }
43953
44020
  active.delete(turn.threadId);
43954
44021
  emit({
43955
- ...base3(turn.threadId, turnId),
44022
+ ...base2(turn.threadId, turnId),
43956
44023
  type: "turn.completed",
43957
44024
  ok,
43958
44025
  stopReason,
@@ -44029,6 +44096,7 @@ var usageFrom, asError;
44029
44096
  var init_openai_chat = __esm({
44030
44097
  "server/drivers/openai-chat.ts"() {
44031
44098
  "use strict";
44099
+ init_plan_policy();
44032
44100
  init_contracts();
44033
44101
  init_redact2();
44034
44102
  init_tool_summary();
@@ -44149,15 +44217,15 @@ function unquote2(raw) {
44149
44217
  }
44150
44218
  return value;
44151
44219
  }
44152
- function readGrokModelCatalog(env = process.env, base3 = STATIC_GROK_MODELS) {
44220
+ function readGrokModelCatalog(env = process.env, base2 = STATIC_GROK_MODELS) {
44153
44221
  const path4 = join35(grokHome(env), "config.toml");
44154
44222
  let text4 = "";
44155
44223
  try {
44156
44224
  text4 = readFileSync19(path4, "utf8");
44157
44225
  } catch {
44158
- return base3;
44226
+ return base2;
44159
44227
  }
44160
- const options = base3.options.map((o) => ({ ...o }));
44228
+ const options = base2.options.map((o) => ({ ...o }));
44161
44229
  const seen = new Set(options.map((o) => o.id));
44162
44230
  let configuredDefault = null;
44163
44231
  let current = null;
@@ -44200,7 +44268,7 @@ function readGrokModelCatalog(env = process.env, base3 = STATIC_GROK_MODELS) {
44200
44268
  }
44201
44269
  flush();
44202
44270
  return {
44203
- default: configuredDefault && seen.has(configuredDefault) ? configuredDefault : base3.default,
44271
+ default: configuredDefault && seen.has(configuredDefault) ? configuredDefault : base2.default,
44204
44272
  options
44205
44273
  };
44206
44274
  }
@@ -44211,12 +44279,12 @@ function classifyGrokError(error51) {
44211
44279
  return classifyEngineFailure(text4)?.kind === "usage_limited" ? "quota_or_region_restriction" : void 0;
44212
44280
  }
44213
44281
  function suggestGrokSlug(host, model, taken) {
44214
- let base3 = `${host}-${model}`.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
44215
- if (!base3 || !/^[a-z]/.test(base3)) base3 = `m-${base3 || "model"}`;
44216
- let slug = base3;
44282
+ let base2 = `${host}-${model}`.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
44283
+ if (!base2 || !/^[a-z]/.test(base2)) base2 = `m-${base2 || "model"}`;
44284
+ let slug = base2;
44217
44285
  let n3 = 2;
44218
44286
  while (taken.has(slug)) {
44219
- slug = `${base3}-${n3}`;
44287
+ slug = `${base2}-${n3}`;
44220
44288
  n3 += 1;
44221
44289
  }
44222
44290
  return slug;
@@ -45150,15 +45218,15 @@ function resolveCursorAcpModelId(available, wanted) {
45150
45218
  if (!want) return null;
45151
45219
  const ids = available.filter((m3) => typeof m3?.modelId === "string" && m3.modelId);
45152
45220
  if (!ids.length) return null;
45153
- const base3 = (id) => id.split("[")[0].trim().toLowerCase();
45221
+ const base2 = (id) => id.split("[")[0].trim().toLowerCase();
45154
45222
  const exact = ids.find((m3) => m3.modelId.toLowerCase() === want);
45155
45223
  if (exact) return exact.modelId;
45156
- const byBase = ids.find((m3) => base3(m3.modelId) === want);
45224
+ const byBase = ids.find((m3) => base2(m3.modelId) === want);
45157
45225
  if (byBase) return byBase.modelId;
45158
45226
  const byName = ids.find((m3) => (m3.name ?? "").trim().toLowerCase() === want);
45159
45227
  if (byName) return byName.modelId;
45160
45228
  if (want === "auto" || want === "default") {
45161
- const dflt = ids.find((m3) => base3(m3.modelId) === "default");
45229
+ const dflt = ids.find((m3) => base2(m3.modelId) === "default");
45162
45230
  if (dflt) return dflt.modelId;
45163
45231
  }
45164
45232
  return null;
@@ -48738,25 +48806,25 @@ var init_atomic_engine_release = __esm({
48738
48806
  "use strict";
48739
48807
  ASSETS = [
48740
48808
  {
48741
- version: "0.21.4-7eb41cb",
48809
+ version: "0.21.4-4cc340e",
48742
48810
  platform: "darwin-arm64",
48743
- url: "https://ugmbfkqvogfuvufzjxtb.supabase.co/storage/v1/object/public/atomic-updates/atomic/K_5FMcrZFZ5AE2K-Lydn-Tis/atomic-engine-0.21.4-7eb41cb-darwin-arm64.tar.gz",
48744
- bytes: 114827855,
48745
- sha512: "357648850bc4042960fa0f1ef01415f9066c1beb445f5a0edd66a2f9c05e8fcf68b1d692ec6d81bb45c80b7d7dadedf828721d8020c6e12639de7730370177af"
48811
+ url: "https://ugmbfkqvogfuvufzjxtb.supabase.co/storage/v1/object/public/atomic-updates/atomic/K_5FMcrZFZ5AE2K-Lydn-Tis/atomic-engine-0.21.4-4cc340e-darwin-arm64.tar.gz",
48812
+ bytes: 115631334,
48813
+ sha512: "666626a535dce48f22087e26f7c14702dda4028b2c6759952017cffa73f37d86b16f5d8b6f81fb0efb535cb285ba4ddb6a5da3fd7e709ea0aa8b2b8c783e0c17"
48746
48814
  },
48747
48815
  {
48748
- version: "0.21.4-7eb41cb",
48816
+ version: "0.21.4-4cc340e",
48749
48817
  platform: "win32-x64",
48750
- url: "https://ugmbfkqvogfuvufzjxtb.supabase.co/storage/v1/object/public/atomic-updates/atomic/K_5FMcrZFZ5AE2K-Lydn-Tis/atomic-engine-0.21.4-7eb41cb-win32-x64.tar.gz",
48751
- bytes: 102379095,
48752
- sha512: "e3f3ff51513df37f79165e9408395d771f6136cfeb3207417741b421347f7f183209a2865c70fe3175a20dc35ef5bc409213bd3bbc5f8b614d7d08f2ff8edd07"
48818
+ url: "https://ugmbfkqvogfuvufzjxtb.supabase.co/storage/v1/object/public/atomic-updates/atomic/K_5FMcrZFZ5AE2K-Lydn-Tis/atomic-engine-0.21.4-4cc340e-win32-x64.tar.gz",
48819
+ bytes: 103355479,
48820
+ sha512: "4a074d1d2254ce697ed7ddf794b6d56e1f62374d1ae5f533f7fea12c7f18af46f47f33fd38f9e09d3ca048ef027882b6b271dd229f40a9ffcb7f3f23db5d1bb0"
48753
48821
  },
48754
48822
  {
48755
- version: "0.21.4-acc9ee6",
48823
+ version: "0.21.4-4cc340e",
48756
48824
  platform: "darwin-x64",
48757
- url: "https://ugmbfkqvogfuvufzjxtb.supabase.co/storage/v1/object/public/atomic-updates/atomic/K_5FMcrZFZ5AE2K-Lydn-Tis/atomic-engine-0.21.4-acc9ee6-darwin-x64.tar.gz",
48758
- bytes: 92108962,
48759
- sha512: "12c3fcba9ff17ec2a53fdbd39e8f14fb23786be256f2e19ce63ba011c2317231e58052b1b1b0a5bdcd7be2b23d9cf7edaeb267ad312e1ac862e128ac70f3aa00"
48825
+ url: "https://ugmbfkqvogfuvufzjxtb.supabase.co/storage/v1/object/public/atomic-updates/atomic/K_5FMcrZFZ5AE2K-Lydn-Tis/atomic-engine-0.21.4-4cc340e-darwin-x64.tar.gz",
48826
+ bytes: 92883251,
48827
+ sha512: "eec5fcbbcdbe44db76f938cb3dd98fedfc6c644cc054c75d0ad3daaf66e39f60dc6b75fa3f8cdbbc2cc3760a2ea7d3fc608a1cf7a42c4a020e6cd4d6dc823df0"
48760
48828
  }
48761
48829
  ];
48762
48830
  }
@@ -56352,7 +56420,43 @@ function customAcpTurnModel(model, env) {
56352
56420
  if (model && decodeInjectId(model)) ensureHermesInjectProvider(model, env, atomicEngineHome2(env));
56353
56421
  return model;
56354
56422
  }
56355
- var PASSTHROUGH, DEFAULT_ATOMIC_ENGINE_CLI, CONFIGURE_HINT, support11, base2, CustomAcpDriver;
56423
+ function customDriver(options) {
56424
+ const base2 = createAcpDriver(options);
56425
+ return {
56426
+ ...base2,
56427
+ // decodeAcpConfig never throws (a missing cli falls back to defaultCli,
56428
+ // which is empty here). Turn every blank-command path into the same
56429
+ // teaching shadow row instead of a bare spawn ENOENT at first send.
56430
+ decodeConfig(raw) {
56431
+ const decoded = base2.decodeConfig(raw);
56432
+ if (!decoded.cli.trim()) throw new Error(CONFIGURE_HINT);
56433
+ return decoded;
56434
+ },
56435
+ async create(input) {
56436
+ if (!input.config.cli.trim()) throw new Error(CONFIGURE_HINT);
56437
+ const instance = await base2.create(input);
56438
+ const asset = atomicEngineAsset();
56439
+ if (!asset || !isAtomicEngine(input.instanceId, input.config.cli) || input.config.cli.trim() !== DEFAULT_ATOMIC_ENGINE_CLI) return instance;
56440
+ const install = {
56441
+ managed: { label: "Install Atomic Engine", downloadBytes: asset.bytes }
56442
+ };
56443
+ return {
56444
+ ...instance,
56445
+ get models() {
56446
+ return instance.models;
56447
+ },
56448
+ install,
56449
+ installRuntime: async (onProgress) => {
56450
+ await installAtomicEngine({
56451
+ // "verifying" is part of the download as far as setup's progress bar goes
56452
+ onProgress: (progress) => onProgress?.({ ...progress, phase: progress.phase === "verifying" ? "downloading" : progress.phase })
56453
+ });
56454
+ }
56455
+ };
56456
+ }
56457
+ };
56458
+ }
56459
+ var PASSTHROUGH, DEFAULT_ATOMIC_ENGINE_CLI, CONFIGURE_HINT, support11, CustomAcpDriver, AtomicEngineDriver;
56356
56460
  var init_custom = __esm({
56357
56461
  "server/drivers/acp/custom.ts"() {
56358
56462
  "use strict";
@@ -56409,40 +56513,21 @@ var init_custom = __esm({
56409
56513
  },
56410
56514
  snapshot: (env, config2, instanceId) => customAcpSnapshot(env, config2.cli, instanceId)
56411
56515
  };
56412
- base2 = createAcpDriver(support11);
56413
- CustomAcpDriver = {
56414
- ...base2,
56415
- // decodeAcpConfig never throws (a missing cli falls back to defaultCli,
56416
- // which is empty here). Turn every blank-command path into the same
56417
- // teaching shadow row instead of a bare spawn ENOENT at first send.
56418
- decodeConfig(raw) {
56419
- const decoded = base2.decodeConfig(raw);
56420
- if (!decoded.cli.trim()) throw new Error(CONFIGURE_HINT);
56421
- return decoded;
56422
- },
56423
- async create(input) {
56424
- if (!input.config.cli.trim()) throw new Error(CONFIGURE_HINT);
56425
- const instance = await base2.create(input);
56426
- const asset = atomicEngineAsset();
56427
- if (!asset || !isAtomicEngine(input.instanceId, input.config.cli) || input.config.cli.trim() !== DEFAULT_ATOMIC_ENGINE_CLI) return instance;
56428
- const install = {
56429
- managed: { label: "Install Atomic Engine", downloadBytes: asset.bytes }
56430
- };
56431
- return {
56432
- ...instance,
56433
- get models() {
56434
- return instance.models;
56435
- },
56436
- install,
56437
- installRuntime: async (onProgress) => {
56438
- await installAtomicEngine({
56439
- // "verifying" is part of the download as far as setup's progress bar goes
56440
- onProgress: (progress) => onProgress?.({ ...progress, phase: progress.phase === "verifying" ? "downloading" : progress.phase })
56441
- });
56442
- }
56443
- };
56516
+ CustomAcpDriver = customDriver(support11);
56517
+ AtomicEngineDriver = customDriver({
56518
+ ...support11,
56519
+ driverKind: "atomicEngine",
56520
+ displayName: "Atomic Engine",
56521
+ defaultCli: DEFAULT_ATOMIC_ENGINE_CLI,
56522
+ async configureSession(context) {
56523
+ await support11.configureSession?.(context);
56524
+ const modeId = context.turn.approvalMode === "plan" ? "atomic-plan-v1" : "default";
56525
+ const reply = await context.request("session/set_mode", { sessionId: context.sessionId, modeId });
56526
+ if (modeId === "atomic-plan-v1" && (reply?._meta?.atomicPlanVersion !== 1 || reply?._meta?.mode !== modeId)) {
56527
+ throw new Error("Update Atomic Engine before using Plan: this build does not enforce read-only tools");
56528
+ }
56444
56529
  }
56445
- };
56530
+ });
56446
56531
  }
56447
56532
  });
56448
56533
 
@@ -57020,17 +57105,17 @@ var init_pi = __esm({
57020
57105
  const catalogEnv = piEnvironment({ ...process.env, ...input.environment });
57021
57106
  let models2 = EMPTY3;
57022
57107
  const readModels = async () => {
57023
- let base4 = models2;
57108
+ let base3 = models2;
57024
57109
  try {
57025
57110
  const resolved = await fetchPiModels(config2.cli, catalogEnv);
57026
- if (resolved.options.length) base4 = resolved;
57111
+ if (resolved.options.length) base3 = resolved;
57027
57112
  } catch {
57028
57113
  }
57029
57114
  try {
57030
- const next = await applyPiLocalCatalog(base4, catalogEnv);
57115
+ const next = await applyPiLocalCatalog(base3, catalogEnv);
57031
57116
  if (next.options.length) models2 = next;
57032
57117
  } catch {
57033
- if (base4.options.length) models2 = base4;
57118
+ if (base3.options.length) models2 = base3;
57034
57119
  }
57035
57120
  };
57036
57121
  const refreshModels = async () => {
@@ -57043,7 +57128,7 @@ var init_pi = __esm({
57043
57128
  const emit = (event) => {
57044
57129
  for (const l of Array.from(listeners)) l(event);
57045
57130
  };
57046
- const base3 = (threadId, turnId) => ({
57131
+ const base2 = (threadId, turnId) => ({
57047
57132
  eventId: newEventId(),
57048
57133
  provider: DRIVER_KIND6,
57049
57134
  providerInstanceId: instanceId,
@@ -57130,14 +57215,14 @@ var init_pi = __esm({
57130
57215
  const text4 = assistantText;
57131
57216
  assistantText = "";
57132
57217
  if (!text4.trim()) return;
57133
- emit({ ...base3(threadId, turnId), type: "item.completed", itemType: "assistant_text", text: text4 });
57218
+ emit({ ...base2(threadId, turnId), type: "item.completed", itemType: "assistant_text", text: text4 });
57134
57219
  };
57135
57220
  const settle = (ok, stopReason, usage) => {
57136
57221
  if (settled) return;
57137
57222
  settled = true;
57138
57223
  flushAssistantText();
57139
57224
  emit({
57140
- ...base3(threadId, turnId),
57225
+ ...base2(threadId, turnId),
57141
57226
  type: "turn.completed",
57142
57227
  ok,
57143
57228
  stopReason: stopReason ?? (ok ? "end_turn" : "failed"),
@@ -57189,16 +57274,16 @@ var init_pi = __esm({
57189
57274
  if (!e) return;
57190
57275
  if (e.type === "text_delta" && typeof e.delta === "string") {
57191
57276
  assistantText += e.delta;
57192
- emit({ ...base3(threadId, turnId), type: "content.delta", streamKind: "assistant_text", delta: e.delta });
57277
+ emit({ ...base2(threadId, turnId), type: "content.delta", streamKind: "assistant_text", delta: e.delta });
57193
57278
  } else if (e.type === "thinking_delta" && typeof e.delta === "string") {
57194
- emit({ ...base3(threadId, turnId), type: "content.delta", streamKind: "reasoning_text", delta: e.delta });
57279
+ emit({ ...base2(threadId, turnId), type: "content.delta", streamKind: "reasoning_text", delta: e.delta });
57195
57280
  }
57196
57281
  return;
57197
57282
  }
57198
57283
  case "tool_execution_start": {
57199
57284
  flushAssistantText();
57200
57285
  emit({
57201
- ...base3(threadId, turnId),
57286
+ ...base2(threadId, turnId),
57202
57287
  type: "item.started",
57203
57288
  itemType: "tool",
57204
57289
  itemId: evt.toolCallId,
@@ -57210,7 +57295,7 @@ var init_pi = __esm({
57210
57295
  }
57211
57296
  case "tool_execution_end": {
57212
57297
  emit({
57213
- ...base3(threadId, turnId),
57298
+ ...base2(threadId, turnId),
57214
57299
  type: "item.completed",
57215
57300
  itemType: "tool",
57216
57301
  itemId: evt.toolCallId,
@@ -57240,7 +57325,7 @@ var init_pi = __esm({
57240
57325
  mcpTempDir ? join47(mcpTempDir, PI_TOOL_HINTS_FILE) : null
57241
57326
  );
57242
57327
  emit({
57243
- ...base3(threadId, turnId),
57328
+ ...base2(threadId, turnId),
57244
57329
  requestId: reqId,
57245
57330
  type: "request.opened",
57246
57331
  requestType: isQuestion ? "question" : "permission",
@@ -57259,7 +57344,7 @@ var init_pi = __esm({
57259
57344
  const usage = evt.usage ?? evt.message?.usage;
57260
57345
  if (sr2 === "error" || sr2 === "failed") {
57261
57346
  emit({
57262
- ...base3(threadId, turnId),
57347
+ ...base2(threadId, turnId),
57263
57348
  type: "runtime.error",
57264
57349
  message: String(evt.message?.errorMessage ?? "pi turn failed").slice(0, 2e3)
57265
57350
  });
@@ -57290,14 +57375,14 @@ var init_pi = __esm({
57290
57375
  child.on("error", (err) => {
57291
57376
  const fail = describeSpawnFailure(err, config2.cli);
57292
57377
  rejectWaiters(new Error(fail.message));
57293
- emit({ ...base3(threadId, turnId), type: "runtime.error", message: fail.message, setup: fail.setup });
57378
+ emit({ ...base2(threadId, turnId), type: "runtime.error", message: fail.message, setup: fail.setup });
57294
57379
  settle(false);
57295
57380
  });
57296
57381
  child.on("close", () => {
57297
57382
  rejectWaiters(new Error("pi process exited before replying"));
57298
57383
  settle(false);
57299
57384
  });
57300
- emit({ ...base3(threadId, turnId), type: "turn.started" });
57385
+ emit({ ...base2(threadId, turnId), type: "turn.started" });
57301
57386
  const sessionPath = typeof turn.resumeCursor === "string" ? turn.resumeCursor : null;
57302
57387
  let sessionFile = sessionPath;
57303
57388
  try {
@@ -57307,7 +57392,7 @@ var init_pi = __esm({
57307
57392
  const hs2 = await hsPromise;
57308
57393
  if (hs2?.sessionFile) sessionFile = hs2.sessionFile;
57309
57394
  emit({
57310
- ...base3(threadId, turnId),
57395
+ ...base2(threadId, turnId),
57311
57396
  type: "session.started",
57312
57397
  sessionId: sessionFile ?? hs2?.sessionId ?? null,
57313
57398
  model: turn.model ?? null
@@ -57419,7 +57504,7 @@ ${turn.text}` : turn.text;
57419
57504
  entry.pending.delete(requestId);
57420
57505
  answer({ behavior: decision.behavior, message: decision.message });
57421
57506
  emit({
57422
- ...base3(threadId, entry.turnId),
57507
+ ...base2(threadId, entry.turnId),
57423
57508
  requestId,
57424
57509
  type: "request.resolved",
57425
57510
  behavior: decision.behavior,
@@ -57929,7 +58014,7 @@ function createAppleIntelligenceInstance(input, deps = {}) {
57929
58014
  const emit = (event) => {
57930
58015
  for (const listener of Array.from(listeners)) listener(event);
57931
58016
  };
57932
- const base3 = (threadId, turnId) => ({
58017
+ const base2 = (threadId, turnId) => ({
57933
58018
  eventId: newEventId(),
57934
58019
  provider: "appleIntelligence",
57935
58020
  threadId,
@@ -57947,25 +58032,25 @@ function createAppleIntelligenceInstance(input, deps = {}) {
57947
58032
  resolveDone = resolve14;
57948
58033
  });
57949
58034
  active.set(turn.threadId, { abort, turnId, done });
57950
- emit({ ...base3(turn.threadId, turnId), type: "turn.started" });
57951
- emit({ ...base3(turn.threadId, turnId), type: "session.started", sessionId: null, model: APPLE_ON_DEVICE_MODEL });
58035
+ emit({ ...base2(turn.threadId, turnId), type: "turn.started" });
58036
+ emit({ ...base2(turn.threadId, turnId), type: "session.started", sessionId: null, model: APPLE_ON_DEVICE_MODEL });
57952
58037
  void (async () => {
57953
58038
  let ok = false;
57954
58039
  let failure;
57955
58040
  try {
57956
58041
  const request = shapeRequest(turn, inputBudget(availability.contextSize));
57957
- const text4 = await client.stream(request, (delta) => emit({ ...base3(turn.threadId, turnId), type: "content.delta", streamKind: "assistant_text", delta }), abort.signal);
58042
+ const text4 = await client.stream(request, (delta) => emit({ ...base2(turn.threadId, turnId), type: "content.delta", streamKind: "assistant_text", delta }), abort.signal);
57958
58043
  if (!text4.trim()) throw new Error("Apple Intelligence returned an empty response.");
57959
- emit({ ...base3(turn.threadId, turnId), type: "item.completed", itemType: "assistant_text", text: text4 });
58044
+ emit({ ...base2(turn.threadId, turnId), type: "item.completed", itemType: "assistant_text", text: text4 });
57960
58045
  ok = true;
57961
58046
  } catch (error51) {
57962
58047
  failure = error51 instanceof Error ? error51.message : String(error51);
57963
58048
  } finally {
57964
58049
  const interrupted = abort.signal.aborted;
57965
- if (failure && !interrupted) emit({ ...base3(turn.threadId, turnId), type: "runtime.error", message: failure, terminal: true });
58050
+ if (failure && !interrupted) emit({ ...base2(turn.threadId, turnId), type: "runtime.error", message: failure, terminal: true });
57966
58051
  active.delete(turn.threadId);
57967
58052
  emit({
57968
- ...base3(turn.threadId, turnId),
58053
+ ...base2(turn.threadId, turnId),
57969
58054
  type: "turn.completed",
57970
58055
  ok: ok && !interrupted,
57971
58056
  stopReason: interrupted ? "interrupted" : ok ? null : "error",
@@ -58098,6 +58183,7 @@ var init_builtIn = __esm({
58098
58183
  VibeAgentDriver,
58099
58184
  HermesAgentDriver,
58100
58185
  CustomAcpDriver,
58186
+ AtomicEngineDriver,
58101
58187
  PiDriver,
58102
58188
  OpenAICompatDriver,
58103
58189
  ClaudeDriver,
@@ -58143,7 +58229,7 @@ function httpFailure(status, completion) {
58143
58229
  return new SetupApiError(`${prefix} Check the API URL and provider settings, then try again.`);
58144
58230
  }
58145
58231
  async function setupJson(url2, key, body) {
58146
- const base3 = normalizeApiUrl(url2);
58232
+ const base2 = normalizeApiUrl(url2);
58147
58233
  const token = key;
58148
58234
  if (!token || /[\u0000-\u0020\u007f]/u.test(token)) throw new SetupApiError("Enter a non-empty API key without spaces or line breaks.");
58149
58235
  const completion = body !== void 0;
@@ -58154,7 +58240,7 @@ async function setupJson(url2, key, body) {
58154
58240
  try {
58155
58241
  return await Promise.race([
58156
58242
  (async () => {
58157
- const response = await fetch(`${base3}${completion ? "/chat/completions" : "/models"}`, {
58243
+ const response = await fetch(`${base2}${completion ? "/chat/completions" : "/models"}`, {
58158
58244
  method: completion ? "POST" : "GET",
58159
58245
  headers: { authorization: `Bearer ${token}`, ...completion ? { "content-type": "application/json" } : {} },
58160
58246
  ...completion ? { body: JSON.stringify(body) } : {},
@@ -59137,7 +59223,7 @@ async function clearBrowserSessionState(binaryPath, session, options = {}) {
59137
59223
  }
59138
59224
  };
59139
59225
  try {
59140
- for (const base3 of bases) for (const suffix of [".json", ".json.enc", ".json.previous", ".json.enc.previous"]) remove(join53(directory, base3 + suffix));
59226
+ for (const base2 of bases) for (const suffix of [".json", ".json.enc", ".json.previous", ".json.enc.previous"]) remove(join53(directory, base2 + suffix));
59141
59227
  const pendingDirectory = join53(directory, ".tmp");
59142
59228
  let pendingFiles = [];
59143
59229
  try {
@@ -59145,7 +59231,7 @@ async function clearBrowserSessionState(binaryPath, session, options = {}) {
59145
59231
  } catch (error51) {
59146
59232
  if (error51.code !== "ENOENT") throw error51;
59147
59233
  }
59148
- const candidates = bases.map((base3) => new RegExp(`^${base3}-candidate-[0-9]+\\.json(?:\\.enc)?$`));
59234
+ const candidates = bases.map((base2) => new RegExp(`^${base2}-candidate-[0-9]+\\.json(?:\\.enc)?$`));
59149
59235
  for (const name of pendingFiles) if (candidates.some((candidate) => candidate.test(name))) remove(join53(pendingDirectory, name));
59150
59236
  preparedBrowserSessions.delete(join53(directory, session));
59151
59237
  return true;
@@ -62335,7 +62421,7 @@ function createFleetAgent(options) {
62335
62421
  const layout = fleetLayout(options.root ?? "/");
62336
62422
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
62337
62423
  const auditFile = options.auditFile ?? layout.auditFile;
62338
- const base3 = () => ({
62424
+ const base2 = () => ({
62339
62425
  action: "list",
62340
62426
  admins: [],
62341
62427
  members: [],
@@ -62411,7 +62497,7 @@ function createFleetAgent(options) {
62411
62497
  if (method === "POST" && url2.pathname === "/workspaces") {
62412
62498
  const body = await readBody(req);
62413
62499
  const result = await act({
62414
- ...base3(),
62500
+ ...base2(),
62415
62501
  action: "create",
62416
62502
  slug: text(body.slug),
62417
62503
  admins: strings(body.admins),
@@ -62430,7 +62516,7 @@ function createFleetAgent(options) {
62430
62516
  return send(result.status, result.body);
62431
62517
  }
62432
62518
  if (method === "POST" && url2.pathname === "/upgrade") {
62433
- const result = await act({ ...base3(), action: "upgrade" });
62519
+ const result = await act({ ...base2(), action: "upgrade" });
62434
62520
  return send(result.status, result.body);
62435
62521
  }
62436
62522
  const match = /^\/workspaces\/([a-z0-9-]+)(?:\/(users|providers|suspend|resume))?$/.exec(url2.pathname);
@@ -62440,22 +62526,22 @@ function createFleetAgent(options) {
62440
62526
  const sub = match[2];
62441
62527
  if (method === "POST" && sub === "providers") {
62442
62528
  const body = await readBody(req);
62443
- const result = await act({ ...base3(), action: "providers", slug, openrouterModels: models(body.models) });
62529
+ const result = await act({ ...base2(), action: "providers", slug, openrouterModels: models(body.models) });
62444
62530
  return send(result.status, result.body);
62445
62531
  }
62446
62532
  if (method === "POST" && sub === "users") {
62447
62533
  const body = await readBody(req);
62448
62534
  const action = body.action === "remove" ? "remove" : body.action === "add" ? "add" : void 0;
62449
- const result = await act({ ...base3(), action: "users", slug, userAction: action, email: text(body.email), chatOnly: body.chatOnly === true });
62535
+ const result = await act({ ...base2(), action: "users", slug, userAction: action, email: text(body.email), chatOnly: body.chatOnly === true });
62450
62536
  return send(result.status, result.body);
62451
62537
  }
62452
62538
  if (method === "POST" && (sub === "suspend" || sub === "resume")) {
62453
- const result = await act({ ...base3(), action: sub, slug });
62539
+ const result = await act({ ...base2(), action: sub, slug });
62454
62540
  return send(result.status, result.body);
62455
62541
  }
62456
62542
  if (method === "DELETE" && !sub) {
62457
62543
  const body = await readBody(req);
62458
- const result = await act({ ...base3(), action: "delete", slug, yes: true, keepData: body.keepData === true });
62544
+ const result = await act({ ...base2(), action: "delete", slug, yes: true, keepData: body.keepData === true });
62459
62545
  return send(result.status, result.body);
62460
62546
  }
62461
62547
  }