@kbediako/codex-orchestrator 0.2.0 → 0.2.1

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 (41) hide show
  1. package/README.md +43 -83
  2. package/dist/bin/codex-orchestrator.js +2 -0
  3. package/dist/orchestrator/src/cli/adapters/CommandBuilder.js +50 -0
  4. package/dist/orchestrator/src/cli/adapters/cloudFailureDiagnostics.js +117 -5
  5. package/dist/orchestrator/src/cli/coStatusAttachCliShell.js +2 -2
  6. package/dist/orchestrator/src/cli/coStatusCliShell.js +28 -6
  7. package/dist/orchestrator/src/cli/codexCliShell.js +48 -1
  8. package/dist/orchestrator/src/cli/codexDefaultsSetup.js +217 -26
  9. package/dist/orchestrator/src/cli/control/controlHostSupervision.js +28 -6
  10. package/dist/orchestrator/src/cli/control/controlRuntime.js +17 -6
  11. package/dist/orchestrator/src/cli/control/controlStatusDashboard.js +6 -1
  12. package/dist/orchestrator/src/cli/control/selectedRunProjection.js +49 -2
  13. package/dist/orchestrator/src/cli/doctor.js +142 -48
  14. package/dist/orchestrator/src/cli/init.js +94 -1
  15. package/dist/orchestrator/src/cli/providerLinearChildLaneRunner.js +64 -1
  16. package/dist/orchestrator/src/cli/providerLinearWorkerRunner.js +1165 -69
  17. package/dist/orchestrator/src/cli/rlm/alignment.js +3 -3
  18. package/dist/orchestrator/src/cli/services/commandRunner.js +31 -0
  19. package/dist/orchestrator/src/cli/utils/cloudPreflight.js +202 -6
  20. package/dist/orchestrator/src/cli/utils/codexFeatures.js +60 -0
  21. package/dist/orchestrator/src/manager.js +74 -4
  22. package/dist/scripts/lib/docs-catalog.js +35 -1
  23. package/docs/README.md +333 -0
  24. package/docs/book/README.md +19 -0
  25. package/docs/book/codex-cli-0124-adoption.md +68 -0
  26. package/docs/book/local-hook-impact.md +73 -0
  27. package/docs/book/operations.md +60 -0
  28. package/docs/book/public-posture.md +34 -0
  29. package/docs/book/setup.md +91 -0
  30. package/docs/book/skills.md +11 -0
  31. package/docs/guides/codex-version-policy.md +104 -0
  32. package/docs/public/downstream-setup.md +25 -18
  33. package/package.json +4 -1
  34. package/plugins/codex-orchestrator/.codex-plugin/plugin.json +1 -1
  35. package/plugins/codex-orchestrator/launcher.mjs +6 -4
  36. package/schemas/manifest.json +17 -0
  37. package/skills/README.md +26 -0
  38. package/skills/collab-subagents-first/SKILL.md +1 -1
  39. package/skills/delegation-usage/DELEGATION_GUIDE.md +12 -7
  40. package/skills/delegation-usage/SKILL.md +13 -8
  41. package/templates/codex/AGENTS.md +12 -10
@@ -4,18 +4,25 @@ import { createRequire } from 'node:module';
4
4
  import { join } from 'node:path';
5
5
  import process from 'node:process';
6
6
  import { resolveCodexHome } from './utils/codexPaths.js';
7
+ import { resolveCodexCliBin } from './utils/codexCli.js';
8
+ import { codexFeatureProbeDisablesMultiAgentV2, codexFeatureProbeRejectsAgentMaxThreads, readCodexFeatureProbe } from './utils/codexFeatures.js';
7
9
  import { findPackageRoot } from './utils/packageInfo.js';
8
10
  import { writeAtomicFile } from '../utils/atomicWrite.js';
9
11
  const require = createRequire(import.meta.url);
10
12
  let tomlLibrary;
11
13
  export const BASELINE_MODEL = 'gpt-5.4';
12
14
  export const BASELINE_REVIEW_MODEL = BASELINE_MODEL;
15
+ export const CURRENT_CHATGPT_MODEL = 'gpt-5.5';
13
16
  export const BASELINE_REASONING = 'xhigh';
14
17
  export const BASELINE_REASONING_MINIMUM = 'high';
15
18
  export const BASELINE_AGENTS = {
16
19
  max_threads: 12,
17
20
  max_depth: 4
18
21
  };
22
+ export const LOCAL_MODEL_OPT_INS = [CURRENT_CHATGPT_MODEL];
23
+ const LOCAL_MODEL_OPT_IN_SET = new Set(LOCAL_MODEL_OPT_INS);
24
+ const CODEX_ORCHESTRATOR_CONFIG_KEY = 'codex_orchestrator';
25
+ const LOCAL_MODEL_OPT_IN_CONFIG_KEY = 'local_model_opt_in';
19
26
  const ROLE_DEFINITIONS = [
20
27
  {
21
28
  key: 'explorer_fast',
@@ -29,26 +36,45 @@ const ROLE_DEFINITIONS = [
29
36
  description: 'Complex implementation role.',
30
37
  fileName: 'worker-complex.toml',
31
38
  configFile: './agents/worker-complex.toml',
32
- templatePath: join('templates', 'codex', '.codex', 'agents', 'worker-complex.toml')
39
+ templatePath: join('templates', 'codex', '.codex', 'agents', 'worker-complex.toml'),
40
+ managedMigrationBaselines: [{ model: 'gpt-5.5', modelReasoningEffort: 'xhigh' }]
33
41
  },
34
42
  {
35
43
  key: 'awaiter',
36
44
  description: 'Awaiter override (keeps awaiter behavior with latest codex/high reasoning).',
37
45
  fileName: 'awaiter-high.toml',
38
46
  configFile: './agents/awaiter-high.toml',
39
- templatePath: join('templates', 'codex', '.codex', 'agents', 'awaiter-high.toml')
47
+ templatePath: join('templates', 'codex', '.codex', 'agents', 'awaiter-high.toml'),
48
+ managedMigrationBaselines: [{ model: 'gpt-5.5', modelReasoningEffort: 'xhigh' }],
49
+ managedMigrationContentVariants: [
50
+ {
51
+ model: 'gpt-5.5',
52
+ modelReasoningEffort: 'high',
53
+ overrideComment: '# with CO portable override to use gpt-5.4 at high reasoning.'
54
+ }
55
+ ]
40
56
  }
41
57
  ];
42
58
  export async function runCodexDefaultsSetup(options = {}) {
43
59
  const env = options.env ?? process.env;
44
60
  const force = Boolean(options.force);
45
61
  const apply = Boolean(options.apply);
46
- const plan = buildPlan(env, force);
62
+ const configState = await loadConfigState(buildConfigPath(env));
63
+ const topLevelLocalModelOptIn = resolveRequestedLocalModelOptIn(configState.parsed, options.authScope);
64
+ const roleAndReviewLocalModelOptIn = resolveRoleAndReviewLocalModelOptIn(configState.parsed, topLevelLocalModelOptIn, options.authScope);
65
+ const authScope = topLevelLocalModelOptIn ? 'chatgpt' : 'portable';
66
+ const plan = buildPlan(env, force, authScope);
47
67
  const roleDefinitions = await loadRoleDefinitions();
48
- const configState = await loadConfigState(plan.configPath);
49
- const nextConfig = mergeBaselineDefaults(configState.parsed, roleDefinitions);
68
+ const featureProbe = readCodexFeatureProbe(resolveCodexCliBin(env), env);
69
+ const nextConfig = mergeBaselineDefaults(configState.parsed, roleDefinitions, {
70
+ topLevelLocalModelOptIn,
71
+ reviewModelOptIn: roleAndReviewLocalModelOptIn,
72
+ requestedAuthScope: options.authScope,
73
+ featureProbe
74
+ });
75
+ const activeRoleDefinitions = buildActiveRoleDefinitions(roleDefinitions, roleAndReviewLocalModelOptIn);
50
76
  const configChanged = canonicalizeConfigValue(configState.parsed) !== canonicalizeConfigValue(nextConfig);
51
- const roleChanges = await planRoleChanges(plan.agentsDir, force, roleDefinitions);
77
+ const roleChanges = await planRoleChanges(plan.agentsDir, force, activeRoleDefinitions);
52
78
  if (!apply) {
53
79
  const changes = buildPlannedChanges({
54
80
  configPath: plan.configPath,
@@ -72,7 +98,7 @@ export async function runCodexDefaultsSetup(options = {}) {
72
98
  path: plan.configPath,
73
99
  status: configState.exists ? 'updated' : 'created',
74
100
  detail: configState.exists
75
- ? 'Updated CO baseline defaults additively and preserved unrelated keys.'
101
+ ? 'Updated CO-compatible baseline defaults and preserved unrelated keys.'
76
102
  : 'Created config.toml with CO baseline defaults.'
77
103
  });
78
104
  }
@@ -87,9 +113,7 @@ export async function runCodexDefaultsSetup(options = {}) {
87
113
  }
88
114
  await mkdir(plan.agentsDir, { recursive: true });
89
115
  for (const roleChange of roleChanges) {
90
- const shouldWrite = roleChange.existingContent === null
91
- || (force && roleChange.existingContent !== roleChange.definition.content);
92
- if (shouldWrite) {
116
+ if (roleChange.writeReason) {
93
117
  await writeAtomicFile(roleChange.path, roleChange.definition.content, {
94
118
  ensureDir: true,
95
119
  encoding: 'utf8'
@@ -99,9 +123,7 @@ export async function runCodexDefaultsSetup(options = {}) {
99
123
  name: roleChange.definition.key,
100
124
  path: roleChange.path,
101
125
  status: roleChange.existingContent === null ? 'created' : 'updated',
102
- detail: roleChange.existingContent === null
103
- ? `Created ${roleChange.definition.fileName}.`
104
- : `Overwrote ${roleChange.definition.fileName} because --force was set.`
126
+ detail: formatAppliedRoleWriteDetail(roleChange)
105
127
  });
106
128
  continue;
107
129
  }
@@ -126,6 +148,7 @@ export function formatCodexDefaultsSetupSummary(result) {
126
148
  lines.push(`- Config: ${result.plan.configPath}`);
127
149
  lines.push(`- Agents dir: ${result.plan.agentsDir}`);
128
150
  lines.push(`- Force overwrite: ${result.plan.force ? 'yes' : 'no'}`);
151
+ lines.push(`- Auth scope: ${result.plan.authScope}`);
129
152
  lines.push('- Changes:');
130
153
  for (const change of result.changes) {
131
154
  lines.push(` - ${change.target}:${change.name} -> ${change.status} (${change.path})`);
@@ -136,13 +159,18 @@ export function formatCodexDefaultsSetupSummary(result) {
136
159
  }
137
160
  return lines;
138
161
  }
139
- function buildPlan(env, force) {
162
+ function buildConfigPath(env) {
163
+ return join(resolveCodexHome(env), 'config.toml');
164
+ }
165
+ function buildPlan(env, force, authScope) {
166
+ const configPath = buildConfigPath(env);
140
167
  const codexHome = resolveCodexHome(env);
141
168
  return {
142
169
  codexHome,
143
- configPath: join(codexHome, 'config.toml'),
170
+ configPath,
144
171
  agentsDir: join(codexHome, 'agents'),
145
- force
172
+ force,
173
+ authScope
146
174
  };
147
175
  }
148
176
  async function loadConfigState(configPath) {
@@ -202,13 +230,19 @@ function canonicalizeConfigValue(value) {
202
230
  }
203
231
  return undefined;
204
232
  }
205
- function mergeBaselineDefaults(existing, roleDefinitions) {
233
+ function mergeBaselineDefaults(existing, roleDefinitions, options) {
206
234
  const next = structuredClone(existing);
207
- next.model = BASELINE_MODEL;
208
- next.review_model = BASELINE_REVIEW_MODEL;
235
+ next.model = resolveModelDefault(options.topLevelLocalModelOptIn, BASELINE_MODEL);
236
+ next.review_model = resolveModelDefault(options.reviewModelOptIn, BASELINE_REVIEW_MODEL);
209
237
  next.model_reasoning_effort = BASELINE_REASONING;
238
+ removeLegacyLocalModelOptInMarker(next);
210
239
  const agents = isRecord(next.agents) ? structuredClone(next.agents) : {};
211
- agents.max_threads = BASELINE_AGENTS.max_threads;
240
+ if (isMultiAgentV2Enabled(next, options.featureProbe)) {
241
+ delete agents.max_threads;
242
+ }
243
+ else {
244
+ agents.max_threads = BASELINE_AGENTS.max_threads;
245
+ }
212
246
  for (const role of roleDefinitions) {
213
247
  const existingRole = isRecord(agents[role.key])
214
248
  ? structuredClone(agents[role.key])
@@ -220,6 +254,71 @@ function mergeBaselineDefaults(existing, roleDefinitions) {
220
254
  next.agents = agents;
221
255
  return next;
222
256
  }
257
+ export function isLocalModelOptIn(value) {
258
+ return typeof value === 'string' && LOCAL_MODEL_OPT_IN_SET.has(value);
259
+ }
260
+ export function resolveLocalModelOptIn(existing) {
261
+ const localConfig = isRecord(existing[CODEX_ORCHESTRATOR_CONFIG_KEY])
262
+ ? existing[CODEX_ORCHESTRATOR_CONFIG_KEY]
263
+ : null;
264
+ const configuredModel = localConfig?.[LOCAL_MODEL_OPT_IN_CONFIG_KEY];
265
+ return isLocalModelOptIn(configuredModel) ? configuredModel : null;
266
+ }
267
+ function resolveRequestedLocalModelOptIn(existing, requestedAuthScope) {
268
+ if (requestedAuthScope === 'portable') {
269
+ return null;
270
+ }
271
+ if (requestedAuthScope === 'chatgpt') {
272
+ return CURRENT_CHATGPT_MODEL;
273
+ }
274
+ const model = readOptionalString(existing.model);
275
+ const reviewModel = readOptionalString(existing.review_model);
276
+ if (isLocalModelOptIn(model)) {
277
+ return model;
278
+ }
279
+ if (isLocalModelOptIn(reviewModel)) {
280
+ return reviewModel;
281
+ }
282
+ return null;
283
+ }
284
+ function resolveRoleAndReviewLocalModelOptIn(existing, topLevelLocalModelOptIn, requestedAuthScope) {
285
+ if (requestedAuthScope === 'portable') {
286
+ return null;
287
+ }
288
+ if (topLevelLocalModelOptIn) {
289
+ return topLevelLocalModelOptIn;
290
+ }
291
+ const reviewModel = readOptionalString(existing.review_model);
292
+ const existingLocalModelOptIn = resolveLocalModelOptIn(existing);
293
+ return existingLocalModelOptIn === topLevelLocalModelOptIn
294
+ && reviewModel === topLevelLocalModelOptIn
295
+ && isLocalModelOptIn(reviewModel)
296
+ ? reviewModel
297
+ : null;
298
+ }
299
+ function readOptionalString(value) {
300
+ return typeof value === 'string' ? value : undefined;
301
+ }
302
+ function removeLegacyLocalModelOptInMarker(next) {
303
+ const existingConfig = isRecord(next[CODEX_ORCHESTRATOR_CONFIG_KEY])
304
+ ? structuredClone(next[CODEX_ORCHESTRATOR_CONFIG_KEY])
305
+ : {};
306
+ if (LOCAL_MODEL_OPT_IN_CONFIG_KEY in existingConfig) {
307
+ delete existingConfig[LOCAL_MODEL_OPT_IN_CONFIG_KEY];
308
+ if (Object.keys(existingConfig).length > 0) {
309
+ next[CODEX_ORCHESTRATOR_CONFIG_KEY] = existingConfig;
310
+ }
311
+ else {
312
+ delete next[CODEX_ORCHESTRATOR_CONFIG_KEY];
313
+ }
314
+ }
315
+ }
316
+ export function formatModelDefaultExpectation(baseline) {
317
+ return `${CURRENT_CHATGPT_MODEL} when ChatGPT-auth access is verified (fallback: ${baseline})`;
318
+ }
319
+ function resolveModelDefault(localModelOptIn, baseline) {
320
+ return localModelOptIn ?? baseline;
321
+ }
223
322
  async function planRoleChanges(agentsDir, force, roleDefinitions) {
224
323
  const changes = [];
225
324
  for (const definition of roleDefinitions) {
@@ -230,7 +329,8 @@ async function planRoleChanges(agentsDir, force, roleDefinitions) {
230
329
  path,
231
330
  existingContent: null,
232
331
  currentStatus: 'pending',
233
- detail: `Will create ${definition.fileName}.`
332
+ detail: `Will create ${definition.fileName}.`,
333
+ writeReason: 'create'
234
334
  });
235
335
  continue;
236
336
  }
@@ -241,7 +341,8 @@ async function planRoleChanges(agentsDir, force, roleDefinitions) {
241
341
  path,
242
342
  existingContent: current,
243
343
  currentStatus: 'unchanged',
244
- detail: `${definition.fileName} already matches CO baseline defaults.`
344
+ detail: `${definition.fileName} already matches CO baseline defaults.`,
345
+ writeReason: null
245
346
  });
246
347
  continue;
247
348
  }
@@ -251,7 +352,19 @@ async function planRoleChanges(agentsDir, force, roleDefinitions) {
251
352
  path,
252
353
  existingContent: current,
253
354
  currentStatus: 'pending',
254
- detail: `Will overwrite ${definition.fileName} because --force is set.`
355
+ detail: `Will overwrite ${definition.fileName} because --force is set.`,
356
+ writeReason: 'force'
357
+ });
358
+ continue;
359
+ }
360
+ if (definition.managedMigrationContents.includes(current)) {
361
+ changes.push({
362
+ definition,
363
+ path,
364
+ existingContent: current,
365
+ currentStatus: 'pending',
366
+ detail: `Will update ${definition.fileName} from a prior CO-managed model baseline.`,
367
+ writeReason: 'managed_migration'
255
368
  });
256
369
  continue;
257
370
  }
@@ -260,7 +373,8 @@ async function planRoleChanges(agentsDir, force, roleDefinitions) {
260
373
  path,
261
374
  existingContent: current,
262
375
  currentStatus: 'preserved',
263
- detail: `${definition.fileName} already exists; preserving without --force.`
376
+ detail: `${definition.fileName} already exists; preserving without --force.`,
377
+ writeReason: null
264
378
  });
265
379
  }
266
380
  return changes;
@@ -278,10 +392,69 @@ async function loadRoleDefinitions() {
278
392
  const reason = error?.message ?? String(error);
279
393
  throw new Error(`Unable to read role template ${templateFile}: ${reason}`);
280
394
  }
281
- loaded.push({ ...definition, content });
395
+ loaded.push({
396
+ ...definition,
397
+ content,
398
+ managedMigrationContents: buildManagedMigrationContents(content, definition)
399
+ });
282
400
  }
283
401
  return loaded;
284
402
  }
403
+ function buildActiveRoleDefinitions(roleDefinitions, localModelOptIn) {
404
+ if (!localModelOptIn) {
405
+ return [...roleDefinitions];
406
+ }
407
+ return roleDefinitions.map((definition) => {
408
+ const matchingOptInBaseline = definition.managedMigrationBaselines?.find((baseline) => baseline.model === localModelOptIn);
409
+ if (!matchingOptInBaseline) {
410
+ return definition;
411
+ }
412
+ const optInContent = applyRoleBaselineOverrides(definition.content, definition.fileName, matchingOptInBaseline);
413
+ return {
414
+ ...definition,
415
+ content: optInContent,
416
+ managedMigrationContents: uniqueRoleContents([
417
+ definition.content,
418
+ ...definition.managedMigrationContents
419
+ ]).filter((migrationContent) => migrationContent !== optInContent)
420
+ };
421
+ });
422
+ }
423
+ function buildManagedMigrationContents(content, definition) {
424
+ const migrationContents = [
425
+ ...(definition.managedMigrationBaselines ?? []),
426
+ ...(definition.managedMigrationContentVariants ?? [])
427
+ ].map((baseline) => applyRoleBaselineOverrides(content, definition.fileName, baseline));
428
+ return uniqueRoleContents(migrationContents).filter((migrationContent) => migrationContent !== content);
429
+ }
430
+ function uniqueRoleContents(contents) {
431
+ return [...new Set(contents)];
432
+ }
433
+ function formatAppliedRoleWriteDetail(roleChange) {
434
+ switch (roleChange.writeReason) {
435
+ case 'create':
436
+ return `Created ${roleChange.definition.fileName}.`;
437
+ case 'force':
438
+ return `Overwrote ${roleChange.definition.fileName} because --force was set.`;
439
+ case 'managed_migration':
440
+ return `Updated ${roleChange.definition.fileName} from a prior CO-managed model baseline.`;
441
+ case null:
442
+ return roleChange.detail;
443
+ }
444
+ }
445
+ function applyRoleBaselineOverrides(content, fileName, baseline) {
446
+ let next = replaceRoleTomlString(content, 'model', baseline.model, fileName);
447
+ next = replaceRoleTomlString(next, 'model_reasoning_effort', baseline.modelReasoningEffort, fileName);
448
+ return next.replace(/^# with CO override to use .+ at .+ reasoning\.$/m, baseline.overrideComment
449
+ ?? `# with CO override to use ${baseline.model} at ${baseline.modelReasoningEffort} reasoning.`);
450
+ }
451
+ function replaceRoleTomlString(content, key, value, fileName) {
452
+ const pattern = new RegExp(`^(${key}\\s*=\\s*)"[^"]*"$`, 'm');
453
+ if (!pattern.test(content)) {
454
+ throw new Error(`Role template ${fileName} is missing a ${key} assignment.`);
455
+ }
456
+ return content.replace(pattern, (_match, prefix) => `${prefix}"${value}"`);
457
+ }
285
458
  function buildPlannedChanges(params) {
286
459
  const changes = [];
287
460
  const configStatus = params.configChanged || !params.configExists ? 'pending' : 'unchanged';
@@ -292,7 +465,7 @@ function buildPlannedChanges(params) {
292
465
  status: configStatus,
293
466
  detail: configStatus === 'pending'
294
467
  ? params.configExists
295
- ? 'Will update CO baseline defaults additively while preserving unrelated keys.'
468
+ ? 'Will update CO-compatible baseline defaults while preserving unrelated keys.'
296
469
  : 'Will create config.toml with CO baseline defaults.'
297
470
  : 'CO baseline defaults already present.'
298
471
  });
@@ -310,3 +483,21 @@ function buildPlannedChanges(params) {
310
483
  function isRecord(value) {
311
484
  return typeof value === 'object' && value !== null && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
312
485
  }
486
+ function isMultiAgentV2Enabled(config, featureProbe) {
487
+ if (featureProbe.flags?.multi_agent_v2 === true) {
488
+ return true;
489
+ }
490
+ if (codexFeatureProbeDisablesMultiAgentV2(featureProbe)) {
491
+ return false;
492
+ }
493
+ if (codexFeatureProbeRejectsAgentMaxThreads(featureProbe)) {
494
+ return true;
495
+ }
496
+ if (!isRecord(config.features)) {
497
+ return false;
498
+ }
499
+ return readBooleanValue(config.features.multi_agent_v2) === true;
500
+ }
501
+ function readBooleanValue(value) {
502
+ return typeof value === 'boolean' ? value : null;
503
+ }
@@ -315,14 +315,12 @@ export function evaluateControlHostSupervisionProbeTimeoutDiagnostic(diagnostic,
315
315
  if (!diagnostic || diagnostic.running_workers.length === 0) {
316
316
  return null;
317
317
  }
318
- if (!isProviderRefreshLifecycleRestartRequiredDiagnostic(diagnostic.polling)) {
319
- return null;
320
- }
321
318
  if (!isCurrentControlHostSupervisionPollingDiagnostic(diagnostic.polling, options.minPollingUpdatedAt)) {
322
319
  return null;
323
320
  }
324
- const pollingReason = diagnostic.polling?.reason ?? diagnostic.polling?.last_error ?? null;
325
- if (!pollingReason) {
321
+ const restartRequired = isProviderRefreshLifecycleRestartRequiredDiagnostic(diagnostic.polling);
322
+ const activeRefresh = isActiveProviderRefreshProbeTimeoutDiagnostic(diagnostic.polling);
323
+ if (!restartRequired && !activeRefresh) {
326
324
  return null;
327
325
  }
328
326
  if (hasAvailableProviderWorkerCapacity(diagnostic)) {
@@ -371,6 +369,18 @@ function isProviderRefreshLifecycleRestartRequiredDiagnostic(polling) {
371
369
  return (polling.reason === 'provider_refresh_lifecycle_stuck' ||
372
370
  polling.last_error === 'provider_refresh_lifecycle_stuck');
373
371
  }
372
+ function isActiveProviderRefreshProbeTimeoutDiagnostic(polling) {
373
+ if (!polling || polling.checking !== true) {
374
+ return false;
375
+ }
376
+ if (polling.restart_required === true || polling.stuck === true) {
377
+ return false;
378
+ }
379
+ if (polling.reason !== null) {
380
+ return false;
381
+ }
382
+ return polling.refresh_phase?.startsWith('refresh:') === true;
383
+ }
374
384
  function isCurrentControlHostSupervisionPollingDiagnostic(polling, minPollingUpdatedAt) {
375
385
  const minimumUpdatedAt = parseIsoTimestampToMs(minPollingUpdatedAt);
376
386
  if (minimumUpdatedAt === null) {
@@ -467,10 +477,22 @@ function buildControlHostSupervisionRestartSignature(diagnostic) {
467
477
  // Quarantine repeated restart churn on the stable active-worker series, not on transient
468
478
  // refresh checkpoints that can legitimately drift within one stuck refresh cycle.
469
479
  return JSON.stringify({
470
- reason: diagnostic.polling.reason ?? diagnostic.polling.last_error ?? null,
480
+ reason: buildControlHostSupervisionRestartReasonKey(diagnostic.polling),
471
481
  worker_series: workerSeries
472
482
  });
473
483
  }
484
+ function buildControlHostSupervisionRestartReasonKey(polling) {
485
+ if (polling.reason) {
486
+ return polling.reason;
487
+ }
488
+ if (isActiveProviderRefreshProbeTimeoutDiagnostic(polling)) {
489
+ return 'active_provider_refresh_probe_timeout';
490
+ }
491
+ if (isProviderRefreshLifecycleRestartRequiredDiagnostic(polling)) {
492
+ return 'provider_refresh_lifecycle_stuck';
493
+ }
494
+ return polling.last_error ?? null;
495
+ }
474
496
  function buildControlHostSupervisionWorkerSeriesKey(worker) {
475
497
  if (worker.issue_identifier.length === 0) {
476
498
  return null;
@@ -883,6 +883,8 @@ function buildCompatibilityTelemetrySnapshot(sources, polling) {
883
883
  let hasOutputTokens = false;
884
884
  let totalTokens = 0;
885
885
  let hasTotalTokens = false;
886
+ let reasoningOutputTokens = 0;
887
+ let hasReasoningOutputTokens = false;
886
888
  let secondsRunning = 0;
887
889
  let latestAuthoritativeRateLimits = polling?.linear_budget
888
890
  ? { ...polling.linear_budget }
@@ -908,6 +910,11 @@ function buildCompatibilityTelemetrySnapshot(sources, polling) {
908
910
  totalTokens += Math.max(0, tokenUsage.total_tokens);
909
911
  hasTotalTokens = true;
910
912
  }
913
+ if (typeof tokenUsage?.reasoning_output_tokens === 'number' &&
914
+ Number.isFinite(tokenUsage.reasoning_output_tokens)) {
915
+ reasoningOutputTokens += Math.max(0, tokenUsage.reasoning_output_tokens);
916
+ hasReasoningOutputTokens = true;
917
+ }
911
918
  secondsRunning += computeCompatibilityRuntimeSeconds(source, now);
912
919
  const linearBudget = proof?.linear_budget ? { ...proof.linear_budget } : null;
913
920
  if (linearBudget) {
@@ -926,13 +933,17 @@ function buildCompatibilityTelemetrySnapshot(sources, polling) {
926
933
  }
927
934
  }
928
935
  }
936
+ const codexTotals = {
937
+ input_tokens: hasInputTokens ? inputTokens : null,
938
+ output_tokens: hasOutputTokens ? outputTokens : null,
939
+ total_tokens: hasTotalTokens ? totalTokens : null,
940
+ seconds_running: Number(secondsRunning.toFixed(3))
941
+ };
942
+ if (hasReasoningOutputTokens) {
943
+ codexTotals.reasoning_output_tokens = reasoningOutputTokens;
944
+ }
929
945
  return {
930
- codexTotals: {
931
- input_tokens: hasInputTokens ? inputTokens : null,
932
- output_tokens: hasOutputTokens ? outputTokens : null,
933
- total_tokens: hasTotalTokens ? totalTokens : null,
934
- seconds_running: Number(secondsRunning.toFixed(3))
935
- },
946
+ codexTotals,
936
947
  rateLimits: combineCompatibilityRateLimits({
937
948
  codex: latestCodexRateLimits,
938
949
  linearBudget: latestAuthoritativeRateLimits
@@ -738,7 +738,12 @@ function renderTokensLine(dataset, terminalColumns) {
738
738
  { text: ' | ', color: ANSI_GRAY },
739
739
  { text: `out ${formatOptionalCount(dataset.totals.output_tokens)}`, color: ANSI_YELLOW },
740
740
  { text: ' | ', color: ANSI_GRAY },
741
- { text: `total ${formatOptionalCount(dataset.totals.total_tokens)}`, color: ANSI_YELLOW }
741
+ { text: `total ${formatOptionalCount(dataset.totals.total_tokens)}`, color: ANSI_YELLOW },
742
+ { text: ' | ', color: ANSI_GRAY },
743
+ {
744
+ text: `reasoning ${formatOptionalCount(dataset.totals.reasoning_output_tokens)}`,
745
+ color: ANSI_YELLOW
746
+ }
742
747
  ], terminalColumns);
743
748
  }
744
749
  function renderRateLimitsLine(dataset, referenceTime, terminalColumns) {
@@ -1370,11 +1370,58 @@ function hasProviderLinearWorkerProjectionRetiredChildLaneResidue(proof) {
1370
1370
  }
1371
1371
  function hasProviderLinearWorkerProjectionTelemetryGap(proof) {
1372
1372
  const tokens = proof.tokens ?? null;
1373
- const hasTokens = tokens?.input_tokens != null || tokens?.output_tokens != null || tokens?.total_tokens != null;
1373
+ const hasTokens = tokens?.input_tokens != null ||
1374
+ tokens?.output_tokens != null ||
1375
+ tokens?.total_tokens != null ||
1376
+ tokens?.reasoning_output_tokens != null;
1374
1377
  return (!proof.latest_turn_id ||
1375
1378
  !proof.latest_session_id ||
1376
1379
  !hasTokens ||
1377
- proof.rate_limits == null);
1380
+ proof.rate_limits == null ||
1381
+ hasProviderLinearWorkerProjectionSessionLogHydrationGap(proof) ||
1382
+ hasProviderLinearWorkerProjectionAppServerSupervisionGap(proof));
1383
+ }
1384
+ function hasProviderLinearWorkerProjectionAppServerSupervisionGap(proof) {
1385
+ const selectedRuntimeMode = proof.runtime?.selected_mode ?? proof.auth_provenance?.runtime_mode ?? null;
1386
+ const requestedRuntimeMode = proof.runtime?.requested_mode ?? null;
1387
+ const fallback = proof.runtime?.fallback ?? null;
1388
+ if (selectedRuntimeMode !== 'appserver' &&
1389
+ requestedRuntimeMode !== 'appserver' &&
1390
+ fallback?.from_mode !== 'appserver' &&
1391
+ fallback?.to_mode !== 'appserver') {
1392
+ return false;
1393
+ }
1394
+ const supervision = proof.appserver_supervision ?? null;
1395
+ if (!supervision) {
1396
+ return true;
1397
+ }
1398
+ const expectedSessionLogTruthRetained = selectedRuntimeMode === 'appserver';
1399
+ return (supervision.selected_runtime?.selected_mode !== selectedRuntimeMode ||
1400
+ supervision.selected_runtime?.requested_mode !== requestedRuntimeMode ||
1401
+ supervision.thread_id !== proof.thread_id ||
1402
+ supervision.latest_turn_id !== proof.latest_turn_id ||
1403
+ supervision.latest_session_id !== proof.latest_session_id ||
1404
+ supervision.session_log_thread_id !== (proof.session_log_thread_id ?? null) ||
1405
+ supervision.session_log_turn_id !== (proof.session_log_turn_id ?? null) ||
1406
+ supervision.session_log_session_id !== (proof.session_log_session_id ?? null) ||
1407
+ supervision.turn_persistence_status == null ||
1408
+ supervision.pagination_status == null ||
1409
+ supervision.resume_status == null ||
1410
+ supervision.fork_status == null ||
1411
+ supervision.jsonl_truth_retained !== true ||
1412
+ supervision.session_log_truth_retained !== expectedSessionLogTruthRetained);
1413
+ }
1414
+ function hasProviderLinearWorkerProjectionSessionLogHydrationGap(proof) {
1415
+ const runtimeMode = proof.runtime?.selected_mode ?? proof.auth_provenance?.runtime_mode ?? null;
1416
+ if (runtimeMode !== 'appserver') {
1417
+ return false;
1418
+ }
1419
+ if (!proof.thread_id || !proof.latest_turn_id || !proof.latest_session_id) {
1420
+ return false;
1421
+ }
1422
+ return (proof.session_log_thread_id !== proof.thread_id ||
1423
+ proof.session_log_turn_id !== proof.latest_turn_id ||
1424
+ proof.session_log_session_id !== proof.latest_session_id);
1378
1425
  }
1379
1426
  function canSkipProviderLinearWorkerProjectionSessionLogHydration(proof, telemetryGap) {
1380
1427
  if (proof.owner_phase !== 'turn_completed') {