orkestrator 2.34.4-beta → 2.34.5-beta

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.
@@ -3752,6 +3752,66 @@ function structuredOutputFailure(provider, code, message, options = {}) {
3752
3752
  };
3753
3753
  }
3754
3754
 
3755
+ // ../protocol/src/agent-platforms.ts
3756
+ var AGENT_PLATFORMS = Object.freeze([
3757
+ "claude",
3758
+ "codex",
3759
+ "cursor",
3760
+ "grok",
3761
+ "opencode",
3762
+ "pi"
3763
+ ]);
3764
+ var AGENT_PLATFORM_LABELS = Object.freeze({
3765
+ claude: "Claude Code",
3766
+ codex: "Codex",
3767
+ cursor: "Cursor Agent",
3768
+ grok: "Grok Build",
3769
+ opencode: "OpenCode",
3770
+ pi: "Pi"
3771
+ });
3772
+ var LEGACY_ENABLED_AGENT_PLATFORMS = Object.freeze([
3773
+ "claude",
3774
+ "codex",
3775
+ "opencode"
3776
+ ]);
3777
+
3778
+ // ../protocol/src/native-agent-goal.ts
3779
+ var NATIVE_AGENT_GOAL_LIMITS = Object.freeze({
3780
+ objectiveMaxChars: 4000,
3781
+ checkMaxBytes: 4 * 1024,
3782
+ reportMessageMaxBytes: 2 * 1024,
3783
+ checkOutputMaxBytes: 8 * 1024,
3784
+ statusDetailMaxBytes: 1024,
3785
+ maxIterationsDefault: 20,
3786
+ maxIterationsLimit: 100,
3787
+ noProgressLimit: 3,
3788
+ checkTimeoutDefaultMs: 10 * 60000,
3789
+ checkTimeoutMaxMs: 30 * 60000,
3790
+ verifierModelMaxChars: 200,
3791
+ verifierTimeoutDefaultMs: 30 * 60000,
3792
+ verdictSummaryMaxBytes: 2 * 1024,
3793
+ verdictReasonsMax: 16,
3794
+ verdictReasonMaxBytes: 512,
3795
+ historyLimit: 5
3796
+ });
3797
+ var AGENT_GOAL_TOOL_NAMES = Object.freeze(["report_goal"]);
3798
+ var GOAL_QUALIFIED_PLATFORMS = Object.freeze([
3799
+ "claude",
3800
+ "codex",
3801
+ "opencode",
3802
+ "cursor",
3803
+ "grok",
3804
+ "pi"
3805
+ ]);
3806
+ var GOAL_TOKEN_BUDGET_PLATFORMS = Object.freeze([
3807
+ "claude",
3808
+ "codex",
3809
+ "opencode",
3810
+ "cursor",
3811
+ "grok",
3812
+ "pi"
3813
+ ]);
3814
+
3755
3815
  // ../protocol/src/permission-rules.ts
3756
3816
  var PERMISSION_RULE_KINDS = [
3757
3817
  "command-prefix",
@@ -4153,29 +4213,6 @@ var JOURNAL_ENVELOPE_BYTES = serializedBytes({
4153
4213
  entries: []
4154
4214
  });
4155
4215
 
4156
- // ../protocol/src/agent-platforms.ts
4157
- var AGENT_PLATFORMS = Object.freeze([
4158
- "claude",
4159
- "codex",
4160
- "cursor",
4161
- "grok",
4162
- "opencode",
4163
- "pi"
4164
- ]);
4165
- var AGENT_PLATFORM_LABELS = Object.freeze({
4166
- claude: "Claude Code",
4167
- codex: "Codex",
4168
- cursor: "Cursor Agent",
4169
- grok: "Grok Build",
4170
- opencode: "OpenCode",
4171
- pi: "Pi"
4172
- });
4173
- var LEGACY_ENABLED_AGENT_PLATFORMS = Object.freeze([
4174
- "claude",
4175
- "codex",
4176
- "opencode"
4177
- ]);
4178
-
4179
4216
  // ../protocol/src/native-agent-transcript-patch.ts
4180
4217
  var FORBIDDEN_KEYS = new Set(["__proto__", "constructor", "prototype"]);
4181
4218
 
@@ -4542,6 +4579,7 @@ var PROJECTION_FIELD_SET = new Set([
4542
4579
  "queue",
4543
4580
  "asyncQuestionResponses",
4544
4581
  "contextUsage",
4582
+ "goal",
4545
4583
  "rateLimits",
4546
4584
  "runtime",
4547
4585
  "runtimeHealthAuthoritative",
@@ -4566,6 +4604,7 @@ var OPTIONAL_PROJECTION_FIELD_SET = new Set([
4566
4604
  "queue",
4567
4605
  "asyncQuestionResponses",
4568
4606
  "contextUsage",
4607
+ "goal",
4569
4608
  "rateLimits",
4570
4609
  "runtime",
4571
4610
  "runtimeHealthAuthoritative",
@@ -5522,7 +5561,7 @@ function parseSessionActivityObservation(value) {
5522
5561
  if (!isRecord(value))
5523
5562
  return;
5524
5563
  const { activity, readyForInput, completionPending, asyncQuestionItemIds, closing } = value;
5525
- const { contentRevision } = value;
5564
+ const { contentRevision, turnFailed } = value;
5526
5565
  if (typeof activity !== "string" || !SESSION_ACTIVITY_STATES.has(activity))
5527
5566
  return;
5528
5567
  if (readyForInput !== undefined && typeof readyForInput !== "boolean")
@@ -5531,6 +5570,8 @@ function parseSessionActivityObservation(value) {
5531
5570
  return;
5532
5571
  if (closing !== undefined && typeof closing !== "boolean")
5533
5572
  return;
5573
+ if (turnFailed !== undefined && typeof turnFailed !== "boolean")
5574
+ return;
5534
5575
  if (contentRevision !== undefined && !isContentRevision(contentRevision))
5535
5576
  return;
5536
5577
  let itemIds;
@@ -5546,6 +5587,7 @@ function parseSessionActivityObservation(value) {
5546
5587
  ...completionPending !== undefined ? { completionPending } : {},
5547
5588
  ...itemIds !== undefined ? { asyncQuestionItemIds: itemIds } : {},
5548
5589
  ...closing !== undefined ? { closing } : {},
5590
+ ...turnFailed === true ? { turnFailed } : {},
5549
5591
  ...contentRevision !== undefined ? { contentRevision } : {}
5550
5592
  };
5551
5593
  }
@@ -12074,6 +12116,7 @@ function unconfirmedTurnId(requestId) {
12074
12116
  class TurnAccumulator {
12075
12117
  threadId;
12076
12118
  requestId;
12119
+ providerRequestId;
12077
12120
  expectsStructuredOutput;
12078
12121
  assistantMessageId;
12079
12122
  startedAt;
@@ -12083,6 +12126,7 @@ class TurnAccumulator {
12083
12126
  phase = "starting";
12084
12127
  cancelRequested = false;
12085
12128
  stoppedByShutdown = false;
12129
+ capacityRetry;
12086
12130
  error;
12087
12131
  finalDiff;
12088
12132
  completedAt;
@@ -12362,12 +12406,22 @@ class TurnAccumulator {
12362
12406
  if (error)
12363
12407
  this.error = error;
12364
12408
  this.completedAt = new Date().toISOString();
12409
+ this.settleEndedAttempt();
12410
+ }
12411
+ settleEndedAttempt() {
12365
12412
  this.interruptOpenAgentMessages();
12366
12413
  for (const accumulator of this.items.values()) {
12367
12414
  if (!accumulator.completed && !accumulator.interrupted)
12368
12415
  this.touch(accumulator);
12369
12416
  }
12370
12417
  }
12418
+ recordRetryCard(id, text, state) {
12419
+ this.onItemCompleted({ id, type: "retry", text, state });
12420
+ }
12421
+ retryCardState(id) {
12422
+ const item = this.items.get(id)?.item;
12423
+ return item?.type === "retry" ? item.state : undefined;
12424
+ }
12371
12425
  recordProviderFailure(error) {
12372
12426
  this.onItemCompleted({
12373
12427
  id: turnFailureItemId(this.turnId),
@@ -12820,6 +12874,8 @@ async function itemToParts(item, cwd, fileChangeContext) {
12820
12874
  ...item.severity ? { severity: item.severity } : {}
12821
12875
  }
12822
12876
  ];
12877
+ case "retry":
12878
+ return [{ type: "retry", content: item.text, toolState: item.state }];
12823
12879
  default: {
12824
12880
  const unknown = item;
12825
12881
  const name = typeof unknown.type === "string" ? unknown.type : "unknown";
@@ -13012,16 +13068,19 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
13012
13068
  "add_ticket_comment",
13013
13069
  "address_multi_review",
13014
13070
  "adopt_workflow",
13071
+ "answer_worker_question",
13015
13072
  "cancel_build_pipeline",
13016
13073
  "cancel_graph_run",
13017
13074
  "cancel_multi_review",
13018
13075
  "cancel_schedule",
13076
+ "clear_worker_goal",
13019
13077
  "compare_project_files",
13020
13078
  "create_pr",
13021
13079
  "create_schedule",
13022
13080
  "create_ticket",
13023
13081
  "custom_fix_multi_review",
13024
13082
  "edit_worker_tab",
13083
+ "fork_worker",
13025
13084
  "get_build_pipeline",
13026
13085
  "get_environment",
13027
13086
  "get_feature_plan",
@@ -13034,6 +13093,9 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
13034
13093
  "get_tab_state",
13035
13094
  "get_tab_transcript",
13036
13095
  "get_ticket",
13096
+ "get_usage_limits",
13097
+ "get_worker_changes",
13098
+ "get_worker_goal",
13037
13099
  "launch_environment",
13038
13100
  "launch_feature_plan",
13039
13101
  "launch_graph",
@@ -13048,14 +13110,18 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
13048
13110
  "list_node_environments",
13049
13111
  "list_nodes",
13050
13112
  "list_projects",
13113
+ "list_provider_accounts",
13051
13114
  "list_schedules",
13052
13115
  "list_tabs",
13053
13116
  "list_tickets",
13117
+ "list_worker_commands",
13054
13118
  "list_workflows",
13055
13119
  "open_multi_review",
13056
13120
  "open_multi_review_fix",
13057
13121
  "open_worker_browser",
13058
13122
  "pause_build_pipeline",
13123
+ "pause_schedule",
13124
+ "pause_worker_goal",
13059
13125
  "propose_agents_md_update",
13060
13126
  "read_messages",
13061
13127
  "read_node_environment",
@@ -13064,18 +13130,25 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
13064
13130
  "request_project_file_update",
13065
13131
  "request_project_on_node",
13066
13132
  "request_root_changes",
13133
+ "resolve_conflicts",
13067
13134
  "resume_build_pipeline",
13135
+ "resume_schedule",
13136
+ "run_worker_command",
13068
13137
  "send_message",
13069
13138
  "send_node_message",
13070
13139
  "send_prompt_to_node_tab",
13071
13140
  "send_prompt_to_tab",
13141
+ "set_worker_goal",
13072
13142
  "start_build_pipeline",
13073
13143
  "start_environment",
13074
13144
  "start_multi_review",
13075
13145
  "start_node_environment",
13076
13146
  "stop_environment",
13077
13147
  "stop_node_environment",
13148
+ "stop_worker_turn",
13149
+ "switch_provider_account",
13078
13150
  "transfer_files",
13151
+ "update_schedule",
13079
13152
  "update_ticket"
13080
13153
  ]);
13081
13154
  var COORDINATOR_CONTEXT_OPEN_TAG = "<orkestrator-coordinator-context>";
@@ -13089,6 +13162,78 @@ function stripCoordinatorContext(text) {
13089
13162
  return text.slice(end + COORDINATOR_CONTEXT_CLOSE_TAG.length).replace(/^\s+/, "");
13090
13163
  }
13091
13164
 
13165
+ // ../protocol/src/capacity-retry.ts
13166
+ var DEFAULT_CAPACITY_RETRY_POLICY = {
13167
+ maxAttempts: 5,
13168
+ baseDelayMs: 2000,
13169
+ multiplier: 3,
13170
+ maxDelayMs: 60000
13171
+ };
13172
+ function capacityRetryDelayMs(policy, nextAttempt, random = Math.random) {
13173
+ const retryIndex = Math.max(0, nextAttempt - 2);
13174
+ const nominal = Math.min(policy.maxDelayMs, policy.baseDelayMs * policy.multiplier ** retryIndex);
13175
+ const unit = Math.min(1, Math.max(0, random()));
13176
+ return Math.round(nominal / 2 + nominal / 2 * unit);
13177
+ }
13178
+ function formatRetryDelay(delayMs) {
13179
+ return `${Math.max(1, Math.round(delayMs / 1000))} s`;
13180
+ }
13181
+
13182
+ // ../../bridges/codex-bridge/src/sessions/capacity-retry.ts
13183
+ var CODEX_SERVER_OVERLOADED_MESSAGE = "Selected model is at capacity. Please try a different model.";
13184
+ var CODEX_POLICY_CAPACITY_UNAVAILABLE_MESSAGE = "Policy-restricted capacity is unavailable. Please try again later.";
13185
+ var CAPACITY_CODES = new Set(["serverOverloaded", "policyCapacityUnavailable"]);
13186
+ var CAPACITY_MESSAGES = new Set([
13187
+ CODEX_SERVER_OVERLOADED_MESSAGE,
13188
+ CODEX_POLICY_CAPACITY_UNAVAILABLE_MESSAGE
13189
+ ]);
13190
+ function isModelCapacityError(error) {
13191
+ if (!error)
13192
+ return false;
13193
+ if (error.httpStatusCode === 401 || error.httpStatusCode === 403)
13194
+ return false;
13195
+ if (error.code !== undefined)
13196
+ return CAPACITY_CODES.has(error.code);
13197
+ return CAPACITY_MESSAGES.has(error.message.trim());
13198
+ }
13199
+ function capacityRetryWaitingText(delayMs, nextAttempt, maxAttempts) {
13200
+ return `Model at capacity, retrying in ${formatRetryDelay(delayMs)} (attempt ${nextAttempt} of ${maxAttempts})`;
13201
+ }
13202
+ function capacityRetryStartedText(nextAttempt, maxAttempts) {
13203
+ return `Model at capacity, retried automatically (attempt ${nextAttempt} of ${maxAttempts})`;
13204
+ }
13205
+ var CAPACITY_RETRY_CANCELLED_TEXT = "Model at capacity, automatic retry cancelled";
13206
+ var CAPACITY_RETRY_START_FAILED_TEXT = "Model at capacity, automatic retry could not be started";
13207
+ function capacityRetriesExhaustedError(error, attempts) {
13208
+ return {
13209
+ ...error,
13210
+ message: `${error.message} Automatic retries exhausted after ${attempts} attempts.`
13211
+ };
13212
+ }
13213
+ function capacityRetryItemId(requestId, nextAttempt) {
13214
+ return `capacity-retry:${requestId}:${nextAttempt}`;
13215
+ }
13216
+ function capacityRetryClientId(requestId, nextAttempt) {
13217
+ return `${requestId}:capacity-retry:${nextAttempt}`;
13218
+ }
13219
+ var CAPACITY_RETRY_CONTINUATION_MARKER = "[Orkestrator automatic retry]";
13220
+ function capacityRetryContinuationText(nextAttempt, maxAttempts) {
13221
+ return [
13222
+ `${CAPACITY_RETRY_CONTINUATION_MARKER} attempt ${nextAttempt} of ${maxAttempts}.`,
13223
+ "The previous attempt of this turn ended because the model was at capacity. This message was sent automatically, not by the user.",
13224
+ "Continue the original request from where it stopped. Do not repeat work or tool calls that already completed; check the conversation so far and finish the request."
13225
+ ].join(`
13226
+ `);
13227
+ }
13228
+ function parseCapacityRetryContinuation(text) {
13229
+ if (!text.startsWith(CAPACITY_RETRY_CONTINUATION_MARKER))
13230
+ return;
13231
+ const match = /^ attempt (\d+) of (\d+)\./.exec(text.slice(CAPACITY_RETRY_CONTINUATION_MARKER.length));
13232
+ if (!match)
13233
+ return;
13234
+ return { attempt: Number(match[1]), maxAttempts: Number(match[2]) };
13235
+ }
13236
+
13092
13237
  // ../../bridges/codex-bridge/src/history/rollout.ts
13093
13238
  function getCodexHomeDir() {
13094
13239
  return process.env.CODEX_HOME || join11(homedir(), ".codex");
@@ -14126,6 +14271,20 @@ async function replayPersistedRollout(threadId, snapshot, meta, structuredTurns)
14126
14271
  assistantMessage.parts.push({ type: "text", content: displayText });
14127
14272
  return;
14128
14273
  }
14274
+ const continuation = parseCapacityRetryContinuation(text);
14275
+ if (continuation) {
14276
+ const previous = messages.at(-1);
14277
+ const row = previous?.role === "assistant" ? previous : ensureAssistantMessage(timestamp);
14278
+ const last = row.parts.at(-1);
14279
+ if (last?.type === "status" && last.severity === "error")
14280
+ row.parts.pop();
14281
+ row.parts.push({
14282
+ type: "retry",
14283
+ content: capacityRetryStartedText(continuation.attempt, continuation.maxAttempts),
14284
+ toolState: "success"
14285
+ });
14286
+ return;
14287
+ }
14129
14288
  closeAssistantRow();
14130
14289
  messages.push({
14131
14290
  id: nextRowId("user"),
@@ -18650,7 +18809,7 @@ function isPersistedRecord(value) {
18650
18809
  if (!value || typeof value !== "object")
18651
18810
  return false;
18652
18811
  const record = value;
18653
- if (typeof record.requestId !== "string" || record.requestId.length === 0 || typeof record.bridgeSessionId !== "string" || record.threadId !== null && typeof record.threadId !== "string" || !isOptionalString(record.turnId) || !DISPATCH_STATES.has(record.state) || !isTimestamp(record.createdAt) || !isTimestamp(record.updatedAt) || record.quarantined !== undefined && record.quarantined !== true) {
18812
+ if (typeof record.requestId !== "string" || record.requestId.length === 0 || typeof record.bridgeSessionId !== "string" || record.threadId !== null && typeof record.threadId !== "string" || !isOptionalString(record.turnId) || !isOptionalString(record.providerRequestId) || !DISPATCH_STATES.has(record.state) || !isTimestamp(record.createdAt) || !isTimestamp(record.updatedAt) || record.quarantined !== undefined && record.quarantined !== true) {
18654
18813
  return false;
18655
18814
  }
18656
18815
  if (record.state === "accepted") {
@@ -18858,6 +19017,21 @@ class DispatchJournal {
18858
19017
  await this.flush({ failClosed: true });
18859
19018
  return record;
18860
19019
  }
19020
+ async markContinuation(requestId, providerRequestId) {
19021
+ if (this.integrityError)
19022
+ throw new DispatchJournalIntegrityError(this.integrityError);
19023
+ const existing = this.records.get(requestId);
19024
+ if (!existing || existing.quarantined) {
19025
+ throw new DispatchJournalIntegrityError(`Dispatch ${requestId} is unavailable`);
19026
+ }
19027
+ this.records.set(requestId, {
19028
+ ...existing,
19029
+ providerRequestId,
19030
+ updatedAt: new Date(this.now()).toISOString(),
19031
+ sequence: ++this.nextSequence
19032
+ });
19033
+ await this.flush({ failClosed: true });
19034
+ }
18861
19035
  async markAccepted(requestId, options) {
18862
19036
  if (this.integrityError)
18863
19037
  throw new DispatchJournalIntegrityError(this.integrityError);
@@ -18868,6 +19042,7 @@ class DispatchJournal {
18868
19042
  const timestamp = new Date(this.now()).toISOString();
18869
19043
  this.records.set(requestId, {
18870
19044
  requestId,
19045
+ providerRequestId: existing?.providerRequestId,
18871
19046
  bridgeSessionId: existing?.bridgeSessionId ?? "",
18872
19047
  threadId: options.threadId,
18873
19048
  turnId: options.turnId,
@@ -18887,6 +19062,7 @@ class DispatchJournal {
18887
19062
  const timestamp = new Date(this.now()).toISOString();
18888
19063
  this.records.set(requestId, {
18889
19064
  requestId,
19065
+ providerRequestId: existing?.providerRequestId,
18890
19066
  bridgeSessionId: existing?.bridgeSessionId ?? "",
18891
19067
  threadId: options.threadId ?? existing?.threadId ?? null,
18892
19068
  turnId: options.turnId ?? existing?.turnId,
@@ -18908,6 +19084,7 @@ class DispatchJournal {
18908
19084
  const timestamp = new Date(this.now()).toISOString();
18909
19085
  this.records.set(requestId, {
18910
19086
  requestId,
19087
+ providerRequestId: existing?.providerRequestId,
18911
19088
  bridgeSessionId: existing?.bridgeSessionId ?? "",
18912
19089
  threadId: existing?.threadId ?? null,
18913
19090
  state: "retryable",
@@ -19543,7 +19720,8 @@ function codexAppServerConfigOverrides(env = process.env) {
19543
19720
  const overrides = {
19544
19721
  "agents.max_concurrent_threads_per_session": String(childLimit),
19545
19722
  "features.multi_agent_v2.max_concurrent_threads_per_session": String(childLimit + 1),
19546
- "features.incremental_tools": "false"
19723
+ "features.incremental_tools": "false",
19724
+ "features.goals": "false"
19547
19725
  };
19548
19726
  const rawUrl = env[ORKESTRATOR_AGENT_MCP_URL_ENV]?.trim();
19549
19727
  const token = env[ORKESTRATOR_AGENT_MCP_TOKEN_ENV]?.trim();
@@ -19613,7 +19791,11 @@ function codexAppServerConfigOverrides(env = process.env) {
19613
19791
  if (isCoordinatorReadOnlyEnvironment(env)) {
19614
19792
  overrides["mcp_servers.orkestrator.default_tools_approval_mode"] = JSON.stringify("approve");
19615
19793
  } else {
19616
- for (const name of [...AGENT_MAIL_TOOL_NAMES, ...ARTIFACT_TOOL_NAMES]) {
19794
+ for (const name of [
19795
+ ...AGENT_MAIL_TOOL_NAMES,
19796
+ ...ARTIFACT_TOOL_NAMES,
19797
+ ...AGENT_GOAL_TOOL_NAMES
19798
+ ]) {
19617
19799
  overrides[`mcp_servers.orkestrator.tools.${name}.approval_mode`] = JSON.stringify("approve");
19618
19800
  }
19619
19801
  }
@@ -19781,7 +19963,7 @@ var APP_SERVER_CAPABILITIES = {
19781
19963
  };
19782
19964
 
19783
19965
  // ../../bridges/codex-bridge/src/engine/app-server-engine.ts
19784
- var AGENT_MAIL_TOOL_APPROVALS = Object.fromEntries([...AGENT_MAIL_TOOL_NAMES, ...ARTIFACT_TOOL_NAMES].map((name) => [
19966
+ var AGENT_MAIL_TOOL_APPROVALS = Object.fromEntries([...AGENT_MAIL_TOOL_NAMES, ...ARTIFACT_TOOL_NAMES, ...AGENT_GOAL_TOOL_NAMES].map((name) => [
19785
19967
  name,
19786
19968
  { approval_mode: "approve" }
19787
19969
  ]));
@@ -21226,7 +21408,11 @@ class AppServerEngine {
21226
21408
  return {
21227
21409
  id: String(turn.id ?? ""),
21228
21410
  status: turn.status ?? "completed",
21229
- items: [],
21411
+ items: items.flatMap((item) => {
21412
+ const adapted = adaptAppServerItem(item).item;
21413
+ return adapted ? [adapted] : [];
21414
+ }),
21415
+ ...turn.error ? { error: toTurnError(turn.error) } : {},
21230
21416
  clientId: clientIds[0] ?? null,
21231
21417
  clientIds,
21232
21418
  reconciliationItems,
@@ -21377,7 +21563,13 @@ class AppServerEngine {
21377
21563
  clientId
21378
21564
  }))
21379
21565
  }));
21380
- return reconcileFromThreadTurns(turns, requestId);
21566
+ const outcome = reconcileFromThreadTurns(turns, requestId);
21567
+ const matched = thread?.turns?.find((turn) => turn.id === outcome.turnId);
21568
+ return {
21569
+ ...outcome,
21570
+ ...matched?.items.length ? { items: matched.items } : {},
21571
+ ...matched?.error ? { error: matched.error } : {}
21572
+ };
21381
21573
  }
21382
21574
  classifyFailure(error) {
21383
21575
  return {
@@ -22725,8 +22917,10 @@ class CodexRuntimeSessions {
22725
22917
  const session = this.ctx.registry.getSession(sessionId);
22726
22918
  if (!session)
22727
22919
  return { activity: "missing" };
22920
+ const turnFailed = activity === "idle" && (this.ctx.registry.getThreadForSession(sessionId)?.phase ?? (session.restartFailure ? "failed" : "idle")) === "failed";
22728
22921
  return {
22729
22922
  activity,
22923
+ ...turnFailed ? { turnFailed: true } : {},
22730
22924
  ...session.asyncQuestionItemIds.length > 0 ? { asyncQuestionItemIds: [...session.asyncQuestionItemIds] } : {},
22731
22925
  contentRevision: `${ACTIVITY_PROCESS}:${session.contentEpoch}.${session.messageRevision}:${session.asyncQuestionItemIds.length}`
22732
22926
  };
@@ -22771,9 +22965,11 @@ class CodexRuntimeSessions {
22771
22965
  const context = this.ctx.registry.getThreadForSession(sessionId);
22772
22966
  const turn = context?.activeTurn;
22773
22967
  if (context && turn && context.bridgeSessionIds.size === 1) {
22774
- await this.ctx.options.engine.interruptTurn(context.engineHandle, turn.turnId).catch(() => {
22775
- return;
22776
- });
22968
+ if (!await this.lifecycle.capacityRetry.cancelForClose(context, turn)) {
22969
+ await this.ctx.options.engine.interruptTurn(context.engineHandle, turn.turnId).catch(() => {
22970
+ return;
22971
+ });
22972
+ }
22777
22973
  }
22778
22974
  this.retiredSessions.add(session);
22779
22975
  try {
@@ -24371,6 +24567,256 @@ class UpdateCoalescer {
24371
24567
  }
24372
24568
  }
24373
24569
 
24570
+ // ../../bridges/codex-bridge/src/app-server-runtime-capacity-retry.ts
24571
+ import { stat as stat8 } from "node:fs/promises";
24572
+ class CodexCapacityRetry {
24573
+ ctx;
24574
+ lifecycle;
24575
+ pending = new Map;
24576
+ constructor(ctx, lifecycle) {
24577
+ this.ctx = ctx;
24578
+ this.lifecycle = lifecycle;
24579
+ }
24580
+ policy() {
24581
+ return { ...DEFAULT_CAPACITY_RETRY_POLICY, ...this.ctx.options.capacityRetry };
24582
+ }
24583
+ isPending(turn) {
24584
+ return this.pending.has(turn);
24585
+ }
24586
+ onTurnFailed(context, turn, error) {
24587
+ const plan = turn.capacityRetry;
24588
+ if (!plan || !turn.requestId || !isModelCapacityError(error))
24589
+ return { retrying: false };
24590
+ const policy = this.policy();
24591
+ if (plan.attempt >= policy.maxAttempts) {
24592
+ return {
24593
+ retrying: false,
24594
+ ...plan.attempt > 1 ? { error: capacityRetriesExhaustedError(error, plan.attempt) } : {}
24595
+ };
24596
+ }
24597
+ if (this.ctx.stopping || turn.cancelRequested || turn.stoppedByShutdown || context.phase !== "running" || context.bridgeSessionIds.size === 0) {
24598
+ return { retrying: false };
24599
+ }
24600
+ const attempt = plan.attempt + 1;
24601
+ const delayMs = capacityRetryDelayMs(policy, attempt, this.ctx.options.capacityRetryRandom);
24602
+ const itemId = capacityRetryItemId(turn.requestId, attempt);
24603
+ turn.settleEndedAttempt();
24604
+ turn.error = undefined;
24605
+ turn.recordRetryCard(itemId, capacityRetryWaitingText(delayMs, attempt, policy.maxAttempts), "pending");
24606
+ const entry = { attempt, itemId, error, stage: "waiting" };
24607
+ entry.timer = setTimeout(() => this.fire(context, turn), delayMs);
24608
+ entry.timer.unref?.();
24609
+ this.pending.set(turn, entry);
24610
+ this.publish(context);
24611
+ return { retrying: true };
24612
+ }
24613
+ stop(context, turn) {
24614
+ const entry = this.pending.get(turn);
24615
+ if (!entry)
24616
+ return false;
24617
+ turn.markCancelling();
24618
+ this.ctx.registry.setPhase(context, "cancelling");
24619
+ this.lifecycle.emitStatus(context);
24620
+ if (entry.stage === "starting") {
24621
+ entry.stopAfterStart = true;
24622
+ return true;
24623
+ }
24624
+ this.end(context, turn, "interrupted");
24625
+ return true;
24626
+ }
24627
+ async cancelForClose(context, turn) {
24628
+ const entry = this.pending.get(turn);
24629
+ if (!entry || !this.stop(context, turn))
24630
+ return false;
24631
+ await entry.work;
24632
+ if (!turn.isTerminal()) {
24633
+ await this.ctx.options.engine.waitForTurnTerminal(context.engineHandle, turn.turnId, {
24634
+ allowRestart: true
24635
+ });
24636
+ if (!turn.isTerminal()) {
24637
+ turn.complete("interrupted");
24638
+ await this.lifecycle.runFinalization(context, turn);
24639
+ }
24640
+ }
24641
+ return true;
24642
+ }
24643
+ endForParentExit(context, turn) {
24644
+ const entry = this.pending.get(turn);
24645
+ if (!entry || entry.stage !== "waiting")
24646
+ return false;
24647
+ turn.stoppedByShutdown = true;
24648
+ this.clear(turn, entry, CAPACITY_RETRY_CANCELLED_TEXT);
24649
+ this.lifecycle.completeRecoveredTurn(turn, "interrupted");
24650
+ this.lifecycle.runFinalization(context, turn);
24651
+ return true;
24652
+ }
24653
+ dispose() {
24654
+ for (const [turn, entry] of Array.from(this.pending)) {
24655
+ if (entry.stage !== "waiting")
24656
+ continue;
24657
+ const context = this.ctx.registry.getThread(turn.threadId);
24658
+ if (!context || context.activeTurn !== turn) {
24659
+ this.clear(turn, entry, CAPACITY_RETRY_CANCELLED_TEXT);
24660
+ continue;
24661
+ }
24662
+ this.end(context, turn, "failed", entry.error, CAPACITY_RETRY_CANCELLED_TEXT);
24663
+ }
24664
+ }
24665
+ discard(turn) {
24666
+ const entry = this.pending.get(turn);
24667
+ if (entry)
24668
+ this.clear(turn, entry, CAPACITY_RETRY_CANCELLED_TEXT);
24669
+ }
24670
+ fire(context, turn) {
24671
+ const entry = this.pending.get(turn);
24672
+ if (!entry || entry.stage !== "waiting")
24673
+ return;
24674
+ entry.timer = undefined;
24675
+ if (!this.owns(context, turn)) {
24676
+ if (context.activeTurn === turn && !turn.isTerminal()) {
24677
+ this.end(context, turn, "failed", entry.error, CAPACITY_RETRY_CANCELLED_TEXT);
24678
+ } else
24679
+ this.clear(turn, entry, CAPACITY_RETRY_CANCELLED_TEXT);
24680
+ return;
24681
+ }
24682
+ entry.stage = "starting";
24683
+ const work = this.startContinuation(context, turn, entry).catch((error) => {
24684
+ console.error(`[codex-bridge] Capacity retry of turn ${turn.turnId} failed:`, error instanceof Error ? error.message : error);
24685
+ if (this.pending.get(turn) === entry && context.activeTurn === turn && !turn.isTerminal()) {
24686
+ this.end(context, turn, "failed", entry.error, CAPACITY_RETRY_START_FAILED_TEXT);
24687
+ }
24688
+ });
24689
+ entry.work = work;
24690
+ this.ctx.pendingFinalizations.add(work);
24691
+ work.finally(() => this.ctx.pendingFinalizations.delete(work));
24692
+ }
24693
+ owns(context, turn) {
24694
+ return !this.ctx.stopping && this.ctx.registry.getThread(context.threadId) === context && context.activeTurn === turn && !turn.isTerminal() && !turn.cancelRequested && context.phase === "running" && context.bridgeSessionIds.size > 0 && turn.engineGeneration === this.ctx.options.engine.info().generation;
24695
+ }
24696
+ async startContinuation(context, turn, entry) {
24697
+ const plan = turn.capacityRetry;
24698
+ const requestId = turn.requestId;
24699
+ const policy = this.policy();
24700
+ const clientId = capacityRetryClientId(requestId, entry.attempt);
24701
+ const engine = this.ctx.options.engine;
24702
+ const previousTurnId = turn.turnId;
24703
+ turn.providerRequestId = clientId;
24704
+ turn.turnId = unconfirmedTurnId(clientId);
24705
+ await this.ctx.journal.markContinuation(requestId, clientId);
24706
+ plan.attempt = entry.attempt;
24707
+ const path = engine.rolloutPath(context.threadId);
24708
+ const offset = path ? await stat8(path).then((stats) => stats.size, () => {
24709
+ return;
24710
+ }) : undefined;
24711
+ this.lifecycle.stateFor(context.threadId).refusedCellOffset = offset;
24712
+ let started;
24713
+ let reconciled;
24714
+ try {
24715
+ started = await engine.startTurn({
24716
+ handle: context.engineHandle,
24717
+ input: [
24718
+ { type: "text", text: capacityRetryContinuationText(entry.attempt, policy.maxAttempts) }
24719
+ ],
24720
+ config: plan.config,
24721
+ requestId: clientId,
24722
+ outputSchema: plan.outputSchema
24723
+ });
24724
+ } catch (error) {
24725
+ const failure = engine.classifyFailure(error);
24726
+ if (!this.ctx.stopping && failure.class !== "rejected" && !failure.retryImmediately) {
24727
+ const outcome = await engine.reconcileRequest(context.threadId, clientId).catch(() => {
24728
+ return;
24729
+ });
24730
+ if (outcome && outcome.result !== "absent" && outcome.turnId) {
24731
+ started = { turnId: outcome.turnId, engineGeneration: engine.info().generation };
24732
+ reconciled = outcome;
24733
+ } else if (!outcome) {
24734
+ this.clear(turn, entry, CAPACITY_RETRY_START_FAILED_TEXT);
24735
+ this.ctx.registry.setPhase(context, "recovering");
24736
+ this.lifecycle.emitStatus(context);
24737
+ this.ctx.callbacks.scheduleRecoveryBackstop(context);
24738
+ return;
24739
+ }
24740
+ }
24741
+ if (!started) {
24742
+ turn.turnId = previousTurnId;
24743
+ this.end(context, turn, entry.stopAfterStart ? "interrupted" : "failed", entry.error, CAPACITY_RETRY_START_FAILED_TEXT);
24744
+ return;
24745
+ }
24746
+ }
24747
+ if (this.pending.get(turn) !== entry || context.activeTurn !== turn || turn.isTerminal()) {
24748
+ await engine.interruptTurn(context.engineHandle, started.turnId).catch(() => {
24749
+ return;
24750
+ });
24751
+ return;
24752
+ }
24753
+ await this.ctx.journal.markAccepted(requestId, { threadId: context.threadId, turnId: started.turnId }).catch((error) => console.warn(`[codex-bridge] Could not journal capacity retry of ${requestId}:`, error instanceof Error ? error.message : error));
24754
+ if (plan.outputSchema) {
24755
+ const ledger = this.ctx.registry.recordStructuredOutputTurn(context.threadId, started.turnId, false);
24756
+ await Promise.all(ledger.map((session) => this.ctx.callbacks.persistSession(session)));
24757
+ }
24758
+ turn.turnId = started.turnId;
24759
+ turn.engineGeneration = started.engineGeneration;
24760
+ context.engineGeneration = started.engineGeneration;
24761
+ if (entry.stopAfterStart)
24762
+ this.clear(turn, entry, CAPACITY_RETRY_CANCELLED_TEXT);
24763
+ else {
24764
+ this.clear(turn, entry, capacityRetryStartedText(entry.attempt, policy.maxAttempts), "success");
24765
+ }
24766
+ if (reconciled?.items) {
24767
+ for (const item of reconciled.items)
24768
+ turn.onItemCompleted(item);
24769
+ }
24770
+ this.publish(context);
24771
+ this.lifecycle.drainPendingEvents(context, started.turnId);
24772
+ if (reconciled?.result === "terminal" && context.activeTurn === turn && !turn.isTerminal() && !this.isPending(turn)) {
24773
+ this.lifecycle.applyEvent(context, {
24774
+ kind: "turn.completed",
24775
+ threadId: context.threadId,
24776
+ turnId: started.turnId,
24777
+ engineGeneration: started.engineGeneration,
24778
+ status: reconciled.status ?? "completed",
24779
+ error: reconciled.error
24780
+ });
24781
+ }
24782
+ if (entry.stopAfterStart && !turn.isTerminal()) {
24783
+ const sessionId = context.bridgeSessionIds.values().next().value;
24784
+ if (sessionId)
24785
+ await this.ctx.callbacks.abort(sessionId);
24786
+ else
24787
+ await engine.interruptTurn(context.engineHandle, started.turnId).catch(() => {
24788
+ return;
24789
+ });
24790
+ }
24791
+ }
24792
+ end(context, turn, status, error, cardText = CAPACITY_RETRY_CANCELLED_TEXT) {
24793
+ const entry = this.pending.get(turn);
24794
+ if (entry)
24795
+ this.clear(turn, entry, cardText);
24796
+ if (status === "failed" && error) {
24797
+ turn.complete("failed", error);
24798
+ turn.recordProviderFailure(error);
24799
+ } else
24800
+ turn.complete(status);
24801
+ const work = this.lifecycle.runFinalization(context, turn);
24802
+ if (entry)
24803
+ entry.work = work;
24804
+ }
24805
+ clear(turn, entry, cardText, state = "failure") {
24806
+ if (entry.timer)
24807
+ clearTimeout(entry.timer);
24808
+ entry.timer = undefined;
24809
+ this.pending.delete(turn);
24810
+ if (turn.retryCardState(entry.itemId) === "pending") {
24811
+ turn.recordRetryCard(entry.itemId, cardText, state);
24812
+ }
24813
+ }
24814
+ publish(context) {
24815
+ this.lifecycle.stateFor(context.threadId).coalescer.flushNow();
24816
+ this.lifecycle.emitStatus(context);
24817
+ }
24818
+ }
24819
+
24374
24820
  // ../../bridges/codex-bridge/src/app-server-runtime-lifecycle.ts
24375
24821
  function artifactConnectionToken(value) {
24376
24822
  if (!value || typeof value !== "object" || Array.isArray(value))
@@ -24381,8 +24827,10 @@ function artifactConnectionToken(value) {
24381
24827
 
24382
24828
  class CodexRuntimeLifecycle {
24383
24829
  ctx;
24830
+ capacityRetry;
24384
24831
  constructor(ctx) {
24385
24832
  this.ctx = ctx;
24833
+ this.capacityRetry = new CodexCapacityRetry(ctx, this);
24386
24834
  }
24387
24835
  async start() {
24388
24836
  if (this.ctx.started)
@@ -24464,6 +24912,7 @@ class CodexRuntimeLifecycle {
24464
24912
  clearTimeout(timer);
24465
24913
  this.ctx.recoveryBackstops.clear();
24466
24914
  this.ctx.commandCatalogue.dispose();
24915
+ this.capacityRetry.dispose();
24467
24916
  this.ctx.callbacks.notifyThreadActivity();
24468
24917
  await this.ctx.options.engine.stop();
24469
24918
  await this.drainPendingWork();
@@ -25144,12 +25593,23 @@ class CodexRuntimeLifecycle {
25144
25593
  this.setThreadAuthNotice(context, refusedLoginNotice(context.authNotice, this.ctx.options.engine.getAccountLoginKind(), () => crypto.randomUUID()));
25145
25594
  }
25146
25595
  this.discardOutlivingCommandWindows(turn);
25596
+ let failure = event.error;
25597
+ if (event.status === "failed") {
25598
+ const decision = this.capacityRetry.onTurnFailed(context, turn, event.error);
25599
+ if (decision.retrying) {
25600
+ this.ctx.options.changeEvidence?.turn(this.evidenceSource(context, turn), "failed");
25601
+ this.takeRefusedCellTail(threadId, turn)?.close();
25602
+ return;
25603
+ }
25604
+ if (decision.error)
25605
+ failure = decision.error;
25606
+ }
25147
25607
  if (turn.stoppedByShutdown && event.status === "interrupted")
25148
25608
  this.completeRecoveredTurn(turn, event.status);
25149
25609
  else {
25150
- turn.complete(event.status, event.error);
25151
- if (event.status === "failed" && event.error)
25152
- turn.recordProviderFailure(event.error);
25610
+ turn.complete(event.status, failure);
25611
+ if (event.status === "failed" && failure)
25612
+ turn.recordProviderFailure(failure);
25153
25613
  }
25154
25614
  this.ctx.options.changeEvidence?.turn(this.evidenceSource(context, turn), event.status);
25155
25615
  const finalize = () => this.runFinalization(context, turn).catch((error) => {
@@ -25627,8 +26087,14 @@ class CodexRuntimeLifecycle {
25627
26087
  this.emitStatus(context);
25628
26088
  continue;
25629
26089
  }
25630
- const outcome = await this.ctx.options.engine.reconcileRequest(context.threadId, turn.requestId);
26090
+ const outcome = await this.ctx.options.engine.reconcileRequest(context.threadId, turn.providerRequestId ?? turn.requestId);
25631
26091
  if (outcome.result === "terminal") {
26092
+ if (outcome.turnId)
26093
+ turn.turnId = outcome.turnId;
26094
+ for (const item of outcome.items ?? [])
26095
+ turn.onItemCompleted(item);
26096
+ if (outcome.error)
26097
+ turn.onError(outcome.error);
25632
26098
  this.completeRecoveredTurn(turn, outcome.status ?? "completed");
25633
26099
  await this.runFinalization(context, turn);
25634
26100
  continue;
@@ -25678,6 +26144,7 @@ class CodexRuntimeLifecycle {
25678
26144
  turn.complete(status);
25679
26145
  }
25680
26146
  async finalizeTurn(context, turn) {
26147
+ this.capacityRetry.discard(turn);
25681
26148
  const state = this.stateFor(context.threadId);
25682
26149
  const structuredResult = turn.expectsStructuredOutput ? parseCodexStructuredOutput(turn) : undefined;
25683
26150
  const structuredSessions = structuredResult ? [...context.bridgeSessionIds].map((sessionId) => this.ctx.registry.getSession(sessionId)).filter((session) => session?.structuredOutputRequestId === turn.requestId) : [];
@@ -25940,7 +26407,7 @@ function requiredCoordinatorPermissionProfile() {
25940
26407
  }
25941
26408
 
25942
26409
  // ../../bridges/codex-bridge/src/app-server-runtime-prompt.ts
25943
- import { stat as stat8 } from "node:fs/promises";
26410
+ import { stat as stat9 } from "node:fs/promises";
25944
26411
  function commandRefusal(message) {
25945
26412
  return { ok: false, status: 422, error: message, kind: "command-unavailable" };
25946
26413
  }
@@ -26043,7 +26510,7 @@ class CodexRuntimePrompt {
26043
26510
  };
26044
26511
  }
26045
26512
  if (decision.action === "reconcile" && decision.record?.threadId) {
26046
- const outcome = await this.ctx.options.engine.reconcileRequest(decision.record.threadId, requestId);
26513
+ const outcome = await this.ctx.options.engine.reconcileRequest(decision.record.threadId, decision.record.providerRequestId ?? requestId);
26047
26514
  if (outcome.result === "terminal") {
26048
26515
  await this.ctx.journal.markTerminal(requestId, outcome.status ?? "completed", {
26049
26516
  threadId: decision.record.threadId,
@@ -26307,7 +26774,7 @@ class CodexRuntimePrompt {
26307
26774
  let refusedCellOffset;
26308
26775
  const startTurn = async () => {
26309
26776
  const path = this.ctx.options.engine.rolloutPath(context.threadId);
26310
- refusedCellOffset = path ? await stat8(path).then((stats) => stats.size, (error) => error.code === "ENOENT" ? 0 : undefined) : undefined;
26777
+ refusedCellOffset = path ? await stat9(path).then((stats) => stats.size, (error) => error.code === "ENOENT" ? 0 : undefined) : undefined;
26311
26778
  return this.ctx.options.engine.startTurn({
26312
26779
  handle: context.engineHandle,
26313
26780
  input: engineInput,
@@ -26464,6 +26931,11 @@ class CodexRuntimePrompt {
26464
26931
  startedAt: context.turnStartedAt
26465
26932
  });
26466
26933
  accumulator.markRunning();
26934
+ accumulator.capacityRetry = {
26935
+ config: turnConfig,
26936
+ ...input.outputSchema ? { outputSchema: input.outputSchema } : {},
26937
+ attempt: 1
26938
+ };
26467
26939
  context.activeTurn = accumulator;
26468
26940
  context.engineGeneration = turn.engineGeneration;
26469
26941
  context.dispatchInFlight = false;
@@ -26590,10 +27062,11 @@ class CodexRuntimePrompt {
26590
27062
  ...recoveredModelId ? { modelId: recoveredModelId } : {}
26591
27063
  });
26592
27064
  }
27065
+ accumulator.providerRequestId = this.ctx.journal.get(requestId)?.providerRequestId ?? requestId;
26593
27066
  this.scheduleRecoveryBackstop(context);
26594
27067
  let outcome;
26595
27068
  try {
26596
- outcome = await this.ctx.options.engine.reconcileRequest(context.threadId, requestId);
27069
+ outcome = await this.ctx.options.engine.reconcileRequest(context.threadId, accumulator.providerRequestId);
26597
27070
  } catch (error) {
26598
27071
  console.warn(`[codex-bridge] Could not reconcile ${requestId} on thread ${context.threadId}:`, error instanceof Error ? error.message : error);
26599
27072
  return "recovering";
@@ -26915,6 +27388,8 @@ class CodexRuntimeTail {
26915
27388
  const turn = context.activeTurn;
26916
27389
  if (!turn || turn.isTerminal() || turn.isUnconfirmed())
26917
27390
  continue;
27391
+ if (this.lifecycle.capacityRetry.endForParentExit(context, turn))
27392
+ continue;
26918
27393
  turn.stoppedByShutdown = true;
26919
27394
  const { engineHandle } = context;
26920
27395
  stopping.push(this.ctx.options.engine.interruptTurn(engineHandle, turn.turnId).then(() => this.ctx.options.engine.waitForTurnTerminal(engineHandle, turn.turnId, {
@@ -26950,6 +27425,9 @@ class CodexRuntimeTail {
26950
27425
  phase: context?.phase ?? "idle"
26951
27426
  };
26952
27427
  }
27428
+ if (this.lifecycle.capacityRetry.stop(context, turn)) {
27429
+ return { status: "cancelling", phase: "cancelling" };
27430
+ }
26953
27431
  this.ctx.threadState.get(context.threadId)?.refusedCellTail?.tail.close();
26954
27432
  turn.markCancelling();
26955
27433
  this.ctx.registry.setPhase(context, "cancelling");
@@ -27051,9 +27529,10 @@ class CodexRuntimeTail {
27051
27529
  const windows = accountUsageFromLimits(limits);
27052
27530
  return windows.length > 0 ? windows : undefined;
27053
27531
  }
27054
- async listSessions() {
27532
+ async listSessions(options = {}) {
27055
27533
  const cwd = getWorkingDirectory(this.ctx.options.cwd);
27056
27534
  const merged = new Map;
27535
+ const archivedRows = new Map;
27057
27536
  try {
27058
27537
  const { threads } = await this.ctx.options.engine.listThreads({ cwd });
27059
27538
  for (const thread of threads) {
@@ -27078,9 +27557,16 @@ class CodexRuntimeTail {
27078
27557
  onlyArchived: true,
27079
27558
  ...cursor ? { cursor } : {}
27080
27559
  });
27081
- for (const thread of page.threads)
27082
- if (thread.id)
27083
- archived.add(thread.id);
27560
+ for (const thread of page.threads) {
27561
+ if (!thread.id)
27562
+ continue;
27563
+ archived.add(thread.id);
27564
+ archivedRows.set(thread.id, {
27565
+ id: thread.id,
27566
+ title: thread.name ?? thread.preview ?? undefined,
27567
+ updatedAt: thread.updatedAt ?? new Date(this.ctx.now()).toISOString()
27568
+ });
27569
+ }
27084
27570
  cursor = page.nextCursor ?? undefined;
27085
27571
  } while (cursor);
27086
27572
  } catch {}
@@ -27105,6 +27591,9 @@ class CodexRuntimeTail {
27105
27591
  for (const meta of persisted) {
27106
27592
  if (isArchived(meta)) {
27107
27593
  merged.delete(meta.id);
27594
+ if (!archivedRows.has(meta.id)) {
27595
+ archivedRows.set(meta.id, { id: meta.id, title: meta.title, updatedAt: meta.updatedAt });
27596
+ }
27108
27597
  continue;
27109
27598
  }
27110
27599
  const existing = merged.get(meta.id);
@@ -27117,14 +27606,18 @@ class CodexRuntimeTail {
27117
27606
  }
27118
27607
  try {
27119
27608
  const generated = generatedTitles ?? await readPersistedSessionTitleEntries(this.ctx.options.codexHome);
27120
- for (const session of merged.values()) {
27609
+ for (const session of [...merged.values(), ...archivedRows.values()]) {
27121
27610
  const entry = generated.get(session.id);
27122
27611
  if (entry)
27123
27612
  session.title = entry.title;
27124
27613
  }
27125
27614
  } catch {}
27615
+ const byActivity = (a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime();
27126
27616
  return {
27127
- sessions: [...merged.values()].sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()),
27617
+ sessions: [
27618
+ ...boundedResumeRows([...merged.values()].sort(byActivity)),
27619
+ ...options.includeArchived ? boundedResumeRows([...archivedRows.values()].filter((row) => !merged.has(row.id)).sort(byActivity).map((row) => ({ ...row, archived: true }))) : []
27620
+ ],
27128
27621
  cwd
27129
27622
  };
27130
27623
  }
@@ -27160,6 +27653,21 @@ class CodexRuntimeTail {
27160
27653
  };
27161
27654
  }
27162
27655
  }
27656
+ function boundedResumeRows(rows) {
27657
+ const result = [];
27658
+ let remaining = 900 * 1024;
27659
+ for (const row of rows) {
27660
+ if (result.length >= 512)
27661
+ break;
27662
+ const bounded = { ...row, title: row.title?.slice(0, 512) };
27663
+ const bytes = Buffer.byteLength(JSON.stringify(bounded), "utf8") + 1;
27664
+ if (bytes > remaining)
27665
+ continue;
27666
+ remaining -= bytes;
27667
+ result.push(bounded);
27668
+ }
27669
+ return result;
27670
+ }
27163
27671
 
27164
27672
  // ../../bridges/codex-bridge/src/app-server-runtime.ts
27165
27673
  class AppServerRuntime {
@@ -28536,7 +29044,9 @@ app.post("/session/:id/commands/refresh", async (c) => {
28536
29044
  return c.json(await appServerRuntime.refreshCommandCatalogue(c.req.param("id")));
28537
29045
  });
28538
29046
  app.get("/session/list", async (c) => {
28539
- return c.json(await appServerRuntime.listSessions());
29047
+ return c.json(await appServerRuntime.listSessions({
29048
+ includeArchived: c.req.query("includeArchived") === "1"
29049
+ }));
28540
29050
  });
28541
29051
  for (const action of ["archive", "unarchive"]) {
28542
29052
  app.post(`/session/list/:threadId/${action}`, async (c) => {