@quantiya/codevibe-claude-plugin 2.0.23 → 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 (24) 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/index.js +289 -289
  6. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/__tests__/quorum-loop-outcome-recency.test.d.ts +1 -0
  7. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/cli.d.ts +7 -0
  8. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/cli.js +388 -121
  9. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/quorum-loop.d.ts +4 -0
  10. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/types.d.ts +24 -0
  11. package/node_modules/@quantiya/codevibe-core/dist/ordinal-presentation.d.ts +4 -0
  12. package/node_modules/@quantiya/codevibe-core/dist/ordinal-presentation.test.d.ts +1 -0
  13. package/node_modules/@quantiya/codevibe-core/package.json +1 -1
  14. package/package.json +2 -2
  15. package/node_modules/fs-ext/build/Makefile +0 -347
  16. package/node_modules/fs-ext/build/Release/.deps/Release/fs_ext.node.d +0 -1
  17. package/node_modules/fs-ext/build/Release/.deps/Release/obj.target/fs_ext/fs-ext.o.d +0 -165
  18. package/node_modules/fs-ext/build/Release/fs_ext.node +0 -0
  19. package/node_modules/fs-ext/build/Release/obj.target/fs_ext/fs-ext.o +0 -0
  20. package/node_modules/fs-ext/build/binding.Makefile +0 -6
  21. package/node_modules/fs-ext/build/config.gypi +0 -503
  22. package/node_modules/fs-ext/build/fs_ext.target.mk +0 -183
  23. package/node_modules/fs-ext/build/gyp-mac-tool +0 -768
  24. 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":
@@ -24679,7 +24759,7 @@ function buildReviewerVerdictModel(payload) {
24679
24759
  let decisionRaw = asString(verdict.verdict);
24680
24760
  if (decisionRaw === null) return null;
24681
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);
24682
- 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);
24683
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") : [];
24684
24764
  return { decisionLabel, provenance, reasoning, suggestedChanges };
24685
24765
  }
@@ -44273,7 +44353,8 @@ var LocalExecutorImpl = class {
44273
44353
  metadata: {
44274
44354
  source: "team_group_resolved",
44275
44355
  task_group_id: taskGroupId,
44276
- outcome
44356
+ outcome,
44357
+ completed_at: (/* @__PURE__ */ new Date()).toISOString()
44277
44358
  }
44278
44359
  }),
44279
44360
  "team_group_resolved"
@@ -52508,7 +52589,16 @@ var QuorumLoop = class _QuorumLoop {
52508
52589
  * first promote/discard.
52509
52590
  */
52510
52591
  getLastWorkspaceOutcome() {
52511
- 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
+ };
52512
52602
  }
52513
52603
  /**
52514
52604
  * Session-owned Class-B ingress. The subscription calls this wrapper instead
@@ -54139,7 +54229,11 @@ var QuorumLoop = class _QuorumLoop {
54139
54229
  err: err.message
54140
54230
  });
54141
54231
  } finally {
54142
- 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(
54143
54237
  {
54144
54238
  phase: "discarded",
54145
54239
  ...terminalDiscard && opts?.taskContinues !== !0 ? { taskId } : {}
@@ -54248,7 +54342,11 @@ var QuorumLoop = class _QuorumLoop {
54248
54342
  err: discardErr.message
54249
54343
  });
54250
54344
  }
54251
- 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");
54252
54350
  return;
54253
54351
  }
54254
54352
  if (this.teamRunIsHalted(teamAuthority)) return;
@@ -54282,7 +54380,11 @@ var QuorumLoop = class _QuorumLoop {
54282
54380
  err: discardErr.message
54283
54381
  });
54284
54382
  }
54285
- 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");
54286
54388
  return;
54287
54389
  }
54288
54390
  this.reviewedSnapshotByTask.set(taskId, reviewedSnapshot);
@@ -54312,7 +54414,11 @@ var QuorumLoop = class _QuorumLoop {
54312
54414
  err: discardErr.message
54313
54415
  });
54314
54416
  }
54315
- 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");
54316
54422
  return;
54317
54423
  }
54318
54424
  }
@@ -54467,7 +54573,12 @@ var QuorumLoop = class _QuorumLoop {
54467
54573
  }), logger.info("[QuorumLoop] shadow promoted to real tree", {
54468
54574
  taskId,
54469
54575
  promoted: appliedPaths.length
54470
- }), 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, {
54471
54582
  kind: "verdict",
54472
54583
  author: { role: "engine" },
54473
54584
  sensitivity: "user",
@@ -57142,7 +57253,7 @@ function seatKey(gateId, seatId) {
57142
57253
  return `${gateId}::${seatId}`;
57143
57254
  }
57144
57255
  function seatLabel(agentKind, seatId) {
57145
- return `${REVIEWER_AGENT_DISPLAY_NAME[agentKind] ?? agentKind} (seat ${seatId})`;
57256
+ return `${REVIEWER_AGENT_DISPLAY_NAME[agentKind] ?? agentKind} (seat ${displayOrdinal(seatId)})`;
57146
57257
  }
57147
57258
  function diffCapturedProgress(round, files) {
57148
57259
  let created = 0, modified = 0, deleted = 0;
@@ -58110,7 +58221,7 @@ async function runOrchestrationShell(args) {
58110
58221
  });
58111
58222
  }
58112
58223
  workspaceTerminalCoordinator && await workspaceTerminalCoordinator.replayPending(), processMarkers();
58113
- 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) => {
58114
58225
  if (!durableDecisionEffectDispatcher) {
58115
58226
  pendingDurableResolutions.set(resolution.eventId, resolution);
58116
58227
  return;
@@ -58246,6 +58357,16 @@ async function runOrchestrationShell(args) {
58246
58357
  text: "Gate history recovery is unavailable. Existing gate actions remain fail-closed until recovery succeeds."
58247
58358
  })) : rejectInitialSubscription(error);
58248
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;
58249
58370
  }
58250
58371
  }
58251
58372
  );
@@ -58488,7 +58609,7 @@ async function runOrchestrationShell(args) {
58488
58609
  if (admitted.length === 0) return;
58489
58610
  await Promise.allSettled(admitted);
58490
58611
  }
58491
- }, 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(
58492
58613
  new Error(
58493
58614
  `${message}: ${reason instanceof Error ? reason.message : String(reason)}`
58494
58615
  ),
@@ -58559,7 +58680,7 @@ async function runOrchestrationShell(args) {
58559
58680
  shutdownFailure("session heartbeat stop failed", reason)
58560
58681
  );
58561
58682
  }
58562
- }, hostedRetirementMutationStarted = !1, retireHostedSession = async () => {
58683
+ }, retireHostedSession = async () => {
58563
58684
  await hostedSessionIngressFenceDrain;
58564
58685
  let failures = [...hostedSessionIngressFenceFailures];
58565
58686
  try {
@@ -58596,11 +58717,11 @@ async function runOrchestrationShell(args) {
58596
58717
  new Error("final ContextItems publication did not converge"),
58597
58718
  { causes: failures }
58598
58719
  );
58599
- }, workspaceResourceRetirement = null, plannerTeardown = createBoundedSharedShutdownOperation({
58720
+ }, workspaceResourceRetirement = null, teardownDeadlineExpired = !1, plannerTeardown = createBoundedSharedShutdownOperation({
58600
58721
  normalTimeoutMs: NORMAL_SHUTDOWN_TIMEOUT_MS,
58601
58722
  signalTimeoutMs: SIGNAL_SHUTDOWN_TIMEOUT_MS,
58602
58723
  onTimeout: (deadlineClass, timeoutMs) => {
58603
- logger.warn("[orchestration-shell] planner teardown timed out", {
58724
+ teardownDeadlineExpired = !0, logger.warn("[orchestration-shell] planner teardown timed out", {
58604
58725
  deadlineClass,
58605
58726
  timeoutMs
58606
58727
  });
@@ -58667,47 +58788,75 @@ async function runOrchestrationShell(args) {
58667
58788
  });
58668
58789
  }
58669
58790
  }), runPlannerTeardown = async (deadlineClass = "normal") => {
58670
- let boundedCompletion = plannerTeardown.wait(deadlineClass);
58671
- if (deadlineClass === "signal") return boundedCompletion;
58672
- let requiredRetirement = workspaceResourceRetirement;
58791
+ let boundedCompletion = plannerTeardown.wait(deadlineClass), requiredRetirement = workspaceResourceRetirement;
58673
58792
  if (!requiredRetirement)
58674
58793
  throw new Error("workspace retirement boundary was not published");
58675
58794
  let [boundedResult] = await Promise.allSettled([boundedCompletion]);
58676
- 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`
58677
58796
  // before it could issue its mutation. If the ordered mutation already
58678
58797
  // started (in flight when the deadline expired, or settled with the
58679
58798
  // deadline expiring on a parallel phase such as the planner flush), the
58680
58799
  // last-ditch duplicate is redundant — skip it.
58681
- !hostedRetirementMutationStarted) {
58682
- let timer;
58683
- try {
58684
- await Promise.race([
58685
- args.appsyncClient.updateSession({
58686
- sessionId: args.session.sessionId,
58687
- status: "INACTIVE" /* INACTIVE */
58688
- }).then(() => {
58689
- logger.warn(
58690
- "[orchestration-shell] last-ditch session retirement landed after teardown timeout",
58691
- { sessionId: args.session.sessionId }
58692
- );
58693
- }).catch((reason) => {
58694
- logger.warn("[orchestration-shell] last-ditch session retirement failed", {
58695
- sessionId: args.session.sessionId,
58696
- errorName: reason?.name ?? "Error"
58697
- });
58698
- }),
58699
- new Promise((resolve21) => {
58700
- timer = setTimeout(resolve21, SESSION_RETIRE_TIMEOUT_MS2), timer.unref?.();
58701
- })
58702
- ]);
58703
- } finally {
58704
- 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;
58705
58830
  }
58706
- }
58707
- let [retirementResult] = await Promise.allSettled([requiredRetirement]);
58831
+ let [retirementResult] = await Promise.allSettled(
58832
+ deadlineClass === "signal" && teardownDeadlineExpired ? [] : [requiredRetirement]
58833
+ );
58708
58834
  if (boundedResult.status === "rejected") throw boundedResult.reason;
58709
- if (retirementResult.status === "rejected") throw retirementResult.reason;
58710
- }, 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) => {
58711
58860
  signalShutdownCompletion || (signalShutdownStarted = !0, disableBracketedPaste(), signalShutdownCompletion = Promise.allSettled([runPlannerTeardown("signal")]).then(() => {
58712
58861
  }).finally(() => {
58713
58862
  logger.info(`[orchestration-shell] caught ${sig}, teardown complete`);
@@ -58726,7 +58875,7 @@ async function runOrchestrationShell(args) {
58726
58875
  try {
58727
58876
  process.kill(process.pid, sig);
58728
58877
  } catch {
58729
- let code = sig === "SIGTERM" ? 143 : 130;
58878
+ let code = sig === "SIGTERM" ? 143 : sig === "SIGHUP" ? 129 : 130;
58730
58879
  process.exit(code);
58731
58880
  }
58732
58881
  }));
@@ -58938,14 +59087,19 @@ async function runOrchestrationShell(args) {
58938
59087
  // land in the active conversation. ABSENT on a non-team session.
58939
59088
  ...args.groupDecisionDeps ? { groupDecision: { ...args.groupDecisionDeps, store } } : {}
58940
59089
  };
58941
- 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");
58942
59091
  try {
58943
59092
  if (signalShutdownCompletion) {
58944
59093
  await signalShutdownCompletion;
58945
59094
  return;
58946
59095
  }
59096
+ if (sessionRetiredExitRequested)
59097
+ return;
58947
59098
  if (args.acceptSessionLifecycleOwnership?.(), !isInteractiveTty()) {
58948
- nonTtyAbort = new AbortController();
59099
+ if (nonTtyAbort = new AbortController(), sessionRetiredExitRequested) {
59100
+ nonTtyAbort = null;
59101
+ return;
59102
+ }
58949
59103
  try {
58950
59104
  await runLineLogFallback({
58951
59105
  store,
@@ -58973,6 +59127,8 @@ async function runOrchestrationShell(args) {
58973
59127
  await signalShutdownCompletion;
58974
59128
  return;
58975
59129
  }
59130
+ if (sessionRetiredExitRequested)
59131
+ return;
58976
59132
  let { ink } = getInkRuntime(), { waitUntilExit, unmount } = ink.render(
58977
59133
  React20.createElement(OrchestrationApp, {
58978
59134
  store,
@@ -59023,19 +59179,21 @@ async function runOrchestrationShell(args) {
59023
59179
  { stdout: process.stdout }
59024
59180
  );
59025
59181
  inkUnmount = unmount;
59026
- let explicitExit = createExplicitTtyExitCoordinator({
59182
+ let ttyExplicitExit = createExplicitTtyExitCoordinator({
59027
59183
  runTeardown: runPlannerTeardown,
59028
59184
  unmount: () => {
59029
59185
  inkUnmount && (inkUnmount(), inkUnmount = null);
59030
59186
  }
59031
- }), unsubscribe = store.subscribe((state) => {
59187
+ });
59188
+ explicitExit = ttyExplicitExit, sessionRetiredExitRequested && ttyExplicitExit.start();
59189
+ let unsubscribe = store.subscribe((state) => {
59032
59190
  let last = state.conversation[state.conversation.length - 1];
59033
- last && last.kind === "slash-output" && isExitCommand(last.command) && explicitExit.start();
59191
+ last && last.kind === "slash-output" && isExitCommand(last.command) && ttyExplicitExit.start();
59034
59192
  });
59035
59193
  try {
59036
- await Promise.race([waitUntilExit(), explicitExit.completion]);
59194
+ await Promise.race([waitUntilExit(), ttyExplicitExit.completion]);
59037
59195
  } finally {
59038
- unsubscribe(), inkUnmount = null;
59196
+ unsubscribe(), inkUnmount = null, explicitExit = null;
59039
59197
  }
59040
59198
  } finally {
59041
59199
  try {
@@ -59592,8 +59750,8 @@ function routeTeamShellEventToStore(store, evt) {
59592
59750
  return;
59593
59751
  }
59594
59752
  if (source === "task_group_halted") {
59595
- let haltReason = typeof md.haltReason == "string" ? md.haltReason : "halted";
59596
- 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 } : {} });
59597
59755
  let team = store.getState().team;
59598
59756
  if (team)
59599
59757
  for (let [trackIndex, t] of team.tracks)
@@ -59639,8 +59797,12 @@ function routeTeamShellEventToStore(store, evt) {
59639
59797
  if (!team || team.groupResolved) return;
59640
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");
59641
59799
  if (!allTerminal || !anyFailed) return;
59642
- 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(", ")}`;
59643
- 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({
59644
59806
  type: "SHELL_ADVISORY",
59645
59807
  source: "shell",
59646
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.`
@@ -59650,8 +59812,12 @@ function routeTeamShellEventToStore(store, evt) {
59650
59812
  if (source === "team_group_resolved") {
59651
59813
  let outcome = md.outcome;
59652
59814
  if (typeof outcome != "string" || outcome.length === 0) return;
59653
- let priorTeam = store.getState().team, alreadyResolvedSame = priorTeam?.groupResolved === !0 && priorTeam.outcome === outcome;
59654
- 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;
59655
59821
  let text2 = outcome === "complete" ? "Agent Teams: Team complete" : `Agent Teams: Team halted \u2014 ${outcome}`;
59656
59822
  store.dispatch({ type: "SHELL_ADVISORY", source: "shell", text: text2 });
59657
59823
  return;
@@ -62834,6 +63000,11 @@ var GATE_PROMPT_KIND_LABEL = {
62834
63000
  orchestration_escalated_gate: "escalated review gate",
62835
63001
  continuation_offer_handoff: "continuation handoff offer"
62836
63002
  };
63003
+ function parseIsoTimestamp(iso) {
63004
+ if (!iso) return 0;
63005
+ let t = Date.parse(iso);
63006
+ return Number.isFinite(t) ? t : 0;
63007
+ }
62837
63008
  function buildStatusSummary(state, quorumLoop) {
62838
63009
  let lines = [];
62839
63010
  if (state.team) {
@@ -62865,7 +63036,80 @@ function buildStatusSummary(state, quorumLoop) {
62865
63036
  `Awaiting your decision: ${label} for task ${entry.envelope.taskId} \u2014 reply with an option number (1-${entry.envelope.options.length})${queued}.`
62866
63037
  );
62867
63038
  }
62868
- 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
+ }
62869
63113
  if (outcome && outcome.kind === "promoted") {
62870
63114
  let n = outcome.files.length;
62871
63115
  n > 0 ? lines.push(
@@ -67619,13 +67863,14 @@ function createAdmittedSessionOwnershipController(appsyncClient) {
67619
67863
  appsyncClient,
67620
67864
  admittedSession.sessionId
67621
67865
  ), retirement) : Promise.resolve(), removeTemporarySignalHandlers = () => {
67622
- process.removeListener("SIGINT", onSigint), process.removeListener("SIGTERM", onSigterm);
67866
+ process.removeListener("SIGINT", onSigint), process.removeListener("SIGTERM", onSigterm), process.removeListener("SIGHUP", onSighup);
67623
67867
  }, handleSignal = (signal) => {
67624
- signalReceived || ownershipAccepted || closed || (signalReceived = !0, removeTemporarySignalHandlers(), retireOnce().finally(() => {
67868
+ signalReceived || ownershipAccepted || closed || (signalReceived = !0, retireOnce().finally(() => {
67869
+ removeTemporarySignalHandlers();
67625
67870
  try {
67626
67871
  process.kill(process.pid, signal);
67627
67872
  } catch {
67628
- process.exit(signal === "SIGTERM" ? 143 : 130);
67873
+ process.exit(signal === "SIGTERM" ? 143 : signal === "SIGHUP" ? 129 : 130);
67629
67874
  }
67630
67875
  }));
67631
67876
  };
@@ -67635,7 +67880,10 @@ function createAdmittedSessionOwnershipController(appsyncClient) {
67635
67880
  function onSigterm() {
67636
67881
  handleSignal("SIGTERM");
67637
67882
  }
67638
- 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);
67639
67887
  let close = async () => {
67640
67888
  if (closed) return retirement ?? Promise.resolve();
67641
67889
  closed = !0, removeTemporarySignalHandlers(), ownershipAccepted || await retireOnce();
@@ -67666,6 +67914,20 @@ async function runWithAdmittedSessionOwnership(args) {
67666
67914
  let ownership = createAdmittedSessionOwnershipController(args.appsyncClient);
67667
67915
  return ownership.onAdmitted(args.session), ownership.run(args.launch);
67668
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
+ }
67669
67931
  async function runCompanion(args) {
67670
67932
  let { runCompanionMode: runCompanionMode2 } = (init_companion_mode(), __toCommonJS(companion_mode_exports));
67671
67933
  return runCompanionMode2({
@@ -67997,7 +68259,7 @@ ${err.message}
67997
68259
  `), 1;
67998
68260
  }
67999
68261
  let session = resolved.session;
68000
- return admissionOwnership.run(async (acceptSessionLifecycleOwnership) => {
68262
+ return triggerStartupOrphanSweep(appsyncClient, session.sessionId), admissionOwnership.run(async (acceptSessionLifecycleOwnership) => {
68001
68263
  let emitter = createShellEventEmitter(appsyncClient, session), localPlanner = await buildLocalGemmaPlannerAdapter({
68002
68264
  state: localModelState
68003
68265
  });
@@ -68429,11 +68691,14 @@ function mapTeamSourceToTypedEvent(sessionId, md) {
68429
68691
  }
68430
68692
  case "team_group_resolved": {
68431
68693
  let outcome = teamMetaStr(md, "outcome");
68432
- 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 = {
68433
68696
  team_event: "group_resolved",
68434
68697
  task_group_id: taskGroupId,
68435
- outcome
68436
- } } : null;
68698
+ outcome,
68699
+ ...completedAt ? { completed_at: completedAt } : {}
68700
+ };
68701
+ return { sessionId, type: "TEAM_GROUP_RESOLVED", source: "DESKTOP", isEncrypted: !0, metadata };
68437
68702
  }
68438
68703
  // NB TEAM_DECOMPOSED is emitted DIRECTLY at the launch layer
68439
68704
  // (launchTeamFromWorkItems, index.ts) which has no `leEmitShellEvent`, so it
@@ -69040,6 +69305,7 @@ require.main === module && main(process.argv.slice(2)).then(
69040
69305
  CliEntitlementError,
69041
69306
  CliUsageError,
69042
69307
  LocalModelPlannerUnavailableAdapter,
69308
+ ORCHESTRATION_ORPHAN_SWEEP_STALE_THRESHOLD_MS,
69043
69309
  OrchestrationSessionBootstrapError,
69044
69310
  PlannerUnavailableError,
69045
69311
  bridgeAuthorityErrorToShellRefusal,
@@ -69065,5 +69331,6 @@ require.main === module && main(process.argv.slice(2)).then(
69065
69331
  resumeOrCreateSession,
69066
69332
  runAuditCli,
69067
69333
  runModelCli,
69068
- runWithAdmittedSessionOwnership
69334
+ runWithAdmittedSessionOwnership,
69335
+ triggerStartupOrphanSweep
69069
69336
  });