@quantiya/codevibe-claude-plugin 2.0.22 → 2.0.23

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.
@@ -20277,6 +20277,37 @@ async function bindWorkingDirectory(payload){
20277
20277
  throw new Error('target cwd incarnation changed before launch');
20278
20278
  }
20279
20279
  }
20280
+ function holdLaunchPathAuthorities(payload){
20281
+ const authorities=payload.launchPathAuthorities;
20282
+ if(authorities===undefined)return [];
20283
+ if(!Array.isArray(authorities)||authorities.length>16)throw new Error('invalid launch path authorities');
20284
+ const held=[];
20285
+ try{
20286
+ for(const authority of authorities){
20287
+ if(!authority||authority.version!==1||
20288
+ typeof authority.canonicalPath!=='string'||!require('node:path').isAbsolute(authority.canonicalPath)||authority.canonicalPath.includes('\0')||
20289
+ typeof authority.dev!=='string'||typeof authority.ino!=='string'||
20290
+ typeof authority.birthtimeNs!=='string')throw new Error('invalid launch path authority');
20291
+ const flags=fs.constants.O_RDONLY|(fs.constants.O_DIRECTORY||0)|(fs.constants.O_NOFOLLOW||0);
20292
+ const fd=fs.openSync(authority.canonicalPath,flags);
20293
+ held.push(fd);
20294
+ const opened=fs.fstatSync(fd,{bigint:true});
20295
+ const live=fs.lstatSync(authority.canonicalPath,{bigint:true});
20296
+ const canonical=fs.realpathSync(authority.canonicalPath);
20297
+ if(!opened.isDirectory()||!live.isDirectory()||live.isSymbolicLink()||
20298
+ opened.dev.toString()!==authority.dev||opened.ino.toString()!==authority.ino||
20299
+ opened.birthtimeNs.toString()!==authority.birthtimeNs||
20300
+ live.dev.toString()!==authority.dev||live.ino.toString()!==authority.ino||
20301
+ live.birthtimeNs.toString()!==authority.birthtimeNs||canonical!==authority.canonicalPath){
20302
+ throw new Error('launch path authority changed before target spawn');
20303
+ }
20304
+ }
20305
+ return held;
20306
+ }catch(err){
20307
+ for(const fd of held){try{fs.closeSync(fd);}catch{}}
20308
+ throw err;
20309
+ }
20310
+ }
20280
20311
  const hostPipe=fs.createReadStream(null,{fd:5,autoClose:false});
20281
20312
  for(const event of ['end','close','error'])hostPipe.once(event,killOwnGroup);
20282
20313
  for(const [signal] of [['SIGTERM'],['SIGINT'],['SIGHUP']])process.on(signal,killOwnGroup);
@@ -20285,9 +20316,17 @@ for(const [signal] of [['SIGTERM'],['SIGINT'],['SIGHUP']])process.on(signal,kill
20285
20316
  const start=await readLine(4);
20286
20317
  if(start!=='START')throw new Error('target start not authorized');
20287
20318
  await bindWorkingDirectory(payload);
20319
+ // This is deliberately synchronous and immediately adjacent to cp.spawn:
20320
+ // keep exact directory descriptors open across native process creation so a
20321
+ // pathname replacement during durable-owner publication cannot be adopted.
20322
+ const launchPathFds=holdLaunchPathAuthorities(payload);
20288
20323
  // No pathname cwd is passed here: the child inherits the wrapper's already-
20289
20324
  // authenticated kernel cwd reference, so a later rename cannot redirect it.
20290
- target=cp.spawn(payload.command,payload.args,{env:payload.env,stdio:['inherit','inherit','inherit'],detached:false,windowsHide:true});
20325
+ try{
20326
+ target=cp.spawn(payload.command,payload.args,{env:payload.env,stdio:['inherit','inherit','inherit'],detached:false,windowsHide:true});
20327
+ }finally{
20328
+ for(const fd of launchPathFds){try{fs.closeSync(fd);}catch{}}
20329
+ }
20291
20330
  target.once('error',err=>{send({type:'target-exit',code:127,error:String(err&&err.message||err)});});
20292
20331
  target.once('exit',(code,signal)=>{send({type:'target-exit',code:Number.isInteger(code)?code:(signal?128:1)});});
20293
20332
  // Remain the authenticated group/session leader until the outside host has
@@ -20387,6 +20426,7 @@ function spawnPosixOwnedProcess(command, args, options, seams = {}) {
20387
20426
  args,
20388
20427
  cwd: targetCwd,
20389
20428
  cwdAuthority: seams.workingDirAuthority,
20429
+ launchPathAuthorities: seams.launchPathAuthorities,
20390
20430
  env: options?.env ?? process.env,
20391
20431
  ...seams.cleanupTimeoutMs === void 0 ? {} : { cleanupTimeoutMs: seams.cleanupTimeoutMs }
20392
20432
  }), "utf8");
@@ -26409,6 +26449,38 @@ var LocalGemmaPlannerAdapter = class {
26409
26449
  // src/orchestration-shell/quorum-loop.ts
26410
26450
  var import_node_fs24 = require("node:fs");
26411
26451
 
26452
+ // src/diagnostics/shape-only.ts
26453
+ function outputShapeOnly(raw) {
26454
+ if (typeof raw != "string" || raw.length === 0)
26455
+ return {
26456
+ bytes: 0,
26457
+ lines: 0,
26458
+ leadClass: "empty",
26459
+ fenceCount: 0,
26460
+ bulletLines: 0,
26461
+ blankLines: 0
26462
+ };
26463
+ let lines = raw.split(`
26464
+ `), trimmed = raw.trim(), leadClass;
26465
+ return trimmed.length === 0 ? leadClass = "empty" : trimmed.startsWith("{") || trimmed.startsWith("[") ? leadClass = "json-open" : trimmed.startsWith("```") ? leadClass = "fence" : /^#{1,6}\s/.test(trimmed) ? leadClass = "heading" : /^[-*+]\s/.test(trimmed) ? leadClass = "bullet" : /^\d+[.)]\s/.test(trimmed) ? leadClass = "ordinal" : /^[A-Za-z]/.test(trimmed) ? leadClass = "prose" : leadClass = "other", {
26466
+ bytes: Buffer.byteLength(raw, "utf8"),
26467
+ lines: lines.length,
26468
+ leadClass,
26469
+ fenceCount: (raw.match(/```/g) ?? []).length,
26470
+ bulletLines: lines.filter((line) => /^\s*[-*+]\s/.test(line)).length,
26471
+ blankLines: lines.filter((line) => line.trim().length === 0).length
26472
+ };
26473
+ }
26474
+ function errorShapeOnly(error) {
26475
+ let value = error, message = typeof value?.message == "string" ? value.message : "", causeCount = Array.isArray(value?.causes) ? value.causes.length : value?.cause !== void 0 ? 1 : 0;
26476
+ return {
26477
+ errorClass: error instanceof Error ? "error" : "non-error",
26478
+ messageBytes: Buffer.byteLength(message, "utf8"),
26479
+ hasCause: value?.cause !== void 0,
26480
+ causeCount
26481
+ };
26482
+ }
26483
+
26412
26484
  // src/reduced-trust-notice.ts
26413
26485
  var surfacedRationales = /* @__PURE__ */ new Set();
26414
26486
  function noticeKey(sessionId, tier, reason) {
@@ -28104,7 +28176,8 @@ var ClassBConsumer = class {
28104
28176
  };
28105
28177
 
28106
28178
  // src/substrate-launch/apikey-bootstrap.ts
28107
- var import_node_fs14 = require("node:fs"), os19 = __toESM(require("node:os")), path32 = __toESM(require("node:path")), HELPER_SCRIPT_NAME = "broker-apikey-helper.sh", TOKEN_FILE_NAME = "broker-token", CLAUDE_SETTINGS_NAME = "settings.json", CODEX_CONFIG_NAME = "config.toml", SANDBOX_BOOTSTRAP_DIR = "/codevibe/agent", SANDBOX_AGENT_CONFIG_DIR = "/codevibe/agent-config", CODEX_PROVIDER_ID = "codevibe_broker";
28179
+ var import_node_fs14 = require("node:fs"), os19 = __toESM(require("node:os")), path32 = __toESM(require("node:path"));
28180
+ var HELPER_SCRIPT_NAME = "broker-apikey-helper.sh", TOKEN_FILE_NAME = "broker-token", CLAUDE_SETTINGS_NAME = "settings.json", CODEX_CONFIG_NAME = "config.toml", SANDBOX_BOOTSTRAP_DIR = "/codevibe/agent", SANDBOX_AGENT_CONFIG_DIR = "/codevibe/agent-config", CODEX_PROVIDER_ID = "codevibe_broker";
28108
28181
  function helperScriptSource(tokenFileAbsPathInSandbox) {
28109
28182
  return `#!/bin/sh
28110
28183
  # CP-7 apiKeyHelper \u2014 emits the current broker token (NEVER the vendor key).
@@ -28145,12 +28218,16 @@ function isInsideOrEqual(child, parent) {
28145
28218
  return rel.length > 0 && !rel.startsWith("..") && !path32.isAbsolute(rel);
28146
28219
  }
28147
28220
  var ApiKeyBootstrap = class _ApiKeyBootstrap {
28148
- constructor(hostDir, sandboxDir, configHostDir, configSandboxDir, tokenFileHostPath) {
28221
+ constructor(hostDir, sandboxDir, configHostDir, configSandboxDir, tokenFileHostPath, claudeScratchHostDir, claudeScratchSandboxDir, claudeScratchIdentity, beforeClaudeScratchCleanupCommit) {
28149
28222
  this.hostDir = hostDir;
28150
28223
  this.sandboxDir = sandboxDir;
28151
28224
  this.configHostDir = configHostDir;
28152
28225
  this.configSandboxDir = configSandboxDir;
28153
28226
  this.tokenFileHostPath = tokenFileHostPath;
28227
+ this.claudeScratchHostDir = claudeScratchHostDir;
28228
+ this.claudeScratchSandboxDir = claudeScratchSandboxDir;
28229
+ this.claudeScratchIdentity = claudeScratchIdentity;
28230
+ this.beforeClaudeScratchCleanupCommit = beforeClaudeScratchCleanupCommit;
28154
28231
  }
28155
28232
  /**
28156
28233
  * Materialize the bootstrap: create the host dir (0700), write the token file
@@ -28166,9 +28243,8 @@ var ApiKeyBootstrap = class _ApiKeyBootstrap {
28166
28243
  );
28167
28244
  await import_node_fs14.promises.chmod(rawConfigHostDir, 448).catch(() => {
28168
28245
  });
28169
- let hostDir = await import_node_fs14.promises.realpath(rawHostDir), configHostDir = await import_node_fs14.promises.realpath(rawConfigHostDir);
28246
+ let hostDir = await import_node_fs14.promises.realpath(rawHostDir), configHostDir = await import_node_fs14.promises.realpath(rawConfigHostDir), realWorkdir;
28170
28247
  if (input.workdir !== void 0) {
28171
- let realWorkdir;
28172
28248
  try {
28173
28249
  realWorkdir = await import_node_fs14.promises.realpath(input.workdir);
28174
28250
  } catch {
@@ -28205,7 +28281,7 @@ var ApiKeyBootstrap = class _ApiKeyBootstrap {
28205
28281
  "ApiKeyBootstrap.create: provide `sandboxConfigDir` or `configDirEqualsHostDir`"
28206
28282
  );
28207
28283
  let tokenInSandbox = path32.posix.join(sandboxDir, TOKEN_FILE_NAME), helperInSandbox = path32.posix.join(sandboxDir, HELPER_SCRIPT_NAME), tokenFileHostPath = path32.join(hostDir, TOKEN_FILE_NAME);
28208
- return await import_node_fs14.promises.writeFile(tokenFileHostPath, input.initialToken, {
28284
+ await import_node_fs14.promises.writeFile(tokenFileHostPath, input.initialToken, {
28209
28285
  encoding: "utf8",
28210
28286
  mode: 384
28211
28287
  }), await import_node_fs14.promises.writeFile(
@@ -28220,12 +28296,107 @@ var ApiKeyBootstrap = class _ApiKeyBootstrap {
28220
28296
  path32.join(configHostDir, CODEX_CONFIG_NAME),
28221
28297
  codexConfig(input.sandboxBrokerUrl, helperInSandbox),
28222
28298
  { encoding: "utf8", mode: 384 }
28223
- ), new _ApiKeyBootstrap(
28299
+ );
28300
+ let claudeScratchHostDir = null, claudeScratchSandboxDir = null, claudeScratchIdentity = null;
28301
+ if (input.claudeScratchHostRoot !== void 0) {
28302
+ if (input.provider !== "anthropic" || !input.sandboxDirEqualsHostDir) {
28303
+ let primary = new Error(
28304
+ "ApiKeyBootstrap.create: a short Claude scratch root is valid only for the A5 anthropic path"
28305
+ ), cleanupErrors = [];
28306
+ for (let directory of [hostDir, configHostDir])
28307
+ try {
28308
+ await import_node_fs14.promises.rm(directory, { recursive: !0, force: !0 });
28309
+ } catch (cleanupError) {
28310
+ cleanupErrors.push(cleanupError);
28311
+ }
28312
+ throw cleanupErrors.length > 0 ? Object.assign(
28313
+ new Error(
28314
+ "ApiKeyBootstrap.create: invalid scratch configuration and cleanup did not complete"
28315
+ ),
28316
+ { causes: [primary, ...cleanupErrors] }
28317
+ ) : primary;
28318
+ }
28319
+ let rawScratchDir = null;
28320
+ try {
28321
+ let canonicalScratchRoot = await import_node_fs14.promises.realpath(
28322
+ input.claudeScratchHostRoot
28323
+ );
28324
+ rawScratchDir = await import_node_fs14.promises.mkdtemp(path32.join(canonicalScratchRoot, "cv"));
28325
+ let initial = await import_node_fs14.promises.lstat(rawScratchDir, { bigint: !0 });
28326
+ if (!initial.isDirectory() || initial.isSymbolicLink())
28327
+ throw new Error(
28328
+ "ApiKeyBootstrap.create: generated Claude scratch root is not a real directory"
28329
+ );
28330
+ await input.beforeClaudeScratchOpen?.(rawScratchDir);
28331
+ let scratchHandle = await import_node_fs14.promises.open(
28332
+ rawScratchDir,
28333
+ import_node_fs14.constants.O_RDONLY | import_node_fs14.constants.O_NOFOLLOW | (typeof import_node_fs14.constants.O_DIRECTORY == "number" ? import_node_fs14.constants.O_DIRECTORY : 0)
28334
+ );
28335
+ try {
28336
+ let opened = await scratchHandle.stat({ bigint: !0 });
28337
+ if (!opened.isDirectory() || opened.dev !== initial.dev || opened.ino !== initial.ino || opened.birthtimeNs !== initial.birthtimeNs)
28338
+ throw new Error(
28339
+ "ApiKeyBootstrap.create: Claude scratch root changed while binding its directory handle"
28340
+ );
28341
+ await scratchHandle.chmod(448);
28342
+ let sealed = await scratchHandle.stat({ bigint: !0 }), live = await import_node_fs14.promises.lstat(rawScratchDir, { bigint: !0 }), canonical = rawScratchDir, currentUid = process.getuid?.(), mode = sealed.mode & 0o777n;
28343
+ if (!sealed.isDirectory() || !live.isDirectory() || live.isSymbolicLink() || live.dev !== sealed.dev || live.ino !== sealed.ino || live.birthtimeNs !== sealed.birthtimeNs || currentUid === void 0 || sealed.uid !== BigInt(currentUid) || mode !== 0o700n)
28344
+ throw new Error(
28345
+ "ApiKeyBootstrap.create: generated Claude scratch root failed its descriptor-bound private-directory identity check"
28346
+ );
28347
+ claudeScratchHostDir = canonical, claudeScratchSandboxDir = rawScratchDir, claudeScratchIdentity = {
28348
+ version: 1,
28349
+ canonicalPath: canonical,
28350
+ dev: sealed.dev.toString(),
28351
+ ino: sealed.ino.toString(),
28352
+ birthtimeNs: sealed.birthtimeNs.toString()
28353
+ };
28354
+ } finally {
28355
+ await scratchHandle.close();
28356
+ }
28357
+ if (Buffer.byteLength(rawScratchDir, "utf8") > 30)
28358
+ throw new Error(
28359
+ "ApiKeyBootstrap.create: generated Claude scratch path exceeds the 30-byte A5 safety bound"
28360
+ );
28361
+ if (realWorkdir !== void 0 && isInsideOrEqual(claudeScratchHostDir, realWorkdir))
28362
+ throw new Error(
28363
+ "ApiKeyBootstrap.create: Claude scratch root is inside the agent workdir (fail-closed)"
28364
+ );
28365
+ } catch (error) {
28366
+ let cleanupErrors = [];
28367
+ if (claudeScratchIdentity !== null)
28368
+ try {
28369
+ await anchoredRemoveTree(
28370
+ claudeScratchIdentity.canonicalPath,
28371
+ claudeScratchIdentity
28372
+ );
28373
+ } catch (cleanupError) {
28374
+ cleanupErrors.push(cleanupError);
28375
+ }
28376
+ for (let directory of [hostDir, configHostDir])
28377
+ try {
28378
+ await import_node_fs14.promises.rm(directory, { recursive: !0, force: !0 });
28379
+ } catch (cleanupError) {
28380
+ cleanupErrors.push(cleanupError);
28381
+ }
28382
+ throw cleanupErrors.length > 0 ? Object.assign(
28383
+ new Error(
28384
+ "ApiKeyBootstrap.create: initialization failed and cleanup did not complete"
28385
+ ),
28386
+ { causes: [error, ...cleanupErrors] }
28387
+ ) : error;
28388
+ }
28389
+ }
28390
+ return new _ApiKeyBootstrap(
28224
28391
  hostDir,
28225
28392
  sandboxDir,
28226
28393
  configHostDir,
28227
28394
  sandboxConfigDir,
28228
- tokenFileHostPath
28395
+ tokenFileHostPath,
28396
+ claudeScratchHostDir,
28397
+ claudeScratchSandboxDir,
28398
+ claudeScratchIdentity,
28399
+ input.beforeClaudeScratchCleanupCommit
28229
28400
  );
28230
28401
  }
28231
28402
  /** The `SubstrateSpec.agentBootstrap` value for this bootstrap. */
@@ -28236,6 +28407,14 @@ var ApiKeyBootstrap = class _ApiKeyBootstrap {
28236
28407
  get configSpec() {
28237
28408
  return { hostDir: this.configHostDir, sandboxDir: this.configSandboxDir };
28238
28409
  }
28410
+ /** The unique writable Claude scratch grant, absent on Docker/Codex paths. */
28411
+ get claudeScratchSpec() {
28412
+ return this.claudeScratchHostDir === null || this.claudeScratchSandboxDir === null || this.claudeScratchIdentity === null ? null : {
28413
+ hostDir: this.claudeScratchHostDir,
28414
+ sandboxDir: this.claudeScratchSandboxDir,
28415
+ authority: this.claudeScratchIdentity
28416
+ };
28417
+ }
28239
28418
  /**
28240
28419
  * REFRESH the broker token (rotation / on a broker 401). Atomically rewrites
28241
28420
  * the token file (write a temp + rename) so the agent never reads a partial
@@ -28246,15 +28425,41 @@ var ApiKeyBootstrap = class _ApiKeyBootstrap {
28246
28425
  let tmp = `${this.tokenFileHostPath}.tmp-${process.pid}-${Date.now()}`;
28247
28426
  await import_node_fs14.promises.writeFile(tmp, token, { encoding: "utf8", mode: 384 }), await import_node_fs14.promises.rename(tmp, this.tokenFileHostPath);
28248
28427
  }
28249
- /** Remove the bootstrap dir (session teardown). Idempotent. */
28428
+ /**
28429
+ * Remove the bootstrap, config, and unique Claude scratch dirs. Scratch
28430
+ * cleanup is identity-bound: a replaced pathname is retained and reported,
28431
+ * never recursively removed as though it still belonged to this seat.
28432
+ */
28250
28433
  async destroy() {
28251
- await import_node_fs14.promises.rm(this.hostDir, { recursive: !0, force: !0 }).catch(
28252
- () => {
28434
+ let cleanupErrors = [];
28435
+ if (this.claudeScratchHostDir !== null && this.claudeScratchIdentity !== null)
28436
+ try {
28437
+ await anchoredRemoveTree(
28438
+ this.claudeScratchIdentity.canonicalPath,
28439
+ this.claudeScratchIdentity,
28440
+ {
28441
+ ...this.beforeClaudeScratchCleanupCommit ? {
28442
+ beforeCommit: () => this.beforeClaudeScratchCleanupCommit(
28443
+ this.claudeScratchHostDir
28444
+ )
28445
+ } : {}
28446
+ }
28447
+ );
28448
+ } catch (error) {
28449
+ cleanupErrors.push(error);
28253
28450
  }
28254
- ), await import_node_fs14.promises.rm(this.configHostDir, { recursive: !0, force: !0 }).catch(
28255
- () => {
28451
+ for (let directory of [this.hostDir, this.configHostDir])
28452
+ try {
28453
+ await import_node_fs14.promises.rm(directory, { recursive: !0, force: !0 });
28454
+ } catch (error) {
28455
+ cleanupErrors.push(error);
28256
28456
  }
28257
- );
28457
+ if (cleanupErrors.length === 1) throw cleanupErrors[0];
28458
+ if (cleanupErrors.length > 1)
28459
+ throw Object.assign(
28460
+ new Error("ApiKeyBootstrap.destroy: cleanup did not complete"),
28461
+ { causes: cleanupErrors }
28462
+ );
28258
28463
  }
28259
28464
  };
28260
28465
 
@@ -42776,10 +42981,7 @@ var LocalExecutorImpl = class {
42776
42981
  }).catch(() => {
42777
42982
  }), {
42778
42983
  args: { ...addClaudeSubstrateAuthArgs(args, result.agentConfigDir), substrate: result.substrateHandle },
42779
- onExit: async () => {
42780
- await result.teardown().catch(() => {
42781
- });
42782
- },
42984
+ onExit: () => result.teardown(),
42783
42985
  // Stage-2 r3 Codex HIGH — the ONLY return that proves a real confining
42784
42986
  // boundary (`result.mode === 'substrate'`, not reduced_trust / no-engager /
42785
42987
  // pre-TaskAuthorized). The danger-sandbox authority keys on THIS boolean,
@@ -42871,7 +43073,11 @@ var LocalExecutorImpl = class {
42871
43073
  }
42872
43074
  }
42873
43075
  async spawnWithLifecycle(args, spawnFn, onSubstrateExit) {
42874
- let role = args.role, lifecycleAudit = args.lifecycleAudit ?? "active_task", lifecycleTaskId = args.taskId ?? this.taskId, lifecycleCtx = lifecycleTaskId ? { taskId: lifecycleTaskId, ...this.baseCtx } : null, full = {
43076
+ let role = args.role, lifecycleAudit = args.lifecycleAudit ?? "active_task", lifecycleTaskId = args.taskId ?? this.taskId, lifecycleCtx = lifecycleTaskId ? { taskId: lifecycleTaskId, ...this.baseCtx } : null, substrateCleanupError, substrateCleanupPromise, cleanupSubstrate = async () => {
43077
+ onSubstrateExit && (substrateCleanupPromise || (substrateCleanupPromise = onSubstrateExit().catch((error) => {
43078
+ throw substrateCleanupError = error, error;
43079
+ })), await substrateCleanupPromise);
43080
+ }, full = {
42875
43081
  ...args,
42876
43082
  agentKind: args.agentKind ?? this.adapter,
42877
43083
  onProcessSpawned: async (info) => {
@@ -42885,35 +43091,51 @@ var LocalExecutorImpl = class {
42885
43091
  });
42886
43092
  },
42887
43093
  onProcessExited: async (info) => {
42888
- if (await args.onProcessExited?.(info), lifecycleAudit !== "none") {
42889
- let ctx = lifecycleCtx;
42890
- ctx !== null && await this.emitter.emitProcessExited(ctx, {
42891
- pid: info.pid,
42892
- exitCode: info.exitCode,
42893
- failureClass: info.failureClass,
42894
- occurredAt: info.exitedAt
42895
- });
43094
+ try {
43095
+ if (await args.onProcessExited?.(info), lifecycleAudit !== "none") {
43096
+ let ctx = lifecycleCtx;
43097
+ ctx !== null && await this.emitter.emitProcessExited(ctx, {
43098
+ pid: info.pid,
43099
+ exitCode: info.exitCode,
43100
+ failureClass: info.failureClass,
43101
+ occurredAt: info.exitedAt
43102
+ });
43103
+ }
43104
+ } catch {
42896
43105
  }
42897
- onSubstrateExit && await onSubstrateExit().catch(() => {
43106
+ await cleanupSubstrate().catch(() => {
42898
43107
  });
42899
43108
  }
42900
43109
  };
42901
43110
  try {
42902
- return await spawnFn(full);
43111
+ let handle = await spawnFn(full), done = handle.done.then((info) => {
43112
+ if (substrateCleanupError !== void 0)
43113
+ throw Object.assign(
43114
+ new Error("implementor substrate cleanup failed after process exit"),
43115
+ { cause: substrateCleanupError }
43116
+ );
43117
+ return info;
43118
+ });
43119
+ return { ...handle, done };
42903
43120
  } catch (err) {
42904
- if (onSubstrateExit && await onSubstrateExit().catch(() => {
42905
- }), err instanceof AuthorityError) {
43121
+ await cleanupSubstrate().catch(() => {
43122
+ });
43123
+ let primary = err;
43124
+ if (err instanceof AuthorityError) {
42906
43125
  let safeArgv = redactAgyPrintArgv(args.argv), safeDetail = redactAgyPrintValuesFromText(err.refusal.detail, args.argv), safeError = new AuthorityError({
42907
43126
  ...err.refusal,
42908
43127
  detail: safeDetail
42909
43128
  });
42910
- throw await this.bridge.bridgeAuthorityRefusal(safeError, {
43129
+ await this.bridge.bridgeAuthorityRefusal(safeError, {
42911
43130
  refusedMessageId: "spawn:" + role + ":" + Date.now(),
42912
43131
  shellContent: `Refused spawn (${role}): ${safeDetail}`,
42913
43132
  shellMetadata: { role, argv: safeArgv }
42914
- }), safeError;
43133
+ }), primary = safeError;
42915
43134
  }
42916
- throw err;
43135
+ throw substrateCleanupError !== void 0 ? Object.assign(
43136
+ new Error("implementor spawn failed and substrate cleanup also failed"),
43137
+ { causes: [primary, substrateCleanupError] }
43138
+ ) : primary;
42917
43139
  }
42918
43140
  }
42919
43141
  // --- Hook bridge surface ---------------------------------------------------
@@ -49035,10 +49257,11 @@ var ChildProcessCommandRunner = class {
49035
49257
  signal: opts.signal,
49036
49258
  ...opts.env !== void 0 ? { env: opts.env } : {},
49037
49259
  ...opts.cwd !== void 0 ? { cwd: opts.cwd } : {}
49038
- }, proc = opts.cwdAuthority ? spawnPosixOwnedProcess(argv[0], argv.slice(1), spawnOptions, {
49039
- workingDirAuthority: opts.cwdAuthority
49260
+ }, needsOwnedProcessHost = opts.cwdAuthority !== void 0 || (opts.launchPathAuthorities?.length ?? 0) > 0, proc = needsOwnedProcessHost ? spawnPosixOwnedProcess(argv[0], argv.slice(1), spawnOptions, {
49261
+ workingDirAuthority: opts.cwdAuthority,
49262
+ launchPathAuthorities: opts.launchPathAuthorities
49040
49263
  }) : (0, import_node_child_process7.spawn)(argv[0], argv.slice(1), spawnOptions);
49041
- return opts.cwdAuthority && Object.defineProperty(proc, "hostProcessTreeOwnership", {
49264
+ return needsOwnedProcessHost && Object.defineProperty(proc, "hostProcessTreeOwnership", {
49042
49265
  value: "posix-host",
49043
49266
  configurable: !1,
49044
49267
  enumerable: !1,
@@ -49618,13 +49841,14 @@ async function resolveOnParentPath(cmd) {
49618
49841
  return null;
49619
49842
  }
49620
49843
  var SandboxExecHandle = class {
49621
- constructor(id, runner, profilePath, sanitizedEnv, workdir, workdirAuthority, auditDir = null) {
49844
+ constructor(id, runner, profilePath, sanitizedEnv, workdir, workdirAuthority, scratchAuthority, auditDir = null) {
49622
49845
  this.id = id;
49623
49846
  this.runner = runner;
49624
49847
  this.profilePath = profilePath;
49625
49848
  this.sanitizedEnv = sanitizedEnv;
49626
49849
  this.workdir = workdir;
49627
49850
  this.workdirAuthority = workdirAuthority;
49851
+ this.scratchAuthority = scratchAuthority;
49628
49852
  this.auditDir = auditDir;
49629
49853
  this.egressFidelity = "coarse";
49630
49854
  this.tornDown = !1;
@@ -49660,13 +49884,18 @@ var SandboxExecHandle = class {
49660
49884
  ; exec-time agent-binary read allowance (argv0 resolution, 2026-07-03)
49661
49885
  ${allowLines}
49662
49886
  `
49887
+ ), this.scratchAuthority && await assertWorkspaceRootAuthority(
49888
+ this.scratchAuthority.canonicalPath,
49889
+ this.scratchAuthority,
49890
+ "sandbox-exec Claude scratch handoff"
49663
49891
  );
49664
49892
  let sbArgv = ["sandbox-exec", "-f", this.profilePath, resolvedArgv0, ...argv.slice(1)], proc = this.runner.spawnLong(sbArgv, {
49665
49893
  stdinTty: opts.stdinTty,
49666
49894
  signal: opts.signal,
49667
49895
  env: this.sanitizedEnv,
49668
49896
  cwd: this.workdir,
49669
- ...this.workdirAuthority ? { cwdAuthority: this.workdirAuthority } : {}
49897
+ ...this.workdirAuthority ? { cwdAuthority: this.workdirAuthority } : {},
49898
+ ...this.scratchAuthority ? { launchPathAuthorities: [this.scratchAuthority] } : {}
49670
49899
  });
49671
49900
  return this.proc = proc, proc;
49672
49901
  }
@@ -49724,6 +49953,22 @@ ${allowLines}
49724
49953
  }
49725
49954
  extraWriteDirs.push(configDir);
49726
49955
  }
49956
+ if (spec.agentScratch) {
49957
+ try {
49958
+ await assertWorkspaceRootAuthority(
49959
+ spec.agentScratch.hostDir,
49960
+ spec.agentScratch.authority,
49961
+ "sandbox-exec Claude scratch launch"
49962
+ );
49963
+ } catch (error) {
49964
+ throw new SubstrateLaunchError(
49965
+ "sandbox_exec",
49966
+ "Claude scratch grant target identity could not be verified (fail-closed)",
49967
+ error
49968
+ );
49969
+ }
49970
+ extraWriteDirs.push(spec.agentScratch.authority.canonicalPath);
49971
+ }
49727
49972
  let resolvedAuditDir = null;
49728
49973
  if (spec.auditDir) {
49729
49974
  resolvedAuditDir = spec.auditDir;
@@ -49764,6 +50009,17 @@ ${allowLines}
49764
50009
  throw await import_node_fs23.promises.rm(profilePath, { force: !0 }).catch(() => {
49765
50010
  }), err;
49766
50011
  }
50012
+ if (spec.agentScratch)
50013
+ try {
50014
+ await assertWorkspaceRootAuthority(
50015
+ spec.agentScratch.authority.canonicalPath,
50016
+ spec.agentScratch.authority,
50017
+ "sandbox-exec Claude scratch profile handoff"
50018
+ );
50019
+ } catch (err) {
50020
+ throw await import_node_fs23.promises.rm(profilePath, { force: !0 }).catch(() => {
50021
+ }), err;
50022
+ }
49767
50023
  return new SandboxExecHandle(
49768
50024
  id,
49769
50025
  this.runner,
@@ -49771,6 +50027,7 @@ ${allowLines}
49771
50027
  finalEnv,
49772
50028
  canonicalWorkdir,
49773
50029
  spec.workdirAuthority,
50030
+ spec.agentScratch?.authority,
49774
50031
  resolvedAuditDir
49775
50032
  );
49776
50033
  }
@@ -50397,13 +50654,13 @@ function formatReviewerError(detail) {
50397
50654
  case "timeout":
50398
50655
  return `${detail.agent} reviewer timed out after ${detail.elapsed_ms}ms`;
50399
50656
  case "spawn_failed":
50400
- return `${detail.agent} reviewer spawn failed: ${detail.reason}`;
50657
+ return `${detail.agent} reviewer spawn failed`;
50401
50658
  case "parse_failure":
50402
50659
  return `${detail.agent} reviewer output was unparseable`;
50403
50660
  case "cancelled":
50404
50661
  return "reviewer cancelled before completion";
50405
50662
  case "internal_join_failure":
50406
- return `reviewer task internal join failure: ${detail.reason}`;
50663
+ return "reviewer task internal join failure";
50407
50664
  }
50408
50665
  }
50409
50666
 
@@ -50945,22 +51202,25 @@ var CONTINUATION_EXPIRES_TTL_MS = 1440 * 60 * 1e3, ACTIVE_AGENT_KINDS = ["CLAUDE
50945
51202
  function isActiveAgentKind2(agent) {
50946
51203
  return agent === "CLAUDE" || agent === "CODEX" || agent === "ANTIGRAVITY";
50947
51204
  }
50948
- var IMPLEMENTOR_TICK_MS = 1e4, TICK_SCAN_MAX_TRACKED = 5e3, TICK_SCAN_TIMEOUT_MS = 1e3, BACKGROUND_RECOVERY_ABORTED = /* @__PURE__ */ Symbol("background-recovery-aborted"), REVIEWER_ERROR_LOG_RAW_OUTPUT_CAP = 4e3;
51205
+ var IMPLEMENTOR_TICK_MS = 1e4, TICK_SCAN_MAX_TRACKED = 5e3, TICK_SCAN_TIMEOUT_MS = 1e3, BACKGROUND_RECOVERY_ABORTED = /* @__PURE__ */ Symbol("background-recovery-aborted");
50949
51206
  function describeReviewerErrorDetail(detail) {
50950
51207
  switch (detail.kind) {
50951
51208
  case "timeout":
50952
51209
  return { agent: detail.agent, elapsedMs: detail.elapsed_ms };
50953
51210
  case "spawn_failed":
50954
- return { agent: detail.agent, reason: detail.reason };
51211
+ return {
51212
+ agent: detail.agent,
51213
+ reasonBytes: Buffer.byteLength(detail.reason ?? "", "utf8")
51214
+ };
50955
51215
  case "parse_failure":
50956
51216
  return {
50957
51217
  agent: detail.agent,
50958
- rawOutput: detail.raw_output.slice(0, REVIEWER_ERROR_LOG_RAW_OUTPUT_CAP)
51218
+ rawOutputShape: outputShapeOnly(detail.raw_output)
50959
51219
  };
50960
51220
  case "cancelled":
50961
51221
  return {};
50962
51222
  case "internal_join_failure":
50963
- return { reason: detail.reason };
51223
+ return { reasonBytes: Buffer.byteLength(detail.reason ?? "", "utf8") };
50964
51224
  }
50965
51225
  }
50966
51226
  var REVIEWER_AGENT_DISPLAY_NAME = {
@@ -53274,8 +53534,8 @@ var QuorumLoop = class _QuorumLoop {
53274
53534
  taskId: args.taskId,
53275
53535
  trackIndex: args.validateDiffScope.trackIndex,
53276
53536
  exitCode: exit.exitCode,
53277
- stderrExcerpt: handle.stderr().slice(0, 800),
53278
- stdoutTail: handle.stdout().slice(-3e3)
53537
+ stderrShape: outputShapeOnly(handle.stderr()),
53538
+ stdoutShape: outputShapeOnly(handle.stdout())
53279
53539
  }
53280
53540
  ), await this.reportTeamTrackFailure(
53281
53541
  args.taskId,
@@ -53297,8 +53557,8 @@ var QuorumLoop = class _QuorumLoop {
53297
53557
  // exits 0 is still attributable from the log.
53298
53558
  exitCode: exit.exitCode,
53299
53559
  runtimeMs: exit.runtimeMs,
53300
- stdoutTail: handle.stdout().slice(-300),
53301
- stderrTail: handle.stderr().slice(-300)
53560
+ stdoutShape: outputShapeOnly(handle.stdout()),
53561
+ stderrShape: outputShapeOnly(handle.stderr())
53302
53562
  });
53303
53563
  try {
53304
53564
  let bundle = createSnapshotTrackBundle({
@@ -53375,8 +53635,8 @@ var QuorumLoop = class _QuorumLoop {
53375
53635
  exitCode: exit.exitCode,
53376
53636
  runtimeMs: exit.runtimeMs,
53377
53637
  ...files.length === 0 ? {
53378
- stdoutTail: handle.stdout().slice(-300),
53379
- stderrTail: handle.stderr().slice(-300)
53638
+ stdoutShape: outputShapeOnly(handle.stdout()),
53639
+ stderrShape: outputShapeOnly(handle.stderr())
53380
53640
  } : {}
53381
53641
  }), this.surfaceHalt(reason), await this.discardShadow(args.taskId);
53382
53642
  }
@@ -53391,7 +53651,7 @@ var QuorumLoop = class _QuorumLoop {
53391
53651
  }
53392
53652
  logger.warn("[QuorumLoop] implementor round failed", {
53393
53653
  gateId,
53394
- err: err.message
53654
+ ...errorShapeOnly(err)
53395
53655
  });
53396
53656
  let reason = classifyImplementorRoundFailure(err);
53397
53657
  this.surfaceHalt(`The task could not proceed \u2014 ${reason}.`);
@@ -54470,7 +54730,7 @@ var QuorumLoop = class _QuorumLoop {
54470
54730
  if (promptText === null) {
54471
54731
  logger.warn("[QuorumLoop] getReviewerPrompt exhausted \u2014 ESCALATE", {
54472
54732
  key,
54473
- err: lastErr?.message,
54733
+ ...errorShapeOnly(lastErr),
54474
54734
  audit: "synthesized ESCALATE (#C1F-8): prompt_fetch_failed"
54475
54735
  }), await this.submitVerdict(args, this.synthesizeEscalate(args, DESKTOP_DISPATCH_ESCALATE_REASONING), sessionKey);
54476
54736
  return;
@@ -54505,13 +54765,13 @@ var QuorumLoop = class _QuorumLoop {
54505
54765
  failureReason,
54506
54766
  audit: "synthesized ESCALATE (#C1F-8): reviewer_substrate_engage_failed",
54507
54767
  ...describeReviewerErrorDetail(engageErr.detail),
54508
- message: engageErr.message
54768
+ ...errorShapeOnly(engageErr)
54509
54769
  }), await this.submitVerdict(args, this.synthesizeEscalate(args, cleanReasoning), sessionKey);
54510
54770
  return;
54511
54771
  }
54512
54772
  throw engageErr;
54513
54773
  }
54514
- let verdict;
54774
+ let verdict, evaluationError;
54515
54775
  try {
54516
54776
  verdict = await this.evaluateSeatWithTimeout(spec, args.gateId, seatSubstrate.handle);
54517
54777
  } catch (err) {
@@ -54539,7 +54799,7 @@ var QuorumLoop = class _QuorumLoop {
54539
54799
  // Audit prefix kept in LOGS only (#C1F-8) — never on the wire.
54540
54800
  audit: `synthesized ESCALATE (#C1F-8): reviewer_error:${err.detail.kind}`,
54541
54801
  ...describeReviewerErrorDetail(err.detail),
54542
- message: err.message,
54802
+ ...errorShapeOnly(err),
54543
54803
  // 2026-08-22 (dogfood) — TIMEOUT DIAGNOSTICS, local log only.
54544
54804
  //
54545
54805
  // A live 5-minute reviewer timeout on a ONE-FILE deletion diff was
@@ -54556,16 +54816,51 @@ var QuorumLoop = class _QuorumLoop {
54556
54816
  ...this.describeReviewShapeForDiagnostics(args.gateId)
54557
54817
  }), verdict = this.synthesizeEscalate(args, cleanReasoning);
54558
54818
  } else
54559
- throw err;
54560
- } finally {
54561
- await seatSubstrate.teardown().catch(() => {
54819
+ evaluationError = err;
54820
+ }
54821
+ let cleanupError;
54822
+ try {
54823
+ await seatSubstrate.teardown();
54824
+ } catch (error) {
54825
+ cleanupError = error, logger.warn("[QuorumLoop] reviewer substrate cleanup failed \u2014 forcing ESCALATE", {
54826
+ key,
54827
+ seatId: args.seatId,
54828
+ role: args.role,
54829
+ agentKind: args.agentKind,
54830
+ ...errorShapeOnly(error),
54831
+ audit: "synthesized ESCALATE: reviewer_substrate_cleanup_failed"
54562
54832
  });
54563
54833
  }
54834
+ if (evaluationError !== void 0) {
54835
+ let terminalError = cleanupError !== void 0 ? Object.assign(
54836
+ new Error("reviewer evaluation failed and substrate cleanup also failed"),
54837
+ { causes: [evaluationError, cleanupError] }
54838
+ ) : Object.assign(
54839
+ new Error("reviewer evaluation failed unexpectedly"),
54840
+ { cause: evaluationError }
54841
+ );
54842
+ logger.warn("[QuorumLoop] unexpected reviewer evaluation failure \u2014 forcing ESCALATE", {
54843
+ key,
54844
+ seatId: args.seatId,
54845
+ role: args.role,
54846
+ agentKind: args.agentKind,
54847
+ ...errorShapeOnly(terminalError),
54848
+ audit: "synthesized ESCALATE: reviewer_evaluation_failed"
54849
+ }), verdict = this.synthesizeEscalate(
54850
+ args,
54851
+ DESKTOP_DISPATCH_ESCALATE_REASONING
54852
+ );
54853
+ }
54854
+ if (cleanupError !== void 0 && (verdict = this.synthesizeEscalate(
54855
+ args,
54856
+ DESKTOP_DISPATCH_ESCALATE_REASONING
54857
+ )), verdict === void 0)
54858
+ throw new Error("reviewer completed without a verdict");
54564
54859
  reviewScope !== null && (verdict = enforceReviewRoundScope(verdict, reviewScope)), await this.submitVerdict(args, verdict, sessionKey);
54565
54860
  } catch (err) {
54566
54861
  logger.warn("[QuorumLoop] spawnOneSeat failed \u2014 dropped", {
54567
54862
  key,
54568
- err: err.message
54863
+ ...errorShapeOnly(err)
54569
54864
  });
54570
54865
  } finally {
54571
54866
  this.runningSeats.delete(key);
@@ -54658,19 +54953,18 @@ var QuorumLoop = class _QuorumLoop {
54658
54953
  if (e.name === REVIEWER_CREDENTIAL_MISSING)
54659
54954
  try {
54660
54955
  this.surfaceHalt(
54661
- `Reviewer (${seatLabel(args.agentKind, args.seatId)}) cannot start sandboxed: ${e.message}`
54956
+ `Reviewer (${seatLabel(args.agentKind, args.seatId)}) cannot start sandboxed: the required provider credential is unavailable. Run \`codevibe vendor-key setup\`` + (args.agentKind === "codex" ? " (or `codevibe vendor-key import-codex` for a ChatGPT subscription)" : "") + ", or set CODEVIBE_SANDBOX_REVIEWERS=0 to opt out of reviewer sandboxing."
54662
54957
  );
54663
54958
  } catch {
54664
54959
  }
54665
54960
  throw new ReviewerErrorClass({
54666
54961
  kind: "spawn_failed",
54667
54962
  agent: args.agentKind,
54668
- reason: `CP-7: reviewer substrate engage failed for a Trusted agent \u2014 refusing the unsandboxed (ambient-cred) fallback (fail-closed): ${e.message}`,
54963
+ reason: "CP-7: reviewer substrate engage failed for a Trusted agent \u2014 refusing the unsandboxed (ambient-cred) fallback (fail-closed)",
54669
54964
  failureReason: "spawn_failed"
54670
54965
  });
54671
54966
  }
54672
- return result.mode === "reduced_trust" ? (this.surfaceReviewerReducedTrust(args, result.reducedTrustReason), await result.teardown().catch(() => {
54673
- }), NO_TEARDOWN) : (result.reducedTrust && this.surfaceReviewerReducedTrust(
54967
+ return result.mode === "reduced_trust" ? (this.surfaceReviewerReducedTrust(args, result.reducedTrustReason), await result.teardown(), NO_TEARDOWN) : (result.reducedTrust && this.surfaceReviewerReducedTrust(
54674
54968
  args,
54675
54969
  result.reducedTrustReason ?? "coarse egress (A5 sandbox-exec) \u2014 reviewer is sandboxed but loopback-only."
54676
54970
  ), {
@@ -54712,7 +55006,7 @@ var QuorumLoop = class _QuorumLoop {
54712
55006
  reason
54713
55007
  ), logger.warn("[QuorumLoop] reduced-trust badge surfaceHalt threw (badge is best-effort) \u2014 continuing", {
54714
55008
  seatId: args.seatId,
54715
- err: e.message
55009
+ ...errorShapeOnly(e)
54716
55010
  });
54717
55011
  }
54718
55012
  }
@@ -54767,13 +55061,20 @@ var QuorumLoop = class _QuorumLoop {
54767
55061
  });
54768
55062
  if (result.mode === "substrate")
54769
55063
  return { mode: "substrate", handle: result.substrateHandle, teardown: () => result.teardown() };
54770
- await result.teardown().catch(() => {
54771
- });
55064
+ try {
55065
+ await result.teardown();
55066
+ } catch (cleanupError) {
55067
+ return logger.warn("[QuorumLoop] Wave B reduced-trust cleanup failed \u2014 refusing fallback", {
55068
+ taskId,
55069
+ agent,
55070
+ ...errorShapeOnly(cleanupError)
55071
+ }), { mode: "none" };
55072
+ }
54772
55073
  } catch (e) {
54773
55074
  logger.warn("[QuorumLoop] Wave B substrate engage failed \u2014 falling through to trusted-container/none", {
54774
55075
  taskId,
54775
55076
  agent,
54776
- err: e.message
55077
+ ...errorShapeOnly(e)
54777
55078
  });
54778
55079
  }
54779
55080
  return isTrustedContainerBoundary() ? { mode: "trusted_container", teardown: async () => {
@@ -54804,9 +55105,11 @@ var QuorumLoop = class _QuorumLoop {
54804
55105
  ...substrate !== void 0 ? { substrate } : {}
54805
55106
  });
54806
55107
  if (!outcome.exit_success)
54807
- throw new Error(
54808
- `class-2 resolver model call exited non-zero: ${outcome.stderr.trim().slice(0, 200)}`
54809
- );
55108
+ throw logger.warn("[QuorumLoop] class-2 resolver model call exited non-zero", {
55109
+ ownerTaskId: args.ownerTaskId,
55110
+ stdoutShape: outputShapeOnly(outcome.stdout),
55111
+ stderrShape: outputShapeOnly(outcome.stderr)
55112
+ }), new Error("class-2 resolver model call exited non-zero");
54810
55113
  return outcome.stdout;
54811
55114
  };
54812
55115
  return {
@@ -54850,7 +55153,7 @@ var QuorumLoop = class _QuorumLoop {
54850
55153
  return { establishable: !0, via: "ladder_tier" };
54851
55154
  } catch (err) {
54852
55155
  logger.warn("[QuorumLoop] A1d resolver probe: ladder select failed \u2014 treating as no tier", {
54853
- err: err.message
55156
+ ...errorShapeOnly(err)
54854
55157
  });
54855
55158
  }
54856
55159
  return (this.deps.resolverProbeDeps?.trustedContainer ?? isTrustedContainerBoundary)() ? { establishable: !0, via: "trusted_container" } : { establishable: !1 };
@@ -54981,8 +55284,8 @@ var QuorumLoop = class _QuorumLoop {
54981
55284
  taskId: tid,
54982
55285
  exitCode: exit.exitCode,
54983
55286
  runtimeMs: exit.runtimeMs,
54984
- stdoutTail: handle.stdout().slice(-2e3),
54985
- stderrExcerpt: handle.stderr().slice(0, 600)
55287
+ stdoutShape: outputShapeOnly(handle.stdout()),
55288
+ stderrShape: outputShapeOnly(handle.stderr())
54986
55289
  }), exit.failureClass !== null)
54987
55290
  throw new Error(`agentic resolver implementor failed: ${exit.failureClass}`);
54988
55291
  }
@@ -55045,6 +55348,7 @@ var QuorumLoop = class _QuorumLoop {
55045
55348
  });
55046
55349
  continue;
55047
55350
  }
55351
+ let candidateError;
55048
55352
  try {
55049
55353
  let substrate = confinement.mode === "substrate" ? confinement.handle : void 0, spec = {
55050
55354
  seat_id: 0,
@@ -55067,14 +55371,27 @@ var QuorumLoop = class _QuorumLoop {
55067
55371
  ...pass ? {} : { reason: `final-tip Tier-2 verdict: ${verdict.verdict}` }
55068
55372
  };
55069
55373
  } catch (e) {
55070
- lastFailure = `final-tip Tier-2 reviewer error (${agent}): ${e.message}`, logger.warn("[QuorumLoop] final-tip Tier-2 candidate failed \u2014 trying next", {
55374
+ candidateError = e, lastFailure = `final-tip Tier-2 reviewer ${agent} failed to produce a verdict`, logger.warn("[QuorumLoop] final-tip Tier-2 candidate failed \u2014 trying next", {
55071
55375
  ownerTaskId: args.ownerTaskId,
55072
55376
  agent,
55073
- err: e.message.slice(0, 300)
55377
+ ...errorShapeOnly(e)
55074
55378
  });
55075
55379
  } finally {
55076
- await confinement.teardown().catch(() => {
55077
- });
55380
+ try {
55381
+ await confinement.teardown();
55382
+ } catch (cleanupError) {
55383
+ throw logger.warn("[QuorumLoop] final-tip Tier-2 substrate cleanup failed \u2014 failing closed", {
55384
+ ownerTaskId: args.ownerTaskId,
55385
+ agent,
55386
+ ...errorShapeOnly(cleanupError)
55387
+ }), candidateError !== void 0 ? Object.assign(
55388
+ new Error("final-tip Tier-2 evaluation failed and substrate cleanup also failed"),
55389
+ { causes: [candidateError, cleanupError] }
55390
+ ) : Object.assign(
55391
+ new Error("final-tip Tier-2 substrate cleanup failed"),
55392
+ { cause: cleanupError }
55393
+ );
55394
+ }
55078
55395
  }
55079
55396
  }
55080
55397
  return {
@@ -65193,7 +65510,7 @@ function buildSanitizedBaseEnv(input) {
65193
65510
  let v = safeSource[key];
65194
65511
  typeof v == "string" && v.length > 0 && (env[key] = v);
65195
65512
  }
65196
- return input.provider === "anthropic" ? env.ANTHROPIC_BASE_URL = input.sandboxBrokerUrl : env.OPENAI_BASE_URL = `${input.sandboxBrokerUrl.replace(/\/+$/, "")}/v1`, input.provider === "anthropic" ? (env.CLAUDE_CONFIG_DIR = input.sandboxBootstrapDir, env.CLAUDE_CODE_TMPDIR = input.sandboxBootstrapDir) : env.CODEX_HOME = input.sandboxBootstrapDir, env;
65513
+ return input.provider === "anthropic" ? env.ANTHROPIC_BASE_URL = input.sandboxBrokerUrl : env.OPENAI_BASE_URL = `${input.sandboxBrokerUrl.replace(/\/+$/, "")}/v1`, input.provider === "anthropic" ? (env.CLAUDE_CONFIG_DIR = input.sandboxBootstrapDir, env.CLAUDE_CODE_TMPDIR = input.claudeScratchDir ?? input.sandboxBootstrapDir) : env.CODEX_HOME = input.sandboxBootstrapDir, env;
65197
65514
  }
65198
65515
  var FORBIDDEN_KEY_PATTERNS = [
65199
65516
  /^ANTHROPIC_API_KEY$/i,
@@ -65319,16 +65636,36 @@ async function engageSubstrate(input) {
65319
65636
  try {
65320
65637
  ({ hostBrokerAddr } = await broker.start());
65321
65638
  } catch (e) {
65322
- throw await broker.stop().catch(() => {
65323
- }), new Error(
65324
- `CP-7: broker failed to start \u2014 refusing to launch the implementor (fail-closed): ${e.message}`
65639
+ let primary = Object.assign(
65640
+ new Error("CP-7: broker failed to start \u2014 refusing to launch the implementor (fail-closed)"),
65641
+ { cause: e }
65325
65642
  );
65643
+ try {
65644
+ await broker.stop();
65645
+ } catch (cleanupError) {
65646
+ throw Object.assign(
65647
+ new Error("CP-7: broker start failed and cleanup also failed"),
65648
+ { causes: [primary, cleanupError] }
65649
+ );
65650
+ }
65651
+ throw primary;
65326
65652
  }
65327
65653
  let initialTokenObj = broker.currentBrokerToken();
65328
- if (!initialTokenObj)
65329
- throw await broker.stop().catch(() => {
65330
- }), new Error("CP-7: broker minted no token \u2014 refusing to launch (fail-closed)");
65331
- let initialToken = initialTokenObj.value, sandboxBrokerUrl = selection.tier === "docker" ? `http://127.0.0.1:${RELAY_PORT}` : `http://${hostBrokerAddr}`, sandboxHome = selection.tier === "docker" ? CONTAINER_WORKDIR : input.workdir, sandboxBootstrapMount = selection.tier === "docker" ? SANDBOX_BOOTSTRAP_DIR : null, sandboxConfigMount = selection.tier === "docker" ? SANDBOX_AGENT_CONFIG_DIR : null, bootstrap, handle;
65654
+ if (!initialTokenObj) {
65655
+ let primary = new Error(
65656
+ "CP-7: broker minted no token \u2014 refusing to launch (fail-closed)"
65657
+ );
65658
+ try {
65659
+ await broker.stop();
65660
+ } catch (cleanupError) {
65661
+ throw Object.assign(
65662
+ new Error("CP-7: broker minted no token and cleanup also failed"),
65663
+ { causes: [primary, cleanupError] }
65664
+ );
65665
+ }
65666
+ throw primary;
65667
+ }
65668
+ let initialToken = initialTokenObj.value, sandboxBrokerUrl = selection.tier === "docker" ? `http://127.0.0.1:${RELAY_PORT}` : `http://${hostBrokerAddr}`, sandboxHome = selection.tier === "docker" ? CONTAINER_WORKDIR : input.workdir, sandboxBootstrapMount = selection.tier === "docker" ? SANDBOX_BOOTSTRAP_DIR : null, sandboxConfigMount = selection.tier === "docker" ? SANDBOX_AGENT_CONFIG_DIR : null, bootstrap, handle, activeTeardown;
65332
65669
  try {
65333
65670
  bootstrap = await ApiKeyBootstrap.create({
65334
65671
  provider,
@@ -65339,6 +65676,7 @@ async function engageSubstrate(input) {
65339
65676
  sandboxConfigDir: sandboxConfigMount ?? SANDBOX_AGENT_CONFIG_DIR
65340
65677
  } : { sandboxDirEqualsHostDir: !0, configDirEqualsHostDir: !0 },
65341
65678
  hostRoot: input.bootstrapHostRoot,
65679
+ ...selection.tier === "sandbox_exec" && provider === "anthropic" ? { claudeScratchHostRoot: input.claudeScratchHostRoot ?? "/tmp" } : {},
65342
65680
  // M-2 — fail closed if the bootstrap dir lands inside the agent's rw
65343
65681
  // workdir mount (the token would leak through it).
65344
65682
  workdir: input.workdir,
@@ -65350,11 +65688,12 @@ async function engageSubstrate(input) {
65350
65688
  ), agentConfigSpec = sandboxConfigMount !== null ? { hostDir: bootstrap.configHostDir, sandboxDir: sandboxConfigMount } : (
65351
65689
  // A5: sandbox fs == host fs → config path equals the host dir.
65352
65690
  { hostDir: bootstrap.configHostDir, sandboxDir: bootstrap.configHostDir }
65353
- ), sanitizedBase = buildSanitizedBaseEnv({
65691
+ ), agentScratchSpec = bootstrap.claudeScratchSpec, sanitizedBase = buildSanitizedBaseEnv({
65354
65692
  provider,
65355
65693
  sandboxBrokerUrl,
65356
65694
  sandboxHome,
65357
65695
  sandboxBootstrapDir: agentConfigSpec.sandboxDir,
65696
+ ...agentScratchSpec !== null ? { claudeScratchDir: agentScratchSpec.sandboxDir } : {},
65358
65697
  safeSource: input.localeSource
65359
65698
  });
65360
65699
  assertNoAmbientCreds(sanitizedBase);
@@ -65368,6 +65707,7 @@ async function engageSubstrate(input) {
65368
65707
  sanitizedEnv: finalEnv,
65369
65708
  agentBootstrap: agentBootstrapSpec,
65370
65709
  agentConfig: agentConfigSpec,
65710
+ ...agentScratchSpec !== null ? { agentScratch: agentScratchSpec } : {},
65371
65711
  // CP-7 W3 — Stage-2 r1 HIGH. The resolved audit dir → A5 trailing deny.
65372
65712
  // Undefined when a test injects its own in-memory sink (no real tree).
65373
65713
  ...resolvedAuditDir !== null ? { auditDir: resolvedAuditDir } : {}
@@ -65376,12 +65716,30 @@ async function engageSubstrate(input) {
65376
65716
  `[CP-7] Substrate engaged (tier=${selection.tier}, egress=${handle.egressFidelity}) \u2014 agent is creditless, broker holds the key`,
65377
65717
  { taskId: input.taskId, agent: input.agentKind }
65378
65718
  );
65379
- let liveBootstrap = bootstrap, liveHandle = handle, liveBroker = broker, refreshTimer = null, teardown = async () => {
65380
- refreshTimer && (clearInterval(refreshTimer), refreshTimer = null), await liveHandle.teardown().catch(() => {
65381
- }), await liveBroker.stop().catch(() => {
65382
- }), await liveBootstrap.destroy().catch(() => {
65383
- });
65384
- }, launchId = `launch-${input.taskId}-${Date.now()}`, denialReason = liveHandle.egressFidelity === "strict" ? "structural_deny_network_none" : "coarse_loopback_only", postureRes;
65719
+ let liveBootstrap = bootstrap, liveHandle = handle, liveBroker = broker, refreshTimer = null, teardownPromise, teardown = () => teardownPromise || (teardownPromise = (async () => {
65720
+ refreshTimer && (clearInterval(refreshTimer), refreshTimer = null);
65721
+ let failures = [];
65722
+ for (let cleanup of [
65723
+ () => liveHandle.teardown(),
65724
+ () => liveBroker.stop(),
65725
+ () => liveBootstrap.destroy()
65726
+ ])
65727
+ try {
65728
+ await cleanup();
65729
+ } catch (error) {
65730
+ failures.push(error);
65731
+ }
65732
+ if (failures.length > 0)
65733
+ throw logger.warn("[CP-7] substrate cleanup failed \u2014 task cannot pass", {
65734
+ taskId: input.taskId,
65735
+ failureCount: failures.length,
65736
+ ...errorShapeOnly(failures[0])
65737
+ }), failures.length === 1 ? failures[0] : Object.assign(new Error("CP-7 substrate cleanup failed"), {
65738
+ causes: failures
65739
+ });
65740
+ })(), teardownPromise);
65741
+ activeTeardown = teardown;
65742
+ let launchId = `launch-${input.taskId}-${Date.now()}`, denialReason = liveHandle.egressFidelity === "strict" ? "structural_deny_network_none" : "coarse_loopback_only", postureRes;
65385
65743
  try {
65386
65744
  postureRes = await auditSink.emit("egress_denied", {
65387
65745
  destination: "*",
@@ -65390,13 +65748,16 @@ async function engageSubstrate(input) {
65390
65748
  caller_event_id: launchId
65391
65749
  });
65392
65750
  } catch (e) {
65393
- throw await teardown(), new Error(
65394
- `CP-7 W3: egress-posture audit emit THREW \u2014 refusing to launch the creditless agent (audit-before-effect fail-closed): ${e.message}`
65751
+ throw Object.assign(
65752
+ new Error(
65753
+ "CP-7 W3: egress-posture audit emit failed \u2014 refusing to launch the creditless agent (audit-before-effect fail-closed)"
65754
+ ),
65755
+ { cause: e }
65395
65756
  );
65396
65757
  }
65397
65758
  if (!("ack" in postureRes))
65398
- throw await teardown(), new Error(
65399
- `CP-7 W3: egress-posture audit nack'd ("${postureRes.nack}") \u2014 refusing to launch the creditless agent (no durable egress posture \u2192 no agent).`
65759
+ throw new Error(
65760
+ "CP-7 W3: egress-posture audit was rejected \u2014 refusing to launch the creditless agent (no durable egress posture \u2192 no agent)"
65400
65761
  );
65401
65762
  let refreshIntervalMs = input.tokenRefreshIntervalMs ?? 600 * 1e3, inFlightRefresh = null, doRefreshOnce = async () => {
65402
65763
  if (!(typeof liveBroker.rotateBrokerToken == "function" && typeof liveBroker.commitBrokerTokenRotation == "function" && typeof liveBroker.rollbackBrokerTokenRotation == "function")) {
@@ -65409,10 +65770,23 @@ async function engageSubstrate(input) {
65409
65770
  try {
65410
65771
  await liveBootstrap.refresh(rotated.token.value), liveBroker.commitBrokerTokenRotation(rotated);
65411
65772
  } catch (e) {
65412
- throw liveBroker.rollbackBrokerTokenRotation(rotated), logger.warn(
65773
+ liveBroker.rollbackBrokerTokenRotation(rotated), logger.warn(
65413
65774
  "[CP-7] broker-token refresh write FAILED \u2014 rolled back to the prior token and tearing down (fail-closed)",
65414
- { taskId: input.taskId, err: e.message }
65415
- ), await teardown(), e instanceof Error ? e : new Error(String(e));
65775
+ { taskId: input.taskId, ...errorShapeOnly(e) }
65776
+ );
65777
+ let refreshFailure = Object.assign(
65778
+ new Error("CP-7 broker-token refresh failed"),
65779
+ { cause: e }
65780
+ );
65781
+ try {
65782
+ await teardown();
65783
+ } catch (cleanupError) {
65784
+ throw Object.assign(
65785
+ new Error("CP-7 broker-token refresh failed and cleanup also failed"),
65786
+ { causes: [refreshFailure, cleanupError] }
65787
+ );
65788
+ }
65789
+ throw refreshFailure;
65416
65790
  }
65417
65791
  }, doRefresh = async () => {
65418
65792
  let next = (inFlightRefresh ?? Promise.resolve()).catch(() => {
@@ -65439,10 +65813,32 @@ async function engageSubstrate(input) {
65439
65813
  teardown
65440
65814
  };
65441
65815
  } catch (e) {
65442
- throw await handle?.teardown().catch(() => {
65443
- }), await bootstrap?.destroy().catch(() => {
65444
- }), await broker.stop().catch(() => {
65445
- }), e instanceof Error ? e : new Error(String(e));
65816
+ let primary = e instanceof Error ? e : new Error("CP-7 substrate launch failed"), cleanupFailures = [];
65817
+ if (activeTeardown)
65818
+ try {
65819
+ await activeTeardown();
65820
+ } catch (cleanupError) {
65821
+ cleanupFailures.push(cleanupError);
65822
+ }
65823
+ else
65824
+ for (let cleanup of [
65825
+ ...handle ? [() => handle.teardown()] : [],
65826
+ ...bootstrap ? [() => bootstrap.destroy()] : [],
65827
+ () => broker.stop()
65828
+ ])
65829
+ try {
65830
+ await cleanup();
65831
+ } catch (cleanupError) {
65832
+ cleanupFailures.push(cleanupError);
65833
+ }
65834
+ throw cleanupFailures.length > 0 ? (logger.warn("[CP-7] failed launch also failed cleanup", {
65835
+ taskId: input.taskId,
65836
+ failureCount: cleanupFailures.length,
65837
+ ...errorShapeOnly(cleanupFailures[0])
65838
+ }), Object.assign(
65839
+ new Error("CP-7 substrate launch failed and cleanup also failed"),
65840
+ { causes: [primary, ...cleanupFailures] }
65841
+ )) : primary;
65446
65842
  }
65447
65843
  }
65448
65844
  var RESOLVER_AGENT_STATE_DIR_SEGMENTS = {