@quantiya/codevibe-claude-plugin 2.0.22 → 2.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/node_modules/@quantiya/codevibe-core/dist/appsync/appsync-client.d.ts +25 -0
  3. package/node_modules/@quantiya/codevibe-core/dist/appsync/index.d.ts +1 -1
  4. package/node_modules/@quantiya/codevibe-core/dist/config/config.d.ts +1 -1
  5. package/node_modules/@quantiya/codevibe-core/dist/diagnostics/shape-only.d.ts +11 -0
  6. package/node_modules/@quantiya/codevibe-core/dist/index.js +329 -290
  7. package/node_modules/@quantiya/codevibe-core/dist/local-executor/process-tree.d.ts +2 -0
  8. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/__tests__/o5-diagnostics.test.d.ts +1 -0
  9. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/__tests__/quorum-loop-outcome-recency.test.d.ts +1 -0
  10. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/cli.d.ts +7 -0
  11. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/cli.js +888 -225
  12. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/quorum-loop.d.ts +12 -1
  13. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/types.d.ts +24 -0
  14. package/node_modules/@quantiya/codevibe-core/dist/ordinal-presentation.d.ts +4 -0
  15. package/node_modules/@quantiya/codevibe-core/dist/ordinal-presentation.test.d.ts +1 -0
  16. package/node_modules/@quantiya/codevibe-core/dist/reviewer/provider.d.ts +7 -7
  17. package/node_modules/@quantiya/codevibe-core/dist/substrate/command-runner.d.ts +1 -0
  18. package/node_modules/@quantiya/codevibe-core/dist/substrate/sandbox-exec.d.ts +3 -2
  19. package/node_modules/@quantiya/codevibe-core/dist/substrate/types.d.ts +20 -0
  20. package/node_modules/@quantiya/codevibe-core/dist/substrate-launch/apikey-bootstrap.d.ts +32 -1
  21. package/node_modules/@quantiya/codevibe-core/dist/substrate-launch/engage-substrate.d.ts +2 -0
  22. package/node_modules/@quantiya/codevibe-core/dist/substrate-launch/sanitized-env.d.ts +6 -0
  23. package/node_modules/@quantiya/codevibe-core/package.json +1 -1
  24. package/package.json +2 -2
  25. package/node_modules/fs-ext/build/Makefile +0 -347
  26. package/node_modules/fs-ext/build/Release/.deps/Release/fs_ext.node.d +0 -1
  27. package/node_modules/fs-ext/build/Release/.deps/Release/obj.target/fs_ext/fs-ext.o.d +0 -165
  28. package/node_modules/fs-ext/build/Release/fs_ext.node +0 -0
  29. package/node_modules/fs-ext/build/Release/obj.target/fs_ext/fs-ext.o +0 -0
  30. package/node_modules/fs-ext/build/binding.Makefile +0 -6
  31. package/node_modules/fs-ext/build/config.gypi +0 -503
  32. package/node_modules/fs-ext/build/fs_ext.target.mk +0 -183
  33. package/node_modules/fs-ext/build/gyp-mac-tool +0 -768
  34. package/node_modules/keytar/build/Release/keytar.node +0 -0
@@ -1042,6 +1042,7 @@ __export(cli_exports, {
1042
1042
  CliEntitlementError: () => CliEntitlementError,
1043
1043
  CliUsageError: () => CliUsageError,
1044
1044
  LocalModelPlannerUnavailableAdapter: () => LocalModelPlannerUnavailableAdapter,
1045
+ ORCHESTRATION_ORPHAN_SWEEP_STALE_THRESHOLD_MS: () => ORCHESTRATION_ORPHAN_SWEEP_STALE_THRESHOLD_MS,
1045
1046
  OrchestrationSessionBootstrapError: () => OrchestrationSessionBootstrapError,
1046
1047
  PlannerUnavailableError: () => PlannerUnavailableError,
1047
1048
  bridgeAuthorityErrorToShellRefusal: () => bridgeAuthorityErrorToShellRefusal,
@@ -1067,7 +1068,8 @@ __export(cli_exports, {
1067
1068
  resumeOrCreateSession: () => resumeOrCreateSession2,
1068
1069
  runAuditCli: () => runAuditCli,
1069
1070
  runModelCli: () => runModelCli,
1070
- runWithAdmittedSessionOwnership: () => runWithAdmittedSessionOwnership
1071
+ runWithAdmittedSessionOwnership: () => runWithAdmittedSessionOwnership,
1072
+ triggerStartupOrphanSweep: () => triggerStartupOrphanSweep
1071
1073
  });
1072
1074
  module.exports = __toCommonJS(cli_exports);
1073
1075
  var import_node_crypto19 = require("node:crypto");
@@ -1173,9 +1175,9 @@ var import_os = __toESM(require("os")), import_path = __toESM(require("path")),
1173
1175
  }
1174
1176
  }
1175
1177
  }, currentConfig = null, configInitialized = !1;
1176
- function getEnvironment() {
1177
- let env = process.env.ENVIRONMENT;
1178
- return env === "development" || env === "production" || env === "experiment" ? env : "production";
1178
+ function getEnvironment(env = process.env) {
1179
+ let target = env.ENVIRONMENT;
1180
+ return target === "development" || target === "production" || target === "experiment" ? target : "production";
1179
1181
  }
1180
1182
  function loadConfig(environment) {
1181
1183
  let env = environment || getEnvironment();
@@ -4467,6 +4469,18 @@ var RECONNECT_CONFIG = {
4467
4469
  function shouldDeliverSubscribedEvent(event, receiveDesktopEvents) {
4468
4470
  return !!(event.source === "MOBILE" /* MOBILE */ || receiveDesktopEvents && event.source === "DESKTOP" /* DESKTOP */);
4469
4471
  }
4472
+ var DEFAULT_HEARTBEAT_INTERVAL_MS = 12e4, MIN_HEARTBEAT_INTERVAL_MS = 1e3, MAX_HEARTBEAT_INTERVAL_MS = 3e5;
4473
+ function isRecognizedNonProductionEnvironment(env = process.env) {
4474
+ let canonicalEnv = getEnvironment(env);
4475
+ return canonicalEnv === "development" || canonicalEnv === "experiment";
4476
+ }
4477
+ function resolveHeartbeatIntervalMs(override, env = process.env) {
4478
+ let candidate;
4479
+ if (override !== void 0 ? candidate = override : isRecognizedNonProductionEnvironment(env) && env.CODEVIBE_HEARTBEAT_INTERVAL_MS !== void 0 && (candidate = env.CODEVIBE_HEARTBEAT_INTERVAL_MS), candidate == null || candidate === "")
4480
+ return DEFAULT_HEARTBEAT_INTERVAL_MS;
4481
+ let num = typeof candidate == "number" ? candidate : Number(candidate);
4482
+ return !Number.isFinite(num) || !Number.isInteger(num) || num < MIN_HEARTBEAT_INTERVAL_MS || num > MAX_HEARTBEAT_INTERVAL_MS ? DEFAULT_HEARTBEAT_INTERVAL_MS : num;
4483
+ }
4470
4484
  var AppSyncClient = class _AppSyncClient {
4471
4485
  constructor() {
4472
4486
  this.authenticated = !1;
@@ -7212,14 +7226,14 @@ var AppSyncClient = class _AppSyncClient {
7212
7226
  * Start periodic heartbeat for a session.
7213
7227
  * Updates lastHeartbeatAt on the session every intervalMs (default 2 minutes).
7214
7228
  */
7215
- startHeartbeat(sessionId, intervalMs = 120 * 1e3) {
7216
- let existingConfirmation = this.heartbeatInitialPulses.get(sessionId);
7229
+ startHeartbeat(sessionId, intervalMs) {
7230
+ let effectiveIntervalMs = resolveHeartbeatIntervalMs(intervalMs), existingConfirmation = this.heartbeatInitialPulses.get(sessionId);
7217
7231
  if (existingConfirmation) return existingConfirmation;
7218
- let confirmation = this.startHeartbeatOutcome(sessionId, intervalMs).then((value) => (value === "retired" && this.notifyRetiredSession(sessionId), value === "confirmed"));
7232
+ let confirmation = this.startHeartbeatOutcome(sessionId, effectiveIntervalMs).then((value) => (value === "retired" && this.notifyRetiredSession(sessionId), value === "confirmed"));
7219
7233
  return this.heartbeatInitialPulses.set(sessionId, confirmation), confirmation;
7220
7234
  }
7221
- startHeartbeatOutcome(sessionId, intervalMs = 120 * 1e3) {
7222
- let existingToken = this.heartbeatTokens.get(sessionId), existingTimer = this.heartbeatTimers.get(sessionId);
7235
+ startHeartbeatOutcome(sessionId, intervalMs) {
7236
+ let effectiveIntervalMs = resolveHeartbeatIntervalMs(intervalMs), existingToken = this.heartbeatTokens.get(sessionId), existingTimer = this.heartbeatTimers.get(sessionId);
7223
7237
  if (existingToken && existingTimer) {
7224
7238
  let existingOutcome = this.heartbeatInitialOutcomes.get(sessionId);
7225
7239
  if (existingOutcome) return existingOutcome;
@@ -7244,8 +7258,8 @@ var AppSyncClient = class _AppSyncClient {
7244
7258
  ).then((outcome) => {
7245
7259
  outcome === "retired" && this.notifyRetiredSession(sessionId);
7246
7260
  });
7247
- }, intervalMs);
7248
- return this.heartbeatTimers.set(sessionId, timer), logger.info("[AppSyncClient] Heartbeat started", { sessionId, intervalMs }), initialHeartbeat;
7261
+ }, effectiveIntervalMs);
7262
+ return this.heartbeatTimers.set(sessionId, timer), logger.info("[AppSyncClient] Heartbeat started", { sessionId, intervalMs: effectiveIntervalMs }), initialHeartbeat;
7249
7263
  }
7250
7264
  /**
7251
7265
  * Stop heartbeat for a session.
@@ -7945,6 +7959,14 @@ function sanitizeForTerminal(input) {
7945
7959
  return out;
7946
7960
  }
7947
7961
 
7962
+ // src/ordinal-presentation.ts
7963
+ function displayOrdinal(index) {
7964
+ return Number.isSafeInteger(index) && index >= 0 ? String(index + 1) : "?";
7965
+ }
7966
+ function displaySeatNumber(seatId) {
7967
+ return /^(0|[1-9][0-9]*)$/.test(seatId) ? displayOrdinal(Number(seatId)) : seatId;
7968
+ }
7969
+
7948
7970
  // src/reviewer/token-usage.ts
7949
7971
  function finiteToken(n) {
7950
7972
  return tokenFieldAvailable(n) ? n : 0;
@@ -8123,7 +8145,7 @@ function renderProgressLine(event) {
8123
8145
  return "Workspace copy ready \u2014 starting implementor";
8124
8146
  case "implementor_running": {
8125
8147
  let files = typeof event.filesChanged == "number" ? `, ${event.filesChanged} ${event.filesChanged === 1 ? "file" : "files"} changed` : "";
8126
- return `Implementor working in shadow \u2014 round ${event.round}${files}`;
8148
+ return `Implementor working in shadow \u2014 round ${displayOrdinal(event.round)}${files}`;
8127
8149
  }
8128
8150
  case "diff_captured": {
8129
8151
  let parts = [];
@@ -8132,7 +8154,7 @@ function renderProgressLine(event) {
8132
8154
  return `Diff captured: ${event.files} ${event.files === 1 ? "file" : "files"}${breakdown}`;
8133
8155
  }
8134
8156
  case "submitting_diff":
8135
- return `Submitting changes for review \u2014 round ${event.round}`;
8157
+ return `Submitting changes for review \u2014 round ${displayOrdinal(event.round)}`;
8136
8158
  case "reviewers_dispatched":
8137
8159
  return `Reviewers dispatched \u2014 ${event.seats} ${event.seats === 1 ? "seat" : "seats"}`;
8138
8160
  case "seat_update":
@@ -8141,10 +8163,10 @@ function renderProgressLine(event) {
8141
8163
  return `Verdicts ${event.received}/${event.expected} received`;
8142
8164
  case "revise_round": {
8143
8165
  let summary = event.feedbackSummary ? ` \u2014 ${event.feedbackSummary}` : "";
8144
- return `Revise round ${event.round} \u2014 re-running implementor${summary}`;
8166
+ return `Revise round ${displayOrdinal(event.round)} \u2014 re-running implementor${summary}`;
8145
8167
  }
8146
8168
  case "round_failed":
8147
- return `Implementor round ${event.round} failed \u2014 ${event.reason}`;
8169
+ return `Implementor round ${displayOrdinal(event.round)} failed \u2014 ${event.reason}`;
8148
8170
  case "continuation_offered":
8149
8171
  return `Implementor halted (${event.reason}) \u2014 choose a continuation agent`;
8150
8172
  case "declared_tests_skipped":
@@ -8279,19 +8301,20 @@ function reducer(state, action) {
8279
8301
  return reduceGateSummaryLoaded(state, action.gateId, action.panelModel);
8280
8302
  // ─── CP-12 W3 team-track arms (per PHASE-CP-12-DESIGN.md §3.5) ──────────
8281
8303
  case "TEAM_STARTED":
8282
- return reduceTeamStarted(state, action.taskGroupId);
8304
+ return reduceTeamStarted(state, action.taskGroupId, action.startedAt);
8283
8305
  case "TEAM_TRACK_ASSIGNED":
8284
8306
  return reduceTeamTrackAssigned(
8285
8307
  state,
8286
8308
  action.trackIndex,
8287
8309
  action.state,
8288
8310
  action.taskId,
8289
- action.agent
8311
+ action.agent,
8312
+ action.startedAt
8290
8313
  );
8291
8314
  case "TEAM_MERGE_GATE":
8292
8315
  return reduceTeamMergeGate(state, action.status, action.startedAt, action.endedAt);
8293
8316
  case "TEAM_HALTED":
8294
- return reduceTeamHalted(state, action.haltReason);
8317
+ return reduceTeamHalted(state, action.haltReason, action.completedAt);
8295
8318
  case "TEAM_TRACK_TERMINAL":
8296
8319
  return reduceTeamTrackTerminal(state, action.trackIndex, action.state);
8297
8320
  case "TEAM_TRACK_REVISING":
@@ -8299,7 +8322,7 @@ function reducer(state, action) {
8299
8322
  case "TEAM_TRACK_AWAITING_DECISION":
8300
8323
  return reduceTeamTrackTerminal(state, action.trackIndex, "AwaitingDecision");
8301
8324
  case "TEAM_GROUP_RESOLVED":
8302
- return reduceTeamGroupResolved(state, action.outcome);
8325
+ return reduceTeamGroupResolved(state, action.outcome, action.completedAt);
8303
8326
  case "TASK_PROGRESS":
8304
8327
  return reduceTaskProgress(
8305
8328
  reduceReviewerStatusLine(
@@ -8410,19 +8433,26 @@ function reduceSessionTaskTerminal(state, event) {
8410
8433
  if (terminal === null) return state;
8411
8434
  let taskId = "taskId" in event ? event.taskId : void 0;
8412
8435
  if (!taskId) return state;
8413
- let runningTasks = new Map(state.runningTasks), evictedRunningTask = runningTasks.delete(taskId), existing = state.sessionTasks.get(taskId), sessionTasks = new Map(state.sessionTasks);
8436
+ let runningTasks = new Map(state.runningTasks), evictedRunningTask = runningTasks.delete(taskId), existing = state.sessionTasks.get(taskId), sessionTasks = new Map(state.sessionTasks), nextSeq = (state.terminalSequence ?? 0) + 1;
8414
8437
  if (existing) {
8415
8438
  if (existing.origin !== "single" || existing.status !== "running")
8416
8439
  return evictedRunningTask ? { ...state, runningTasks } : state;
8417
- sessionTasks.set(taskId, { ...existing, status: terminal });
8440
+ sessionTasks.set(taskId, {
8441
+ ...existing,
8442
+ status: terminal,
8443
+ completedAt: (/* @__PURE__ */ new Date()).toISOString(),
8444
+ terminalSeq: nextSeq
8445
+ });
8418
8446
  } else
8419
8447
  sessionTasks.set(taskId, {
8420
8448
  taskId,
8421
8449
  origin: "single",
8422
8450
  status: terminal,
8423
- startedAt: (/* @__PURE__ */ new Date()).toISOString()
8451
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
8452
+ completedAt: (/* @__PURE__ */ new Date()).toISOString(),
8453
+ terminalSeq: nextSeq
8424
8454
  });
8425
- return { ...state, runningTasks, sessionTasks };
8455
+ return { ...state, runningTasks, sessionTasks, terminalSequence: nextSeq };
8426
8456
  }
8427
8457
  var REVIEWER_STATUS_PHASES = /* @__PURE__ */ new Set([
8428
8458
  "reviewers_dispatched",
@@ -8727,14 +8757,21 @@ function reduceClarificationAnswered(state, answer) {
8727
8757
  function reduceTaskLifecycle(state, task) {
8728
8758
  let existing = state.sessionTasks.get(task.taskId), existingIsTerminal = existing?.origin === "single" && (existing.status === "completed" || existing.status === "cancelled" || existing.status === "failed"), incomingIsTerminal = task.status === "completed" || task.status === "cancelled" || task.status === "failed", next = new Map(state.runningTasks);
8729
8759
  incomingIsTerminal || existingIsTerminal && !incomingIsTerminal ? next.delete(task.taskId) : next.set(task.taskId, task);
8730
- let history = new Map(state.sessionTasks);
8760
+ let history = new Map(state.sessionTasks), nextSeq = incomingIsTerminal ? existing?.terminalSeq ?? (state.terminalSequence ?? 0) + 1 : existing?.terminalSeq, terminalSequence = incomingIsTerminal && !existing?.terminalSeq ? nextSeq : state.terminalSequence;
8731
8761
  return history.set(task.taskId, {
8732
8762
  taskId: task.taskId,
8733
8763
  agentKind: task.agentKind,
8734
8764
  origin: "single",
8735
8765
  status: existingIsTerminal && !incomingIsTerminal ? existing.status : task.status,
8736
- startedAt: task.startedAt
8737
- }), { ...state, runningTasks: next, sessionTasks: history };
8766
+ startedAt: task.startedAt,
8767
+ completedAt: incomingIsTerminal ? task.completedAt ?? existing?.completedAt ?? task.startedAt : existing?.completedAt,
8768
+ ...nextSeq !== void 0 ? { terminalSeq: nextSeq } : {}
8769
+ }), {
8770
+ ...state,
8771
+ runningTasks: next,
8772
+ sessionTasks: history,
8773
+ ...terminalSequence !== void 0 ? { terminalSequence } : {}
8774
+ };
8738
8775
  }
8739
8776
  function findUnfinalizedGatePromptIndex(state, taskId) {
8740
8777
  for (let i = 0; i < state.conversation.length; i++) {
@@ -9059,28 +9096,38 @@ function reduceGateSummaryLoaded(state, gateId, panelModel) {
9059
9096
  ], attached = { ...state, conversation: nextConversation };
9060
9097
  return isFirstLoad && panelModel.rounds.length > 0 ? appendConversation(attached, makeGateSummaryPanel(panelModel)) : attached;
9061
9098
  }
9062
- function reduceTeamStarted(state, taskGroupId) {
9099
+ function reduceTeamStarted(state, taskGroupId, startedAt) {
9063
9100
  if (state.team) {
9064
9101
  if (state.team.taskGroupId === taskGroupId)
9065
9102
  return state;
9066
9103
  if (state.team.taskGroupId === "")
9067
- return { ...state, team: { ...state.team, taskGroupId } };
9104
+ return {
9105
+ ...state,
9106
+ team: {
9107
+ ...state.team,
9108
+ taskGroupId,
9109
+ startedAt: state.team.startedAt ?? startedAt ?? (/* @__PURE__ */ new Date()).toISOString()
9110
+ }
9111
+ };
9068
9112
  }
9069
- return { ...state, team: {
9113
+ let team = {
9070
9114
  taskGroupId,
9071
9115
  tracks: /* @__PURE__ */ new Map(),
9072
9116
  mergeGate: "none",
9073
9117
  haltReason: null,
9074
- groupResolved: !1
9075
- } };
9118
+ groupResolved: !1,
9119
+ startedAt: startedAt ?? (/* @__PURE__ */ new Date()).toISOString()
9120
+ };
9121
+ return { ...state, team };
9076
9122
  }
9077
- function reduceTeamTrackAssigned(state, trackIndex, trackState, taskId, agent) {
9123
+ function reduceTeamTrackAssigned(state, trackIndex, trackState, taskId, agent, startedAt) {
9078
9124
  let base = state.team ?? {
9079
9125
  taskGroupId: "",
9080
9126
  tracks: /* @__PURE__ */ new Map(),
9081
9127
  mergeGate: "none",
9082
9128
  haltReason: null,
9083
- groupResolved: !1
9129
+ groupResolved: !1,
9130
+ startedAt: startedAt ?? (/* @__PURE__ */ new Date()).toISOString()
9084
9131
  }, tracks = new Map(base.tracks), prior = tracks.get(trackIndex), merged = {
9085
9132
  state: trackState,
9086
9133
  // Preserve a prior taskId/agent when the new event omits them.
@@ -9091,12 +9138,16 @@ function reduceTeamTrackAssigned(state, trackIndex, trackState, taskId, agent) {
9091
9138
  let sessionTasks = state.sessionTasks;
9092
9139
  if (merged.taskId) {
9093
9140
  let existing = state.sessionTasks.get(merged.taskId);
9094
- sessionTasks = new Map(state.sessionTasks), sessionTasks.set(merged.taskId, {
9141
+ sessionTasks = new Map(state.sessionTasks);
9142
+ let isTerminal = trackState === "Passed" || trackState === "Failed";
9143
+ sessionTasks.set(merged.taskId, {
9095
9144
  taskId: merged.taskId,
9096
9145
  agentKind: merged.agent ?? existing?.agentKind,
9097
9146
  origin: "team",
9098
9147
  status: trackState,
9099
- startedAt: existing?.startedAt ?? (/* @__PURE__ */ new Date()).toISOString()
9148
+ startedAt: existing?.startedAt ?? startedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
9149
+ completedAt: isTerminal ? existing?.completedAt : void 0,
9150
+ ...isTerminal && existing?.terminalSeq !== void 0 ? { terminalSeq: existing.terminalSeq } : {}
9100
9151
  });
9101
9152
  }
9102
9153
  return { ...state, team: { ...base, tracks }, sessionTasks };
@@ -9132,12 +9183,19 @@ function reduceTeamMergeGate(state, status, startedAt, endedAt) {
9132
9183
  let elapsed = team.mergeGateElapsedMs ?? mergeGateElapsed(team.mergeGateStartedAt, endedAt);
9133
9184
  return { ...state, team: { ...team, mergeGate: status, mergeGateElapsedMs: elapsed } };
9134
9185
  }
9135
- function reduceTeamHalted(state, haltReason) {
9186
+ function reduceTeamHalted(state, haltReason, completedAt) {
9136
9187
  if (!state.team) return state;
9137
- let t = state.team, frozen = t.mergeGateStartedAt != null && t.mergeGateElapsedMs == null ? mergeGateElapsed(t.mergeGateStartedAt, (/* @__PURE__ */ new Date()).toISOString()) : t.mergeGateElapsedMs;
9188
+ let t = state.team, frozen = t.mergeGateStartedAt != null && t.mergeGateElapsedMs == null ? mergeGateElapsed(t.mergeGateStartedAt, (/* @__PURE__ */ new Date()).toISOString()) : t.mergeGateElapsedMs, nextSeq = (state.terminalSequence ?? 0) + 1;
9138
9189
  return {
9139
9190
  ...state,
9140
- team: { ...t, haltReason, mergeGateElapsedMs: frozen },
9191
+ team: {
9192
+ ...t,
9193
+ haltReason,
9194
+ mergeGateElapsedMs: frozen,
9195
+ completedAt: completedAt ?? t.completedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
9196
+ terminalSeq: nextSeq
9197
+ },
9198
+ terminalSequence: nextSeq,
9141
9199
  lastReviewerProgress: null
9142
9200
  };
9143
9201
  }
@@ -9147,15 +9205,37 @@ function reduceTeamTrackTerminal(state, trackIndex, trackState) {
9147
9205
  if (!prior || prior.state === trackState) return state;
9148
9206
  let tracks = new Map(state.team.tracks);
9149
9207
  tracks.set(trackIndex, { ...prior, state: trackState });
9150
- let sessionTasks = state.sessionTasks, record = prior.taskId ? state.sessionTasks.get(prior.taskId) : void 0;
9151
- return prior.taskId && record && (sessionTasks = new Map(state.sessionTasks), sessionTasks.set(prior.taskId, { ...record, status: trackState })), { ...state, team: { ...state.team, tracks }, sessionTasks };
9208
+ let sessionTasks = state.sessionTasks, record = prior.taskId ? state.sessionTasks.get(prior.taskId) : void 0, terminalSequence = state.terminalSequence;
9209
+ if (prior.taskId && record) {
9210
+ let isTerminal = trackState === "Passed" || trackState === "Failed", nextSeq = isTerminal ? record.terminalSeq ?? (state.terminalSequence ?? 0) + 1 : void 0;
9211
+ isTerminal && !record.terminalSeq && (terminalSequence = nextSeq), sessionTasks = new Map(state.sessionTasks), sessionTasks.set(prior.taskId, {
9212
+ ...record,
9213
+ status: trackState,
9214
+ completedAt: isTerminal ? record.completedAt ?? (/* @__PURE__ */ new Date()).toISOString() : void 0,
9215
+ ...isTerminal && nextSeq !== void 0 ? { terminalSeq: nextSeq } : {}
9216
+ });
9217
+ }
9218
+ return {
9219
+ ...state,
9220
+ team: { ...state.team, tracks },
9221
+ sessionTasks,
9222
+ ...terminalSequence !== void 0 ? { terminalSequence } : {}
9223
+ };
9152
9224
  }
9153
- function reduceTeamGroupResolved(state, outcome) {
9225
+ function reduceTeamGroupResolved(state, outcome, completedAt) {
9154
9226
  if (!state.team || state.team.groupResolved && state.team.outcome === outcome) return state;
9155
- let t = state.team, frozen = t.mergeGateStartedAt != null && t.mergeGateElapsedMs == null ? mergeGateElapsed(t.mergeGateStartedAt, (/* @__PURE__ */ new Date()).toISOString()) : t.mergeGateElapsedMs;
9227
+ let t = state.team, frozen = t.mergeGateStartedAt != null && t.mergeGateElapsedMs == null ? mergeGateElapsed(t.mergeGateStartedAt, (/* @__PURE__ */ new Date()).toISOString()) : t.mergeGateElapsedMs, nextSeq = (state.terminalSequence ?? 0) + 1;
9156
9228
  return {
9157
9229
  ...state,
9158
- team: { ...t, groupResolved: !0, outcome, mergeGateElapsedMs: frozen },
9230
+ team: {
9231
+ ...t,
9232
+ groupResolved: !0,
9233
+ outcome,
9234
+ mergeGateElapsedMs: frozen,
9235
+ completedAt: completedAt ?? t.completedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
9236
+ terminalSeq: nextSeq
9237
+ },
9238
+ terminalSequence: nextSeq,
9159
9239
  lastReviewerProgress: null
9160
9240
  };
9161
9241
  }
@@ -9441,7 +9521,7 @@ function ReviewerQuorumStatusNode(props) {
9441
9521
  React5.createElement(
9442
9522
  Text,
9443
9523
  null,
9444
- `${seat.seatId} ${seat.reviewerKind.padEnd(7)} `
9524
+ `${displaySeatNumber(seat.seatId)} ${seat.reviewerKind.padEnd(7)} `
9445
9525
  ),
9446
9526
  React5.createElement(Text, { color: STATUS_COLOR[seat.status] }, STATUS_GLYPH[seat.status]),
9447
9527
  React5.createElement(Text, null, ` ${seat.status}`)
@@ -9819,7 +9899,7 @@ function buildPerSeatTruncationHint(findingsTruncated, truncatedFindingCount) {
9819
9899
  return truncatedFindingCount > 0 ? `(${truncatedFindingCount} more \u2014 ${PER_SEAT_AUDIT_BROWSER_HINT})` : findingsTruncated ? `(more \u2014 ${PER_SEAT_AUDIT_BROWSER_HINT})` : null;
9820
9900
  }
9821
9901
  function buildGateDetailsPanelModel(envelope) {
9822
- let headline = envelope.summary ?? null, reason = formatHaltReason(envelope.reason), reasonLabel = reason?.label ?? null, reasonExplanation = reason?.explanation ?? null, roundLabel = `Round ${envelope.currentRound}`, timeline = (envelope.timeline ?? []).map((t) => ({
9902
+ let headline = envelope.summary ?? null, reason = formatHaltReason(envelope.reason), reasonLabel = reason?.label ?? null, reasonExplanation = reason?.explanation ?? null, roundLabel = `Round ${displayOrdinal(envelope.currentRound)}`, timeline = (envelope.timeline ?? []).map((t) => ({
9823
9903
  kind: t.kind,
9824
9904
  at: t.at
9825
9905
  })), elapsedSeconds = computeElapsedSeconds(envelope.timeline), history = envelope.roundHistory ?? [], cascade = history.length > 1 ? history.map((h) => ({
@@ -9837,7 +9917,7 @@ function buildGateDetailsPanelModel(envelope) {
9837
9917
  seatId: seat.seatId,
9838
9918
  role: seat.role,
9839
9919
  reviewerAgent: seat.reviewerAgent,
9840
- header: `Reviewer ${seat.seatId} (${seat.role}, ${seat.reviewerAgent}): ${formatDecision(
9920
+ header: `Reviewer ${displayOrdinal(seat.seatId)} (${seat.role}, ${seat.reviewerAgent}): ${formatDecision(
9841
9921
  seat.decision
9842
9922
  )}`,
9843
9923
  findings: seat.findings,
@@ -9933,7 +10013,7 @@ function buildReviewSummaryPanelModel(summary) {
9933
10013
  seatId,
9934
10014
  role,
9935
10015
  agent,
9936
- header: `Reviewer ${seatId} (${role}, ${agent}): ${formatDecision(decision)}`,
10016
+ header: `Reviewer ${displayOrdinal(seatId)} (${role}, ${agent}): ${formatDecision(decision)}`,
9937
10017
  decisionLabel: formatDecision(decision),
9938
10018
  reasoning,
9939
10019
  suggestedChanges
@@ -9941,7 +10021,7 @@ function buildReviewSummaryPanelModel(summary) {
9941
10021
  }
9942
10022
  rounds.push({
9943
10023
  round,
9944
- roundLabel: `Round ${round}`,
10024
+ roundLabel: `Round ${displayOrdinal(round)}`,
9945
10025
  proposal,
9946
10026
  reviewers,
9947
10027
  outcome,
@@ -10216,7 +10296,7 @@ function GateContextPanel(props) {
10216
10296
  React10.createElement(
10217
10297
  Text,
10218
10298
  { dimColor: !0 },
10219
- `Round ${c.round}: ${c.reasonLabel}`
10299
+ `Round ${displayOrdinal(c.round)}: ${c.reasonLabel}`
10220
10300
  )
10221
10301
  )
10222
10302
  );
@@ -10423,7 +10503,7 @@ function renderReviewSummarySection(reviewSummary) {
10423
10503
  )
10424
10504
  );
10425
10505
  for (let [index, usage] of terminal.tokenUsage.invocations.entries()) {
10426
- let locator = usage.role === "reviewer" ? `r${usage.round ?? "?"} seat ${usage.seatId ?? "?"}` : usage.round !== null ? `r${usage.round}` : usage.role, usageBreakdown = [
10506
+ let locator = usage.role === "reviewer" ? `r${usage.round == null ? "?" : displayOrdinal(usage.round)} seat ${usage.seatId == null ? "?" : displayOrdinal(usage.seatId)}` : usage.round !== null ? `r${displayOrdinal(usage.round)}` : usage.role, usageBreakdown = [
10427
10507
  usage.inputTokens !== null ? `${usage.inputTokens.toLocaleString()} input` : null,
10428
10508
  usage.outputTokens !== null ? `${usage.outputTokens.toLocaleString()} output` : null,
10429
10509
  usage.cachedInputTokens !== null ? `cached ${usage.cachedInputTokens.toLocaleString()}` : null
@@ -17859,7 +17939,7 @@ function renderEntryAsLine(entry) {
17859
17939
  case "subagent-event":
17860
17940
  return ` \u23BF [${entry.role}] ${entry.event.kind === "NOTIFICATION" && "payload" in entry.event && typeof entry.event.payload.message == "string" ? entry.event.payload.message : entry.event.kind}`;
17861
17941
  case "reviewer-status-node": {
17862
- let seatLines = entry.seats.map((s) => `${s.seatId} ${s.reviewerKind} ${s.status}`).join(", ");
17942
+ let seatLines = entry.seats.map((s) => `${displaySeatNumber(s.seatId)} ${s.reviewerKind} ${s.status}`).join(", ");
17863
17943
  return `[quorum ${entry.quorumStatus}] ${seatLines}`;
17864
17944
  }
17865
17945
  case "gate-status-node":
@@ -20277,6 +20357,37 @@ async function bindWorkingDirectory(payload){
20277
20357
  throw new Error('target cwd incarnation changed before launch');
20278
20358
  }
20279
20359
  }
20360
+ function holdLaunchPathAuthorities(payload){
20361
+ const authorities=payload.launchPathAuthorities;
20362
+ if(authorities===undefined)return [];
20363
+ if(!Array.isArray(authorities)||authorities.length>16)throw new Error('invalid launch path authorities');
20364
+ const held=[];
20365
+ try{
20366
+ for(const authority of authorities){
20367
+ if(!authority||authority.version!==1||
20368
+ typeof authority.canonicalPath!=='string'||!require('node:path').isAbsolute(authority.canonicalPath)||authority.canonicalPath.includes('\0')||
20369
+ typeof authority.dev!=='string'||typeof authority.ino!=='string'||
20370
+ typeof authority.birthtimeNs!=='string')throw new Error('invalid launch path authority');
20371
+ const flags=fs.constants.O_RDONLY|(fs.constants.O_DIRECTORY||0)|(fs.constants.O_NOFOLLOW||0);
20372
+ const fd=fs.openSync(authority.canonicalPath,flags);
20373
+ held.push(fd);
20374
+ const opened=fs.fstatSync(fd,{bigint:true});
20375
+ const live=fs.lstatSync(authority.canonicalPath,{bigint:true});
20376
+ const canonical=fs.realpathSync(authority.canonicalPath);
20377
+ if(!opened.isDirectory()||!live.isDirectory()||live.isSymbolicLink()||
20378
+ opened.dev.toString()!==authority.dev||opened.ino.toString()!==authority.ino||
20379
+ opened.birthtimeNs.toString()!==authority.birthtimeNs||
20380
+ live.dev.toString()!==authority.dev||live.ino.toString()!==authority.ino||
20381
+ live.birthtimeNs.toString()!==authority.birthtimeNs||canonical!==authority.canonicalPath){
20382
+ throw new Error('launch path authority changed before target spawn');
20383
+ }
20384
+ }
20385
+ return held;
20386
+ }catch(err){
20387
+ for(const fd of held){try{fs.closeSync(fd);}catch{}}
20388
+ throw err;
20389
+ }
20390
+ }
20280
20391
  const hostPipe=fs.createReadStream(null,{fd:5,autoClose:false});
20281
20392
  for(const event of ['end','close','error'])hostPipe.once(event,killOwnGroup);
20282
20393
  for(const [signal] of [['SIGTERM'],['SIGINT'],['SIGHUP']])process.on(signal,killOwnGroup);
@@ -20285,9 +20396,17 @@ for(const [signal] of [['SIGTERM'],['SIGINT'],['SIGHUP']])process.on(signal,kill
20285
20396
  const start=await readLine(4);
20286
20397
  if(start!=='START')throw new Error('target start not authorized');
20287
20398
  await bindWorkingDirectory(payload);
20399
+ // This is deliberately synchronous and immediately adjacent to cp.spawn:
20400
+ // keep exact directory descriptors open across native process creation so a
20401
+ // pathname replacement during durable-owner publication cannot be adopted.
20402
+ const launchPathFds=holdLaunchPathAuthorities(payload);
20288
20403
  // No pathname cwd is passed here: the child inherits the wrapper's already-
20289
20404
  // 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});
20405
+ try{
20406
+ target=cp.spawn(payload.command,payload.args,{env:payload.env,stdio:['inherit','inherit','inherit'],detached:false,windowsHide:true});
20407
+ }finally{
20408
+ for(const fd of launchPathFds){try{fs.closeSync(fd);}catch{}}
20409
+ }
20291
20410
  target.once('error',err=>{send({type:'target-exit',code:127,error:String(err&&err.message||err)});});
20292
20411
  target.once('exit',(code,signal)=>{send({type:'target-exit',code:Number.isInteger(code)?code:(signal?128:1)});});
20293
20412
  // Remain the authenticated group/session leader until the outside host has
@@ -20387,6 +20506,7 @@ function spawnPosixOwnedProcess(command, args, options, seams = {}) {
20387
20506
  args,
20388
20507
  cwd: targetCwd,
20389
20508
  cwdAuthority: seams.workingDirAuthority,
20509
+ launchPathAuthorities: seams.launchPathAuthorities,
20390
20510
  env: options?.env ?? process.env,
20391
20511
  ...seams.cleanupTimeoutMs === void 0 ? {} : { cleanupTimeoutMs: seams.cleanupTimeoutMs }
20392
20512
  }), "utf8");
@@ -24639,7 +24759,7 @@ function buildReviewerVerdictModel(payload) {
24639
24759
  let decisionRaw = asString(verdict.verdict);
24640
24760
  if (decisionRaw === null) return null;
24641
24761
  let decisionLabel = decisionCopy[decisionRaw] ?? decisionRaw, parts = [], seatId = payload.seat_id ?? payload.seatId, role = asString(payload.role), agent = asString(payload.reviewer_agent) ?? asString(payload.reviewerAgent);
24642
- typeof seatId == "number" && parts.push(`Seat ${seatId}`), role && parts.push(role), agent && parts.push(agent);
24762
+ typeof seatId == "number" && parts.push(`Seat ${displayOrdinal(seatId)}`), role && parts.push(role), agent && parts.push(agent);
24643
24763
  let provenance = parts.length > 0 ? parts.join(" \xB7 ") : null, reasoning = asString(verdict.reasoning), sugRaw = verdict.suggested_changes ?? verdict.suggestedChanges, suggestedChanges = Array.isArray(sugRaw) ? sugRaw.filter((s) => typeof s == "string") : [];
24644
24764
  return { decisionLabel, provenance, reasoning, suggestedChanges };
24645
24765
  }
@@ -26409,6 +26529,38 @@ var LocalGemmaPlannerAdapter = class {
26409
26529
  // src/orchestration-shell/quorum-loop.ts
26410
26530
  var import_node_fs24 = require("node:fs");
26411
26531
 
26532
+ // src/diagnostics/shape-only.ts
26533
+ function outputShapeOnly(raw) {
26534
+ if (typeof raw != "string" || raw.length === 0)
26535
+ return {
26536
+ bytes: 0,
26537
+ lines: 0,
26538
+ leadClass: "empty",
26539
+ fenceCount: 0,
26540
+ bulletLines: 0,
26541
+ blankLines: 0
26542
+ };
26543
+ let lines = raw.split(`
26544
+ `), trimmed = raw.trim(), leadClass;
26545
+ 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", {
26546
+ bytes: Buffer.byteLength(raw, "utf8"),
26547
+ lines: lines.length,
26548
+ leadClass,
26549
+ fenceCount: (raw.match(/```/g) ?? []).length,
26550
+ bulletLines: lines.filter((line) => /^\s*[-*+]\s/.test(line)).length,
26551
+ blankLines: lines.filter((line) => line.trim().length === 0).length
26552
+ };
26553
+ }
26554
+ function errorShapeOnly(error) {
26555
+ let value = error, message = typeof value?.message == "string" ? value.message : "", causeCount = Array.isArray(value?.causes) ? value.causes.length : value?.cause !== void 0 ? 1 : 0;
26556
+ return {
26557
+ errorClass: error instanceof Error ? "error" : "non-error",
26558
+ messageBytes: Buffer.byteLength(message, "utf8"),
26559
+ hasCause: value?.cause !== void 0,
26560
+ causeCount
26561
+ };
26562
+ }
26563
+
26412
26564
  // src/reduced-trust-notice.ts
26413
26565
  var surfacedRationales = /* @__PURE__ */ new Set();
26414
26566
  function noticeKey(sessionId, tier, reason) {
@@ -28104,7 +28256,8 @@ var ClassBConsumer = class {
28104
28256
  };
28105
28257
 
28106
28258
  // 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";
28259
+ var import_node_fs14 = require("node:fs"), os19 = __toESM(require("node:os")), path32 = __toESM(require("node:path"));
28260
+ 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
28261
  function helperScriptSource(tokenFileAbsPathInSandbox) {
28109
28262
  return `#!/bin/sh
28110
28263
  # CP-7 apiKeyHelper \u2014 emits the current broker token (NEVER the vendor key).
@@ -28145,12 +28298,16 @@ function isInsideOrEqual(child, parent) {
28145
28298
  return rel.length > 0 && !rel.startsWith("..") && !path32.isAbsolute(rel);
28146
28299
  }
28147
28300
  var ApiKeyBootstrap = class _ApiKeyBootstrap {
28148
- constructor(hostDir, sandboxDir, configHostDir, configSandboxDir, tokenFileHostPath) {
28301
+ constructor(hostDir, sandboxDir, configHostDir, configSandboxDir, tokenFileHostPath, claudeScratchHostDir, claudeScratchSandboxDir, claudeScratchIdentity, beforeClaudeScratchCleanupCommit) {
28149
28302
  this.hostDir = hostDir;
28150
28303
  this.sandboxDir = sandboxDir;
28151
28304
  this.configHostDir = configHostDir;
28152
28305
  this.configSandboxDir = configSandboxDir;
28153
28306
  this.tokenFileHostPath = tokenFileHostPath;
28307
+ this.claudeScratchHostDir = claudeScratchHostDir;
28308
+ this.claudeScratchSandboxDir = claudeScratchSandboxDir;
28309
+ this.claudeScratchIdentity = claudeScratchIdentity;
28310
+ this.beforeClaudeScratchCleanupCommit = beforeClaudeScratchCleanupCommit;
28154
28311
  }
28155
28312
  /**
28156
28313
  * Materialize the bootstrap: create the host dir (0700), write the token file
@@ -28166,9 +28323,8 @@ var ApiKeyBootstrap = class _ApiKeyBootstrap {
28166
28323
  );
28167
28324
  await import_node_fs14.promises.chmod(rawConfigHostDir, 448).catch(() => {
28168
28325
  });
28169
- let hostDir = await import_node_fs14.promises.realpath(rawHostDir), configHostDir = await import_node_fs14.promises.realpath(rawConfigHostDir);
28326
+ let hostDir = await import_node_fs14.promises.realpath(rawHostDir), configHostDir = await import_node_fs14.promises.realpath(rawConfigHostDir), realWorkdir;
28170
28327
  if (input.workdir !== void 0) {
28171
- let realWorkdir;
28172
28328
  try {
28173
28329
  realWorkdir = await import_node_fs14.promises.realpath(input.workdir);
28174
28330
  } catch {
@@ -28205,7 +28361,7 @@ var ApiKeyBootstrap = class _ApiKeyBootstrap {
28205
28361
  "ApiKeyBootstrap.create: provide `sandboxConfigDir` or `configDirEqualsHostDir`"
28206
28362
  );
28207
28363
  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, {
28364
+ await import_node_fs14.promises.writeFile(tokenFileHostPath, input.initialToken, {
28209
28365
  encoding: "utf8",
28210
28366
  mode: 384
28211
28367
  }), await import_node_fs14.promises.writeFile(
@@ -28220,12 +28376,107 @@ var ApiKeyBootstrap = class _ApiKeyBootstrap {
28220
28376
  path32.join(configHostDir, CODEX_CONFIG_NAME),
28221
28377
  codexConfig(input.sandboxBrokerUrl, helperInSandbox),
28222
28378
  { encoding: "utf8", mode: 384 }
28223
- ), new _ApiKeyBootstrap(
28379
+ );
28380
+ let claudeScratchHostDir = null, claudeScratchSandboxDir = null, claudeScratchIdentity = null;
28381
+ if (input.claudeScratchHostRoot !== void 0) {
28382
+ if (input.provider !== "anthropic" || !input.sandboxDirEqualsHostDir) {
28383
+ let primary = new Error(
28384
+ "ApiKeyBootstrap.create: a short Claude scratch root is valid only for the A5 anthropic path"
28385
+ ), cleanupErrors = [];
28386
+ for (let directory of [hostDir, configHostDir])
28387
+ try {
28388
+ await import_node_fs14.promises.rm(directory, { recursive: !0, force: !0 });
28389
+ } catch (cleanupError) {
28390
+ cleanupErrors.push(cleanupError);
28391
+ }
28392
+ throw cleanupErrors.length > 0 ? Object.assign(
28393
+ new Error(
28394
+ "ApiKeyBootstrap.create: invalid scratch configuration and cleanup did not complete"
28395
+ ),
28396
+ { causes: [primary, ...cleanupErrors] }
28397
+ ) : primary;
28398
+ }
28399
+ let rawScratchDir = null;
28400
+ try {
28401
+ let canonicalScratchRoot = await import_node_fs14.promises.realpath(
28402
+ input.claudeScratchHostRoot
28403
+ );
28404
+ rawScratchDir = await import_node_fs14.promises.mkdtemp(path32.join(canonicalScratchRoot, "cv"));
28405
+ let initial = await import_node_fs14.promises.lstat(rawScratchDir, { bigint: !0 });
28406
+ if (!initial.isDirectory() || initial.isSymbolicLink())
28407
+ throw new Error(
28408
+ "ApiKeyBootstrap.create: generated Claude scratch root is not a real directory"
28409
+ );
28410
+ await input.beforeClaudeScratchOpen?.(rawScratchDir);
28411
+ let scratchHandle = await import_node_fs14.promises.open(
28412
+ rawScratchDir,
28413
+ 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)
28414
+ );
28415
+ try {
28416
+ let opened = await scratchHandle.stat({ bigint: !0 });
28417
+ if (!opened.isDirectory() || opened.dev !== initial.dev || opened.ino !== initial.ino || opened.birthtimeNs !== initial.birthtimeNs)
28418
+ throw new Error(
28419
+ "ApiKeyBootstrap.create: Claude scratch root changed while binding its directory handle"
28420
+ );
28421
+ await scratchHandle.chmod(448);
28422
+ 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;
28423
+ 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)
28424
+ throw new Error(
28425
+ "ApiKeyBootstrap.create: generated Claude scratch root failed its descriptor-bound private-directory identity check"
28426
+ );
28427
+ claudeScratchHostDir = canonical, claudeScratchSandboxDir = rawScratchDir, claudeScratchIdentity = {
28428
+ version: 1,
28429
+ canonicalPath: canonical,
28430
+ dev: sealed.dev.toString(),
28431
+ ino: sealed.ino.toString(),
28432
+ birthtimeNs: sealed.birthtimeNs.toString()
28433
+ };
28434
+ } finally {
28435
+ await scratchHandle.close();
28436
+ }
28437
+ if (Buffer.byteLength(rawScratchDir, "utf8") > 30)
28438
+ throw new Error(
28439
+ "ApiKeyBootstrap.create: generated Claude scratch path exceeds the 30-byte A5 safety bound"
28440
+ );
28441
+ if (realWorkdir !== void 0 && isInsideOrEqual(claudeScratchHostDir, realWorkdir))
28442
+ throw new Error(
28443
+ "ApiKeyBootstrap.create: Claude scratch root is inside the agent workdir (fail-closed)"
28444
+ );
28445
+ } catch (error) {
28446
+ let cleanupErrors = [];
28447
+ if (claudeScratchIdentity !== null)
28448
+ try {
28449
+ await anchoredRemoveTree(
28450
+ claudeScratchIdentity.canonicalPath,
28451
+ claudeScratchIdentity
28452
+ );
28453
+ } catch (cleanupError) {
28454
+ cleanupErrors.push(cleanupError);
28455
+ }
28456
+ for (let directory of [hostDir, configHostDir])
28457
+ try {
28458
+ await import_node_fs14.promises.rm(directory, { recursive: !0, force: !0 });
28459
+ } catch (cleanupError) {
28460
+ cleanupErrors.push(cleanupError);
28461
+ }
28462
+ throw cleanupErrors.length > 0 ? Object.assign(
28463
+ new Error(
28464
+ "ApiKeyBootstrap.create: initialization failed and cleanup did not complete"
28465
+ ),
28466
+ { causes: [error, ...cleanupErrors] }
28467
+ ) : error;
28468
+ }
28469
+ }
28470
+ return new _ApiKeyBootstrap(
28224
28471
  hostDir,
28225
28472
  sandboxDir,
28226
28473
  configHostDir,
28227
28474
  sandboxConfigDir,
28228
- tokenFileHostPath
28475
+ tokenFileHostPath,
28476
+ claudeScratchHostDir,
28477
+ claudeScratchSandboxDir,
28478
+ claudeScratchIdentity,
28479
+ input.beforeClaudeScratchCleanupCommit
28229
28480
  );
28230
28481
  }
28231
28482
  /** The `SubstrateSpec.agentBootstrap` value for this bootstrap. */
@@ -28236,6 +28487,14 @@ var ApiKeyBootstrap = class _ApiKeyBootstrap {
28236
28487
  get configSpec() {
28237
28488
  return { hostDir: this.configHostDir, sandboxDir: this.configSandboxDir };
28238
28489
  }
28490
+ /** The unique writable Claude scratch grant, absent on Docker/Codex paths. */
28491
+ get claudeScratchSpec() {
28492
+ return this.claudeScratchHostDir === null || this.claudeScratchSandboxDir === null || this.claudeScratchIdentity === null ? null : {
28493
+ hostDir: this.claudeScratchHostDir,
28494
+ sandboxDir: this.claudeScratchSandboxDir,
28495
+ authority: this.claudeScratchIdentity
28496
+ };
28497
+ }
28239
28498
  /**
28240
28499
  * REFRESH the broker token (rotation / on a broker 401). Atomically rewrites
28241
28500
  * the token file (write a temp + rename) so the agent never reads a partial
@@ -28246,15 +28505,41 @@ var ApiKeyBootstrap = class _ApiKeyBootstrap {
28246
28505
  let tmp = `${this.tokenFileHostPath}.tmp-${process.pid}-${Date.now()}`;
28247
28506
  await import_node_fs14.promises.writeFile(tmp, token, { encoding: "utf8", mode: 384 }), await import_node_fs14.promises.rename(tmp, this.tokenFileHostPath);
28248
28507
  }
28249
- /** Remove the bootstrap dir (session teardown). Idempotent. */
28508
+ /**
28509
+ * Remove the bootstrap, config, and unique Claude scratch dirs. Scratch
28510
+ * cleanup is identity-bound: a replaced pathname is retained and reported,
28511
+ * never recursively removed as though it still belonged to this seat.
28512
+ */
28250
28513
  async destroy() {
28251
- await import_node_fs14.promises.rm(this.hostDir, { recursive: !0, force: !0 }).catch(
28252
- () => {
28514
+ let cleanupErrors = [];
28515
+ if (this.claudeScratchHostDir !== null && this.claudeScratchIdentity !== null)
28516
+ try {
28517
+ await anchoredRemoveTree(
28518
+ this.claudeScratchIdentity.canonicalPath,
28519
+ this.claudeScratchIdentity,
28520
+ {
28521
+ ...this.beforeClaudeScratchCleanupCommit ? {
28522
+ beforeCommit: () => this.beforeClaudeScratchCleanupCommit(
28523
+ this.claudeScratchHostDir
28524
+ )
28525
+ } : {}
28526
+ }
28527
+ );
28528
+ } catch (error) {
28529
+ cleanupErrors.push(error);
28253
28530
  }
28254
- ), await import_node_fs14.promises.rm(this.configHostDir, { recursive: !0, force: !0 }).catch(
28255
- () => {
28531
+ for (let directory of [this.hostDir, this.configHostDir])
28532
+ try {
28533
+ await import_node_fs14.promises.rm(directory, { recursive: !0, force: !0 });
28534
+ } catch (error) {
28535
+ cleanupErrors.push(error);
28256
28536
  }
28257
- );
28537
+ if (cleanupErrors.length === 1) throw cleanupErrors[0];
28538
+ if (cleanupErrors.length > 1)
28539
+ throw Object.assign(
28540
+ new Error("ApiKeyBootstrap.destroy: cleanup did not complete"),
28541
+ { causes: cleanupErrors }
28542
+ );
28258
28543
  }
28259
28544
  };
28260
28545
 
@@ -42776,10 +43061,7 @@ var LocalExecutorImpl = class {
42776
43061
  }).catch(() => {
42777
43062
  }), {
42778
43063
  args: { ...addClaudeSubstrateAuthArgs(args, result.agentConfigDir), substrate: result.substrateHandle },
42779
- onExit: async () => {
42780
- await result.teardown().catch(() => {
42781
- });
42782
- },
43064
+ onExit: () => result.teardown(),
42783
43065
  // Stage-2 r3 Codex HIGH — the ONLY return that proves a real confining
42784
43066
  // boundary (`result.mode === 'substrate'`, not reduced_trust / no-engager /
42785
43067
  // pre-TaskAuthorized). The danger-sandbox authority keys on THIS boolean,
@@ -42871,7 +43153,11 @@ var LocalExecutorImpl = class {
42871
43153
  }
42872
43154
  }
42873
43155
  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 = {
43156
+ 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 () => {
43157
+ onSubstrateExit && (substrateCleanupPromise || (substrateCleanupPromise = onSubstrateExit().catch((error) => {
43158
+ throw substrateCleanupError = error, error;
43159
+ })), await substrateCleanupPromise);
43160
+ }, full = {
42875
43161
  ...args,
42876
43162
  agentKind: args.agentKind ?? this.adapter,
42877
43163
  onProcessSpawned: async (info) => {
@@ -42885,35 +43171,51 @@ var LocalExecutorImpl = class {
42885
43171
  });
42886
43172
  },
42887
43173
  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
- });
43174
+ try {
43175
+ if (await args.onProcessExited?.(info), lifecycleAudit !== "none") {
43176
+ let ctx = lifecycleCtx;
43177
+ ctx !== null && await this.emitter.emitProcessExited(ctx, {
43178
+ pid: info.pid,
43179
+ exitCode: info.exitCode,
43180
+ failureClass: info.failureClass,
43181
+ occurredAt: info.exitedAt
43182
+ });
43183
+ }
43184
+ } catch {
42896
43185
  }
42897
- onSubstrateExit && await onSubstrateExit().catch(() => {
43186
+ await cleanupSubstrate().catch(() => {
42898
43187
  });
42899
43188
  }
42900
43189
  };
42901
43190
  try {
42902
- return await spawnFn(full);
43191
+ let handle = await spawnFn(full), done = handle.done.then((info) => {
43192
+ if (substrateCleanupError !== void 0)
43193
+ throw Object.assign(
43194
+ new Error("implementor substrate cleanup failed after process exit"),
43195
+ { cause: substrateCleanupError }
43196
+ );
43197
+ return info;
43198
+ });
43199
+ return { ...handle, done };
42903
43200
  } catch (err) {
42904
- if (onSubstrateExit && await onSubstrateExit().catch(() => {
42905
- }), err instanceof AuthorityError) {
43201
+ await cleanupSubstrate().catch(() => {
43202
+ });
43203
+ let primary = err;
43204
+ if (err instanceof AuthorityError) {
42906
43205
  let safeArgv = redactAgyPrintArgv(args.argv), safeDetail = redactAgyPrintValuesFromText(err.refusal.detail, args.argv), safeError = new AuthorityError({
42907
43206
  ...err.refusal,
42908
43207
  detail: safeDetail
42909
43208
  });
42910
- throw await this.bridge.bridgeAuthorityRefusal(safeError, {
43209
+ await this.bridge.bridgeAuthorityRefusal(safeError, {
42911
43210
  refusedMessageId: "spawn:" + role + ":" + Date.now(),
42912
43211
  shellContent: `Refused spawn (${role}): ${safeDetail}`,
42913
43212
  shellMetadata: { role, argv: safeArgv }
42914
- }), safeError;
43213
+ }), primary = safeError;
42915
43214
  }
42916
- throw err;
43215
+ throw substrateCleanupError !== void 0 ? Object.assign(
43216
+ new Error("implementor spawn failed and substrate cleanup also failed"),
43217
+ { causes: [primary, substrateCleanupError] }
43218
+ ) : primary;
42917
43219
  }
42918
43220
  }
42919
43221
  // --- Hook bridge surface ---------------------------------------------------
@@ -44051,7 +44353,8 @@ var LocalExecutorImpl = class {
44051
44353
  metadata: {
44052
44354
  source: "team_group_resolved",
44053
44355
  task_group_id: taskGroupId,
44054
- outcome
44356
+ outcome,
44357
+ completed_at: (/* @__PURE__ */ new Date()).toISOString()
44055
44358
  }
44056
44359
  }),
44057
44360
  "team_group_resolved"
@@ -49035,10 +49338,11 @@ var ChildProcessCommandRunner = class {
49035
49338
  signal: opts.signal,
49036
49339
  ...opts.env !== void 0 ? { env: opts.env } : {},
49037
49340
  ...opts.cwd !== void 0 ? { cwd: opts.cwd } : {}
49038
- }, proc = opts.cwdAuthority ? spawnPosixOwnedProcess(argv[0], argv.slice(1), spawnOptions, {
49039
- workingDirAuthority: opts.cwdAuthority
49341
+ }, needsOwnedProcessHost = opts.cwdAuthority !== void 0 || (opts.launchPathAuthorities?.length ?? 0) > 0, proc = needsOwnedProcessHost ? spawnPosixOwnedProcess(argv[0], argv.slice(1), spawnOptions, {
49342
+ workingDirAuthority: opts.cwdAuthority,
49343
+ launchPathAuthorities: opts.launchPathAuthorities
49040
49344
  }) : (0, import_node_child_process7.spawn)(argv[0], argv.slice(1), spawnOptions);
49041
- return opts.cwdAuthority && Object.defineProperty(proc, "hostProcessTreeOwnership", {
49345
+ return needsOwnedProcessHost && Object.defineProperty(proc, "hostProcessTreeOwnership", {
49042
49346
  value: "posix-host",
49043
49347
  configurable: !1,
49044
49348
  enumerable: !1,
@@ -49618,13 +49922,14 @@ async function resolveOnParentPath(cmd) {
49618
49922
  return null;
49619
49923
  }
49620
49924
  var SandboxExecHandle = class {
49621
- constructor(id, runner, profilePath, sanitizedEnv, workdir, workdirAuthority, auditDir = null) {
49925
+ constructor(id, runner, profilePath, sanitizedEnv, workdir, workdirAuthority, scratchAuthority, auditDir = null) {
49622
49926
  this.id = id;
49623
49927
  this.runner = runner;
49624
49928
  this.profilePath = profilePath;
49625
49929
  this.sanitizedEnv = sanitizedEnv;
49626
49930
  this.workdir = workdir;
49627
49931
  this.workdirAuthority = workdirAuthority;
49932
+ this.scratchAuthority = scratchAuthority;
49628
49933
  this.auditDir = auditDir;
49629
49934
  this.egressFidelity = "coarse";
49630
49935
  this.tornDown = !1;
@@ -49660,13 +49965,18 @@ var SandboxExecHandle = class {
49660
49965
  ; exec-time agent-binary read allowance (argv0 resolution, 2026-07-03)
49661
49966
  ${allowLines}
49662
49967
  `
49968
+ ), this.scratchAuthority && await assertWorkspaceRootAuthority(
49969
+ this.scratchAuthority.canonicalPath,
49970
+ this.scratchAuthority,
49971
+ "sandbox-exec Claude scratch handoff"
49663
49972
  );
49664
49973
  let sbArgv = ["sandbox-exec", "-f", this.profilePath, resolvedArgv0, ...argv.slice(1)], proc = this.runner.spawnLong(sbArgv, {
49665
49974
  stdinTty: opts.stdinTty,
49666
49975
  signal: opts.signal,
49667
49976
  env: this.sanitizedEnv,
49668
49977
  cwd: this.workdir,
49669
- ...this.workdirAuthority ? { cwdAuthority: this.workdirAuthority } : {}
49978
+ ...this.workdirAuthority ? { cwdAuthority: this.workdirAuthority } : {},
49979
+ ...this.scratchAuthority ? { launchPathAuthorities: [this.scratchAuthority] } : {}
49670
49980
  });
49671
49981
  return this.proc = proc, proc;
49672
49982
  }
@@ -49724,6 +50034,22 @@ ${allowLines}
49724
50034
  }
49725
50035
  extraWriteDirs.push(configDir);
49726
50036
  }
50037
+ if (spec.agentScratch) {
50038
+ try {
50039
+ await assertWorkspaceRootAuthority(
50040
+ spec.agentScratch.hostDir,
50041
+ spec.agentScratch.authority,
50042
+ "sandbox-exec Claude scratch launch"
50043
+ );
50044
+ } catch (error) {
50045
+ throw new SubstrateLaunchError(
50046
+ "sandbox_exec",
50047
+ "Claude scratch grant target identity could not be verified (fail-closed)",
50048
+ error
50049
+ );
50050
+ }
50051
+ extraWriteDirs.push(spec.agentScratch.authority.canonicalPath);
50052
+ }
49727
50053
  let resolvedAuditDir = null;
49728
50054
  if (spec.auditDir) {
49729
50055
  resolvedAuditDir = spec.auditDir;
@@ -49764,6 +50090,17 @@ ${allowLines}
49764
50090
  throw await import_node_fs23.promises.rm(profilePath, { force: !0 }).catch(() => {
49765
50091
  }), err;
49766
50092
  }
50093
+ if (spec.agentScratch)
50094
+ try {
50095
+ await assertWorkspaceRootAuthority(
50096
+ spec.agentScratch.authority.canonicalPath,
50097
+ spec.agentScratch.authority,
50098
+ "sandbox-exec Claude scratch profile handoff"
50099
+ );
50100
+ } catch (err) {
50101
+ throw await import_node_fs23.promises.rm(profilePath, { force: !0 }).catch(() => {
50102
+ }), err;
50103
+ }
49767
50104
  return new SandboxExecHandle(
49768
50105
  id,
49769
50106
  this.runner,
@@ -49771,6 +50108,7 @@ ${allowLines}
49771
50108
  finalEnv,
49772
50109
  canonicalWorkdir,
49773
50110
  spec.workdirAuthority,
50111
+ spec.agentScratch?.authority,
49774
50112
  resolvedAuditDir
49775
50113
  );
49776
50114
  }
@@ -50397,13 +50735,13 @@ function formatReviewerError(detail) {
50397
50735
  case "timeout":
50398
50736
  return `${detail.agent} reviewer timed out after ${detail.elapsed_ms}ms`;
50399
50737
  case "spawn_failed":
50400
- return `${detail.agent} reviewer spawn failed: ${detail.reason}`;
50738
+ return `${detail.agent} reviewer spawn failed`;
50401
50739
  case "parse_failure":
50402
50740
  return `${detail.agent} reviewer output was unparseable`;
50403
50741
  case "cancelled":
50404
50742
  return "reviewer cancelled before completion";
50405
50743
  case "internal_join_failure":
50406
- return `reviewer task internal join failure: ${detail.reason}`;
50744
+ return "reviewer task internal join failure";
50407
50745
  }
50408
50746
  }
50409
50747
 
@@ -50945,22 +51283,25 @@ var CONTINUATION_EXPIRES_TTL_MS = 1440 * 60 * 1e3, ACTIVE_AGENT_KINDS = ["CLAUDE
50945
51283
  function isActiveAgentKind2(agent) {
50946
51284
  return agent === "CLAUDE" || agent === "CODEX" || agent === "ANTIGRAVITY";
50947
51285
  }
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;
51286
+ var IMPLEMENTOR_TICK_MS = 1e4, TICK_SCAN_MAX_TRACKED = 5e3, TICK_SCAN_TIMEOUT_MS = 1e3, BACKGROUND_RECOVERY_ABORTED = /* @__PURE__ */ Symbol("background-recovery-aborted");
50949
51287
  function describeReviewerErrorDetail(detail) {
50950
51288
  switch (detail.kind) {
50951
51289
  case "timeout":
50952
51290
  return { agent: detail.agent, elapsedMs: detail.elapsed_ms };
50953
51291
  case "spawn_failed":
50954
- return { agent: detail.agent, reason: detail.reason };
51292
+ return {
51293
+ agent: detail.agent,
51294
+ reasonBytes: Buffer.byteLength(detail.reason ?? "", "utf8")
51295
+ };
50955
51296
  case "parse_failure":
50956
51297
  return {
50957
51298
  agent: detail.agent,
50958
- rawOutput: detail.raw_output.slice(0, REVIEWER_ERROR_LOG_RAW_OUTPUT_CAP)
51299
+ rawOutputShape: outputShapeOnly(detail.raw_output)
50959
51300
  };
50960
51301
  case "cancelled":
50961
51302
  return {};
50962
51303
  case "internal_join_failure":
50963
- return { reason: detail.reason };
51304
+ return { reasonBytes: Buffer.byteLength(detail.reason ?? "", "utf8") };
50964
51305
  }
50965
51306
  }
50966
51307
  var REVIEWER_AGENT_DISPLAY_NAME = {
@@ -52248,7 +52589,16 @@ var QuorumLoop = class _QuorumLoop {
52248
52589
  * first promote/discard.
52249
52590
  */
52250
52591
  getLastWorkspaceOutcome() {
52251
- return this.lastWorkspaceOutcome === null ? null : this.lastWorkspaceOutcome.kind === "promoted" ? { kind: "promoted", files: [...this.lastWorkspaceOutcome.files] } : { kind: "discarded" };
52592
+ return this.lastWorkspaceOutcome === null ? null : this.lastWorkspaceOutcome.kind === "promoted" ? {
52593
+ kind: "promoted",
52594
+ files: [...this.lastWorkspaceOutcome.files],
52595
+ taskId: this.lastWorkspaceOutcome.taskId,
52596
+ completedAt: this.lastWorkspaceOutcome.completedAt
52597
+ } : {
52598
+ kind: "discarded",
52599
+ taskId: this.lastWorkspaceOutcome.taskId,
52600
+ completedAt: this.lastWorkspaceOutcome.completedAt
52601
+ };
52252
52602
  }
52253
52603
  /**
52254
52604
  * Session-owned Class-B ingress. The subscription calls this wrapper instead
@@ -53274,8 +53624,8 @@ var QuorumLoop = class _QuorumLoop {
53274
53624
  taskId: args.taskId,
53275
53625
  trackIndex: args.validateDiffScope.trackIndex,
53276
53626
  exitCode: exit.exitCode,
53277
- stderrExcerpt: handle.stderr().slice(0, 800),
53278
- stdoutTail: handle.stdout().slice(-3e3)
53627
+ stderrShape: outputShapeOnly(handle.stderr()),
53628
+ stdoutShape: outputShapeOnly(handle.stdout())
53279
53629
  }
53280
53630
  ), await this.reportTeamTrackFailure(
53281
53631
  args.taskId,
@@ -53297,8 +53647,8 @@ var QuorumLoop = class _QuorumLoop {
53297
53647
  // exits 0 is still attributable from the log.
53298
53648
  exitCode: exit.exitCode,
53299
53649
  runtimeMs: exit.runtimeMs,
53300
- stdoutTail: handle.stdout().slice(-300),
53301
- stderrTail: handle.stderr().slice(-300)
53650
+ stdoutShape: outputShapeOnly(handle.stdout()),
53651
+ stderrShape: outputShapeOnly(handle.stderr())
53302
53652
  });
53303
53653
  try {
53304
53654
  let bundle = createSnapshotTrackBundle({
@@ -53375,8 +53725,8 @@ var QuorumLoop = class _QuorumLoop {
53375
53725
  exitCode: exit.exitCode,
53376
53726
  runtimeMs: exit.runtimeMs,
53377
53727
  ...files.length === 0 ? {
53378
- stdoutTail: handle.stdout().slice(-300),
53379
- stderrTail: handle.stderr().slice(-300)
53728
+ stdoutShape: outputShapeOnly(handle.stdout()),
53729
+ stderrShape: outputShapeOnly(handle.stderr())
53380
53730
  } : {}
53381
53731
  }), this.surfaceHalt(reason), await this.discardShadow(args.taskId);
53382
53732
  }
@@ -53391,7 +53741,7 @@ var QuorumLoop = class _QuorumLoop {
53391
53741
  }
53392
53742
  logger.warn("[QuorumLoop] implementor round failed", {
53393
53743
  gateId,
53394
- err: err.message
53744
+ ...errorShapeOnly(err)
53395
53745
  });
53396
53746
  let reason = classifyImplementorRoundFailure(err);
53397
53747
  this.surfaceHalt(`The task could not proceed \u2014 ${reason}.`);
@@ -53879,7 +54229,11 @@ var QuorumLoop = class _QuorumLoop {
53879
54229
  err: err.message
53880
54230
  });
53881
54231
  } finally {
53882
- discarded && this.shadowsByTask.get(taskId) === shadow && this.shadowsByTask.delete(taskId), discarded && terminalDiscard && this.terminallyRetiredWorkspaceTasks.add(taskId), this.lastWorkspaceOutcome = { kind: "discarded" }, this.emitProgress(
54232
+ discarded && this.shadowsByTask.get(taskId) === shadow && this.shadowsByTask.delete(taskId), discarded && terminalDiscard && this.terminallyRetiredWorkspaceTasks.add(taskId), terminalDiscard && opts?.taskContinues !== !0 && (this.lastWorkspaceOutcome = {
54233
+ kind: "discarded",
54234
+ taskId,
54235
+ completedAt: (/* @__PURE__ */ new Date()).toISOString()
54236
+ }), this.emitProgress(
53883
54237
  {
53884
54238
  phase: "discarded",
53885
54239
  ...terminalDiscard && opts?.taskContinues !== !0 ? { taskId } : {}
@@ -53988,7 +54342,11 @@ var QuorumLoop = class _QuorumLoop {
53988
54342
  err: discardErr.message
53989
54343
  });
53990
54344
  }
53991
- discarded && this.shadowsByTask.get(taskId) === shadow && this.shadowsByTask.delete(taskId), discarded && this.terminallyRetiredWorkspaceTasks.add(taskId), this.lastWorkspaceOutcome = { kind: "discarded" }, this.emitProgress({ phase: "discarded", taskId }, originEpoch), discarded && terminalOutcomeReserved && await this.workspaceOutcomeSink.completeTask(taskId, "apply_failed");
54345
+ discarded && this.shadowsByTask.get(taskId) === shadow && this.shadowsByTask.delete(taskId), discarded && this.terminallyRetiredWorkspaceTasks.add(taskId), this.lastWorkspaceOutcome = {
54346
+ kind: "discarded",
54347
+ taskId,
54348
+ completedAt: (/* @__PURE__ */ new Date()).toISOString()
54349
+ }, this.emitProgress({ phase: "discarded", taskId }, originEpoch), discarded && terminalOutcomeReserved && await this.workspaceOutcomeSink.completeTask(taskId, "apply_failed");
53992
54350
  return;
53993
54351
  }
53994
54352
  if (this.teamRunIsHalted(teamAuthority)) return;
@@ -54022,7 +54380,11 @@ var QuorumLoop = class _QuorumLoop {
54022
54380
  err: discardErr.message
54023
54381
  });
54024
54382
  }
54025
- discarded && this.shadowsByTask.get(taskId) === shadow && this.shadowsByTask.delete(taskId), discarded && this.terminallyRetiredWorkspaceTasks.add(taskId), this.reviewedSnapshotByTask.delete(taskId), this.lastWorkspaceOutcome = { kind: "discarded" }, this.emitProgress({ phase: "discarded", taskId }, originEpoch), discarded && terminalOutcomeReserved && await this.workspaceOutcomeSink.completeTask(taskId, "apply_failed");
54383
+ discarded && this.shadowsByTask.get(taskId) === shadow && this.shadowsByTask.delete(taskId), discarded && this.terminallyRetiredWorkspaceTasks.add(taskId), this.reviewedSnapshotByTask.delete(taskId), this.lastWorkspaceOutcome = {
54384
+ kind: "discarded",
54385
+ taskId,
54386
+ completedAt: (/* @__PURE__ */ new Date()).toISOString()
54387
+ }, this.emitProgress({ phase: "discarded", taskId }, originEpoch), discarded && terminalOutcomeReserved && await this.workspaceOutcomeSink.completeTask(taskId, "apply_failed");
54026
54388
  return;
54027
54389
  }
54028
54390
  this.reviewedSnapshotByTask.set(taskId, reviewedSnapshot);
@@ -54052,7 +54414,11 @@ var QuorumLoop = class _QuorumLoop {
54052
54414
  err: discardErr.message
54053
54415
  });
54054
54416
  }
54055
- discarded && this.shadowsByTask.get(taskId) === shadow && this.shadowsByTask.delete(taskId), discarded && this.terminallyRetiredWorkspaceTasks.add(taskId), this.lastWorkspaceOutcome = { kind: "discarded" }, this.emitProgress({ phase: "discarded", taskId }, originEpoch), discarded && terminalOutcomeReserved && await this.workspaceOutcomeSink.completeTask(taskId, "apply_failed");
54417
+ discarded && this.shadowsByTask.get(taskId) === shadow && this.shadowsByTask.delete(taskId), discarded && this.terminallyRetiredWorkspaceTasks.add(taskId), this.lastWorkspaceOutcome = {
54418
+ kind: "discarded",
54419
+ taskId,
54420
+ completedAt: (/* @__PURE__ */ new Date()).toISOString()
54421
+ }, this.emitProgress({ phase: "discarded", taskId }, originEpoch), discarded && terminalOutcomeReserved && await this.workspaceOutcomeSink.completeTask(taskId, "apply_failed");
54056
54422
  return;
54057
54423
  }
54058
54424
  }
@@ -54207,7 +54573,12 @@ var QuorumLoop = class _QuorumLoop {
54207
54573
  }), logger.info("[QuorumLoop] shadow promoted to real tree", {
54208
54574
  taskId,
54209
54575
  promoted: appliedPaths.length
54210
- }), this.lastWorkspaceOutcome = { kind: "promoted", files: [...appliedPaths] }, appendContextItem(this.deps.session.sessionId, {
54576
+ }), this.lastWorkspaceOutcome = {
54577
+ kind: "promoted",
54578
+ files: [...appliedPaths],
54579
+ taskId,
54580
+ completedAt: (/* @__PURE__ */ new Date()).toISOString()
54581
+ }, appendContextItem(this.deps.session.sessionId, {
54211
54582
  kind: "verdict",
54212
54583
  author: { role: "engine" },
54213
54584
  sensitivity: "user",
@@ -54470,7 +54841,7 @@ var QuorumLoop = class _QuorumLoop {
54470
54841
  if (promptText === null) {
54471
54842
  logger.warn("[QuorumLoop] getReviewerPrompt exhausted \u2014 ESCALATE", {
54472
54843
  key,
54473
- err: lastErr?.message,
54844
+ ...errorShapeOnly(lastErr),
54474
54845
  audit: "synthesized ESCALATE (#C1F-8): prompt_fetch_failed"
54475
54846
  }), await this.submitVerdict(args, this.synthesizeEscalate(args, DESKTOP_DISPATCH_ESCALATE_REASONING), sessionKey);
54476
54847
  return;
@@ -54505,13 +54876,13 @@ var QuorumLoop = class _QuorumLoop {
54505
54876
  failureReason,
54506
54877
  audit: "synthesized ESCALATE (#C1F-8): reviewer_substrate_engage_failed",
54507
54878
  ...describeReviewerErrorDetail(engageErr.detail),
54508
- message: engageErr.message
54879
+ ...errorShapeOnly(engageErr)
54509
54880
  }), await this.submitVerdict(args, this.synthesizeEscalate(args, cleanReasoning), sessionKey);
54510
54881
  return;
54511
54882
  }
54512
54883
  throw engageErr;
54513
54884
  }
54514
- let verdict;
54885
+ let verdict, evaluationError;
54515
54886
  try {
54516
54887
  verdict = await this.evaluateSeatWithTimeout(spec, args.gateId, seatSubstrate.handle);
54517
54888
  } catch (err) {
@@ -54539,7 +54910,7 @@ var QuorumLoop = class _QuorumLoop {
54539
54910
  // Audit prefix kept in LOGS only (#C1F-8) — never on the wire.
54540
54911
  audit: `synthesized ESCALATE (#C1F-8): reviewer_error:${err.detail.kind}`,
54541
54912
  ...describeReviewerErrorDetail(err.detail),
54542
- message: err.message,
54913
+ ...errorShapeOnly(err),
54543
54914
  // 2026-08-22 (dogfood) — TIMEOUT DIAGNOSTICS, local log only.
54544
54915
  //
54545
54916
  // A live 5-minute reviewer timeout on a ONE-FILE deletion diff was
@@ -54556,16 +54927,51 @@ var QuorumLoop = class _QuorumLoop {
54556
54927
  ...this.describeReviewShapeForDiagnostics(args.gateId)
54557
54928
  }), verdict = this.synthesizeEscalate(args, cleanReasoning);
54558
54929
  } else
54559
- throw err;
54560
- } finally {
54561
- await seatSubstrate.teardown().catch(() => {
54930
+ evaluationError = err;
54931
+ }
54932
+ let cleanupError;
54933
+ try {
54934
+ await seatSubstrate.teardown();
54935
+ } catch (error) {
54936
+ cleanupError = error, logger.warn("[QuorumLoop] reviewer substrate cleanup failed \u2014 forcing ESCALATE", {
54937
+ key,
54938
+ seatId: args.seatId,
54939
+ role: args.role,
54940
+ agentKind: args.agentKind,
54941
+ ...errorShapeOnly(error),
54942
+ audit: "synthesized ESCALATE: reviewer_substrate_cleanup_failed"
54562
54943
  });
54563
54944
  }
54945
+ if (evaluationError !== void 0) {
54946
+ let terminalError = cleanupError !== void 0 ? Object.assign(
54947
+ new Error("reviewer evaluation failed and substrate cleanup also failed"),
54948
+ { causes: [evaluationError, cleanupError] }
54949
+ ) : Object.assign(
54950
+ new Error("reviewer evaluation failed unexpectedly"),
54951
+ { cause: evaluationError }
54952
+ );
54953
+ logger.warn("[QuorumLoop] unexpected reviewer evaluation failure \u2014 forcing ESCALATE", {
54954
+ key,
54955
+ seatId: args.seatId,
54956
+ role: args.role,
54957
+ agentKind: args.agentKind,
54958
+ ...errorShapeOnly(terminalError),
54959
+ audit: "synthesized ESCALATE: reviewer_evaluation_failed"
54960
+ }), verdict = this.synthesizeEscalate(
54961
+ args,
54962
+ DESKTOP_DISPATCH_ESCALATE_REASONING
54963
+ );
54964
+ }
54965
+ if (cleanupError !== void 0 && (verdict = this.synthesizeEscalate(
54966
+ args,
54967
+ DESKTOP_DISPATCH_ESCALATE_REASONING
54968
+ )), verdict === void 0)
54969
+ throw new Error("reviewer completed without a verdict");
54564
54970
  reviewScope !== null && (verdict = enforceReviewRoundScope(verdict, reviewScope)), await this.submitVerdict(args, verdict, sessionKey);
54565
54971
  } catch (err) {
54566
54972
  logger.warn("[QuorumLoop] spawnOneSeat failed \u2014 dropped", {
54567
54973
  key,
54568
- err: err.message
54974
+ ...errorShapeOnly(err)
54569
54975
  });
54570
54976
  } finally {
54571
54977
  this.runningSeats.delete(key);
@@ -54658,19 +55064,18 @@ var QuorumLoop = class _QuorumLoop {
54658
55064
  if (e.name === REVIEWER_CREDENTIAL_MISSING)
54659
55065
  try {
54660
55066
  this.surfaceHalt(
54661
- `Reviewer (${seatLabel(args.agentKind, args.seatId)}) cannot start sandboxed: ${e.message}`
55067
+ `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
55068
  );
54663
55069
  } catch {
54664
55070
  }
54665
55071
  throw new ReviewerErrorClass({
54666
55072
  kind: "spawn_failed",
54667
55073
  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}`,
55074
+ reason: "CP-7: reviewer substrate engage failed for a Trusted agent \u2014 refusing the unsandboxed (ambient-cred) fallback (fail-closed)",
54669
55075
  failureReason: "spawn_failed"
54670
55076
  });
54671
55077
  }
54672
- return result.mode === "reduced_trust" ? (this.surfaceReviewerReducedTrust(args, result.reducedTrustReason), await result.teardown().catch(() => {
54673
- }), NO_TEARDOWN) : (result.reducedTrust && this.surfaceReviewerReducedTrust(
55078
+ return result.mode === "reduced_trust" ? (this.surfaceReviewerReducedTrust(args, result.reducedTrustReason), await result.teardown(), NO_TEARDOWN) : (result.reducedTrust && this.surfaceReviewerReducedTrust(
54674
55079
  args,
54675
55080
  result.reducedTrustReason ?? "coarse egress (A5 sandbox-exec) \u2014 reviewer is sandboxed but loopback-only."
54676
55081
  ), {
@@ -54712,7 +55117,7 @@ var QuorumLoop = class _QuorumLoop {
54712
55117
  reason
54713
55118
  ), logger.warn("[QuorumLoop] reduced-trust badge surfaceHalt threw (badge is best-effort) \u2014 continuing", {
54714
55119
  seatId: args.seatId,
54715
- err: e.message
55120
+ ...errorShapeOnly(e)
54716
55121
  });
54717
55122
  }
54718
55123
  }
@@ -54767,13 +55172,20 @@ var QuorumLoop = class _QuorumLoop {
54767
55172
  });
54768
55173
  if (result.mode === "substrate")
54769
55174
  return { mode: "substrate", handle: result.substrateHandle, teardown: () => result.teardown() };
54770
- await result.teardown().catch(() => {
54771
- });
55175
+ try {
55176
+ await result.teardown();
55177
+ } catch (cleanupError) {
55178
+ return logger.warn("[QuorumLoop] Wave B reduced-trust cleanup failed \u2014 refusing fallback", {
55179
+ taskId,
55180
+ agent,
55181
+ ...errorShapeOnly(cleanupError)
55182
+ }), { mode: "none" };
55183
+ }
54772
55184
  } catch (e) {
54773
55185
  logger.warn("[QuorumLoop] Wave B substrate engage failed \u2014 falling through to trusted-container/none", {
54774
55186
  taskId,
54775
55187
  agent,
54776
- err: e.message
55188
+ ...errorShapeOnly(e)
54777
55189
  });
54778
55190
  }
54779
55191
  return isTrustedContainerBoundary() ? { mode: "trusted_container", teardown: async () => {
@@ -54804,9 +55216,11 @@ var QuorumLoop = class _QuorumLoop {
54804
55216
  ...substrate !== void 0 ? { substrate } : {}
54805
55217
  });
54806
55218
  if (!outcome.exit_success)
54807
- throw new Error(
54808
- `class-2 resolver model call exited non-zero: ${outcome.stderr.trim().slice(0, 200)}`
54809
- );
55219
+ throw logger.warn("[QuorumLoop] class-2 resolver model call exited non-zero", {
55220
+ ownerTaskId: args.ownerTaskId,
55221
+ stdoutShape: outputShapeOnly(outcome.stdout),
55222
+ stderrShape: outputShapeOnly(outcome.stderr)
55223
+ }), new Error("class-2 resolver model call exited non-zero");
54810
55224
  return outcome.stdout;
54811
55225
  };
54812
55226
  return {
@@ -54850,7 +55264,7 @@ var QuorumLoop = class _QuorumLoop {
54850
55264
  return { establishable: !0, via: "ladder_tier" };
54851
55265
  } catch (err) {
54852
55266
  logger.warn("[QuorumLoop] A1d resolver probe: ladder select failed \u2014 treating as no tier", {
54853
- err: err.message
55267
+ ...errorShapeOnly(err)
54854
55268
  });
54855
55269
  }
54856
55270
  return (this.deps.resolverProbeDeps?.trustedContainer ?? isTrustedContainerBoundary)() ? { establishable: !0, via: "trusted_container" } : { establishable: !1 };
@@ -54981,8 +55395,8 @@ var QuorumLoop = class _QuorumLoop {
54981
55395
  taskId: tid,
54982
55396
  exitCode: exit.exitCode,
54983
55397
  runtimeMs: exit.runtimeMs,
54984
- stdoutTail: handle.stdout().slice(-2e3),
54985
- stderrExcerpt: handle.stderr().slice(0, 600)
55398
+ stdoutShape: outputShapeOnly(handle.stdout()),
55399
+ stderrShape: outputShapeOnly(handle.stderr())
54986
55400
  }), exit.failureClass !== null)
54987
55401
  throw new Error(`agentic resolver implementor failed: ${exit.failureClass}`);
54988
55402
  }
@@ -55045,6 +55459,7 @@ var QuorumLoop = class _QuorumLoop {
55045
55459
  });
55046
55460
  continue;
55047
55461
  }
55462
+ let candidateError;
55048
55463
  try {
55049
55464
  let substrate = confinement.mode === "substrate" ? confinement.handle : void 0, spec = {
55050
55465
  seat_id: 0,
@@ -55067,14 +55482,27 @@ var QuorumLoop = class _QuorumLoop {
55067
55482
  ...pass ? {} : { reason: `final-tip Tier-2 verdict: ${verdict.verdict}` }
55068
55483
  };
55069
55484
  } 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", {
55485
+ 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
55486
  ownerTaskId: args.ownerTaskId,
55072
55487
  agent,
55073
- err: e.message.slice(0, 300)
55488
+ ...errorShapeOnly(e)
55074
55489
  });
55075
55490
  } finally {
55076
- await confinement.teardown().catch(() => {
55077
- });
55491
+ try {
55492
+ await confinement.teardown();
55493
+ } catch (cleanupError) {
55494
+ throw logger.warn("[QuorumLoop] final-tip Tier-2 substrate cleanup failed \u2014 failing closed", {
55495
+ ownerTaskId: args.ownerTaskId,
55496
+ agent,
55497
+ ...errorShapeOnly(cleanupError)
55498
+ }), candidateError !== void 0 ? Object.assign(
55499
+ new Error("final-tip Tier-2 evaluation failed and substrate cleanup also failed"),
55500
+ { causes: [candidateError, cleanupError] }
55501
+ ) : Object.assign(
55502
+ new Error("final-tip Tier-2 substrate cleanup failed"),
55503
+ { cause: cleanupError }
55504
+ );
55505
+ }
55078
55506
  }
55079
55507
  }
55080
55508
  return {
@@ -56825,7 +57253,7 @@ function seatKey(gateId, seatId) {
56825
57253
  return `${gateId}::${seatId}`;
56826
57254
  }
56827
57255
  function seatLabel(agentKind, seatId) {
56828
- return `${REVIEWER_AGENT_DISPLAY_NAME[agentKind] ?? agentKind} (seat ${seatId})`;
57256
+ return `${REVIEWER_AGENT_DISPLAY_NAME[agentKind] ?? agentKind} (seat ${displayOrdinal(seatId)})`;
56829
57257
  }
56830
57258
  function diffCapturedProgress(round, files) {
56831
57259
  let created = 0, modified = 0, deleted = 0;
@@ -57793,7 +58221,7 @@ async function runOrchestrationShell(args) {
57793
58221
  });
57794
58222
  }
57795
58223
  workspaceTerminalCoordinator && await workspaceTerminalCoordinator.replayPending(), processMarkers();
57796
- let unsubscribeEvents = null, pendingDurableResolutions = /* @__PURE__ */ new Map(), durableDecisionEffectDispatcher = null, dispatchDurableDecisionEffect = async (resolution) => {
58224
+ let inkUnmount = null, nonTtyAbort = null, explicitExit = null, sessionRetiredExitRequested = !1, triggerSessionRetiredTeardown = null, unsubscribeEvents = null, pendingDurableResolutions = /* @__PURE__ */ new Map(), durableDecisionEffectDispatcher = null, dispatchDurableDecisionEffect = async (resolution) => {
57797
58225
  if (!durableDecisionEffectDispatcher) {
57798
58226
  pendingDurableResolutions.set(resolution.eventId, resolution);
57799
58227
  return;
@@ -57929,6 +58357,16 @@ async function runOrchestrationShell(args) {
57929
58357
  text: "Gate history recovery is unavailable. Existing gate actions remain fail-closed until recovery succeeds."
57930
58358
  })) : rejectInitialSubscription(error);
57931
58359
  });
58360
+ },
58361
+ onSessionRetired: () => {
58362
+ logger.warn(
58363
+ "[orchestration-shell] Session permanently retired on backend; triggering fail-closed teardown",
58364
+ { sessionId: args.session.sessionId }
58365
+ ), store.dispatch({
58366
+ type: "SHELL_ADVISORY",
58367
+ source: "shell",
58368
+ text: "Hosted session has been retired. Exiting orchestration shell."
58369
+ }), triggerSessionRetiredTeardown ? triggerSessionRetiredTeardown() : sessionRetiredExitRequested = !0;
57932
58370
  }
57933
58371
  }
57934
58372
  );
@@ -58171,7 +58609,7 @@ async function runOrchestrationShell(args) {
58171
58609
  if (admitted.length === 0) return;
58172
58610
  await Promise.allSettled(admitted);
58173
58611
  }
58174
- }, NORMAL_SHUTDOWN_TIMEOUT_MS = 1e4, SIGNAL_SHUTDOWN_TIMEOUT_MS = 3e3, SESSION_RETIRE_TIMEOUT_MS2 = 3e3, hostedSessionIngressFenced = !1, hostedSessionIngressFenceFailures = [], hostedSessionIngressFenceDrain = Promise.resolve(), shutdownFailure = (message, reason) => Object.assign(
58612
+ }, NORMAL_SHUTDOWN_TIMEOUT_MS = 1e4, SIGNAL_SHUTDOWN_TIMEOUT_MS = 3e3, SESSION_RETIRE_TIMEOUT_MS2 = 3e3, hostedSessionIngressFenced = !1, hostedRetirementMutationStarted = !1, lastDitchRetirementPromise = null, hostedSessionIngressFenceFailures = [], hostedSessionIngressFenceDrain = Promise.resolve(), shutdownFailure = (message, reason) => Object.assign(
58175
58613
  new Error(
58176
58614
  `${message}: ${reason instanceof Error ? reason.message : String(reason)}`
58177
58615
  ),
@@ -58242,7 +58680,7 @@ async function runOrchestrationShell(args) {
58242
58680
  shutdownFailure("session heartbeat stop failed", reason)
58243
58681
  );
58244
58682
  }
58245
- }, hostedRetirementMutationStarted = !1, retireHostedSession = async () => {
58683
+ }, retireHostedSession = async () => {
58246
58684
  await hostedSessionIngressFenceDrain;
58247
58685
  let failures = [...hostedSessionIngressFenceFailures];
58248
58686
  try {
@@ -58279,11 +58717,11 @@ async function runOrchestrationShell(args) {
58279
58717
  new Error("final ContextItems publication did not converge"),
58280
58718
  { causes: failures }
58281
58719
  );
58282
- }, workspaceResourceRetirement = null, plannerTeardown = createBoundedSharedShutdownOperation({
58720
+ }, workspaceResourceRetirement = null, teardownDeadlineExpired = !1, plannerTeardown = createBoundedSharedShutdownOperation({
58283
58721
  normalTimeoutMs: NORMAL_SHUTDOWN_TIMEOUT_MS,
58284
58722
  signalTimeoutMs: SIGNAL_SHUTDOWN_TIMEOUT_MS,
58285
58723
  onTimeout: (deadlineClass, timeoutMs) => {
58286
- logger.warn("[orchestration-shell] planner teardown timed out", {
58724
+ teardownDeadlineExpired = !0, logger.warn("[orchestration-shell] planner teardown timed out", {
58287
58725
  deadlineClass,
58288
58726
  timeoutMs
58289
58727
  });
@@ -58350,47 +58788,75 @@ async function runOrchestrationShell(args) {
58350
58788
  });
58351
58789
  }
58352
58790
  }), runPlannerTeardown = async (deadlineClass = "normal") => {
58353
- let boundedCompletion = plannerTeardown.wait(deadlineClass);
58354
- if (deadlineClass === "signal") return boundedCompletion;
58355
- let requiredRetirement = workspaceResourceRetirement;
58791
+ let boundedCompletion = plannerTeardown.wait(deadlineClass), requiredRetirement = workspaceResourceRetirement;
58356
58792
  if (!requiredRetirement)
58357
58793
  throw new Error("workspace retirement boundary was not published");
58358
58794
  let [boundedResult] = await Promise.allSettled([boundedCompletion]);
58359
- if (boundedResult.status === "rejected" && boundedResult.reason?.teardownDeadlineExpired === !0 && // The leak scenario is a hung EARLIER phase starving `retireHostedSession`
58795
+ if ((teardownDeadlineExpired || boundedResult.status === "rejected" && boundedResult.reason?.teardownDeadlineExpired === !0) && // The leak scenario is a hung EARLIER phase starving `retireHostedSession`
58360
58796
  // before it could issue its mutation. If the ordered mutation already
58361
58797
  // started (in flight when the deadline expired, or settled with the
58362
58798
  // deadline expiring on a parallel phase such as the planner flush), the
58363
58799
  // last-ditch duplicate is redundant — skip it.
58364
- !hostedRetirementMutationStarted) {
58365
- let timer;
58366
- try {
58367
- await Promise.race([
58368
- args.appsyncClient.updateSession({
58369
- sessionId: args.session.sessionId,
58370
- status: "INACTIVE" /* INACTIVE */
58371
- }).then(() => {
58372
- logger.warn(
58373
- "[orchestration-shell] last-ditch session retirement landed after teardown timeout",
58374
- { sessionId: args.session.sessionId }
58375
- );
58376
- }).catch((reason) => {
58377
- logger.warn("[orchestration-shell] last-ditch session retirement failed", {
58378
- sessionId: args.session.sessionId,
58379
- errorName: reason?.name ?? "Error"
58380
- });
58381
- }),
58382
- new Promise((resolve21) => {
58383
- timer = setTimeout(resolve21, SESSION_RETIRE_TIMEOUT_MS2), timer.unref?.();
58384
- })
58385
- ]);
58386
- } finally {
58387
- timer && clearTimeout(timer);
58800
+ !hostedRetirementMutationStarted)
58801
+ if (lastDitchRetirementPromise)
58802
+ await lastDitchRetirementPromise;
58803
+ else {
58804
+ let timer;
58805
+ lastDitchRetirementPromise = (async () => {
58806
+ try {
58807
+ await Promise.race([
58808
+ args.appsyncClient.updateSession({
58809
+ sessionId: args.session.sessionId,
58810
+ status: "INACTIVE" /* INACTIVE */
58811
+ }).then(() => {
58812
+ logger.warn(
58813
+ "[orchestration-shell] last-ditch session retirement landed after teardown timeout",
58814
+ { sessionId: args.session.sessionId }
58815
+ );
58816
+ }).catch((reason) => {
58817
+ logger.warn("[orchestration-shell] last-ditch session retirement failed", {
58818
+ sessionId: args.session.sessionId,
58819
+ errorName: reason?.name ?? "Error"
58820
+ });
58821
+ }),
58822
+ new Promise((resolve21) => {
58823
+ timer = setTimeout(resolve21, SESSION_RETIRE_TIMEOUT_MS2), timer.unref?.();
58824
+ })
58825
+ ]);
58826
+ } finally {
58827
+ timer && clearTimeout(timer);
58828
+ }
58829
+ })(), await lastDitchRetirementPromise;
58388
58830
  }
58389
- }
58390
- let [retirementResult] = await Promise.allSettled([requiredRetirement]);
58831
+ let [retirementResult] = await Promise.allSettled(
58832
+ deadlineClass === "signal" && teardownDeadlineExpired ? [] : [requiredRetirement]
58833
+ );
58391
58834
  if (boundedResult.status === "rejected") throw boundedResult.reason;
58392
- if (retirementResult.status === "rejected") throw retirementResult.reason;
58393
- }, inkUnmount = null, nonTtyAbort = null, signalShutdownStarted = !1, signalShutdownCompletion = null, signalsRegistered = /* @__PURE__ */ new Set(), onSignal = (sig) => {
58835
+ if (retirementResult && retirementResult.status === "rejected") throw retirementResult.reason;
58836
+ };
58837
+ triggerSessionRetiredTeardown = () => {
58838
+ if (sessionRetiredExitRequested = !0, nonTtyAbort)
58839
+ try {
58840
+ nonTtyAbort.abort();
58841
+ } catch {
58842
+ }
58843
+ if (explicitExit)
58844
+ try {
58845
+ explicitExit.start();
58846
+ } catch {
58847
+ }
58848
+ else if (inkUnmount)
58849
+ try {
58850
+ inkUnmount(), inkUnmount = null;
58851
+ } catch {
58852
+ }
58853
+ runPlannerTeardown("normal").catch((err) => {
58854
+ logger.warn("[orchestration-shell] session-retired teardown failed", {
58855
+ error: err instanceof Error ? err.message : String(err)
58856
+ });
58857
+ });
58858
+ }, sessionRetiredExitRequested && triggerSessionRetiredTeardown();
58859
+ let signalShutdownStarted = !1, signalShutdownCompletion = null, signalsRegistered = /* @__PURE__ */ new Set(), onSignal = (sig) => {
58394
58860
  signalShutdownCompletion || (signalShutdownStarted = !0, disableBracketedPaste(), signalShutdownCompletion = Promise.allSettled([runPlannerTeardown("signal")]).then(() => {
58395
58861
  }).finally(() => {
58396
58862
  logger.info(`[orchestration-shell] caught ${sig}, teardown complete`);
@@ -58409,7 +58875,7 @@ async function runOrchestrationShell(args) {
58409
58875
  try {
58410
58876
  process.kill(process.pid, sig);
58411
58877
  } catch {
58412
- let code = sig === "SIGTERM" ? 143 : 130;
58878
+ let code = sig === "SIGTERM" ? 143 : sig === "SIGHUP" ? 129 : 130;
58413
58879
  process.exit(code);
58414
58880
  }
58415
58881
  }));
@@ -58621,14 +59087,19 @@ async function runOrchestrationShell(args) {
58621
59087
  // land in the active conversation. ABSENT on a non-team session.
58622
59088
  ...args.groupDecisionDeps ? { groupDecision: { ...args.groupDecisionDeps, store } } : {}
58623
59089
  };
58624
- mobileGateDecisionDeps = gateDecisionDeps, process.once("SIGINT", onSignal), signalsRegistered.add("SIGINT"), process.once("SIGTERM", onSignal), signalsRegistered.add("SIGTERM");
59090
+ mobileGateDecisionDeps = gateDecisionDeps, process.on("SIGINT", onSignal), signalsRegistered.add("SIGINT"), process.on("SIGTERM", onSignal), signalsRegistered.add("SIGTERM"), process.on("SIGHUP", onSignal), signalsRegistered.add("SIGHUP");
58625
59091
  try {
58626
59092
  if (signalShutdownCompletion) {
58627
59093
  await signalShutdownCompletion;
58628
59094
  return;
58629
59095
  }
59096
+ if (sessionRetiredExitRequested)
59097
+ return;
58630
59098
  if (args.acceptSessionLifecycleOwnership?.(), !isInteractiveTty()) {
58631
- nonTtyAbort = new AbortController();
59099
+ if (nonTtyAbort = new AbortController(), sessionRetiredExitRequested) {
59100
+ nonTtyAbort = null;
59101
+ return;
59102
+ }
58632
59103
  try {
58633
59104
  await runLineLogFallback({
58634
59105
  store,
@@ -58656,6 +59127,8 @@ async function runOrchestrationShell(args) {
58656
59127
  await signalShutdownCompletion;
58657
59128
  return;
58658
59129
  }
59130
+ if (sessionRetiredExitRequested)
59131
+ return;
58659
59132
  let { ink } = getInkRuntime(), { waitUntilExit, unmount } = ink.render(
58660
59133
  React20.createElement(OrchestrationApp, {
58661
59134
  store,
@@ -58706,19 +59179,21 @@ async function runOrchestrationShell(args) {
58706
59179
  { stdout: process.stdout }
58707
59180
  );
58708
59181
  inkUnmount = unmount;
58709
- let explicitExit = createExplicitTtyExitCoordinator({
59182
+ let ttyExplicitExit = createExplicitTtyExitCoordinator({
58710
59183
  runTeardown: runPlannerTeardown,
58711
59184
  unmount: () => {
58712
59185
  inkUnmount && (inkUnmount(), inkUnmount = null);
58713
59186
  }
58714
- }), unsubscribe = store.subscribe((state) => {
59187
+ });
59188
+ explicitExit = ttyExplicitExit, sessionRetiredExitRequested && ttyExplicitExit.start();
59189
+ let unsubscribe = store.subscribe((state) => {
58715
59190
  let last = state.conversation[state.conversation.length - 1];
58716
- last && last.kind === "slash-output" && isExitCommand(last.command) && explicitExit.start();
59191
+ last && last.kind === "slash-output" && isExitCommand(last.command) && ttyExplicitExit.start();
58717
59192
  });
58718
59193
  try {
58719
- await Promise.race([waitUntilExit(), explicitExit.completion]);
59194
+ await Promise.race([waitUntilExit(), ttyExplicitExit.completion]);
58720
59195
  } finally {
58721
- unsubscribe(), inkUnmount = null;
59196
+ unsubscribe(), inkUnmount = null, explicitExit = null;
58722
59197
  }
58723
59198
  } finally {
58724
59199
  try {
@@ -59275,8 +59750,8 @@ function routeTeamShellEventToStore(store, evt) {
59275
59750
  return;
59276
59751
  }
59277
59752
  if (source === "task_group_halted") {
59278
- let haltReason = typeof md.haltReason == "string" ? md.haltReason : "halted";
59279
- store.dispatch({ type: "TEAM_HALTED", haltReason });
59753
+ let haltReason = typeof md.haltReason == "string" ? md.haltReason : "halted", completedAt = typeof md.completed_at == "string" ? md.completed_at : typeof md.completedAt == "string" ? md.completedAt : void 0;
59754
+ store.dispatch({ type: "TEAM_HALTED", haltReason, ...completedAt ? { completedAt } : {} });
59280
59755
  let team = store.getState().team;
59281
59756
  if (team)
59282
59757
  for (let [trackIndex, t] of team.tracks)
@@ -59322,8 +59797,12 @@ function routeTeamShellEventToStore(store, evt) {
59322
59797
  if (!team || team.groupResolved) return;
59323
59798
  let trackStates = [...team.tracks.values()].map((t) => t.state), allTerminal = trackStates.length > 0 && trackStates.every((s) => s === "Passed" || s === "Failed"), anyFailed = trackStates.some((s) => s === "Failed");
59324
59799
  if (!allTerminal || !anyFailed) return;
59325
- let failedTrackIndices = [...team.tracks.entries()].filter(([, t]) => t.state === "Failed").map(([idx]) => idx).sort((a, b) => a - b), failedLabel = failedTrackIndices.length === 1 ? `track ${displayTrackNumber(failedTrackIndices[0])}` : `tracks ${failedTrackIndices.map(displayTrackNumber).join(", ")}`;
59326
- store.dispatch({ type: "TEAM_GROUP_RESOLVED", outcome: `team_halted:${reason}` }), store.dispatch({
59800
+ let failedTrackIndices = [...team.tracks.entries()].filter(([, t]) => t.state === "Failed").map(([idx]) => idx).sort((a, b) => a - b), failedLabel = failedTrackIndices.length === 1 ? `track ${displayTrackNumber(failedTrackIndices[0])}` : `tracks ${failedTrackIndices.map(displayTrackNumber).join(", ")}`, localHaltCompletedAt = typeof md.completed_at == "string" ? md.completed_at : typeof md.completedAt == "string" ? md.completedAt : void 0;
59801
+ store.dispatch({
59802
+ type: "TEAM_GROUP_RESOLVED",
59803
+ outcome: `team_halted:${reason}`,
59804
+ ...localHaltCompletedAt ? { completedAt: localHaltCompletedAt } : {}
59805
+ }), store.dispatch({
59327
59806
  type: "SHELL_ADVISORY",
59328
59807
  source: "shell",
59329
59808
  text: `Agent Teams: Team halted \u2014 ${failedLabel} failed (${reason}). Passing tracks were not applied to your files; CodeVibe will clean up their disposable task resources automatically.`
@@ -59333,8 +59812,12 @@ function routeTeamShellEventToStore(store, evt) {
59333
59812
  if (source === "team_group_resolved") {
59334
59813
  let outcome = md.outcome;
59335
59814
  if (typeof outcome != "string" || outcome.length === 0) return;
59336
- let priorTeam = store.getState().team, alreadyResolvedSame = priorTeam?.groupResolved === !0 && priorTeam.outcome === outcome;
59337
- if (store.dispatch({ type: "TEAM_GROUP_RESOLVED", outcome }), alreadyResolvedSame) return;
59815
+ let completedAt = typeof md.completed_at == "string" ? md.completed_at : typeof md.completedAt == "string" ? md.completedAt : void 0, priorTeam = store.getState().team, alreadyResolvedSame = priorTeam?.groupResolved === !0 && priorTeam.outcome === outcome;
59816
+ if (store.dispatch({
59817
+ type: "TEAM_GROUP_RESOLVED",
59818
+ outcome,
59819
+ ...completedAt ? { completedAt } : {}
59820
+ }), alreadyResolvedSame) return;
59338
59821
  let text2 = outcome === "complete" ? "Agent Teams: Team complete" : `Agent Teams: Team halted \u2014 ${outcome}`;
59339
59822
  store.dispatch({ type: "SHELL_ADVISORY", source: "shell", text: text2 });
59340
59823
  return;
@@ -62517,6 +63000,11 @@ var GATE_PROMPT_KIND_LABEL = {
62517
63000
  orchestration_escalated_gate: "escalated review gate",
62518
63001
  continuation_offer_handoff: "continuation handoff offer"
62519
63002
  };
63003
+ function parseIsoTimestamp(iso) {
63004
+ if (!iso) return 0;
63005
+ let t = Date.parse(iso);
63006
+ return Number.isFinite(t) ? t : 0;
63007
+ }
62520
63008
  function buildStatusSummary(state, quorumLoop) {
62521
63009
  let lines = [];
62522
63010
  if (state.team) {
@@ -62548,7 +63036,80 @@ function buildStatusSummary(state, quorumLoop) {
62548
63036
  `Awaiting your decision: ${label} for task ${entry.envelope.taskId} \u2014 reply with an option number (1-${entry.envelope.options.length})${queued}.`
62549
63037
  );
62550
63038
  }
62551
- let outcome = state.team ? null : quorumLoop?.getLastWorkspaceOutcome() ?? null;
63039
+ let rawOutcome = quorumLoop?.getLastWorkspaceOutcome() ?? null, outcome = null;
63040
+ if (rawOutcome !== null && !teamActive)
63041
+ if (rawOutcome.taskId) {
63042
+ let refTask = state.sessionTasks.get(rawOutcome.taskId);
63043
+ if (refTask && refTask.origin === "single")
63044
+ if (!state.team)
63045
+ outcome = rawOutcome;
63046
+ else {
63047
+ let singleTaskTime = Math.max(
63048
+ parseIsoTimestamp(rawOutcome.completedAt),
63049
+ parseIsoTimestamp(refTask.completedAt),
63050
+ parseIsoTimestamp(refTask.startedAt)
63051
+ ), teamTasks = [...state.sessionTasks.values()].filter(
63052
+ (t) => t.origin === "team"
63053
+ ), maxTeamTrackTime = teamTasks.length > 0 ? Math.max(
63054
+ ...teamTasks.map(
63055
+ (tt) => Math.max(
63056
+ parseIsoTimestamp(tt.completedAt),
63057
+ parseIsoTimestamp(tt.startedAt)
63058
+ )
63059
+ )
63060
+ ) : 0, teamStartedTime = parseIsoTimestamp(state.team.startedAt), teamCompletedTime = parseIsoTimestamp(state.team.completedAt), latestTeamTime = Math.max(
63061
+ teamStartedTime,
63062
+ teamCompletedTime,
63063
+ maxTeamTrackTime
63064
+ ), teamRanAfter;
63065
+ if (latestTeamTime !== singleTaskTime)
63066
+ teamRanAfter = latestTeamTime > singleTaskTime || teamTasks.length === 0 && teamStartedTime === 0 && teamCompletedTime === 0;
63067
+ else {
63068
+ let teamSeq = Math.max(
63069
+ state.team.terminalSeq ?? 0,
63070
+ ...teamTasks.map((tt) => tt.terminalSeq ?? 0)
63071
+ ), singleSeq = refTask.terminalSeq ?? 0;
63072
+ teamSeq > 0 && singleSeq > 0 ? teamRanAfter = teamSeq > singleSeq : teamRanAfter = teamTasks.length === 0 && teamStartedTime === 0 && teamCompletedTime === 0;
63073
+ }
63074
+ teamRanAfter || (outcome = rawOutcome);
63075
+ }
63076
+ } else if (!state.team)
63077
+ outcome = rawOutcome;
63078
+ else {
63079
+ let singleTasks = [...state.sessionTasks.values()].filter(
63080
+ (t) => t.origin === "single"
63081
+ );
63082
+ if (singleTasks.length > 0) {
63083
+ let latestSingle = singleTasks.reduce((latest, curr) => {
63084
+ let currTime = Math.max(
63085
+ parseIsoTimestamp(curr.completedAt),
63086
+ parseIsoTimestamp(curr.startedAt)
63087
+ ), latestTime = Math.max(
63088
+ parseIsoTimestamp(latest.completedAt),
63089
+ parseIsoTimestamp(latest.startedAt)
63090
+ );
63091
+ return currTime > latestTime ? curr : latest;
63092
+ }), singleTaskTime = Math.max(
63093
+ parseIsoTimestamp(rawOutcome.completedAt),
63094
+ parseIsoTimestamp(latestSingle.completedAt),
63095
+ parseIsoTimestamp(latestSingle.startedAt)
63096
+ ), teamTasks = [...state.sessionTasks.values()].filter(
63097
+ (t) => t.origin === "team"
63098
+ ), maxTeamTrackTime = teamTasks.length > 0 ? Math.max(
63099
+ ...teamTasks.map(
63100
+ (tt) => Math.max(
63101
+ parseIsoTimestamp(tt.completedAt),
63102
+ parseIsoTimestamp(tt.startedAt)
63103
+ )
63104
+ )
63105
+ ) : 0, teamStartedTime = parseIsoTimestamp(state.team.startedAt), teamCompletedTime = parseIsoTimestamp(state.team.completedAt);
63106
+ Math.max(
63107
+ teamStartedTime,
63108
+ teamCompletedTime,
63109
+ maxTeamTrackTime
63110
+ ) > singleTaskTime || teamTasks.length === 0 && teamStartedTime === 0 && teamCompletedTime === 0 || (outcome = rawOutcome);
63111
+ }
63112
+ }
62552
63113
  if (outcome && outcome.kind === "promoted") {
62553
63114
  let n = outcome.files.length;
62554
63115
  n > 0 ? lines.push(
@@ -65193,7 +65754,7 @@ function buildSanitizedBaseEnv(input) {
65193
65754
  let v = safeSource[key];
65194
65755
  typeof v == "string" && v.length > 0 && (env[key] = v);
65195
65756
  }
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;
65757
+ 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
65758
  }
65198
65759
  var FORBIDDEN_KEY_PATTERNS = [
65199
65760
  /^ANTHROPIC_API_KEY$/i,
@@ -65319,16 +65880,36 @@ async function engageSubstrate(input) {
65319
65880
  try {
65320
65881
  ({ hostBrokerAddr } = await broker.start());
65321
65882
  } 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}`
65883
+ let primary = Object.assign(
65884
+ new Error("CP-7: broker failed to start \u2014 refusing to launch the implementor (fail-closed)"),
65885
+ { cause: e }
65325
65886
  );
65887
+ try {
65888
+ await broker.stop();
65889
+ } catch (cleanupError) {
65890
+ throw Object.assign(
65891
+ new Error("CP-7: broker start failed and cleanup also failed"),
65892
+ { causes: [primary, cleanupError] }
65893
+ );
65894
+ }
65895
+ throw primary;
65326
65896
  }
65327
65897
  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;
65898
+ if (!initialTokenObj) {
65899
+ let primary = new Error(
65900
+ "CP-7: broker minted no token \u2014 refusing to launch (fail-closed)"
65901
+ );
65902
+ try {
65903
+ await broker.stop();
65904
+ } catch (cleanupError) {
65905
+ throw Object.assign(
65906
+ new Error("CP-7: broker minted no token and cleanup also failed"),
65907
+ { causes: [primary, cleanupError] }
65908
+ );
65909
+ }
65910
+ throw primary;
65911
+ }
65912
+ 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
65913
  try {
65333
65914
  bootstrap = await ApiKeyBootstrap.create({
65334
65915
  provider,
@@ -65339,6 +65920,7 @@ async function engageSubstrate(input) {
65339
65920
  sandboxConfigDir: sandboxConfigMount ?? SANDBOX_AGENT_CONFIG_DIR
65340
65921
  } : { sandboxDirEqualsHostDir: !0, configDirEqualsHostDir: !0 },
65341
65922
  hostRoot: input.bootstrapHostRoot,
65923
+ ...selection.tier === "sandbox_exec" && provider === "anthropic" ? { claudeScratchHostRoot: input.claudeScratchHostRoot ?? "/tmp" } : {},
65342
65924
  // M-2 — fail closed if the bootstrap dir lands inside the agent's rw
65343
65925
  // workdir mount (the token would leak through it).
65344
65926
  workdir: input.workdir,
@@ -65350,11 +65932,12 @@ async function engageSubstrate(input) {
65350
65932
  ), agentConfigSpec = sandboxConfigMount !== null ? { hostDir: bootstrap.configHostDir, sandboxDir: sandboxConfigMount } : (
65351
65933
  // A5: sandbox fs == host fs → config path equals the host dir.
65352
65934
  { hostDir: bootstrap.configHostDir, sandboxDir: bootstrap.configHostDir }
65353
- ), sanitizedBase = buildSanitizedBaseEnv({
65935
+ ), agentScratchSpec = bootstrap.claudeScratchSpec, sanitizedBase = buildSanitizedBaseEnv({
65354
65936
  provider,
65355
65937
  sandboxBrokerUrl,
65356
65938
  sandboxHome,
65357
65939
  sandboxBootstrapDir: agentConfigSpec.sandboxDir,
65940
+ ...agentScratchSpec !== null ? { claudeScratchDir: agentScratchSpec.sandboxDir } : {},
65358
65941
  safeSource: input.localeSource
65359
65942
  });
65360
65943
  assertNoAmbientCreds(sanitizedBase);
@@ -65368,6 +65951,7 @@ async function engageSubstrate(input) {
65368
65951
  sanitizedEnv: finalEnv,
65369
65952
  agentBootstrap: agentBootstrapSpec,
65370
65953
  agentConfig: agentConfigSpec,
65954
+ ...agentScratchSpec !== null ? { agentScratch: agentScratchSpec } : {},
65371
65955
  // CP-7 W3 — Stage-2 r1 HIGH. The resolved audit dir → A5 trailing deny.
65372
65956
  // Undefined when a test injects its own in-memory sink (no real tree).
65373
65957
  ...resolvedAuditDir !== null ? { auditDir: resolvedAuditDir } : {}
@@ -65376,12 +65960,30 @@ async function engageSubstrate(input) {
65376
65960
  `[CP-7] Substrate engaged (tier=${selection.tier}, egress=${handle.egressFidelity}) \u2014 agent is creditless, broker holds the key`,
65377
65961
  { taskId: input.taskId, agent: input.agentKind }
65378
65962
  );
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;
65963
+ let liveBootstrap = bootstrap, liveHandle = handle, liveBroker = broker, refreshTimer = null, teardownPromise, teardown = () => teardownPromise || (teardownPromise = (async () => {
65964
+ refreshTimer && (clearInterval(refreshTimer), refreshTimer = null);
65965
+ let failures = [];
65966
+ for (let cleanup of [
65967
+ () => liveHandle.teardown(),
65968
+ () => liveBroker.stop(),
65969
+ () => liveBootstrap.destroy()
65970
+ ])
65971
+ try {
65972
+ await cleanup();
65973
+ } catch (error) {
65974
+ failures.push(error);
65975
+ }
65976
+ if (failures.length > 0)
65977
+ throw logger.warn("[CP-7] substrate cleanup failed \u2014 task cannot pass", {
65978
+ taskId: input.taskId,
65979
+ failureCount: failures.length,
65980
+ ...errorShapeOnly(failures[0])
65981
+ }), failures.length === 1 ? failures[0] : Object.assign(new Error("CP-7 substrate cleanup failed"), {
65982
+ causes: failures
65983
+ });
65984
+ })(), teardownPromise);
65985
+ activeTeardown = teardown;
65986
+ let launchId = `launch-${input.taskId}-${Date.now()}`, denialReason = liveHandle.egressFidelity === "strict" ? "structural_deny_network_none" : "coarse_loopback_only", postureRes;
65385
65987
  try {
65386
65988
  postureRes = await auditSink.emit("egress_denied", {
65387
65989
  destination: "*",
@@ -65390,13 +65992,16 @@ async function engageSubstrate(input) {
65390
65992
  caller_event_id: launchId
65391
65993
  });
65392
65994
  } 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}`
65995
+ throw Object.assign(
65996
+ new Error(
65997
+ "CP-7 W3: egress-posture audit emit failed \u2014 refusing to launch the creditless agent (audit-before-effect fail-closed)"
65998
+ ),
65999
+ { cause: e }
65395
66000
  );
65396
66001
  }
65397
66002
  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).`
66003
+ throw new Error(
66004
+ "CP-7 W3: egress-posture audit was rejected \u2014 refusing to launch the creditless agent (no durable egress posture \u2192 no agent)"
65400
66005
  );
65401
66006
  let refreshIntervalMs = input.tokenRefreshIntervalMs ?? 600 * 1e3, inFlightRefresh = null, doRefreshOnce = async () => {
65402
66007
  if (!(typeof liveBroker.rotateBrokerToken == "function" && typeof liveBroker.commitBrokerTokenRotation == "function" && typeof liveBroker.rollbackBrokerTokenRotation == "function")) {
@@ -65409,10 +66014,23 @@ async function engageSubstrate(input) {
65409
66014
  try {
65410
66015
  await liveBootstrap.refresh(rotated.token.value), liveBroker.commitBrokerTokenRotation(rotated);
65411
66016
  } catch (e) {
65412
- throw liveBroker.rollbackBrokerTokenRotation(rotated), logger.warn(
66017
+ liveBroker.rollbackBrokerTokenRotation(rotated), logger.warn(
65413
66018
  "[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));
66019
+ { taskId: input.taskId, ...errorShapeOnly(e) }
66020
+ );
66021
+ let refreshFailure = Object.assign(
66022
+ new Error("CP-7 broker-token refresh failed"),
66023
+ { cause: e }
66024
+ );
66025
+ try {
66026
+ await teardown();
66027
+ } catch (cleanupError) {
66028
+ throw Object.assign(
66029
+ new Error("CP-7 broker-token refresh failed and cleanup also failed"),
66030
+ { causes: [refreshFailure, cleanupError] }
66031
+ );
66032
+ }
66033
+ throw refreshFailure;
65416
66034
  }
65417
66035
  }, doRefresh = async () => {
65418
66036
  let next = (inFlightRefresh ?? Promise.resolve()).catch(() => {
@@ -65439,10 +66057,32 @@ async function engageSubstrate(input) {
65439
66057
  teardown
65440
66058
  };
65441
66059
  } 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));
66060
+ let primary = e instanceof Error ? e : new Error("CP-7 substrate launch failed"), cleanupFailures = [];
66061
+ if (activeTeardown)
66062
+ try {
66063
+ await activeTeardown();
66064
+ } catch (cleanupError) {
66065
+ cleanupFailures.push(cleanupError);
66066
+ }
66067
+ else
66068
+ for (let cleanup of [
66069
+ ...handle ? [() => handle.teardown()] : [],
66070
+ ...bootstrap ? [() => bootstrap.destroy()] : [],
66071
+ () => broker.stop()
66072
+ ])
66073
+ try {
66074
+ await cleanup();
66075
+ } catch (cleanupError) {
66076
+ cleanupFailures.push(cleanupError);
66077
+ }
66078
+ throw cleanupFailures.length > 0 ? (logger.warn("[CP-7] failed launch also failed cleanup", {
66079
+ taskId: input.taskId,
66080
+ failureCount: cleanupFailures.length,
66081
+ ...errorShapeOnly(cleanupFailures[0])
66082
+ }), Object.assign(
66083
+ new Error("CP-7 substrate launch failed and cleanup also failed"),
66084
+ { causes: [primary, ...cleanupFailures] }
66085
+ )) : primary;
65446
66086
  }
65447
66087
  }
65448
66088
  var RESOLVER_AGENT_STATE_DIR_SEGMENTS = {
@@ -67223,13 +67863,14 @@ function createAdmittedSessionOwnershipController(appsyncClient) {
67223
67863
  appsyncClient,
67224
67864
  admittedSession.sessionId
67225
67865
  ), retirement) : Promise.resolve(), removeTemporarySignalHandlers = () => {
67226
- process.removeListener("SIGINT", onSigint), process.removeListener("SIGTERM", onSigterm);
67866
+ process.removeListener("SIGINT", onSigint), process.removeListener("SIGTERM", onSigterm), process.removeListener("SIGHUP", onSighup);
67227
67867
  }, handleSignal = (signal) => {
67228
- signalReceived || ownershipAccepted || closed || (signalReceived = !0, removeTemporarySignalHandlers(), retireOnce().finally(() => {
67868
+ signalReceived || ownershipAccepted || closed || (signalReceived = !0, retireOnce().finally(() => {
67869
+ removeTemporarySignalHandlers();
67229
67870
  try {
67230
67871
  process.kill(process.pid, signal);
67231
67872
  } catch {
67232
- process.exit(signal === "SIGTERM" ? 143 : 130);
67873
+ process.exit(signal === "SIGTERM" ? 143 : signal === "SIGHUP" ? 129 : 130);
67233
67874
  }
67234
67875
  }));
67235
67876
  };
@@ -67239,7 +67880,10 @@ function createAdmittedSessionOwnershipController(appsyncClient) {
67239
67880
  function onSigterm() {
67240
67881
  handleSignal("SIGTERM");
67241
67882
  }
67242
- process.once("SIGINT", onSigint), process.once("SIGTERM", onSigterm);
67883
+ function onSighup() {
67884
+ handleSignal("SIGHUP");
67885
+ }
67886
+ process.on("SIGINT", onSigint), process.on("SIGTERM", onSigterm), process.on("SIGHUP", onSighup);
67243
67887
  let close = async () => {
67244
67888
  if (closed) return retirement ?? Promise.resolve();
67245
67889
  closed = !0, removeTemporarySignalHandlers(), ownershipAccepted || await retireOnce();
@@ -67270,6 +67914,20 @@ async function runWithAdmittedSessionOwnership(args) {
67270
67914
  let ownership = createAdmittedSessionOwnershipController(args.appsyncClient);
67271
67915
  return ownership.onAdmitted(args.session), ownership.run(args.launch);
67272
67916
  }
67917
+ var ORCHESTRATION_ORPHAN_SWEEP_STALE_THRESHOLD_MS = 600 * 1e3;
67918
+ async function triggerStartupOrphanSweep(appsyncClient, currentSessionId, staleThresholdMs = ORCHESTRATION_ORPHAN_SWEEP_STALE_THRESHOLD_MS) {
67919
+ try {
67920
+ return await appsyncClient.sweepOrphanSessions({
67921
+ agentType: "CODEVIBE" /* CODEVIBE */,
67922
+ staleThresholdMs,
67923
+ excludeSessionIds: [currentSessionId]
67924
+ });
67925
+ } catch (err) {
67926
+ return logger.warn("[cli] startup orphan sweep failed (non-fatal)", {
67927
+ error: err?.message
67928
+ }), 0;
67929
+ }
67930
+ }
67273
67931
  async function runCompanion(args) {
67274
67932
  let { runCompanionMode: runCompanionMode2 } = (init_companion_mode(), __toCommonJS(companion_mode_exports));
67275
67933
  return runCompanionMode2({
@@ -67601,7 +68259,7 @@ ${err.message}
67601
68259
  `), 1;
67602
68260
  }
67603
68261
  let session = resolved.session;
67604
- return admissionOwnership.run(async (acceptSessionLifecycleOwnership) => {
68262
+ return triggerStartupOrphanSweep(appsyncClient, session.sessionId), admissionOwnership.run(async (acceptSessionLifecycleOwnership) => {
67605
68263
  let emitter = createShellEventEmitter(appsyncClient, session), localPlanner = await buildLocalGemmaPlannerAdapter({
67606
68264
  state: localModelState
67607
68265
  });
@@ -68033,11 +68691,14 @@ function mapTeamSourceToTypedEvent(sessionId, md) {
68033
68691
  }
68034
68692
  case "team_group_resolved": {
68035
68693
  let outcome = teamMetaStr(md, "outcome");
68036
- return outcome ? { sessionId, type: "TEAM_GROUP_RESOLVED", source: "DESKTOP", isEncrypted: !0, metadata: {
68694
+ if (!outcome) return null;
68695
+ let completedAt = typeof md.completed_at == "string" ? md.completed_at : typeof md.completedAt == "string" ? md.completedAt : void 0, metadata = {
68037
68696
  team_event: "group_resolved",
68038
68697
  task_group_id: taskGroupId,
68039
- outcome
68040
- } } : null;
68698
+ outcome,
68699
+ ...completedAt ? { completed_at: completedAt } : {}
68700
+ };
68701
+ return { sessionId, type: "TEAM_GROUP_RESOLVED", source: "DESKTOP", isEncrypted: !0, metadata };
68041
68702
  }
68042
68703
  // NB TEAM_DECOMPOSED is emitted DIRECTLY at the launch layer
68043
68704
  // (launchTeamFromWorkItems, index.ts) which has no `leEmitShellEvent`, so it
@@ -68644,6 +69305,7 @@ require.main === module && main(process.argv.slice(2)).then(
68644
69305
  CliEntitlementError,
68645
69306
  CliUsageError,
68646
69307
  LocalModelPlannerUnavailableAdapter,
69308
+ ORCHESTRATION_ORPHAN_SWEEP_STALE_THRESHOLD_MS,
68647
69309
  OrchestrationSessionBootstrapError,
68648
69310
  PlannerUnavailableError,
68649
69311
  bridgeAuthorityErrorToShellRefusal,
@@ -68669,5 +69331,6 @@ require.main === module && main(process.argv.slice(2)).then(
68669
69331
  resumeOrCreateSession,
68670
69332
  runAuditCli,
68671
69333
  runModelCli,
68672
- runWithAdmittedSessionOwnership
69334
+ runWithAdmittedSessionOwnership,
69335
+ triggerStartupOrphanSweep
68673
69336
  });