@quantiya/codevibe-core 2.0.76 → 2.0.77

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.
@@ -749,10 +749,10 @@ ${yaml}---
749
749
 
750
750
  ${prose}`;
751
751
  }
752
- function getTaskDirectoryPath(taskId, homeDir = os38.homedir()) {
752
+ function getTaskDirectoryPath(taskId, homeDir = os39.homedir()) {
753
753
  return path66.join(homeDir, ".codevibe", "tasks", taskId);
754
754
  }
755
- function getPacketFilePath(taskId, homeDir = os38.homedir()) {
755
+ function getPacketFilePath(taskId, homeDir = os39.homedir()) {
756
756
  return path66.join(getTaskDirectoryPath(taskId, homeDir), "CONTINUATION.md");
757
757
  }
758
758
  async function atomicWritePacket(taskDir, finalPath, bytes, pid, ts, randHex) {
@@ -802,7 +802,7 @@ async function atomicWritePacket(taskDir, finalPath, bytes, pid, ts, randHex) {
802
802
  }
803
803
  }
804
804
  function createContinuationPacketWriter(deps) {
805
- let perTaskMutex = /* @__PURE__ */ new Map(), homeDir = deps.homeDir ?? os38.homedir(), clock = deps.clock ?? Date.now, randomHex = deps.randomHex ?? defaultRandomHex, collector = deps.dirtyStateCollector ?? {
805
+ let perTaskMutex = /* @__PURE__ */ new Map(), homeDir = deps.homeDir ?? os39.homedir(), clock = deps.clock ?? Date.now, randomHex = deps.randomHex ?? defaultRandomHex, collector = deps.dirtyStateCollector ?? {
806
806
  collect: async () => collectRepoStates(deps.authority, [], {}).catch(() => [])
807
807
  }, getMutex = (taskId) => {
808
808
  let m = perTaskMutex.get(taskId);
@@ -871,10 +871,10 @@ function createContinuationPacketWriter(deps) {
871
871
  }
872
872
  };
873
873
  }
874
- var import_node_fs28, path66, os38, crypto26, import_json_freeze15, import_yaml, Mutex, WORK_NOT_ENUMERATED_SENTINEL, VERIFICATION_NOT_RECORDED_SENTINEL, init_packet_writer = __esm({
874
+ var import_node_fs28, path66, os39, crypto26, import_json_freeze15, import_yaml, Mutex, WORK_NOT_ENUMERATED_SENTINEL, VERIFICATION_NOT_RECORDED_SENTINEL, init_packet_writer = __esm({
875
875
  "src/continuation/packet-writer.ts"() {
876
876
  "use strict";
877
- import_node_fs28 = require("node:fs"), path66 = __toESM(require("node:path")), os38 = __toESM(require("node:os")), crypto26 = __toESM(require("node:crypto")), import_json_freeze15 = require("json-freeze"), import_yaml = require("yaml");
877
+ import_node_fs28 = require("node:fs"), path66 = __toESM(require("node:path")), os39 = __toESM(require("node:os")), crypto26 = __toESM(require("node:crypto")), import_json_freeze15 = require("json-freeze"), import_yaml = require("yaml");
878
878
  init_types();
879
879
  init_dirty_state_collector();
880
880
  Mutex = class {
@@ -943,7 +943,7 @@ var init_resolve_agent = __esm({
943
943
 
944
944
  // src/companion-mode/persist-preference.ts
945
945
  function resolveHome11() {
946
- return (process.env.VITEST === "true" || process.env.NODE_ENV === "test") && process.env.CODEVIBE_HOME_OVERRIDE ? process.env.CODEVIBE_HOME_OVERRIDE : os51.homedir();
946
+ return (process.env.VITEST === "true" || process.env.NODE_ENV === "test") && process.env.CODEVIBE_HOME_OVERRIDE ? process.env.CODEVIBE_HOME_OVERRIDE : os52.homedir();
947
947
  }
948
948
  function companionPreferenceFilePath() {
949
949
  return path84.join(resolveHome11(), ".codevibe", "companion-preference.json");
@@ -991,10 +991,10 @@ async function persistCompanionPreference(pref) {
991
991
  } catch {
992
992
  }
993
993
  }
994
- var fs63, path84, os51, import_zod16, CompanionPreferenceSchema, init_persist_preference = __esm({
994
+ var fs63, path84, os52, import_zod16, CompanionPreferenceSchema, init_persist_preference = __esm({
995
995
  "src/companion-mode/persist-preference.ts"() {
996
996
  "use strict";
997
- fs63 = __toESM(require("fs/promises")), path84 = __toESM(require("path")), os51 = __toESM(require("os")), import_zod16 = require("zod");
997
+ fs63 = __toESM(require("fs/promises")), path84 = __toESM(require("path")), os52 = __toESM(require("os")), import_zod16 = require("zod");
998
998
  init_logger2();
999
999
  CompanionPreferenceSchema = import_zod16.z.object({
1000
1000
  lastAgent: import_zod16.z.union([
@@ -4319,15 +4319,15 @@ async function probeWorkspaceTreeFingerprint(workspaceRoot, options = {}) {
4319
4319
  ), fields = stdout.split("\0").filter((field2) => field2.length > 0), parts = [];
4320
4320
  for (let i = 0; i < fields.length; i++) {
4321
4321
  if (remaining() <= 0) return failed();
4322
- let xy = fields[i].slice(0, 2), relative22 = fields[i].slice(3);
4322
+ let xy = fields[i].slice(0, 2), relative23 = fields[i].slice(3);
4323
4323
  /[RC]/.test(xy) && (i += 1);
4324
4324
  let stat13 = "absent";
4325
4325
  try {
4326
- let st = await fs10.lstat(path13.join(root, relative22));
4326
+ let st = await fs10.lstat(path13.join(root, relative23));
4327
4327
  stat13 = `${st.size}:${st.mtimeMs}`;
4328
4328
  } catch {
4329
4329
  }
4330
- parts.push(`${xy} ${relative22} ${stat13}`);
4330
+ parts.push(`${xy} ${relative23} ${stat13}`);
4331
4331
  }
4332
4332
  return remaining() <= 0 ? failed() : {
4333
4333
  kind: "fingerprint",
@@ -13151,10 +13151,10 @@ var DecisionAttemptReceiptStore = class _DecisionAttemptReceiptStore {
13151
13151
  }
13152
13152
  }
13153
13153
  async assertNoSymlinkComponents(target2) {
13154
- let relative22 = path22.relative(this.rootDir, target2);
13155
- if (relative22.startsWith("..") || path22.isAbsolute(relative22))
13154
+ let relative23 = path22.relative(this.rootDir, target2);
13155
+ if (relative23.startsWith("..") || path22.isAbsolute(relative23))
13156
13156
  throw new Error("DecisionAttemptReceiptStore: path escaped receipt root");
13157
- let components = relative22 ? relative22.split(path22.sep) : [], current = this.rootDir;
13157
+ let components = relative23 ? relative23.split(path22.sep) : [], current = this.rootDir;
13158
13158
  for (let index = -1; index < components.length; index += 1) {
13159
13159
  index >= 0 && (current = path22.join(current, components[index]));
13160
13160
  try {
@@ -21244,9 +21244,9 @@ init_logger2();
21244
21244
  function truncate(s, max) {
21245
21245
  return max <= 0 ? "" : s.length <= max ? s : max === 1 ? "\u2026" : s.slice(0, max - 1) + "\u2026";
21246
21246
  }
21247
- function formatProjectPath(projectPath, homedir40, maxCols) {
21247
+ function formatProjectPath(projectPath, homedir41, maxCols) {
21248
21248
  let p = projectPath ?? "";
21249
- if (homedir40 && (p === homedir40 || p.startsWith(homedir40 + "/")) && (p = "~" + p.slice(homedir40.length)), p.length > maxCols) {
21249
+ if (homedir41 && (p === homedir41 || p.startsWith(homedir41 + "/")) && (p = "~" + p.slice(homedir41.length)), p.length > maxCols) {
21250
21250
  if (maxCols <= 0) return "";
21251
21251
  if (maxCols === 1) return "\u2026";
21252
21252
  p = "\u2026" + p.slice(p.length - (maxCols - 1));
@@ -21263,7 +21263,7 @@ function plannerBadge(kind, label) {
21263
21263
  return kind === "local_gemma_qat" ? { text: `${(label ?? "").trim() || "local model"} \xB7 local` } : kind === "local_unavailable" ? { text: `\u26A0 ${(label ?? "").trim() || "local model"} offline \u2014 NL disabled`, color: "yellow" } : kind === "hosted" ? { text: "Gemma \xB7 hosted" } : null;
21264
21264
  }
21265
21265
  function computeStatusLayout(args) {
21266
- let { tier, homedir: homedir40, columns, badge } = args, SEP = " \xB7 ", badgeText = badge ? truncate(badge.text, Math.max(0, columns - SEP.length - tier.length - 2)) : "", rightWidth = badgeText ? badgeText.length + 1 : 0, cwdMax = Math.max(0, columns - rightWidth - SEP.length - tier.length), cwd = formatProjectPath(args.cwd, homedir40, cwdMax), leftFull = cwd ? `${cwd}${SEP}${tier}` : tier;
21266
+ let { tier, homedir: homedir41, columns, badge } = args, SEP = " \xB7 ", badgeText = badge ? truncate(badge.text, Math.max(0, columns - SEP.length - tier.length - 2)) : "", rightWidth = badgeText ? badgeText.length + 1 : 0, cwdMax = Math.max(0, columns - rightWidth - SEP.length - tier.length), cwd = formatProjectPath(args.cwd, homedir41, cwdMax), leftFull = cwd ? `${cwd}${SEP}${tier}` : tier;
21267
21267
  return { leftText: truncate(leftFull, Math.max(0, columns - rightWidth)), badgeText };
21268
21268
  }
21269
21269
 
@@ -21474,7 +21474,7 @@ async function clearRetiredPlannerCache(userId, cacheRoot = process.env.CODEVIBE
21474
21474
  }
21475
21475
 
21476
21476
  // src/orchestration-shell/index.ts
21477
- var path78 = __toESM(require("path")), os47 = __toESM(require("os")), import_promises = require("node:fs/promises"), import_node_fs37 = require("node:fs"), import_node_child_process10 = require("node:child_process"), import_node_util3 = require("node:util"), import_node_async_hooks = require("node:async_hooks");
21477
+ var path78 = __toESM(require("path")), os48 = __toESM(require("os")), import_promises = require("node:fs/promises"), import_node_fs37 = require("node:fs"), import_node_child_process10 = require("node:child_process"), import_node_util3 = require("node:util"), import_node_async_hooks = require("node:async_hooks");
21478
21478
 
21479
21479
  // src/orchestration-shell/web/sanitize.ts
21480
21480
  function sanitizeForTerminal(input) {
@@ -32831,17 +32831,17 @@ function sniffImageMime(head) {
32831
32831
  return null;
32832
32832
  }
32833
32833
  var IMAGE_EXT_RE = /\.(png|jpe?g|gif|webp|bmp)$/i;
32834
- function unescapeToken(raw, homedir40) {
32834
+ function unescapeToken(raw, homedir41) {
32835
32835
  let s = raw.trim(), wasQuoted = !1;
32836
32836
  if ((s.startsWith("'") && s.endsWith("'") || s.startsWith('"') && s.endsWith('"')) && (s = s.slice(1, -1), wasQuoted = !0), s.startsWith("file://"))
32837
32837
  try {
32838
32838
  return (0, import_node_url.fileURLToPath)(s);
32839
32839
  } catch {
32840
32840
  }
32841
- return wasQuoted || (s = s.replace(/\\(.)/g, "$1")), expandHome(s, homedir40);
32841
+ return wasQuoted || (s = s.replace(/\\(.)/g, "$1")), expandHome(s, homedir41);
32842
32842
  }
32843
- function expandHome(p, homedir40) {
32844
- return p === "~" ? homedir40 : p.startsWith("~/") ? path33.join(homedir40, p.slice(2)) : p;
32843
+ function expandHome(p, homedir41) {
32844
+ return p === "~" ? homedir41 : p.startsWith("~/") ? path33.join(homedir41, p.slice(2)) : p;
32845
32845
  }
32846
32846
  function extractImagePathSpans(text2) {
32847
32847
  let spans = [], quoted = /'[^']*\.(?:png|jpe?g|gif|webp|bmp)'|"[^"]*\.(?:png|jpe?g|gif|webp|bmp)"/gi, m, masked = text2.split("");
@@ -34596,7 +34596,7 @@ function collapsedReducedTrustLine(actor) {
34596
34596
  }
34597
34597
 
34598
34598
  // src/orchestration-shell/quorum-loop.ts
34599
- var path71 = __toESM(require("node:path")), os42 = __toESM(require("node:os")), nodeCrypto = __toESM(require("node:crypto")), import_uuid12 = require("uuid"), import_ulid4 = require("ulid");
34599
+ var path71 = __toESM(require("node:path")), os43 = __toESM(require("node:os")), nodeCrypto = __toESM(require("node:crypto")), import_uuid12 = require("uuid"), import_ulid4 = require("ulid");
34600
34600
 
34601
34601
  // src/audit-keys/index.ts
34602
34602
  var import_node_crypto26 = require("node:crypto");
@@ -36490,6 +36490,255 @@ function enforceNetwork(scope) {
36490
36490
  function redactSensitiveTokens(cmd) {
36491
36491
  return cmd.replace(/--token\s+[0-9a-fA-F]+/g, "--token [REDACTED]").replace(/mcp_servers\.codevibe_web\.args=\S+/g, "mcp_servers.codevibe_web.args=[REDACTED]");
36492
36492
  }
36493
+ function normalizeArgv(argv) {
36494
+ if (!Array.isArray(argv)) return [];
36495
+ let normalized = [];
36496
+ for (let token4 of argv)
36497
+ if (typeof token4 == "string")
36498
+ if (token4.startsWith("--") && token4.includes("=")) {
36499
+ let eqIdx = token4.indexOf("="), rawKey = token4.slice(0, eqIdx), val = token4.slice(eqIdx + 1), key = normalizeFlagName(rawKey);
36500
+ normalized.push(key, val);
36501
+ } else if (token4.startsWith("--"))
36502
+ normalized.push(normalizeFlagName(token4));
36503
+ else if (token4.startsWith("-s") && token4.length > 2) {
36504
+ let val = token4[2] === "=" ? token4.slice(3) : token4.slice(2);
36505
+ normalized.push("--sandbox", val);
36506
+ } else if (token4 === "-s")
36507
+ normalized.push("--sandbox");
36508
+ else if (token4.startsWith("-c") && token4.length > 2 && !token4.startsWith("--")) {
36509
+ let val = token4[2] === "=" ? token4.slice(3) : token4.slice(2);
36510
+ normalized.push("-c", val);
36511
+ } else
36512
+ normalized.push(token4);
36513
+ return normalized;
36514
+ }
36515
+ function normalizeFlagName(flag) {
36516
+ return flag.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
36517
+ }
36518
+ function parseToolRuleM8(rule) {
36519
+ let trimmed = rule.trim();
36520
+ if (trimmed.length === 0) return null;
36521
+ let firstParen = trimmed.indexOf("("), lastParen = trimmed.lastIndexOf(")");
36522
+ if (firstParen === -1 && lastParen === -1)
36523
+ return /^[A-Za-z0-9_]+$/.test(trimmed) ? { toolName: trimmed } : null;
36524
+ if (firstParen === -1 || lastParen <= firstParen || lastParen !== trimmed.length - 1)
36525
+ return null;
36526
+ let toolName = trimmed.substring(0, firstParen);
36527
+ if (!/^[A-Za-z0-9_]+$/.test(toolName)) return null;
36528
+ let rawContent = trimmed.substring(firstParen + 1, lastParen);
36529
+ return { toolName, rawContent };
36530
+ }
36531
+ function vendorBu(e) {
36532
+ let n = [];
36533
+ for (let t of e) {
36534
+ if (!t) continue;
36535
+ let r = "", o = !1;
36536
+ for (let s of t)
36537
+ switch (s) {
36538
+ case "(":
36539
+ o = !0, r += s;
36540
+ break;
36541
+ case ")":
36542
+ o = !1, r += s;
36543
+ break;
36544
+ case ",":
36545
+ o ? r += s : (r.trim() && n.push(r.trim()), r = "");
36546
+ break;
36547
+ case " ":
36548
+ o ? r += s : r.trim() && (n.push(r.trim()), r = "");
36549
+ break;
36550
+ default:
36551
+ (r || s.trim()) && (r += s);
36552
+ }
36553
+ r.trim() && n.push(r.trim());
36554
+ }
36555
+ return n;
36556
+ }
36557
+ function parseAllowedToolsRules(value) {
36558
+ let arr = Array.isArray(value) ? value : [value], rules = vendorBu(arr);
36559
+ return rules.length > 0 ? rules : null;
36560
+ }
36561
+ var ADVISORY_FORBIDDEN_TOOLS = /* @__PURE__ */ new Set([
36562
+ "Write",
36563
+ "Edit",
36564
+ "Bash",
36565
+ "NotebookEditCell",
36566
+ "KillShell"
36567
+ ]), ADVISORY_PERMITTED_TOOLS = /* @__PURE__ */ new Set(["Read", "Grep", "Glob"]);
36568
+ function isStrictReadOnlyAdvisoryClaude(argv) {
36569
+ if (!Array.isArray(argv) || argv.length === 0) return !1;
36570
+ let norm = normalizeArgv(argv);
36571
+ if (norm.length === 0) return !1;
36572
+ let baseExe = path38.basename(norm[0] || "");
36573
+ if (baseExe !== "claude" && baseExe !== "claude.exe" || norm.some(
36574
+ (arg) => arg.startsWith("--dangerously") || arg.startsWith("--allow-dangerously") || arg === "--yolo" || arg === "--full-auto" || arg === "--approve-for-me" || arg === "--permission-mode" || arg.startsWith("--permission-mode=") || arg === "--plugin-dir" || arg.startsWith("--plugin-dir=") || arg === "--plugin-dir-no-mcp" || arg.startsWith("--plugin-dir-no-mcp=") || arg === "--plugin-url" || arg.startsWith("--plugin-url=") || arg === "--plugin" || arg.startsWith("--plugin=") || arg === "--plugins" || arg.startsWith("--plugins=") || arg === "--system-prompt" || arg.startsWith("--system-prompt=") || arg === "--append-system-prompt" || arg.startsWith("--append-system-prompt=") || arg === "--exec" || arg.startsWith("--exec=")
36575
+ ) || norm.includes("--mcp-config") || norm.filter((a) => a === "--strict-mcp-config").length !== 1)
36576
+ return !1;
36577
+ let settingSourcesIndices = [];
36578
+ for (let i = 0; i < norm.length; i++)
36579
+ norm[i] === "--setting-sources" && settingSourcesIndices.push(i);
36580
+ if (settingSourcesIndices.length !== 1)
36581
+ return !1;
36582
+ let ssIdx = settingSourcesIndices[0];
36583
+ if (norm[ssIdx + 1] !== "user" || norm.filter((a) => a === "--tools").length !== 1 || norm.filter((a) => a === "--allowed-tools").length !== 1 || norm.filter((a) => a === "--settings").length !== 1) return !1;
36584
+ let settingsIdx = norm.indexOf("--settings"), settingsRaw = norm[settingsIdx + 1];
36585
+ if (!settingsRaw || !settingsRaw.startsWith("{")) return !1;
36586
+ let settingsObj;
36587
+ try {
36588
+ settingsObj = JSON.parse(settingsRaw);
36589
+ } catch {
36590
+ return !1;
36591
+ }
36592
+ if (!settingsObj || typeof settingsObj != "object" || Array.isArray(settingsObj))
36593
+ return !1;
36594
+ let topKeys = Object.keys(settingsObj);
36595
+ if (topKeys.length !== 1 || topKeys[0] !== "permissions")
36596
+ return !1;
36597
+ let permissionsObj = settingsObj.permissions;
36598
+ if (!permissionsObj || typeof permissionsObj != "object" || Array.isArray(permissionsObj))
36599
+ return !1;
36600
+ for (let pk of Object.keys(permissionsObj))
36601
+ if (pk !== "defaultMode" && pk !== "allow")
36602
+ return !1;
36603
+ if (permissionsObj.defaultMode !== "dontAsk" || !Array.isArray(permissionsObj.allow))
36604
+ return !1;
36605
+ let toolsIdx = norm.indexOf("--tools"), toolTokens = [];
36606
+ for (let i = toolsIdx + 1; i < norm.length && !norm[i].startsWith("-"); i++)
36607
+ toolTokens.push(norm[i]);
36608
+ if (toolTokens.length === 0) return !1;
36609
+ let firstTool = toolTokens[0], allowedToolsIdx = norm.indexOf("--allowed-tools");
36610
+ if (allowedToolsIdx === -1) return !1;
36611
+ let allowedToolsTokens = [];
36612
+ for (let i = allowedToolsIdx + 1; i < norm.length && !norm[i].startsWith("-"); i++)
36613
+ allowedToolsTokens.push(norm[i]);
36614
+ if (firstTool === "")
36615
+ return !(toolTokens.length !== 1 || allowedToolsTokens.length !== 1 || allowedToolsTokens[0] !== "" || permissionsObj.allow.length !== 0);
36616
+ let allToolNames = toolTokens.flatMap((t) => t.split(/[\s,]+/)).map((t) => t.trim()).filter(Boolean);
36617
+ if (allToolNames.length === 0) return !1;
36618
+ for (let t of allToolNames)
36619
+ if (!ADVISORY_PERMITTED_TOOLS.has(t) || ADVISORY_FORBIDDEN_TOOLS.has(t)) return !1;
36620
+ if (allowedToolsTokens.length === 0) return !1;
36621
+ let parsedAllowedRules = parseAllowedToolsRules(allowedToolsTokens);
36622
+ if (!parsedAllowedRules || parsedAllowedRules.length === 0) return !1;
36623
+ for (let rule of parsedAllowedRules) {
36624
+ let parsed = parseToolRuleM8(rule);
36625
+ if (!parsed || !ADVISORY_PERMITTED_TOOLS.has(parsed.toolName) || ADVISORY_FORBIDDEN_TOOLS.has(parsed.toolName) || parsed.rawContent === void 0) return !1;
36626
+ let p = parsed.rawContent;
36627
+ if (p === "/*" || p === "*" || !p.startsWith("//") || p === "//" || p === "//*" || p === "//**" || /[\r\n\0]/.test(p)) return !1;
36628
+ }
36629
+ if (permissionsObj.allow.length === 0) return !1;
36630
+ for (let rule of permissionsObj.allow) {
36631
+ if (typeof rule != "string") return !1;
36632
+ let parsed = parseToolRuleM8(rule);
36633
+ if (!parsed || !ADVISORY_PERMITTED_TOOLS.has(parsed.toolName) || ADVISORY_FORBIDDEN_TOOLS.has(parsed.toolName) || parsed.rawContent === void 0) return !1;
36634
+ let p = parsed.rawContent;
36635
+ if (p === "/*" || p === "*" || !p.startsWith("//") || p === "//" || p === "//*" || p === "//**" || /[\r\n\0]/.test(p)) return !1;
36636
+ }
36637
+ return !0;
36638
+ }
36639
+ var CODEX_LONG_OPTIONS_WITH_VALUE = /* @__PURE__ */ new Set([
36640
+ "config",
36641
+ "enable",
36642
+ "disable",
36643
+ "image",
36644
+ "model",
36645
+ "local-provider",
36646
+ "profile",
36647
+ "sandbox",
36648
+ "cd",
36649
+ "add-dir",
36650
+ "thread-source",
36651
+ "cyber-access-program",
36652
+ "output-schema",
36653
+ "color",
36654
+ "output-last-message"
36655
+ ]), CODEX_SHORT_OPTIONS_WITH_VALUE = /* @__PURE__ */ new Set([
36656
+ "c",
36657
+ "i",
36658
+ "m",
36659
+ "p",
36660
+ "s",
36661
+ "C",
36662
+ "o"
36663
+ ]);
36664
+ function extractConfigKey(cfg) {
36665
+ let s = (cfg || "").trim();
36666
+ s = s.replace(/^['"]|['"]$/g, "").trim(), s = s.replace(/^=+/, "").trim(), s = s.replace(/^['"]|['"]$/g, "").trim();
36667
+ let eqIdx = s.indexOf("=");
36668
+ return (eqIdx !== -1 ? s.slice(0, eqIdx) : s).replace(/^['"]|['"]$/g, "").trim().toLowerCase();
36669
+ }
36670
+ function isProtectedCodexConfigKey(key) {
36671
+ return key.startsWith("sandbox") || key.includes("sandbox") || key.startsWith("permissions") || key.includes("permission") || key.startsWith("shell_environment_policy") || key.includes("shell_environment") || key.startsWith("approval_policy") || key.includes("approval");
36672
+ }
36673
+ function isStrictReadOnlyAdvisoryCodex(argv) {
36674
+ if (!Array.isArray(argv) || argv.length === 0) return !1;
36675
+ let baseExe = path38.basename(argv[0] || "");
36676
+ if (baseExe !== "codex" && baseExe !== "codex.exe" || argv.some(
36677
+ (arg) => typeof arg == "string" && (arg.includes("workspace-write") || arg.includes("danger-full-access"))
36678
+ ))
36679
+ return !1;
36680
+ let delimiterFound = !1, waitingForOption = null, structuredOptions = [], positionalBeforeDelimiter = [], positionalAfterDelimiter = [];
36681
+ for (let i = 1; i < argv.length; i++) {
36682
+ let rawToken = argv[i];
36683
+ if (typeof rawToken == "string") {
36684
+ if (delimiterFound) {
36685
+ positionalAfterDelimiter.push(rawToken);
36686
+ continue;
36687
+ }
36688
+ if (waitingForOption !== null) {
36689
+ waitingForOption.value = rawToken, waitingForOption = null;
36690
+ continue;
36691
+ }
36692
+ if (rawToken === "--") {
36693
+ delimiterFound = !0;
36694
+ continue;
36695
+ }
36696
+ if (rawToken.startsWith("--"))
36697
+ if (rawToken.includes("=")) {
36698
+ let eqIdx = rawToken.indexOf("="), flagName = normalizeFlagName(rawToken.slice(0, eqIdx)), flagVal = rawToken.slice(eqIdx + 1);
36699
+ structuredOptions.push({ name: flagName, value: flagVal });
36700
+ } else {
36701
+ let flagName = normalizeFlagName(rawToken), bareName = flagName.slice(2), optObj = { name: flagName };
36702
+ structuredOptions.push(optObj), CODEX_LONG_OPTIONS_WITH_VALUE.has(bareName) && (waitingForOption = optObj);
36703
+ }
36704
+ else if (rawToken.startsWith("-") && rawToken.length > 1) {
36705
+ let optChar = rawToken[1];
36706
+ if (CODEX_SHORT_OPTIONS_WITH_VALUE.has(optChar)) {
36707
+ let normOpt = optChar === "s" ? "--sandbox" : optChar === "c" ? "-c" : optChar === "C" ? "-C" : optChar === "m" ? "-m" : optChar === "o" ? "-o" : optChar === "i" ? "-i" : optChar === "p" ? "-p" : `-${optChar}`;
36708
+ if (rawToken.length > 2) {
36709
+ let val = rawToken[2] === "=" ? rawToken.slice(3) : rawToken.slice(2);
36710
+ structuredOptions.push({ name: normOpt, value: val });
36711
+ } else {
36712
+ let optObj = { name: normOpt };
36713
+ structuredOptions.push(optObj), waitingForOption = optObj;
36714
+ }
36715
+ } else
36716
+ structuredOptions.push({ name: rawToken });
36717
+ } else
36718
+ positionalBeforeDelimiter.push(rawToken);
36719
+ }
36720
+ }
36721
+ let isBypassFlag = (s) => s.startsWith("--dangerously") || s === "--yolo" || s === "--full-auto" || s === "--approve-for-me" || s === "--add-dir" || s === "-a" || s === "--ask-for-approval";
36722
+ if (structuredOptions.some((opt) => isBypassFlag(opt.name)) || positionalBeforeDelimiter.some(isBypassFlag) || positionalAfterDelimiter.some(isBypassFlag) || positionalBeforeDelimiter.length > 0 && positionalBeforeDelimiter[0] !== "exec")
36723
+ return !1;
36724
+ for (let opt of structuredOptions) {
36725
+ if (opt.name === "-c" || opt.name === "--config") {
36726
+ let cfgVal = opt.value || "", cfgKey = extractConfigKey(cfgVal);
36727
+ if (isProtectedCodexConfigKey(cfgKey))
36728
+ return !1;
36729
+ }
36730
+ if (opt.name === "--enable" || opt.name === "--disable") {
36731
+ let featVal = (opt.value || "").toLowerCase();
36732
+ if (isProtectedCodexConfigKey(featVal))
36733
+ return !1;
36734
+ }
36735
+ }
36736
+ let sandboxOptions = structuredOptions.filter((opt) => opt.name === "--sandbox");
36737
+ if (sandboxOptions.length === 0) return !1;
36738
+ for (let sOpt of sandboxOptions)
36739
+ if (sOpt.value !== "read-only") return !1;
36740
+ return !0;
36741
+ }
36493
36742
  function enforceCommand(scope, argv) {
36494
36743
  for (let entry5 of scope.commandAllowlist) {
36495
36744
  let isTemplate = entry5.endsWith(" *"), containsSpace = entry5.includes(" ");
@@ -36518,6 +36767,96 @@ function enforceCommand(scope, argv) {
36518
36767
  let expectedSessionsDir = path38.join(os25.homedir(), ".codevibe", "sessions");
36519
36768
  return !!path38.resolve(remainder).startsWith(expectedSessionsDir + path38.sep);
36520
36769
  }
36770
+ if (entry5.includes("--allowed-tools *")) {
36771
+ if (argv.includes("--mcp-config") || !argv.includes("--strict-mcp-config") || argv.includes("--dangerously-skip-permissions"))
36772
+ return !1;
36773
+ let allowedToolsIndex = argv.indexOf("--allowed-tools");
36774
+ if (allowedToolsIndex === -1 || allowedToolsIndex + 1 >= argv.length)
36775
+ return !1;
36776
+ let allowedToolsTokens = [];
36777
+ for (let i2 = allowedToolsIndex + 1; i2 < argv.length && !argv[i2].startsWith("-"); i2++)
36778
+ allowedToolsTokens.push(argv[i2]);
36779
+ if (allowedToolsTokens.length === 0) return !1;
36780
+ let parsedRules = parseAllowedToolsRules(allowedToolsTokens);
36781
+ if (!parsedRules || parsedRules.length === 0)
36782
+ return !1;
36783
+ let parsedRuleSet = /* @__PURE__ */ new Set();
36784
+ for (let rule of parsedRules) {
36785
+ let parsed = parseToolRuleM8(rule);
36786
+ if (!parsed || !ADVISORY_PERMITTED_TOOLS.has(parsed.toolName) || ADVISORY_FORBIDDEN_TOOLS.has(parsed.toolName) || parsed.rawContent === void 0)
36787
+ return !1;
36788
+ let pattern = parsed.rawContent;
36789
+ if (pattern === "/*" || pattern === "*" || !pattern.startsWith("//") || pattern === "//" || pattern === "//*" || pattern === "//**" || /[\r\n\0]/.test(pattern))
36790
+ return !1;
36791
+ parsedRuleSet.add(rule);
36792
+ }
36793
+ let settingsTokenIdx = remainderTokens.indexOf("--settings");
36794
+ if (settingsTokenIdx !== -1 && settingsTokenIdx + 1 < remainderTokens.length)
36795
+ try {
36796
+ let s = JSON.parse(remainderTokens[settingsTokenIdx + 1]);
36797
+ if (Array.isArray(s?.permissions?.allow))
36798
+ for (let rule of s.permissions.allow) {
36799
+ if (typeof rule != "string") return !1;
36800
+ let parsed = parseToolRuleM8(rule);
36801
+ if (!parsed || !ADVISORY_PERMITTED_TOOLS.has(parsed.toolName) || ADVISORY_FORBIDDEN_TOOLS.has(parsed.toolName) || parsed.rawContent === void 0) return !1;
36802
+ let pattern = parsed.rawContent;
36803
+ if (pattern === "/*" || pattern === "*" || !pattern.startsWith("//") || pattern === "//" || pattern === "//*" || pattern === "//**" || /[\r\n\0]/.test(pattern)) return !1;
36804
+ }
36805
+ } catch {
36806
+ return !1;
36807
+ }
36808
+ let seenStrictMcp = !1, seenSettings = !1, seenSettingSources = !1, i = 1;
36809
+ for (; i < remainderTokens.length; ) {
36810
+ let token4 = remainderTokens[i];
36811
+ if (token4 === "--strict-mcp-config") {
36812
+ if (seenStrictMcp) return !1;
36813
+ seenStrictMcp = !0, i += 1;
36814
+ } else if (token4 === "--setting-sources") {
36815
+ if (seenSettingSources || i + 1 >= remainderTokens.length || remainderTokens[i + 1] !== "user") return !1;
36816
+ seenSettingSources = !0, i += 2;
36817
+ } else if (token4 === "--settings") {
36818
+ if (seenSettings || i + 1 >= remainderTokens.length) return !1;
36819
+ let settingsRaw = remainderTokens[i + 1];
36820
+ if (!settingsRaw.startsWith("{"))
36821
+ return !1;
36822
+ let settingsObj;
36823
+ try {
36824
+ settingsObj = JSON.parse(settingsRaw);
36825
+ } catch {
36826
+ return !1;
36827
+ }
36828
+ if (!settingsObj || typeof settingsObj != "object" || Array.isArray(settingsObj))
36829
+ return !1;
36830
+ let topKeys = Object.keys(settingsObj);
36831
+ if (topKeys.length !== 1 || topKeys[0] !== "permissions")
36832
+ return !1;
36833
+ let permissionsObj = settingsObj.permissions;
36834
+ if (!permissionsObj || typeof permissionsObj != "object" || Array.isArray(permissionsObj))
36835
+ return !1;
36836
+ let permKeys = Object.keys(permissionsObj);
36837
+ for (let pk of permKeys)
36838
+ if (pk !== "defaultMode" && pk !== "allow")
36839
+ return !1;
36840
+ if (permissionsObj.defaultMode !== "dontAsk" || !Array.isArray(permissionsObj.allow))
36841
+ return !1;
36842
+ let allowList = permissionsObj.allow;
36843
+ if (allowList.length !== parsedRuleSet.size)
36844
+ return !1;
36845
+ for (let r of allowList)
36846
+ if (typeof r != "string" || !parsedRuleSet.has(r))
36847
+ return !1;
36848
+ seenSettings = !0, i += 2;
36849
+ } else if (token4 === "--add-dir") {
36850
+ if (i + 1 >= remainderTokens.length) return !1;
36851
+ let addDirVal = remainderTokens[i + 1];
36852
+ if (typeof addDirVal != "string" || addDirVal.length === 0 || addDirVal.startsWith("-"))
36853
+ return !1;
36854
+ i += 2;
36855
+ } else
36856
+ return !1;
36857
+ }
36858
+ return !(!seenStrictMcp || !seenSettings || !seenSettingSources);
36859
+ }
36521
36860
  if (entry5.includes('mcp_servers.codevibe_web.command="node" *')) {
36522
36861
  if (remainderTokens.length !== 5 || remainderTokens[0] !== "-c" || !remainderTokens[1]?.startsWith("mcp_servers.codevibe_web.args=") || remainderTokens[2] !== "--output-last-message" || !remainderTokens[3] || remainderTokens[3].startsWith("-") || remainderTokens[4] !== "-")
36523
36862
  return !1;
@@ -37082,8 +37421,8 @@ async function readDirectoryBounded(dirPath, budget, label, relativePrefix = "",
37082
37421
  }
37083
37422
  if (value.type !== "entry" || typeof value.name != "string" || value.name.length === 0 || value.name === "." || value.name === ".." || value.name.includes("/") || value.name.includes("\\") || value.name.includes("\0") || !Number.isSafeInteger(value.kind))
37084
37423
  throw new Error(`${label} helper returned an invalid entry`);
37085
- let relative22 = relativePrefix ? path39.join(relativePrefix, value.name) : value.name;
37086
- consumeMetadataPath(budget, relative22, label);
37424
+ let relative23 = relativePrefix ? path39.join(relativePrefix, value.name) : value.name;
37425
+ consumeMetadataPath(budget, relative23, label);
37087
37426
  let stat13;
37088
37427
  if (opts.includeStats === !0) {
37089
37428
  let raw = value.stat;
@@ -38693,6 +39032,86 @@ function buildClaudeNoToolsPlanArgv() {
38693
39032
  capture: { kind: "stdout" }
38694
39033
  };
38695
39034
  }
39035
+ function escapeClaudePathGlob(p) {
39036
+ return (process.platform === "win32" ? p.replace(/\\/g, "/") : p).replace(/\[/g, "\\[").replace(/\]/g, "\\]").replace(/\*/g, "\\*").replace(/\?/g, "\\?");
39037
+ }
39038
+ function formatClaudePermissionPath(p) {
39039
+ return p.startsWith("//") ? p : p.startsWith("/") ? "/" + p : p;
39040
+ }
39041
+ function buildClaudeReadOnlyAdvisoryPlanArgv(workdir, attachments) {
39042
+ let rulesList = [], argv = ["claude", "--print", "--output-format", "json"], addDirs = [];
39043
+ if (workdir) {
39044
+ let resolvedWorkdir = import_node_path.default.resolve(workdir);
39045
+ addDirs.push(resolvedWorkdir);
39046
+ let realWorkdir = null;
39047
+ try {
39048
+ realWorkdir = (0, import_node_fs19.realpathSync)(resolvedWorkdir);
39049
+ } catch {
39050
+ }
39051
+ let targetPatterns = /* @__PURE__ */ new Set(), escapedWorkdir = escapeClaudePathGlob(resolvedWorkdir);
39052
+ if (targetPatterns.add(formatClaudePermissionPath(`${escapedWorkdir}/**`)), realWorkdir && realWorkdir !== resolvedWorkdir) {
39053
+ let escapedRealWorkdir = escapeClaudePathGlob(realWorkdir);
39054
+ targetPatterns.add(formatClaudePermissionPath(`${escapedRealWorkdir}/**`));
39055
+ }
39056
+ if (attachments && attachments.length > 0)
39057
+ for (let att of attachments) {
39058
+ let resolvedAtt = import_node_path.default.resolve(att), stat13 = null;
39059
+ try {
39060
+ stat13 = (0, import_node_fs19.statSync)(resolvedAtt);
39061
+ } catch {
39062
+ }
39063
+ let isDir = !!stat13?.isDirectory(), escapedAtt = escapeClaudePathGlob(resolvedAtt);
39064
+ isDir ? (targetPatterns.add(formatClaudePermissionPath(`${escapedAtt}/**`)), addDirs.push(resolvedAtt)) : targetPatterns.add(formatClaudePermissionPath(escapedAtt));
39065
+ try {
39066
+ let realAtt = (0, import_node_fs19.realpathSync)(resolvedAtt);
39067
+ if (realAtt !== resolvedAtt) {
39068
+ let escapedRealAtt = escapeClaudePathGlob(realAtt);
39069
+ isDir ? targetPatterns.add(formatClaudePermissionPath(`${escapedRealAtt}/**`)) : targetPatterns.add(formatClaudePermissionPath(escapedRealAtt));
39070
+ }
39071
+ } catch {
39072
+ }
39073
+ }
39074
+ let readRules = Array.from(targetPatterns).map((p) => `Read(${p})`), grepRules = Array.from(targetPatterns).map((p) => `Grep(${p})`), globRules = Array.from(targetPatterns).map((p) => `Glob(${p})`);
39075
+ rulesList.push(...readRules, ...grepRules, ...globRules);
39076
+ }
39077
+ let hasRules = rulesList.length > 0, toolsValue = hasRules ? "Read,Grep,Glob" : "", allowedToolsValue = hasRules ? rulesList.join(",") : "";
39078
+ if (hasRules) {
39079
+ let parsedVendor = vendorBu([allowedToolsValue]);
39080
+ if (parsedVendor.length !== rulesList.length)
39081
+ throw new Error(
39082
+ `Ambiguous Claude allowed-tools path serialization: vendor tokenizer split ${rulesList.length} rules into ${parsedVendor.length} tokens for path: ${workdir}`
39083
+ );
39084
+ for (let token4 of parsedVendor) {
39085
+ let parsed = parseToolRuleM8(token4);
39086
+ if (!parsed || parsed.rawContent === void 0)
39087
+ throw new Error(
39088
+ `Ambiguous Claude allowed-tools path serialization: vendor tokenizer produced bare tool rule '${token4}' for path: ${workdir}`
39089
+ );
39090
+ }
39091
+ }
39092
+ argv.push(
39093
+ "--tools",
39094
+ toolsValue,
39095
+ "--allowed-tools",
39096
+ allowedToolsValue,
39097
+ "--settings",
39098
+ JSON.stringify({
39099
+ permissions: {
39100
+ defaultMode: "dontAsk",
39101
+ allow: rulesList
39102
+ }
39103
+ }),
39104
+ "--setting-sources",
39105
+ "user",
39106
+ "--strict-mcp-config"
39107
+ );
39108
+ for (let d of addDirs)
39109
+ argv.push("--add-dir", d);
39110
+ return {
39111
+ argv,
39112
+ capture: { kind: "stdout" }
39113
+ };
39114
+ }
38696
39115
  var AGY_MODEL_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.:/-]*$/;
38697
39116
  function resolveAgyModel(env = process.env) {
38698
39117
  let raw = env.CODEVIBE_AGY_MODEL;
@@ -38705,15 +39124,8 @@ function resolveAgyModel(env = process.env) {
38705
39124
  }
38706
39125
  var AGY_IMPLEMENTOR_PRINT_TIMEOUT = "3600s";
38707
39126
  function agyImplementorArgvPrefix(mode) {
38708
- let model = resolveAgyModel();
38709
- return [
38710
- "agy",
38711
- ...mode === "plan" ? ["--sandbox"] : ["--dangerously-skip-permissions"],
38712
- ...model !== null ? ["--model", model] : [],
38713
- "--print-timeout",
38714
- AGY_IMPLEMENTOR_PRINT_TIMEOUT,
38715
- "--add-dir"
38716
- ];
39127
+ let model = resolveAgyModel(), argv = ["agy"];
39128
+ return mode === "plan" ? argv.push("--mode", "plan", "--sandbox") : argv.push("--dangerously-skip-permissions"), model !== null && argv.push("--model", model), argv.push("--print-timeout", AGY_IMPLEMENTOR_PRINT_TIMEOUT, "--add-dir"), argv;
38717
39129
  }
38718
39130
  function agyImplementorAllowlistEntries() {
38719
39131
  return [
@@ -38961,6 +39373,8 @@ var IMPLEMENTOR_COMMAND_ALLOWLIST = [
38961
39373
  // is part of the fixed argv → mirrored here or the drift test fails.
38962
39374
  "claude --print --output-format json --allowed-tools Read,Grep,Glob",
38963
39375
  buildClaudeNoToolsPlanArgv().argv.join(" "),
39376
+ buildClaudeReadOnlyAdvisoryPlanArgv().argv.join(" "),
39377
+ "claude --print --output-format json --tools Read,Grep,Glob --allowed-tools *",
38964
39378
  "claude --print --bare --output-format json --allowed-tools Read,Grep,Glob --settings *",
38965
39379
  "claude --print --output-format json --allowed-tools Read,Grep,Glob,Edit,Write,Bash",
38966
39380
  // Agent web access allowlist templates (Phase 1 Claude Code)
@@ -39267,8 +39681,7 @@ var HookBridge = class {
39267
39681
  };
39268
39682
 
39269
39683
  // src/local-executor/local-executor-impl.ts
39270
- var import_node_crypto34 = require("node:crypto"), syncFs = __toESM(require("node:fs"));
39271
- var path60 = __toESM(require("node:path"));
39684
+ var import_node_crypto34 = require("node:crypto"), syncFs = __toESM(require("node:fs")), os37 = __toESM(require("node:os")), path60 = __toESM(require("node:path"));
39272
39685
 
39273
39686
  // src/local-executor/class-a-emit.ts
39274
39687
  var import_node_fs20 = require("node:fs"), import_ulid3 = require("ulid");
@@ -39497,17 +39910,17 @@ function pick(obj, camel, snake) {
39497
39910
  }
39498
39911
  function normalizeTrackAssignedWire(raw) {
39499
39912
  if (!raw || typeof raw != "object") return {};
39500
- let o = raw, osRaw = pick(o, "ownershipScope", "ownership_scope"), os52 = osRaw && typeof osRaw == "object" ? osRaw : {}, sharedContracts = pick(os52, "sharedContracts", "shared_contracts"), ccRaw = pick(o, "conflictContext", "conflict_context"), conflictContext = ccRaw && typeof ccRaw == "object" ? normalizeConflictContext(ccRaw) : void 0;
39913
+ let o = raw, osRaw = pick(o, "ownershipScope", "ownership_scope"), os53 = osRaw && typeof osRaw == "object" ? osRaw : {}, sharedContracts = pick(os53, "sharedContracts", "shared_contracts"), ccRaw = pick(o, "conflictContext", "conflict_context"), conflictContext = ccRaw && typeof ccRaw == "object" ? normalizeConflictContext(ccRaw) : void 0;
39501
39914
  return {
39502
39915
  task_group_id: pick(o, "taskGroupId", "task_group_id"),
39503
39916
  track_index: pick(o, "trackIndex", "track_index"),
39504
39917
  task_id: pick(o, "taskId", "task_id"),
39505
39918
  implementor_agent: upperAgentKind(pick(o, "implementorAgent", "implementor_agent")),
39506
39919
  ownership_scope: {
39507
- write_paths: pick(os52, "writePaths", "write_paths"),
39920
+ write_paths: pick(os53, "writePaths", "write_paths"),
39508
39921
  shared_contracts: sharedContracts,
39509
- test_surfaces: pick(os52, "testSurfaces", "test_surfaces"),
39510
- auto_modified_files: pick(os52, "autoModifiedFiles", "auto_modified_files")
39922
+ test_surfaces: pick(os53, "testSurfaces", "test_surfaces"),
39923
+ auto_modified_files: pick(os53, "autoModifiedFiles", "auto_modified_files")
39511
39924
  },
39512
39925
  is_shared_test_owner: pick(o, "isSharedTestOwner", "is_shared_test_owner"),
39513
39926
  state_dir: pick(o, "stateDir", "state_dir"),
@@ -39613,8 +40026,8 @@ function isU32(value) {
39613
40026
  }
39614
40027
  function isValidTrackAssignedPayload(payload) {
39615
40028
  if (!payload || typeof payload != "object" || typeof payload.task_group_id != "string" || payload.task_group_id.length === 0 || typeof payload.task_id != "string" || payload.task_id.length === 0 || typeof payload.state_dir != "string" || payload.state_dir.length === 0 || typeof payload.dispatch_id != "string" || payload.dispatch_id.length === 0 || !isU32(payload.track_index) || !isU32(payload.dispatch_generation) || typeof payload.implementor_agent != "string" || !VALID_IMPLEMENTOR_AGENTS.has(payload.implementor_agent) || typeof payload.is_shared_test_owner != "boolean") return !1;
39616
- let os52 = payload.ownership_scope;
39617
- return !(!os52 || typeof os52 != "object" || !Array.isArray(os52.write_paths) || !Array.isArray(os52.auto_modified_files) || !Array.isArray(os52.shared_contracts) || !Array.isArray(os52.test_surfaces) || !os52.write_paths.every((p) => typeof p == "string") || !os52.auto_modified_files.every((p) => typeof p == "string") || !os52.test_surfaces.every((p) => typeof p == "string"));
40029
+ let os53 = payload.ownership_scope;
40030
+ return !(!os53 || typeof os53 != "object" || !Array.isArray(os53.write_paths) || !Array.isArray(os53.auto_modified_files) || !Array.isArray(os53.shared_contracts) || !Array.isArray(os53.test_surfaces) || !os53.write_paths.every((p) => typeof p == "string") || !os53.auto_modified_files.every((p) => typeof p == "string") || !os53.test_surfaces.every((p) => typeof p == "string"));
39618
40031
  }
39619
40032
  function isValidMergeGateDispatchPayload(payload) {
39620
40033
  if (!payload || typeof payload != "object" || typeof payload.task_group_id != "string" || payload.task_group_id.length === 0 || typeof payload.merge_gate_id != "string" || payload.merge_gate_id.length === 0 || typeof payload.dispatch_id != "string" || payload.dispatch_id.length === 0 || payload.integration_check_spec === void 0 || payload.integration_check_spec === null || !isU32(payload.owner_track_index) || !isU32(payload.dispatch_generation) || payload.decision_protocol_version !== void 0 && payload.decision_protocol_version !== 1 && payload.decision_protocol_version !== 2) return !1;
@@ -41567,9 +41980,9 @@ async function main(){
41567
41980
  main().catch(err=>{process.stderr.write(String(err&&err.stack?err.stack:err));process.exitCode=1;});
41568
41981
  `;
41569
41982
  function safeRelative(root, candidate) {
41570
- let relative22 = path45.relative(root, path45.resolve(candidate));
41571
- if (relative22.length === 0 || path45.isAbsolute(relative22) || relative22 === ".." || relative22.startsWith(`..${path45.sep}`) || relative22.includes("\0") || relative22.split(path45.sep).some((part) => part.length === 0 || part === "." || part === "..")) throw new Error("workspace materializer path escaped its root");
41572
- return relative22;
41983
+ let relative23 = path45.relative(root, path45.resolve(candidate));
41984
+ if (relative23.length === 0 || path45.isAbsolute(relative23) || relative23 === ".." || relative23.startsWith(`..${path45.sep}`) || relative23.includes("\0") || relative23.split(path45.sep).some((part) => part.length === 0 || part === "." || part === "..")) throw new Error("workspace materializer path escaped its root");
41985
+ return relative23;
41573
41986
  }
41574
41987
  function createWorkspaceMaterializerSession(opts) {
41575
41988
  assertWorkspaceRootAuthoritySchema(opts.sourceRoot), assertWorkspaceRootAuthoritySchema(opts.destinationRoot);
@@ -44184,29 +44597,29 @@ async function sealSnapshotTreeDurably(snapshotRoot, snapshotRootAuthority, env)
44184
44597
  }
44185
44598
  );
44186
44599
  for (let entry5 of entries) {
44187
- let relative22 = current.relative ? path48.join(current.relative, entry5.name) : entry5.name, absolute = path48.join(snapshotRoot, relative22), stat13 = await fs39.lstat(absolute, { bigint: !0 });
44600
+ let relative23 = current.relative ? path48.join(current.relative, entry5.name) : entry5.name, absolute = path48.join(snapshotRoot, relative23), stat13 = await fs39.lstat(absolute, { bigint: !0 });
44188
44601
  if (stat13.isDirectory() && !stat13.isSymbolicLink()) {
44189
44602
  let enumerated = boundedDirectoryStat(entry5);
44190
44603
  if (!enumerated || enumerated.dev !== stat13.dev.toString() || enumerated.ino !== stat13.ino.toString())
44191
- throw new Error(`snapshot directory changed after enumeration: ${relative22}`);
44192
- pending2.push({ absolute, relative: relative22, identity: enumerated });
44604
+ throw new Error(`snapshot directory changed after enumeration: ${relative23}`);
44605
+ pending2.push({ absolute, relative: relative23, identity: enumerated });
44193
44606
  continue;
44194
44607
  }
44195
44608
  if (stat13.isSymbolicLink())
44196
44609
  continue;
44197
44610
  if (!stat13.isFile())
44198
- throw new Error(`snapshot durability seal refuses special file: ${relative22}`);
44611
+ throw new Error(`snapshot durability seal refuses special file: ${relative23}`);
44199
44612
  let handle = await fs39.open(absolute, import_node_fs23.constants.O_RDONLY | import_node_fs23.constants.O_NOFOLLOW);
44200
44613
  try {
44201
44614
  let before = await handle.stat({ bigint: !0 });
44202
44615
  if (!before.isFile() || before.dev !== stat13.dev || before.ino !== stat13.ino)
44203
- throw new Error(`snapshot file changed before durability sync: ${relative22}`);
44616
+ throw new Error(`snapshot file changed before durability sync: ${relative23}`);
44204
44617
  await env.beforeSnapshotDurabilitySync?.("file", absolute), await handle.sync();
44205
44618
  let [after, live] = await Promise.all([
44206
44619
  handle.stat({ bigint: !0 }),
44207
44620
  fs39.lstat(absolute, { bigint: !0 })
44208
44621
  ]);
44209
- if (!after.isFile() || !live.isFile() || live.isSymbolicLink() || after.dev !== before.dev || after.ino !== before.ino || live.dev !== before.dev || live.ino !== before.ino || after.size !== before.size || after.mtimeNs !== before.mtimeNs || after.ctimeNs !== before.ctimeNs) throw new Error(`snapshot file changed during durability sync: ${relative22}`);
44622
+ if (!after.isFile() || !live.isFile() || live.isSymbolicLink() || after.dev !== before.dev || after.ino !== before.ino || live.dev !== before.dev || live.ino !== before.ino || after.size !== before.size || after.mtimeNs !== before.mtimeNs || after.ctimeNs !== before.ctimeNs) throw new Error(`snapshot file changed during durability sync: ${relative23}`);
44210
44623
  } finally {
44211
44624
  await handle.close().catch(() => {
44212
44625
  });
@@ -44715,12 +45128,12 @@ function assertStateMarkerLifecycleCapacity(marker, maxBytes) {
44715
45128
  }, maxBytes);
44716
45129
  }
44717
45130
  async function retireFailedCreateStateMarker(stateFile, taskId, stateRootAuthority, maxBytes) {
44718
- let relative22 = path48.relative(stateRootAuthority.canonicalPath, stateFile), parts = relative22.split(path48.sep);
44719
- if (parts.length !== 2 || parts[0] !== taskId || path48.isAbsolute(relative22) || parts.some((part) => part === ".."))
45131
+ let relative23 = path48.relative(stateRootAuthority.canonicalPath, stateFile), parts = relative23.split(path48.sep);
45132
+ if (parts.length !== 2 || parts[0] !== taskId || path48.isAbsolute(relative23) || parts.some((part) => part === ".."))
44720
45133
  throw new Error("failed-create marker is outside its state-root authority");
44721
45134
  let read = await anchoredReadFileWithinRoot(
44722
45135
  stateRootAuthority.canonicalPath,
44723
- relative22,
45136
+ relative23,
44724
45137
  {
44725
45138
  maxBytes,
44726
45139
  expectedRoot: stateRootAuthority,
@@ -55729,14 +56142,14 @@ var LocalExecutorImpl = class {
55729
56142
  deps.deviceSigningKey
55730
56143
  )), this.bridge = new HookBridge({
55731
56144
  emitter: this.emitter,
55732
- emitShellEvent: this.emitShellEvent,
56145
+ emitShellEvent: this.emitShellEvent ?? (async () => ({ kind: "dropped", reason: "unwired" })),
55733
56146
  getAuthorityScope: () => this.baseScope,
55734
56147
  getCurrentTaskId: () => null,
55735
56148
  getContextWithoutTask: () => this.baseCtx,
55736
56149
  adapter: this.adapter
55737
56150
  }), this.classBConsumer = new ClassBConsumer({
55738
56151
  emitter: this.emitter,
55739
- emitShellEvent: this.emitShellEvent,
56152
+ emitShellEvent: this.emitShellEvent ?? (async () => ({ kind: "dropped", reason: "unwired" })),
55740
56153
  getContext: () => this.contextOrFallback(),
55741
56154
  advanceAuthorityScope: (newScope, taskId) => {
55742
56155
  this.scopeByTask.set(taskId, mergeAuthorityScope(this.baseScope, newScope));
@@ -55946,7 +56359,56 @@ var LocalExecutorImpl = class {
55946
56359
  args: launchArgs,
55947
56360
  onExit,
55948
56361
  substrateEngaged
55949
- } = await this.engageSubstrateForSpawn(promptBoundArgs, spawnTaskScope), outerBoundaryConfines = substrateEngaged || isTrustedContainerBoundary(), finalArgs = {
56362
+ } = await this.engageSubstrateForSpawn(promptBoundArgs, spawnTaskScope), outerBoundaryConfines = substrateEngaged || isTrustedContainerBoundary(), claudeIsolatedConfigDir = null, claudeSecureStorageConfigDir = null, cleanupClaudeConfig = null;
56363
+ if ((launchArgs.agentKind ?? this.adapter) === "CLAUDE" && launchArgs.role === "implementor" && launchArgs.lifecycleAudit === "none" && isStrictReadOnlyAdvisoryClaude(launchArgs.argv) && !launchArgs.envOverrides?.CLAUDE_CONFIG_DIR && !outerBoundaryConfines) {
56364
+ claudeIsolatedConfigDir = syncFs.mkdtempSync(
56365
+ path60.join(os37.tmpdir(), `cv-claude-cfg-${process.pid}-${(0, import_node_crypto34.randomUUID)().slice(0, 8)}-`)
56366
+ );
56367
+ let ambientClaudeDir = process.env.CLAUDE_CONFIG_DIR || path60.join(os37.homedir(), ".claude");
56368
+ claudeSecureStorageConfigDir = process.env.CLAUDE_SECURESTORAGE_CONFIG_DIR !== void 0 ? process.env.CLAUDE_SECURESTORAGE_CONFIG_DIR : process.env.CLAUDE_CONFIG_DIR !== void 0 ? process.env.CLAUDE_CONFIG_DIR : "";
56369
+ try {
56370
+ let authFiles = [".credentials.json", "credentials.json", "config.json", "auth.json"];
56371
+ for (let f of authFiles) {
56372
+ let src = path60.join(ambientClaudeDir, f);
56373
+ if (syncFs.existsSync(src)) {
56374
+ let dest = path60.join(claudeIsolatedConfigDir, f);
56375
+ try {
56376
+ syncFs.copyFileSync(src, dest), syncFs.chmodSync(dest, 384);
56377
+ } catch (copyErr) {
56378
+ throw new Error(
56379
+ `failed to copy Claude credential file ${f}: ${copyErr?.message}`
56380
+ );
56381
+ }
56382
+ }
56383
+ }
56384
+ let settingsIdx = launchArgs.argv.indexOf("--settings"), settingsPayload = settingsIdx !== -1 && settingsIdx + 1 < launchArgs.argv.length ? launchArgs.argv[settingsIdx + 1] : "{}";
56385
+ syncFs.writeFileSync(
56386
+ path60.join(claudeIsolatedConfigDir, "settings.json"),
56387
+ settingsPayload,
56388
+ { mode: 384 }
56389
+ );
56390
+ } catch (setupErr) {
56391
+ let cleanupErr = null;
56392
+ try {
56393
+ syncFs.rmSync(claudeIsolatedConfigDir, { recursive: !0, force: !0 }), syncFs.existsSync(claudeIsolatedConfigDir) && (cleanupErr = new Error(`setup cleanup failed: ${claudeIsolatedConfigDir} still exists`));
56394
+ } catch (err) {
56395
+ cleanupErr = err;
56396
+ }
56397
+ throw cleanupErr ? Object.assign(
56398
+ new Error(
56399
+ `ephemeral Claude config setup failed (${setupErr?.message}) and cleanup also failed (${cleanupErr?.message})`
56400
+ ),
56401
+ { cause: setupErr, cleanupCause: cleanupErr }
56402
+ ) : setupErr;
56403
+ }
56404
+ cleanupClaudeConfig = async () => {
56405
+ if (claudeIsolatedConfigDir && syncFs.existsSync(claudeIsolatedConfigDir) && (syncFs.rmSync(claudeIsolatedConfigDir, { recursive: !0, force: !0 }), syncFs.existsSync(claudeIsolatedConfigDir)))
56406
+ throw new Error(
56407
+ `failed to cleanup ephemeral Claude config directory: ${claudeIsolatedConfigDir} still exists`
56408
+ );
56409
+ };
56410
+ }
56411
+ let finalArgs = {
55950
56412
  ...launchArgs,
55951
56413
  argv: applyOuterBoundarySandbox(
55952
56414
  relocateCodexLastMessageForSubstrate(
@@ -55956,11 +56418,24 @@ var LocalExecutorImpl = class {
55956
56418
  ),
55957
56419
  launchArgs.agentKind ?? this.adapter,
55958
56420
  outerBoundaryConfines
55959
- )
55960
- }, spawnScope = outerBoundaryConfines ? withBoundaryConfinedCommandAuthority(spawnTaskScope) : spawnTaskScope, handle = await this.spawnWithLifecycle(
56421
+ ),
56422
+ ...claudeIsolatedConfigDir ? {
56423
+ envOverrides: {
56424
+ ...launchArgs.envOverrides,
56425
+ CLAUDE_CONFIG_DIR: claudeIsolatedConfigDir,
56426
+ ...claudeSecureStorageConfigDir !== null ? { CLAUDE_SECURESTORAGE_CONFIG_DIR: claudeSecureStorageConfigDir } : {}
56427
+ }
56428
+ } : {}
56429
+ }, spawnScope = outerBoundaryConfines ? withBoundaryConfinedCommandAuthority(spawnTaskScope) : spawnTaskScope, combinedOnExit = async () => {
56430
+ try {
56431
+ cleanupClaudeConfig && await cleanupClaudeConfig();
56432
+ } finally {
56433
+ onExit && await onExit();
56434
+ }
56435
+ }, handle = await this.spawnWithLifecycle(
55961
56436
  finalArgs,
55962
56437
  (a) => spawnImplementor(spawnScope, a),
55963
- onExit
56438
+ combinedOnExit
55964
56439
  );
55965
56440
  return advisoryAdmission && this.bindAdvisorySpawn(advisoryAdmission, handle), handle;
55966
56441
  } catch (error) {
@@ -56063,19 +56538,59 @@ var LocalExecutorImpl = class {
56063
56538
  * takes the legacy path.
56064
56539
  */
56065
56540
  async engageSubstrateForSpawn(args, scope) {
56541
+ let agentKind = args.agentKind ?? this.adapter;
56066
56542
  if (args.role !== "implementor" || this.substrateEngager === void 0) {
56067
- if (args.requireConfined)
56543
+ if (args.requireConfined) {
56544
+ if (args.lifecycleAudit === "none") {
56545
+ if (agentKind === "CLAUDE" && isStrictReadOnlyAdvisoryClaude(args.argv))
56546
+ return { args, substrateEngaged: !1 };
56547
+ if (agentKind === "CODEX" && isStrictReadOnlyAdvisoryCodex(args.argv))
56548
+ return { args, substrateEngaged: !1 };
56549
+ }
56068
56550
  throw new SpawnConfinementUnavailable(
56069
56551
  "requireConfined implementor spawn but no substrate engager is wired \u2014 fail closed"
56070
56552
  );
56553
+ }
56071
56554
  return { args, substrateEngaged: !1 };
56072
56555
  }
56073
- let agentKind = args.agentKind ?? this.adapter;
56074
56556
  if (args.lifecycleAudit === "none") {
56075
- if (args.requireConfined)
56557
+ if (agentKind === "ANTIGRAVITY") {
56558
+ if (args.requireConfined) {
56559
+ if (!this.substrateEngager)
56560
+ throw new SpawnConfinementUnavailable(
56561
+ "requireConfined advisory spawn for ANTIGRAVITY but no substrate engager is wired \u2014 fail closed"
56562
+ );
56563
+ let result2 = await this.substrateEngager({
56564
+ taskId: args.taskId ?? "advisory",
56565
+ agentKind,
56566
+ workdir: args.workingDir,
56567
+ role: args.role,
56568
+ requireConfined: !0,
56569
+ writableWorkdir: !1,
56570
+ ...args.processTreeId ? { processTreeId: args.processTreeId } : {}
56571
+ });
56572
+ if (result2.mode === "substrate")
56573
+ return {
56574
+ args: { ...args, substrate: result2.substrateHandle },
56575
+ onExit: () => result2.teardown(),
56576
+ substrateEngaged: !0
56577
+ };
56578
+ throw await result2.teardown().catch(() => {
56579
+ }), new SpawnConfinementUnavailable(
56580
+ `requireConfined advisory spawn for ${agentKind} but substrate engaged reduced_trust (${result2.reducedTrustReason}) \u2014 fail closed`
56581
+ );
56582
+ }
56583
+ return { args, substrateEngaged: !1 };
56584
+ }
56585
+ if (args.requireConfined) {
56586
+ if (agentKind === "CLAUDE" && isStrictReadOnlyAdvisoryClaude(args.argv))
56587
+ return { args, substrateEngaged: !1 };
56588
+ if (agentKind === "CODEX" && isStrictReadOnlyAdvisoryCodex(args.argv))
56589
+ return { args, substrateEngaged: !1 };
56076
56590
  throw new SpawnConfinementUnavailable(
56077
- "requireConfined implementor spawn cannot opt out of lifecycle audit/substrate \u2014 fail closed"
56591
+ `requireConfined advisory spawn for ${agentKind} requires a strictly validated read-only invocation \u2014 fail closed`
56078
56592
  );
56593
+ }
56079
56594
  return { args, substrateEngaged: !1 };
56080
56595
  }
56081
56596
  let effectiveTaskId = args.taskId ?? null;
@@ -58168,7 +58683,7 @@ var NO_OP_PASS = Object.freeze({
58168
58683
  });
58169
58684
 
58170
58685
  // src/local-executor/durable-store.ts
58171
- var crypto24 = __toESM(require("node:crypto")), os37 = __toESM(require("node:os")), path64 = __toESM(require("node:path"));
58686
+ var crypto24 = __toESM(require("node:crypto")), os38 = __toESM(require("node:os")), path64 = __toESM(require("node:path"));
58172
58687
  init_logger2();
58173
58688
  var DIR_MODE8 = 448, FILE_MODE10 = 384, MAX_STORE_JSON_BYTES = 64 * 1024 * 1024, MAX_PREIMAGE_BYTES = MAX_REVIEW_FILE_BYTES, MAX_PREIMAGE_AGGREGATE_BYTES = MAX_PROMOTION_PREIMAGE_AGGREGATE_BYTES, REVERT_MANIFEST_VERSION = 1, ABSENT_PREIMAGE = "ABSENT", ORPHAN_TTL_MS = 2880 * 60 * 1e3, MANIFEST_FILE = "manifest.json", JOURNAL_FILE = "journal.json", PREIMAGE_DIR = "preimages", TRACK_TIPS_FILE = "track-tips.json", TRACK_TIPS_VERSION = 1, TRACK_BUNDLES_FILE = "track-bundles.json", TRACK_BUNDLES_VERSION = 1, OWNER_LEASE_RE = /^\.codevibe-owner-[0-9a-f]{64}\.json$/, STORE_HELPER_SOURCE_ENV = "CODEVIBE_STORE_HELPER_SOURCE_B64", STORE_HELPER_BOOTSTRAP = `const k=${JSON.stringify(STORE_HELPER_SOURCE_ENV)};const s=process.env[k];delete process.env[k];if(!s)throw new Error('missing store helper source');Function(Buffer.from(s,'base64').toString('utf8'))();`, STORE_HELPER_SOURCE = String.raw`
58174
58689
  const fs = require('node:fs');
@@ -58380,7 +58895,7 @@ function validateTrackBundlesRecord(value, taskGroupId, limits = {
58380
58895
  return value;
58381
58896
  }
58382
58897
  function defaultRoot4() {
58383
- return path64.join(os37.homedir(), ".codevibe", "revert-state");
58898
+ return path64.join(os38.homedir(), ".codevibe", "revert-state");
58384
58899
  }
58385
58900
  function hashContent2(content) {
58386
58901
  return crypto24.createHash("sha256").update(Buffer.from(content, "utf8")).digest("hex");
@@ -58641,7 +59156,7 @@ var RevertManifestStore = class {
58641
59156
  if (!this.rootDirectoryPromise) {
58642
59157
  let creation = (async () => {
58643
59158
  let durable = await ensureDurableDirectoryTree(root, {
58644
- anchorPath: root === path64.resolve(os37.homedir()) || root.startsWith(`${path64.resolve(os37.homedir())}${path64.sep}`) ? path64.resolve(os37.homedir()) : path64.dirname(root),
59159
+ anchorPath: root === path64.resolve(os38.homedir()) || root.startsWith(`${path64.resolve(os38.homedir())}${path64.sep}`) ? path64.resolve(os38.homedir()) : path64.dirname(root),
58645
59160
  mode: DIR_MODE8,
58646
59161
  enforceTargetMode: !0,
58647
59162
  helperTimeoutMs: this.helperTimeoutMs,
@@ -61636,7 +62151,7 @@ function withImplementorCredentialPreflight(engage, store) {
61636
62151
  }
61637
62152
 
61638
62153
  // src/substrate/docker.ts
61639
- var import_node_fs30 = require("node:fs"), os39 = __toESM(require("node:os")), path67 = __toESM(require("node:path"));
62154
+ var import_node_fs30 = require("node:fs"), os40 = __toESM(require("node:os")), path67 = __toESM(require("node:path"));
61640
62155
 
61641
62156
  // src/substrate/command-runner.ts
61642
62157
  var import_node_child_process9 = require("node:child_process");
@@ -61939,7 +62454,7 @@ var DockerHandle = class {
61939
62454
  }
61940
62455
  }, DockerSubstrate = class {
61941
62456
  constructor(deps = {}) {
61942
- this.runner = deps.runner ?? new ChildProcessCommandRunner(), this.forwarderFactory = deps.forwarderFactory ?? defaultForwarderFactory, this.onEgressDenied = deps.onEgressDenied ?? NOOP_EGRESS_HOOK, this.image = deps.image ?? DEFAULT_IMAGE, this.sessionRoot = deps.sessionRoot ?? os39.tmpdir(), this.acquireWorkdirLease = deps.acquireWorkdirLease ?? acquireWorkspaceRootLease;
62457
+ this.runner = deps.runner ?? new ChildProcessCommandRunner(), this.forwarderFactory = deps.forwarderFactory ?? defaultForwarderFactory, this.onEgressDenied = deps.onEgressDenied ?? NOOP_EGRESS_HOOK, this.image = deps.image ?? DEFAULT_IMAGE, this.sessionRoot = deps.sessionRoot ?? os40.tmpdir(), this.acquireWorkdirLease = deps.acquireWorkdirLease ?? acquireWorkspaceRootLease;
61943
62458
  }
61944
62459
  async launch(spec) {
61945
62460
  await this.assertDockerAvailable();
@@ -62070,7 +62585,7 @@ function randomId() {
62070
62585
  }
62071
62586
 
62072
62587
  // src/substrate/sandbox-exec.ts
62073
- var import_node_fs31 = require("node:fs"), os40 = __toESM(require("node:os")), path68 = __toESM(require("node:path"));
62588
+ var import_node_fs31 = require("node:fs"), os41 = __toESM(require("node:os")), path68 = __toESM(require("node:path"));
62074
62589
  var import_node_path2 = require("node:path");
62075
62590
  var NODE_PREFIXES = ["/opt/homebrew", "/usr/local", "/opt/local"], FILE_KEYCHAIN_DIR_NAME = ".codevibe", SYSCTL_READ_ALLOW = [
62076
62591
  '(sysctl-name-prefix "hw.")',
@@ -62109,7 +62624,12 @@ function nodeReadDirs(execPath) {
62109
62624
  }
62110
62625
  return [...dirs];
62111
62626
  }
62112
- function buildSeatbeltProfile(workdir, execPath = process.execPath, extraReadDirs = [], homedir40 = os40.homedir(), auditDir = null, extraWriteDirs = [], networkPosture = "loopback") {
62627
+ function isInsideOrEqual2(child, parent) {
62628
+ if (child === parent) return !0;
62629
+ let rel = path68.relative(parent, child);
62630
+ return rel.length > 0 && !rel.startsWith("..") && !path68.isAbsolute(rel);
62631
+ }
62632
+ function buildSeatbeltProfile(workdir, execPath = process.execPath, extraReadDirs = [], homedir41 = os41.homedir(), auditDir = null, extraWriteDirs = [], networkPosture = "loopback", writableWorkdir = !0) {
62113
62633
  let esc = (s) => s.replace(/"/g, '\\"'), wd = esc(workdir), lines = [
62114
62634
  "(version 1)",
62115
62635
  "(deny default)",
@@ -62221,7 +62741,14 @@ function buildSeatbeltProfile(workdir, execPath = process.execPath, extraReadDir
62221
62741
  for (let dir of extraWriteDirs) subpathReadGrants.push(dir);
62222
62742
  for (let dir of subpathReadGrants)
62223
62743
  lines.push(`(allow file-read* (subpath "${esc(dir)}"))`);
62224
- lines.push(`(allow file-write* (subpath "${wd}"))`);
62744
+ if (!writableWorkdir) {
62745
+ for (let dir of extraWriteDirs)
62746
+ if (isInsideOrEqual2(dir, wd))
62747
+ throw new Error(
62748
+ `writable runtime grant "${dir}" is inside read-only workdir "${wd}" \u2014 conflicting boundary`
62749
+ );
62750
+ }
62751
+ writableWorkdir && lines.push(`(allow file-write* (subpath "${wd}"))`);
62225
62752
  for (let dir of extraWriteDirs)
62226
62753
  lines.push(`(allow file-write* (subpath "${esc(dir)}"))`);
62227
62754
  let litReads = /* @__PURE__ */ new Set();
@@ -62233,11 +62760,11 @@ function buildSeatbeltProfile(workdir, execPath = process.execPath, extraReadDir
62233
62760
  `(deny file-read* (subpath "${wd}/.git"))`,
62234
62761
  `(deny file-write* (subpath "${wd}/.git"))`
62235
62762
  );
62236
- let fileKeychainDir = esc(path68.join(homedir40, FILE_KEYCHAIN_DIR_NAME));
62763
+ let fileKeychainDir = esc(path68.join(homedir41, FILE_KEYCHAIN_DIR_NAME));
62237
62764
  if (lines.push(
62238
62765
  `(deny file-read* (subpath "${fileKeychainDir}"))`,
62239
62766
  `(deny file-write* (subpath "${fileKeychainDir}"))`
62240
- ), auditDir !== null) {
62767
+ ), writableWorkdir || lines.push(`(deny file-write* (subpath "${wd}"))`), auditDir !== null) {
62241
62768
  let ad = esc(auditDir);
62242
62769
  lines.push(
62243
62770
  `(deny file-read* (subpath "${ad}"))`,
@@ -62246,12 +62773,12 @@ function buildSeatbeltProfile(workdir, execPath = process.execPath, extraReadDir
62246
62773
  }
62247
62774
  return networkPosture === "open" ? lines.push(
62248
62775
  // A1d resolver-confinement row: deny all network, then re-allow ALL
62249
- // OUTBOUND (inbound stays denied). The broker-less reduced-trust agent
62250
- // (agy/gemini) dials its vendor directly with its own file-based auth —
62251
- // egress is NOT pinned on this row (the engage result badges it); the
62252
- // row's confinement objective is the FS-write fence above.
62776
+ // OUTBOUND (external inbound stays denied). Loopback bind/inbound is permitted
62777
+ // for local IPC (e.g. Antigravity local RPC daemon on 127.0.0.1).
62253
62778
  "(deny network*)",
62254
62779
  "(allow network-outbound)",
62780
+ '(allow network-bind (local ip "localhost:*"))',
62781
+ '(allow network-inbound (local ip "localhost:*"))',
62255
62782
  ""
62256
62783
  ) : lines.push(
62257
62784
  // Network: deny all, then re-allow loopback ONLY (the coarse tier).
@@ -62298,12 +62825,12 @@ var SandboxExecHandle = class {
62298
62825
  `agent binary '${argv[0]}' not found on the host PATH \u2014 cannot exec in the sandbox`
62299
62826
  );
62300
62827
  let realArgv0 = await import_node_fs31.promises.realpath(resolvedArgv0).catch(() => resolvedArgv0), deniedRoots = [
62301
- path68.join(os40.homedir(), ".codevibe"),
62828
+ path68.join(os41.homedir(), ".codevibe"),
62302
62829
  path68.join(this.workdir, ".git"),
62303
62830
  ...this.auditDir !== null ? [this.auditDir] : []
62304
62831
  ], overlapsDeniedRoot = (d) => deniedRoots.some(
62305
62832
  (root) => d === root || d.startsWith(root + path68.sep) || root.startsWith(d + path68.sep)
62306
- ), broadRoots = /* @__PURE__ */ new Set(["/", "/Users", "/home", os40.homedir()]), binDirs = /* @__PURE__ */ new Set([path68.dirname(resolvedArgv0), path68.dirname(realArgv0)]);
62833
+ ), broadRoots = /* @__PURE__ */ new Set(["/", "/Users", "/home", os41.homedir()]), binDirs = /* @__PURE__ */ new Set([path68.dirname(resolvedArgv0), path68.dirname(realArgv0)]);
62307
62834
  for (let d of binDirs)
62308
62835
  if (overlapsDeniedRoot(d) || broadRoots.has(d))
62309
62836
  throw new SubstrateLaunchError(
@@ -62350,7 +62877,7 @@ ${allowLines}
62350
62877
  }
62351
62878
  }, SandboxExecSubstrate = class {
62352
62879
  constructor(deps = {}) {
62353
- this.runner = deps.runner ?? new ChildProcessCommandRunner(), this.profileRoot = deps.profileRoot ?? os40.tmpdir(), this.platform = deps.platform ?? process.platform;
62880
+ this.runner = deps.runner ?? new ChildProcessCommandRunner(), this.profileRoot = deps.profileRoot ?? os41.tmpdir(), this.platform = deps.platform ?? process.platform;
62354
62881
  }
62355
62882
  async launch(spec) {
62356
62883
  if (this.platform !== "darwin")
@@ -62416,12 +62943,13 @@ ${allowLines}
62416
62943
  canonicalWorkdir,
62417
62944
  process.execPath,
62418
62945
  extraReadDirs,
62419
- os40.homedir(),
62946
+ os41.homedir(),
62420
62947
  resolvedAuditDir,
62421
62948
  extraWriteDirs,
62422
62949
  // A1d — the resolver-confinement row threads `'open'`; absent → loopback
62423
62950
  // (the CP-7 broker tier, byte-identical profile).
62424
- spec.networkPosture ?? "loopback"
62951
+ spec.networkPosture ?? "loopback",
62952
+ spec.writableWorkdir !== !1
62425
62953
  ), profilePath = path68.join(this.profileRoot, `codevibe-seatbelt-${id5}.sb`);
62426
62954
  try {
62427
62955
  await import_node_fs31.promises.writeFile(profilePath, profile, { encoding: "utf8", mode: 384 });
@@ -63102,7 +63630,7 @@ function formatReviewerError(detail) {
63102
63630
  }
63103
63631
 
63104
63632
  // src/reviewer/providers/codex.ts
63105
- var fs51 = __toESM(require("fs")), os41 = __toESM(require("os")), path70 = __toESM(require("path")), import_uuid11 = require("uuid");
63633
+ var fs51 = __toESM(require("fs")), os42 = __toESM(require("os")), path70 = __toESM(require("path")), import_uuid11 = require("uuid");
63106
63634
 
63107
63635
  // src/reviewer/output-parser.ts
63108
63636
  var RESIDUAL_OBSERVATIONS_HEADING = "RESIDUAL OBSERVATIONS:";
@@ -63337,7 +63865,7 @@ function trimEnd(s) {
63337
63865
  var FAILURE_EVENT_TYPES = /* @__PURE__ */ new Set([
63338
63866
  "error",
63339
63867
  "turn.failed"
63340
- ]), USAGE_LIMIT_RE = /usage limit|purchase more credits|try again at|rate.?limit/i, AUTH_FAILED_RE = /unauthorized|not logged in|authenticat|invalid api key|\b401\b/i;
63868
+ ]), USAGE_LIMIT_RE = /(?:usage|weekly|daily|monthly|rate).?limit|purchase more credits|try again at/i, AUTH_FAILED_RE = /unauthorized|not logged in|authenticat|invalid api key|\b401\b/i;
63341
63869
  function classifyFailureMessage(message2) {
63342
63870
  return USAGE_LIMIT_RE.test(message2) ? "usage_limit" : AUTH_FAILED_RE.test(message2) ? "auth_failed" : null;
63343
63871
  }
@@ -63612,7 +64140,7 @@ function extractCodexUsageSnapshot(stdout, elapsedMs) {
63612
64140
  }) : unavailableUsageSnapshot("usage_not_reported", elapsedMs);
63613
64141
  }
63614
64142
  function makeLastMessagePath() {
63615
- return path70.join(os41.tmpdir(), `quorum-codex-last-${process.pid}-${(0, import_uuid11.v4)()}.txt`);
64143
+ return path70.join(os42.tmpdir(), `quorum-codex-last-${process.pid}-${(0, import_uuid11.v4)()}.txt`);
63616
64144
  }
63617
64145
  function makeLastMessageBasename() {
63618
64146
  return `quorum-codex-last-${process.pid}-${(0, import_uuid11.v4)()}.txt`;
@@ -63957,7 +64485,7 @@ function classifyImplementorQuotaFailure(exitCode, text2) {
63957
64485
  function defaultOutageNotificationRoot() {
63958
64486
  if (process.env.CODEVIBE_OUTAGE_NOTIFICATION_ROOT)
63959
64487
  return process.env.CODEVIBE_OUTAGE_NOTIFICATION_ROOT;
63960
- let baseHome = process.env.CODEVIBE_HOME_OVERRIDE || os42.homedir();
64488
+ let baseHome = process.env.CODEVIBE_HOME_OVERRIDE || os43.homedir();
63961
64489
  return path71.join(baseHome, ".codevibe", "outage-notifications");
63962
64490
  }
63963
64491
  function cleanDirectorySafely(dir) {
@@ -69940,7 +70468,7 @@ ${section}`);
69940
70468
  oursCommit,
69941
70469
  theirsCommit,
69942
70470
  baseSha
69943
- }), parentDir = await import_node_fs32.promises.mkdtemp(path71.join(os42.tmpdir(), "codevibe-wb2-resolve-"));
70471
+ }), parentDir = await import_node_fs32.promises.mkdtemp(path71.join(os43.tmpdir(), "codevibe-wb2-resolve-"));
69944
70472
  return resolveAgenticSlot({
69945
70473
  realRepoDir: this.deps.workingDir,
69946
70474
  // canonical loop workdir == repo root
@@ -75163,10 +75691,10 @@ function destructiveConfirmationQuestion(text2) {
75163
75691
  }
75164
75692
 
75165
75693
  // src/orchestration-shell/advisory-attachment-journal.ts
75166
- var import_node_crypto37 = require("node:crypto"), os43 = __toESM(require("node:os")), path72 = __toESM(require("node:path")), import_node_fs33 = require("node:fs");
75694
+ var import_node_crypto37 = require("node:crypto"), os44 = __toESM(require("node:os")), path72 = __toESM(require("node:path")), import_node_fs33 = require("node:fs");
75167
75695
  var JOURNAL_DIR_NAME = "advisory-attachment-authority", JOURNAL_MAX_RECORD_BYTES = 32 * 1024, JOURNAL_MAX_ENTRIES = 1e4, JOURNAL_MAX_METADATA_BYTES = 16 * 1024 * 1024, BATCH_NAME_RE = /^batch-([0-9a-f]{64})\.json$/, ATTACHMENT_NAME_RE = /^attachment-([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})\.json$/i, PROCESS_NAME_RE = /^process-([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})\.json$/i, RETIREMENT_NAME_RE = /^\.codevibe-retire-([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$/i, UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i, SHA256_RE = /^[0-9a-f]{64}$/;
75168
75696
  function resolveHome10() {
75169
- return (process.env.VITEST === "true" || process.env.NODE_ENV === "test") && process.env.CODEVIBE_HOME_OVERRIDE ? process.env.CODEVIBE_HOME_OVERRIDE : os43.homedir();
75697
+ return (process.env.VITEST === "true" || process.env.NODE_ENV === "test") && process.env.CODEVIBE_HOME_OVERRIDE ? process.env.CODEVIBE_HOME_OVERRIDE : os44.homedir();
75170
75698
  }
75171
75699
  function advisoryAttachmentJournalRoot(home = resolveHome10()) {
75172
75700
  return path72.join(home, ".codevibe", JOURNAL_DIR_NAME);
@@ -75353,8 +75881,8 @@ var AdvisoryAttachmentJournal = class {
75353
75881
  opts.anchorPath ?? (opts.rootDir ? path72.dirname(this.rootPath) : resolveHome10())
75354
75882
  ), this.currentOwner = opts.currentProcessIdentityFn ?? currentProcessIdentity, this.ownerIsAlive = opts.isProcessIdentityAliveFn ?? isProcessIdentityAlive, this.retryProcessOwner = opts.retryProcessCleanupOwnerFn ?? retryProcessCleanupOwner, this.mintId = opts.newId ?? import_node_crypto37.randomUUID, this.now = opts.now ?? Date.now, this.retirementSettleMs = opts.retirementSettleMs ?? 1e3, !Number.isSafeInteger(this.retirementSettleMs) || this.retirementSettleMs < 0 || this.retirementSettleMs > 1e4)
75355
75883
  throw new Error("invalid advisory attachment retirement settle window");
75356
- let relative22 = path72.relative(this.anchorPath, this.rootPath);
75357
- if (relative22 === ".." || relative22.startsWith(`..${path72.sep}`) || path72.isAbsolute(relative22))
75884
+ let relative23 = path72.relative(this.anchorPath, this.rootPath);
75885
+ if (relative23 === ".." || relative23.startsWith(`..${path72.sep}`) || path72.isAbsolute(relative23))
75358
75886
  throw new Error("advisory attachment journal root escaped its private anchor");
75359
75887
  }
75360
75888
  async openRoot(create) {
@@ -76158,7 +76686,7 @@ function classifyDurableWorkspaceOutcome(event, displayedSessionId, decryptedMet
76158
76686
  }
76159
76687
 
76160
76688
  // src/orchestration-shell/workspace-terminal-coordinator.ts
76161
- var crypto29 = __toESM(require("node:crypto")), import_node_fs34 = require("node:fs"), os44 = __toESM(require("node:os")), path73 = __toESM(require("node:path"));
76689
+ var crypto29 = __toESM(require("node:crypto")), import_node_fs34 = require("node:fs"), os45 = __toESM(require("node:os")), path73 = __toESM(require("node:path"));
76162
76690
  init_logger2();
76163
76691
 
76164
76692
  // src/orchestration-shell/emit-shell-event.ts
@@ -76781,7 +77309,7 @@ var WorkspaceTerminalCoordinator = class {
76781
77309
  this.operationChain = Promise.resolve();
76782
77310
  if (!options.session.sessionGenerationId || !UUID_RE5.test(options.session.sessionGenerationId))
76783
77311
  throw new Error("WorkspaceTerminalCoordinator: session generation is unavailable");
76784
- this.session = options.session, this.appsyncClient = options.appsyncClient, this.getSessionKey = options.getSessionKey, this.rootDir = options.rootDir ?? path73.join(os44.homedir(), ".codevibe"), this.now = options.now ?? (() => /* @__PURE__ */ new Date()), this.onSyncIssue = options.onSyncIssue;
77312
+ this.session = options.session, this.appsyncClient = options.appsyncClient, this.getSessionKey = options.getSessionKey, this.rootDir = options.rootDir ?? path73.join(os45.homedir(), ".codevibe"), this.now = options.now ?? (() => /* @__PURE__ */ new Date()), this.onSyncIssue = options.onSyncIssue;
76785
77313
  }
76786
77314
  generation() {
76787
77315
  return this.session.sessionGenerationId;
@@ -77343,7 +77871,7 @@ var WorkspaceTerminalCoordinator = class {
77343
77871
  };
77344
77872
 
77345
77873
  // src/orchestration-shell/decision-effect.ts
77346
- var crypto30 = __toESM(require("node:crypto")), fs55 = __toESM(require("node:fs/promises")), import_node_fs35 = require("node:fs"), os45 = __toESM(require("node:os")), path74 = __toESM(require("node:path"));
77874
+ var crypto30 = __toESM(require("node:crypto")), fs55 = __toESM(require("node:fs/promises")), import_node_fs35 = require("node:fs"), os46 = __toESM(require("node:os")), path74 = __toESM(require("node:path"));
77347
77875
  var RECEIPT_VERSION = 1, DIRECTORY_MODE2 = 448, FILE_MODE11 = 384, MAX_RECEIPT_BYTES2 = 64 * 1024, MAX_SESSION_RECEIPTS2 = 64, KNOWN_DECISIONS2 = /* @__PURE__ */ new Set([
77348
77876
  "APPROVE",
77349
77877
  "CANCEL",
@@ -77358,7 +77886,7 @@ function opaque(value) {
77358
77886
  return crypto30.createHash("sha256").update(value, "utf8").digest("hex");
77359
77887
  }
77360
77888
  function defaultRoot5() {
77361
- return path74.join(os45.homedir(), ".codevibe", "decision-effects");
77889
+ return path74.join(os46.homedir(), ".codevibe", "decision-effects");
77362
77890
  }
77363
77891
  function durableResolutionDescriptorHash(resolution) {
77364
77892
  return crypto30.createHash("sha256").update(JSON.stringify({
@@ -77400,10 +77928,10 @@ var DecisionEffectReceiptStore = class {
77400
77928
  return path74.join(this.rootDir, opaque(sessionId), `${opaque(decisionClaimId)}.json`);
77401
77929
  }
77402
77930
  async assertNoSymlinkComponents(target2) {
77403
- let relative22 = path74.relative(this.rootDir, target2);
77404
- if (relative22.startsWith("..") || path74.isAbsolute(relative22))
77931
+ let relative23 = path74.relative(this.rootDir, target2);
77932
+ if (relative23.startsWith("..") || path74.isAbsolute(relative23))
77405
77933
  throw new Error("DecisionEffectReceiptStore: path escaped root");
77406
- let components = relative22 ? relative22.split(path74.sep) : [], current = this.rootDir;
77934
+ let components = relative23 ? relative23.split(path74.sep) : [], current = this.rootDir;
77407
77935
  for (let index = -1; index < components.length; index += 1) {
77408
77936
  index >= 0 && (current = path74.join(current, components[index]));
77409
77937
  try {
@@ -79448,7 +79976,7 @@ ${notFoundMsg}`), fallbackSection && sections.push(fallbackSection);
79448
79976
  }
79449
79977
 
79450
79978
  // src/continuation/packet-reader.ts
79451
- var import_node_fs36 = require("node:fs"), path76 = __toESM(require("node:path")), os46 = __toESM(require("node:os")), import_yaml2 = require("yaml");
79979
+ var import_node_fs36 = require("node:fs"), path76 = __toESM(require("node:path")), os47 = __toESM(require("node:os")), import_yaml2 = require("yaml");
79452
79980
  init_types();
79453
79981
  init_packet_writer();
79454
79982
  init_packet_writer();
@@ -79519,7 +80047,7 @@ async function packetHashIsInAudit(appsync, packet, packetHash) {
79519
80047
  }
79520
80048
  }
79521
80049
  function createContinuationPacketReader(deps) {
79522
- let homeDir = deps.homeDir ?? os46.homedir();
80050
+ let homeDir = deps.homeDir ?? os47.homedir();
79523
80051
  return {
79524
80052
  async read(taskId, expectedHash) {
79525
80053
  let filePath = getPacketFilePath(taskId, homeDir), stat13;
@@ -83982,12 +84510,11 @@ function readOnlyAgentPrompt(args) {
83982
84510
  `);
83983
84511
  return args.agent === "ANTIGRAVITY" && args.allowRepositoryTools ? `${agyReadOnlyWorkdirPreamble(args.workingDir)}${body}` : body;
83984
84512
  }
83985
- function readOnlyAdvisoryAllowsRepositoryTools(input) {
83986
- let text2 = input.toLowerCase(), concreteRepositoryTarget = /\b(repo|repository|codebase|workspace|source tree|working tree|pending diff|diff|files?|folders?|directories|logs?|tests?)\b/.test(text2) || /(?:^|\s)(?:\.{1,2}\/|\/|~\/)/.test(text2) || /\b[a-z0-9_-]+\.(?:ts|tsx|js|jsx|swift|kt|java|rs|py|md|json|ya?ml|toml|gradle|xml)\b/i.test(input), inspectionIntent = /\b(read|inspect|check|look at|look into|review|summarize|analyze|investigate|scan|search|find|trace|debug)\b/.test(text2) || /\bpending diff\b/.test(text2) || /\brun tests?\b/.test(text2);
83987
- return concreteRepositoryTarget && inspectionIntent;
84513
+ function readOnlyAdvisoryAllowsRepositoryTools(_input) {
84514
+ return !0;
83988
84515
  }
83989
- function applyReadOnlyAdvisoryToolPolicy(agent, plan, allowRepositoryTools) {
83990
- return allowRepositoryTools || agent !== "CLAUDE" ? plan : buildClaudeNoToolsPlanArgv();
84516
+ function applyReadOnlyAdvisoryToolPolicy(agent, plan, allowRepositoryTools, workingDir, attachments) {
84517
+ return agent !== "CLAUDE" ? plan : allowRepositoryTools ? buildClaudeReadOnlyAdvisoryPlanArgv(workingDir, attachments) : buildClaudeNoToolsPlanArgv();
83991
84518
  }
83992
84519
  async function readPlanModeOutput(args) {
83993
84520
  try {
@@ -84041,7 +84568,11 @@ function syntheticReadOnlyAdvisoryTaskId() {
84041
84568
  return (0, import_uuid15.v4)();
84042
84569
  }
84043
84570
  function classifyClaudeResultStdoutQuota(stdout) {
84044
- let result = firstResultEnvelope(stdout)?.result;
84571
+ let env = firstResultEnvelope(stdout);
84572
+ if (!env) return !1;
84573
+ if (env.api_error === "usage_limit_reached" || env.api_error_status === 429 || typeof env.api_error == "string" && classifyFailureMessage(env.api_error) === "usage_limit")
84574
+ return !0;
84575
+ let result = env.result;
84045
84576
  return typeof result == "string" && result.length > 0 && classifyFailureMessage(result) === "usage_limit";
84046
84577
  }
84047
84578
  function extractReadOnlyAdvisoryUsage(args) {
@@ -84093,10 +84624,14 @@ async function runReadOnlyAgentAdvisoryResult(args) {
84093
84624
  args.attachmentBatch,
84094
84625
  args.workingDir
84095
84626
  ) : await attachmentJournal.assertWorkspaceSafe(args.workingDir);
84096
- let allowRepositoryTools = readOnlyAdvisoryAllowsRepositoryTools(args.advisoryBrief), plan = applyReadOnlyAdvisoryToolPolicy(
84627
+ let allowRepositoryTools = readOnlyAdvisoryAllowsRepositoryTools(args.advisoryBrief), attachmentPaths = [];
84628
+ args.attachmentBatch?.value.directory.canonicalPath && attachmentPaths.push(args.attachmentBatch.value.directory.canonicalPath);
84629
+ let plan = applyReadOnlyAdvisoryToolPolicy(
84097
84630
  args.agent,
84098
84631
  buildImplementorArgv(args.agent, "plan", args.workingDir),
84099
- allowRepositoryTools
84632
+ allowRepositoryTools,
84633
+ args.workingDir,
84634
+ attachmentPaths
84100
84635
  ), processAuthority = null, bindProcessOwner = args.attachmentBatch ? async (owner) => {
84101
84636
  if (processAuthority)
84102
84637
  throw new Error("advisory process authority was already bound");
@@ -84109,13 +84644,27 @@ async function runReadOnlyAgentAdvisoryResult(args) {
84109
84644
  throw new Error("advisory process retirement has no durable authority");
84110
84645
  let authority = processAuthority;
84111
84646
  await attachmentJournal.retireProcess(authority, owner), processAuthority = null;
84112
- } : void 0, advisoryTaskId = syntheticReadOnlyAdvisoryTaskId(), handle = await args.shellArgs.localExecutor.spawnImplementor({
84647
+ } : void 0, advisoryTaskId = syntheticReadOnlyAdvisoryTaskId(), treeBefore = await probeWorkspaceTreeFingerprint(args.workingDir);
84648
+ if (treeBefore.kind !== "fingerprint")
84649
+ return logger.error("[orchestration-shell] workspace fingerprint unavailable before read-only advisory turn \u2014 failing closed", {
84650
+ agent: args.agent,
84651
+ workingDir: args.workingDir
84652
+ }), plan.capture.kind === "file" && await (0, import_promises.unlink)(plan.capture.path).catch(() => {
84653
+ }), {
84654
+ ok: !1,
84655
+ text: readOnlyRouteFallback({ agent: args.agent }),
84656
+ quotaWalled: !1,
84657
+ timedOut: !1,
84658
+ usage: unavailableUsageSnapshot("workspace_verification_unavailable")
84659
+ };
84660
+ let handle = await args.shellArgs.localExecutor.spawnImplementor({
84113
84661
  argv: plan.argv,
84114
84662
  workingDir: args.workingDir,
84115
84663
  role: "implementor",
84116
84664
  agentKind: args.agent,
84117
84665
  taskId: advisoryTaskId,
84118
84666
  lifecycleAudit: "none",
84667
+ requireConfined: !0,
84119
84668
  timeoutMs: args.timeoutMs ?? READ_ONLY_ADVISORY_TIMEOUT_MS,
84120
84669
  stdinTty: !0,
84121
84670
  stdinPayload: readOnlyAgentPrompt({
@@ -84128,7 +84677,24 @@ async function runReadOnlyAgentAdvisoryResult(args) {
84128
84677
  }),
84129
84678
  ...bindProcessOwner ? { onCleanupOwnerReady: bindProcessOwner } : {},
84130
84679
  ...retireProcessOwner ? { onCleanupOwnerRetired: retireProcessOwner } : {}
84131
- }), exit = await handle.done, output = await readPlanModeOutput({
84680
+ }), exit = await handle.done, treeAfter = await probeWorkspaceTreeFingerprint(args.workingDir);
84681
+ if (treeAfter.kind !== "fingerprint" || treeAfter.value !== treeBefore.value)
84682
+ return logger.error("[orchestration-shell] workspace mutation or verification failure detected during read-only advisory turn", {
84683
+ agent: args.agent,
84684
+ workingDir: args.workingDir,
84685
+ before: treeBefore.value,
84686
+ after: treeAfter.kind === "fingerprint" ? treeAfter.value : "probe-failed"
84687
+ }), plan.capture.kind === "file" && await (0, import_promises.unlink)(plan.capture.path).catch(() => {
84688
+ }), {
84689
+ ok: !1,
84690
+ text: readOnlyRouteFallback({ agent: args.agent }),
84691
+ quotaWalled: !1,
84692
+ timedOut: !1,
84693
+ usage: unavailableUsageSnapshot(
84694
+ treeAfter.kind === "fingerprint" ? "workspace_mutation_detected" : "workspace_verification_unavailable"
84695
+ )
84696
+ };
84697
+ let output = await readPlanModeOutput({
84132
84698
  agent: args.agent,
84133
84699
  plan,
84134
84700
  handle,
@@ -86243,21 +86809,21 @@ async function handleShellUserInput(deps) {
86243
86809
  store.getState().session.sessionId
86244
86810
  ), store.dispatch({ type: "CLEAR_PENDING_PLANNER_OFFER" });
86245
86811
  }
86246
- let pendingForIngest = store.getState().pendingClarification, isAnsweringPending = !!pendingForIngest && pendingForIngest.rounds.length > 0 && pendingForIngest.rounds[pendingForIngest.rounds.length - 1].answer === void 0, existingAttachments = !slashCommandText.startsWith("/") && isAnsweringPending ? pendingForIngest.attachments : void 0, cwd = args.cwd ?? process.cwd(), homedir40 = os47.homedir(), runAttachmentIngest = (existing) => {
86812
+ let pendingForIngest = store.getState().pendingClarification, isAnsweringPending = !!pendingForIngest && pendingForIngest.rounds.length > 0 && pendingForIngest.rounds[pendingForIngest.rounds.length - 1].answer === void 0, existingAttachments = !slashCommandText.startsWith("/") && isAnsweringPending ? pendingForIngest.attachments : void 0, cwd = args.cwd ?? process.cwd(), homedir41 = os48.homedir(), runAttachmentIngest = (existing) => {
86247
86813
  if (deps.images && deps.images.length > 0) {
86248
86814
  let carried = attachmentsFromPaths(
86249
86815
  deps.images,
86250
86816
  existing && existing.length ? { existing } : void 0
86251
86817
  ), fromText = detectImageAttachments(text2.replace(/\[Image #\d+\]/g, " "), {
86252
86818
  cwd,
86253
- homedir: homedir40,
86819
+ homedir: homedir41,
86254
86820
  existing: carried.attachments
86255
86821
  });
86256
86822
  return { attachments: fromText.attachments, rejects: [...carried.rejects, ...fromText.rejects] };
86257
86823
  }
86258
86824
  return detectImageAttachments(text2, {
86259
86825
  cwd,
86260
- homedir: homedir40,
86826
+ homedir: homedir41,
86261
86827
  ...existing && existing.length ? { existing } : {}
86262
86828
  });
86263
86829
  }, ingest = runAttachmentIngest(existingAttachments), turnAttachments = ingest.attachments, currentTurnIngest = existingAttachments && existingAttachments.length > 0 ? runAttachmentIngest() : ingest, currentTurnAttachments = currentTurnIngest.attachments, turnImagePaths = (deps.images ?? []).map((p) => path78.resolve(p)), teamDurableStore = typeof args.quorumLoop?.getDurableStore == "function" ? args.quorumLoop.getDurableStore() : void 0, ingestRejectsSurfaced = !1, surfaceIngestRejects = (result) => {
@@ -88140,7 +88706,7 @@ async function runStructuralSummarySessionStart(deps) {
88140
88706
  try {
88141
88707
  let optIn = await readOptIn(args.tier), discovery = await discoverRepositoryRoots({ launchDir: process.cwd() }), summary = await generator.generate({
88142
88708
  rootPaths: discovery.rootPaths,
88143
- ignoreFile: path78.join(os47.homedir(), ".codevibe", "structural-summary.ignore"),
88709
+ ignoreFile: path78.join(os48.homedir(), ".codevibe", "structural-summary.ignore"),
88144
88710
  includeBodies: optIn?.bodyInclusionPaths ?? [],
88145
88711
  userId: args.session.userId,
88146
88712
  tier: args.tier
@@ -89508,7 +90074,7 @@ ${colors.green}\u2713${colors.reset} Cleared ${clearable.length} entr${clearable
89508
90074
  }
89509
90075
 
89510
90076
  // src/substrate-launch/engage-substrate.ts
89511
- var import_node_fs38 = require("node:fs"), os48 = __toESM(require("node:os")), path80 = __toESM(require("node:path"));
90077
+ var import_node_fs38 = require("node:fs"), os49 = __toESM(require("node:os")), path80 = __toESM(require("node:path"));
89512
90078
 
89513
90079
  // src/credential-broker/types.ts
89514
90080
  var BROKER_ROUTES = [
@@ -91491,7 +92057,7 @@ function providerForAgent(agentKind) {
91491
92057
  return agentKind === "CLAUDE" ? "anthropic" : agentKind === "CODEX" ? "openai" : null;
91492
92058
  }
91493
92059
  var AUDIT_SEGMENT = "audit", STATE_SEGMENT = "state", CODEVIBE_DIR = ".codevibe", AUDIT_FILE_NAME = "audit.jsonl";
91494
- function isInsideOrEqual2(child, parent) {
92060
+ function isInsideOrEqual3(child, parent) {
91495
92061
  if (child === parent) return !0;
91496
92062
  let rel = path80.relative(parent, child);
91497
92063
  return rel.length > 0 && !rel.startsWith("..") && !path80.isAbsolute(rel);
@@ -91540,7 +92106,7 @@ async function assertAuditOutsideWorkdir(auditPath, workdir) {
91540
92106
  `CP-7 W3: audit leaf "${resolvedAuditPath}" is a symlink that does not resolve (dangling) \u2014 refusing to launch (fail-closed). The audit file must be a regular file in the sibling .codevibe/audit/ tree.`
91541
92107
  );
91542
92108
  }
91543
- if (isInsideOrEqual2(realAuditDir, realWorkdir) || isInsideOrEqual2(resolvedAuditPath, realWorkdir) || isInsideOrEqual2(resolvedLeafTarget, realWorkdir))
92109
+ if (isInsideOrEqual3(realAuditDir, realWorkdir) || isInsideOrEqual3(resolvedAuditPath, realWorkdir) || isInsideOrEqual3(resolvedLeafTarget, realWorkdir))
91544
92110
  throw new Error(
91545
92111
  `CP-7 W3: audit path "${resolvedAuditPath}" (resolved target "${resolvedLeafTarget}") is inside the agent workdir "${realWorkdir}" \u2014 the agent could truncate/forge the forensic record (Stage-1 r1 HIGH fail-closed). The audit MUST live in the sibling .codevibe/audit/ tree, never under .codevibe/state/<group>/<track>/.`
91546
92112
  );
@@ -91666,7 +92232,8 @@ async function engageSubstrate(input) {
91666
92232
  ...agentScratchSpec !== null ? { agentScratch: agentScratchSpec } : {},
91667
92233
  // CP-7 W3 — Stage-2 r1 HIGH. The resolved audit dir → A5 trailing deny.
91668
92234
  // Undefined when a test injects its own in-memory sink (no real tree).
91669
- ...resolvedAuditDir !== null ? { auditDir: resolvedAuditDir } : {}
92235
+ ...resolvedAuditDir !== null ? { auditDir: resolvedAuditDir } : {},
92236
+ ...input.writableWorkdir !== void 0 ? { writableWorkdir: input.writableWorkdir } : {}
91670
92237
  };
91671
92238
  handle = await selection.substrate.launch(spec), logger.info(
91672
92239
  `[CP-7] Substrate engaged (tier=${selection.tier}, egress=${handle.egressFidelity}) \u2014 agent is creditless, broker holds the key`,
@@ -91799,7 +92366,7 @@ async function engageSubstrate(input) {
91799
92366
  }
91800
92367
  var RESOLVER_AGENT_STATE_DIR_SEGMENTS = {
91801
92368
  GEMINI: [".gemini"],
91802
- ANTIGRAVITY: [".gemini", "antigravity-cli"]
92369
+ ANTIGRAVITY: [".gemini"]
91803
92370
  };
91804
92371
  async function engageWriteConfinedResolverSandbox(input) {
91805
92372
  let agentKind = input.agentKind;
@@ -91824,14 +92391,14 @@ async function engageWriteConfinedResolverSandbox(input) {
91824
92391
  teardown: async () => {
91825
92392
  }
91826
92393
  };
91827
- let homedir40 = os48.homedir(), stateDir = path80.join(
91828
- homedir40,
92394
+ let homedir41 = os49.homedir(), stateDir = path80.join(
92395
+ homedir41,
91829
92396
  ...RESOLVER_AGENT_STATE_DIR_SEGMENTS[agentKind]
91830
92397
  );
91831
92398
  await import_node_fs38.promises.mkdir(stateDir, { recursive: !0 }).catch(() => {
91832
92399
  });
91833
92400
  let env = {}, nodeDir = process.execPath.slice(0, process.execPath.lastIndexOf("/"));
91834
- env.PATH = `${SANDBOX_DEFAULT_PATH}:${nodeDir}`, env.HOME = homedir40;
92401
+ env.PATH = `${SANDBOX_DEFAULT_PATH}:${nodeDir}`, env.HOME = homedir41;
91835
92402
  let safeSource = input.localeSource ?? {};
91836
92403
  for (let key of SAFE_ENV_ALLOWLIST) {
91837
92404
  let v = safeSource[key];
@@ -91851,7 +92418,8 @@ async function engageWriteConfinedResolverSandbox(input) {
91851
92418
  // The agent's own auth/state home — writable, read-granted (A5: sandbox fs
91852
92419
  // == host fs, so sandboxDir == hostDir).
91853
92420
  agentConfig: { hostDir: stateDir, sandboxDir: stateDir },
91854
- networkPosture: "open"
92421
+ networkPosture: "open",
92422
+ writableWorkdir: input.writableWorkdir !== !1
91855
92423
  }, handle = await selection.substrate.launch(spec);
91856
92424
  return logger.info(
91857
92425
  `[A1d] Resolver-confinement row engaged (tier=sandbox_exec, egress=open) \u2014 writes fenced to the disposable workspace; ${agentKind} runs on its own file-based auth (no broker)`,
@@ -92526,8 +93094,8 @@ function isLocalModelEntitled(entitlement) {
92526
93094
  }
92527
93095
  function localModelArtifactPath(config) {
92528
93096
  if (!config.artifactId || !isSafeArtifactId(config.artifactId)) return null;
92529
- let cacheRoot = import_path5.default.resolve(config.cacheDir), artifactPath = import_path5.default.resolve(cacheRoot, config.artifactId), relative22 = import_path5.default.relative(cacheRoot, artifactPath);
92530
- return !relative22 || relative22.startsWith("..") || import_path5.default.isAbsolute(relative22) ? null : artifactPath;
93097
+ let cacheRoot = import_path5.default.resolve(config.cacheDir), artifactPath = import_path5.default.resolve(cacheRoot, config.artifactId), relative23 = import_path5.default.relative(cacheRoot, artifactPath);
93098
+ return !relative23 || relative23.startsWith("..") || import_path5.default.isAbsolute(relative23) ? null : artifactPath;
92531
93099
  }
92532
93100
  function isSafeArtifactId(artifactId) {
92533
93101
  return artifactId === import_path5.default.basename(artifactId) && artifactId !== "." && artifactId !== ".." && !import_path5.default.isAbsolute(artifactId);