throughline 0.4.9 → 0.4.11

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.
@@ -3,7 +3,10 @@ import { buildHandoffRecord } from '../handoff-record.mjs';
3
3
  import { renderCodexNewThreadHandoff } from '../codex-handoff.mjs';
4
4
  import { buildCodexHandoffSmoke } from '../codex-handoff-smoke.mjs';
5
5
  import { buildCodexHandoffModelSmokePrompt } from '../codex-handoff-model-smoke.mjs';
6
+ import { runCodexNewThreadHandoff } from '../codex-app-server.mjs';
6
7
  import { estimateTokens } from '../token-estimator.mjs';
8
+ import { sameProjectPath } from '../project-path.mjs';
9
+ import { spawnSync } from 'node:child_process';
7
10
 
8
11
  async function readStdin() {
9
12
  let raw = '';
@@ -43,6 +46,11 @@ function parseArgs(args) {
43
46
  maxDetailRefs: undefined,
44
47
  maxRecentBodies: undefined,
45
48
  maxBodyChars: undefined,
49
+ execute: false,
50
+ openHost: 'auto',
51
+ codexAppServerBin: null,
52
+ timeoutMs: 120_000,
53
+ requestTimeoutMs: 60_000,
46
54
  };
47
55
 
48
56
  for (let i = 0; i < args.length; i++) {
@@ -67,6 +75,24 @@ function parseArgs(args) {
67
75
  out.maxRecentBodies = parseNonNegativeInteger(args, ++i, '--max-recent-bodies');
68
76
  } else if (arg === '--max-body-chars') {
69
77
  out.maxBodyChars = parseNonNegativeInteger(args, ++i, '--max-body-chars');
78
+ } else if (arg === '--execute') {
79
+ out.execute = true;
80
+ } else if (arg === '--open-host') {
81
+ const value = args[++i];
82
+ if (!['auto', 'vscode', 'cli', 'none'].includes(value)) {
83
+ throw new Error('--open-host must be auto, vscode, cli, or none');
84
+ }
85
+ out.openHost = value;
86
+ } else if (arg === '--codex-app-server-bin') {
87
+ const value = args[++i];
88
+ if (!value || value.startsWith('-')) {
89
+ throw new Error('--codex-app-server-bin requires a command path');
90
+ }
91
+ out.codexAppServerBin = value;
92
+ } else if (arg === '--timeout-ms') {
93
+ out.timeoutMs = parsePositiveInteger(args, ++i, '--timeout-ms');
94
+ } else if (arg === '--request-timeout-ms') {
95
+ out.requestTimeoutMs = parsePositiveInteger(args, ++i, '--request-timeout-ms');
70
96
  } else if (!arg.startsWith('-') && !out.sessionId) {
71
97
  out.sessionId = arg;
72
98
  } else {
@@ -78,16 +104,15 @@ function parseArgs(args) {
78
104
  }
79
105
 
80
106
  function findLatestCodexSessionId(db, projectPath) {
81
- const row = db
107
+ const rows = db
82
108
  .prepare(
83
- `SELECT session_id
109
+ `SELECT session_id, project_path
84
110
  FROM sessions
85
- WHERE lower(project_path) = lower(?)
86
- AND session_id LIKE 'codex:%'
87
- ORDER BY updated_at DESC
88
- LIMIT 1`,
111
+ WHERE session_id LIKE 'codex:%'
112
+ ORDER BY updated_at DESC`,
89
113
  )
90
- .get(projectPath);
114
+ .all();
115
+ const row = rows.find((candidate) => sameProjectPath(candidate.project_path, projectPath));
91
116
  return row?.session_id ?? null;
92
117
  }
93
118
 
@@ -170,7 +195,9 @@ function buildGuidance({ sessionId, parsed, handoffSmoke, handoffPrompt }) {
170
195
  reason: ready ? 'fresh_thread_handoff_start_ready' : 'handoff_smoke_not_ready',
171
196
  sessionId,
172
197
  mutatesCurrentThread: false,
173
- startThreadManually: true,
198
+ startThreadManually: !parsed.execute,
199
+ execute: parsed.execute,
200
+ openHost: parsed.openHost,
174
201
  handoffSmoke,
175
202
  modelPromptChars: modelPrompt.length,
176
203
  estimatedModelPromptTokens: estimateTokens(modelPrompt),
@@ -187,7 +214,9 @@ function buildGuidance({ sessionId, parsed, handoffSmoke, handoffPrompt }) {
187
214
  ...(parsed.memoStdin
188
215
  ? ['When replaying individual commands, pipe the same memo because they include --memo-stdin.']
189
216
  : []),
190
- 'Start a new Codex thread and provide the handoff prompt as the opening context.',
217
+ parsed.execute
218
+ ? 'This command will start a new Codex thread through app-server, inject handoff memory, then open the selected host.'
219
+ : 'Start a new Codex thread and provide the handoff prompt as the opening context.',
191
220
  ]
192
221
  : [
193
222
  'Fix the failing handoff smoke checks before starting a new Codex thread.',
@@ -204,12 +233,23 @@ function renderTextResult(result) {
204
233
  lines.push(` reason: ${result.reason}`);
205
234
  lines.push(` session: ${result.sessionId}`);
206
235
  lines.push(` mutates thread: ${result.mutatesCurrentThread ? 'yes' : 'no'}`);
236
+ lines.push(` execute: ${result.execute ? 'yes' : 'no'}`);
237
+ lines.push(` open host: ${result.openHost}`);
207
238
  lines.push(` handoff smoke: ${result.handoffSmoke.status}`);
208
239
  lines.push(` prompt chars: ${result.handoffSmoke.promptChars}/${result.handoffSmoke.maxPromptChars}`);
209
240
  lines.push(` model prompt: ${result.modelPromptChars}`);
210
241
  if (result.memoReplayNote) {
211
242
  lines.push(` memo replay: ${result.memoReplayNote}`);
212
243
  }
244
+ if (result.newThread) {
245
+ lines.push(` new thread: ${result.newThread.threadId}`);
246
+ lines.push(` turn status: ${result.newThread.turnStatus}`);
247
+ }
248
+ if (result.open) {
249
+ lines.push(` open status: ${result.open.status}`);
250
+ lines.push(` vscode url: ${result.open.vscodeUrl}`);
251
+ lines.push(` resume command: ${result.open.resumeCommand}`);
252
+ }
213
253
  lines.push('');
214
254
  lines.push(' commands:');
215
255
  lines.push(` structural smoke: ${result.commands.structuralSmoke}`);
@@ -228,6 +268,114 @@ function renderTextResult(result) {
228
268
  return lines.join('\n');
229
269
  }
230
270
 
271
+ function resolveOpenHost(host) {
272
+ if (host !== 'auto') return host;
273
+ if (
274
+ process.env.VSCODE_IPC_HOOK_CLI ||
275
+ process.env.VSCODE_IPC_HOOK ||
276
+ process.env.TERM_PROGRAM === 'vscode' ||
277
+ process.env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === 'codex_vscode'
278
+ ) {
279
+ return 'vscode';
280
+ }
281
+ return 'cli';
282
+ }
283
+
284
+ function openStartedCodexThread({ threadId, host, cwd }) {
285
+ const resolvedHost = resolveOpenHost(host);
286
+ const vscodeUrl = `vscode://openai.chatgpt/local/${encodeURIComponent(threadId)}`;
287
+ const resumeCommand = `codex resume ${threadId} --no-alt-screen`;
288
+ if (resolvedHost === 'none') {
289
+ return {
290
+ status: 'skipped',
291
+ reason: 'open_host_none',
292
+ host: resolvedHost,
293
+ vscodeUrl,
294
+ resumeCommand,
295
+ };
296
+ }
297
+
298
+ if (resolvedHost === 'vscode') {
299
+ const result =
300
+ process.platform === 'darwin'
301
+ ? spawnSync('open', [vscodeUrl], { encoding: 'utf8' })
302
+ : process.platform === 'win32'
303
+ ? spawnSync('cmd.exe', ['/c', 'start', '', vscodeUrl], { encoding: 'utf8' })
304
+ : spawnSync('xdg-open', [vscodeUrl], { encoding: 'utf8' });
305
+ if (result.status !== 0) {
306
+ return {
307
+ status: 'failed',
308
+ reason: 'vscode_deep_link_open_failed',
309
+ host: resolvedHost,
310
+ vscodeUrl,
311
+ resumeCommand,
312
+ error: (result.stderr || result.stdout || '').trim(),
313
+ };
314
+ }
315
+ return {
316
+ status: 'opened',
317
+ reason: 'vscode_deep_link_opened',
318
+ host: resolvedHost,
319
+ vscodeUrl,
320
+ resumeCommand,
321
+ };
322
+ }
323
+
324
+ if (resolvedHost === 'cli') {
325
+ if (process.platform === 'darwin') {
326
+ const shellCommand = `cd ${shQuote(cwd)} && TERM=xterm-256color codex resume ${shQuote(threadId)} --no-alt-screen`;
327
+ const result = spawnSync('osascript', [], {
328
+ input: `tell application "Terminal"
329
+ activate
330
+ do script ${appleString(shellCommand)}
331
+ end tell
332
+ `,
333
+ encoding: 'utf8',
334
+ });
335
+ if (result.status !== 0) {
336
+ return {
337
+ status: 'failed',
338
+ reason: 'terminal_open_failed',
339
+ host: resolvedHost,
340
+ vscodeUrl,
341
+ resumeCommand,
342
+ error: (result.stderr || result.stdout || '').trim(),
343
+ };
344
+ }
345
+ return {
346
+ status: 'opened',
347
+ reason: 'terminal_resume_opened',
348
+ host: resolvedHost,
349
+ vscodeUrl,
350
+ resumeCommand,
351
+ };
352
+ }
353
+ return {
354
+ status: 'manual',
355
+ reason: 'cli_auto_open_unsupported_on_platform',
356
+ host: resolvedHost,
357
+ vscodeUrl,
358
+ resumeCommand,
359
+ };
360
+ }
361
+
362
+ return {
363
+ status: 'failed',
364
+ reason: 'unsupported_open_host',
365
+ host: resolvedHost,
366
+ vscodeUrl,
367
+ resumeCommand,
368
+ };
369
+ }
370
+
371
+ function shQuote(value) {
372
+ return `'${String(value).replaceAll("'", "'\\''")}'`;
373
+ }
374
+
375
+ function appleString(value) {
376
+ return `"${String(value).replaceAll('\\', '\\\\').replaceAll('"', '\\"')}"`;
377
+ }
378
+
231
379
  export async function run(args) {
232
380
  let parsed;
233
381
  try {
@@ -280,9 +428,35 @@ export async function run(args) {
280
428
  result.prompt = handoffPrompt;
281
429
  }
282
430
 
431
+ if (parsed.execute && result.status === 'ready') {
432
+ const startResult = await runCodexNewThreadHandoff({
433
+ cwd: process.cwd(),
434
+ prompt: handoffPrompt,
435
+ command: parsed.codexAppServerBin ?? 'codex',
436
+ commandArgs: parsed.codexAppServerBin ? [] : ['app-server', '--listen', 'stdio://'],
437
+ timeoutMs: parsed.timeoutMs,
438
+ requestTimeoutMs: parsed.requestTimeoutMs,
439
+ waitForTurn: false,
440
+ delivery: 'developer-item',
441
+ });
442
+ const openResult = openStartedCodexThread({
443
+ threadId: startResult.threadId,
444
+ host: parsed.openHost,
445
+ cwd: process.cwd(),
446
+ });
447
+ result.status = startResult.status === 'started' && openResult.status !== 'failed' ? 'started' : 'started-unverified';
448
+ result.reason =
449
+ startResult.status === 'started' && openResult.status !== 'failed'
450
+ ? 'new_thread_handoff_started'
451
+ : 'new_thread_handoff_started_but_open_unverified';
452
+ result.startThreadManually = openResult.status === 'manual';
453
+ result.newThread = startResult;
454
+ result.open = openResult;
455
+ }
456
+
283
457
  if (parsed.json) process.stdout.write(JSON.stringify(result, null, 2) + '\n');
284
458
  else process.stdout.write(renderTextResult(result) + '\n');
285
- process.exit(result.status === 'ready' ? 0 : 1);
459
+ process.exit(result.status === 'ready' || result.status === 'started' ? 0 : 1);
286
460
  }
287
461
 
288
462
  export const _internal = {
@@ -1,7 +1,7 @@
1
1
  import { test } from 'node:test';
2
2
  import assert from 'node:assert/strict';
3
3
  import { spawnSync } from 'node:child_process';
4
- import { mkdtempSync, rmSync } from 'node:fs';
4
+ import { chmodSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
5
5
  import { tmpdir } from 'node:os';
6
6
  import { dirname, join } from 'node:path';
7
7
  import { fileURLToPath } from 'node:url';
@@ -16,6 +16,41 @@ function makeTempProject() {
16
16
  return mkdtempSync(join(tmpdir(), 'tl-codex-handoff-start-project-'));
17
17
  }
18
18
 
19
+ function makeFakeCodexAppServer(dir) {
20
+ const script = join(dir, 'fake-codex-app-server.mjs');
21
+ const log = join(dir, 'fake-codex-app-server.log');
22
+ writeFileSync(
23
+ script,
24
+ `#!/usr/bin/env node
25
+ import { appendFileSync } from 'node:fs';
26
+ import { createInterface } from 'node:readline';
27
+ const log = ${JSON.stringify(log)};
28
+ const rl = createInterface({ input: process.stdin });
29
+ function send(message) { process.stdout.write(JSON.stringify(message) + '\\n'); }
30
+ rl.on('line', (line) => {
31
+ const msg = JSON.parse(line);
32
+ appendFileSync(log, JSON.stringify(msg) + '\\n');
33
+ if (msg.method === 'initialized') return;
34
+ if (msg.method === 'initialize') {
35
+ send({ id: msg.id, result: { userAgent: 'fake-codex' } });
36
+ } else if (msg.method === 'thread/start') {
37
+ send({ id: msg.id, result: { thread: { id: '019e2000-0000-7000-8000-000000000001', turns: [] } } });
38
+ } else if (msg.method === 'thread/inject_items') {
39
+ send({ id: msg.id, result: { thread: { id: msg.params.threadId, turns: [] } } });
40
+ } else if (msg.method === 'turn/start') {
41
+ send({ id: msg.id, result: { turn: { id: 'turn-handoff' } } });
42
+ send({ method: 'item/agentMessage/delta', params: { threadId: msg.params.threadId, turnId: 'turn-handoff', itemId: 'item-1', delta: 'OK' } });
43
+ send({ method: 'turn/completed', params: { threadId: msg.params.threadId, turnId: 'turn-handoff' } });
44
+ } else {
45
+ send({ id: msg.id, error: { code: -32601, message: 'unknown method' } });
46
+ }
47
+ });
48
+ `,
49
+ );
50
+ chmodSync(script, 0o755);
51
+ return { script, log };
52
+ }
53
+
19
54
  async function seedDb(home, project) {
20
55
  const originalHome = process.env.HOME;
21
56
  const originalUserProfile = process.env.USERPROFILE;
@@ -192,3 +227,47 @@ test('codex-handoff-start propagates memo-stdin to replay commands in JSON guida
192
227
  rmSync(home, { recursive: true, force: true });
193
228
  }
194
229
  });
230
+
231
+ test('codex-handoff-start execute creates a new app-server thread and can skip opening a host', async () => {
232
+ const home = makeTempHome();
233
+ const project = makeTempProject();
234
+ try {
235
+ await seedDb(home, project);
236
+ const { script, log } = makeFakeCodexAppServer(project);
237
+ const result = runStart(home, project, [
238
+ '--session',
239
+ 'codex:thread-handoff-start',
240
+ '--execute',
241
+ '--open-host',
242
+ 'none',
243
+ '--codex-app-server-bin',
244
+ script,
245
+ '--json',
246
+ ]);
247
+
248
+ assert.equal(result.status, 0, result.stderr);
249
+ const payload = JSON.parse(result.stdout);
250
+ assert.equal(payload.status, 'started');
251
+ assert.equal(payload.reason, 'new_thread_handoff_started');
252
+ assert.equal(payload.execute, true);
253
+ assert.equal(payload.startThreadManually, false);
254
+ assert.equal(payload.newThread.threadId, '019e2000-0000-7000-8000-000000000001');
255
+ assert.equal(payload.newThread.delivery, 'developer-item');
256
+ assert.equal(payload.newThread.injectSent, true);
257
+ assert.equal(payload.newThread.turnStatus, 'not-started');
258
+ assert.equal(payload.open.status, 'skipped');
259
+ assert.equal(payload.open.vscodeUrl, 'vscode://openai.chatgpt/local/019e2000-0000-7000-8000-000000000001');
260
+ assert.match(payload.open.resumeCommand, /codex resume 019e2000-0000-7000-8000-000000000001/);
261
+
262
+ const fakeLog = readFileSync(log, 'utf8');
263
+ assert.match(fakeLog, /"method":"thread\/start"/);
264
+ assert.match(fakeLog, /"sessionStartSource":"clear"/);
265
+ assert.match(fakeLog, /"method":"thread\/inject_items"/);
266
+ assert.doesNotMatch(fakeLog, /"method":"turn\/start"/);
267
+ assert.match(fakeLog, /## Throughline: New Codex Thread Handoff/);
268
+ assert.match(fakeLog, /latest handoff start body/);
269
+ } finally {
270
+ rmSync(project, { recursive: true, force: true });
271
+ rmSync(home, { recursive: true, force: true });
272
+ }
273
+ });
@@ -71,36 +71,11 @@ function suppressExperimentalWarnings() {
71
71
  });
72
72
  }
73
73
 
74
- export function codexContextOutput(text, eventName = 'UserPromptSubmit') {
75
- return JSON.stringify({
76
- hookSpecificOutput: {
77
- hookEventName: eventName,
78
- additionalContext: text,
79
- },
80
- });
81
- }
82
-
83
- function renderAutoRefreshContext({ usage, decision, threadId, eventName = 'UserPromptSubmit' }) {
84
- const tokens = Number(usage?.tokens);
85
- const contextWindowSize = Number(usage?.contextWindowSize);
86
- const ratio = Number(decision?.ratio);
87
- const percent = Number.isFinite(ratio) ? `${(ratio * 100).toFixed(1)}%` : 'unknown';
88
- const timing =
89
- eventName === 'PostToolUse'
90
- ? 'Before continuing the current tool loop, run the installed $throughline workflow now.'
91
- : 'Before answering the user prompt, run the installed $throughline workflow now.';
92
- return [
93
- '[throughline] Codex context refresh threshold reached for this current session.',
94
- `Thread: ${threadId}`,
95
- `Usage: ${Number.isFinite(tokens) ? tokens : 'unknown'} / ${Number.isFinite(contextWindowSize) ? contextWindowSize : 'unknown'} (${percent}); threshold ${(decision.threshold * 100).toFixed(0)}%.`,
96
- '',
97
- timing,
98
- 'Execute exactly:',
99
- 'throughline trim --execute --host codex --all --json',
100
- '',
101
- 'Report only the execution status, whether rollback/inject were sent, durable evidence if present, and the selected memory session; then continue from the refreshed context.',
102
- 'This trigger was computed from the current Codex rollout token_count, not from token-monitor output.',
103
- ].join('\n');
74
+ function codexAutoRefreshDisabledResult() {
75
+ return {
76
+ status: 'skipped',
77
+ reason: 'codex_auto_refresh_disabled',
78
+ };
104
79
  }
105
80
 
106
81
  async function captureCodexHookSession({
@@ -236,20 +211,20 @@ export async function runCodexStopHook({
236
211
  ensureMonitorTask = null,
237
212
  buildMonitorUsage = null,
238
213
  runAutoRefresh = null,
214
+ autoRefreshStateStore = null,
239
215
  } = {}) {
240
- const [{ runCodexAutoRefresh }, capturedState] = await Promise.all([
241
- import('../codex-auto-refresh.mjs'),
242
- captureCodexHookSession({
243
- args,
244
- payload,
245
- env,
246
- db,
247
- writeMonitorState,
248
- ensureMonitorTask,
249
- buildMonitorUsage,
250
- summarize: true,
251
- }),
252
- ]);
216
+ void runAutoRefresh;
217
+ void autoRefreshStateStore;
218
+ const capturedState = await captureCodexHookSession({
219
+ args,
220
+ payload,
221
+ env,
222
+ db,
223
+ writeMonitorState,
224
+ ensureMonitorTask,
225
+ buildMonitorUsage,
226
+ summarize: true,
227
+ });
253
228
 
254
229
  if (capturedState.status !== 'ok') {
255
230
  return {
@@ -261,28 +236,6 @@ export async function runCodexStopHook({
261
236
  };
262
237
  }
263
238
 
264
- let autoRefresh = null;
265
- try {
266
- autoRefresh = await (runAutoRefresh ?? runCodexAutoRefresh)({
267
- db: capturedState.db,
268
- threadId: capturedState.identity.codexThreadId,
269
- codexThreadIdSource: capturedState.identity.codexThreadIdSource,
270
- codexHome: capturedState.codexHome,
271
- projectPath: capturedState.captured.projectPath ?? capturedState.projectPath,
272
- sessionId: capturedState.captured.sessionId,
273
- usage: capturedState.usage,
274
- command: env.THROUGHLINE_CODEX_APP_SERVER_BIN ?? process.env.THROUGHLINE_CODEX_APP_SERVER_BIN ?? 'codex',
275
- });
276
- } catch (err) {
277
- const msg = err instanceof Error ? err.message : 'unknown';
278
- autoRefresh = {
279
- status: 'error',
280
- reason: 'auto_refresh_failed',
281
- message: msg,
282
- };
283
- process.stderr.write(`[codex-hook:auto-refresh] ${msg}\n`);
284
- }
285
-
286
239
  return {
287
240
  status: 'ok',
288
241
  reason: 'codex_rollout_captured',
@@ -290,7 +243,7 @@ export async function runCodexStopHook({
290
243
  captured: capturedState.captured,
291
244
  summarized: capturedState.summarized,
292
245
  monitorState: capturedState.monitorState,
293
- autoRefresh,
246
+ autoRefresh: codexAutoRefreshDisabledResult(),
294
247
  };
295
248
  }
296
249
 
@@ -302,6 +255,7 @@ export async function runCodexUserPromptSubmitHook({
302
255
  writeMonitorState = null,
303
256
  ensureMonitorTask = null,
304
257
  buildMonitorUsage = null,
258
+ autoRefreshStateStore = null,
305
259
  } = {}) {
306
260
  return runCodexContextRefreshInstructionHook({
307
261
  eventName: 'UserPromptSubmit',
@@ -312,6 +266,7 @@ export async function runCodexUserPromptSubmitHook({
312
266
  writeMonitorState,
313
267
  ensureMonitorTask,
314
268
  buildMonitorUsage,
269
+ autoRefreshStateStore,
315
270
  });
316
271
  }
317
272
 
@@ -323,6 +278,7 @@ export async function runCodexPostToolUseHook({
323
278
  writeMonitorState = null,
324
279
  ensureMonitorTask = null,
325
280
  buildMonitorUsage = null,
281
+ autoRefreshStateStore = null,
326
282
  } = {}) {
327
283
  return runCodexContextRefreshInstructionHook({
328
284
  eventName: 'PostToolUse',
@@ -333,6 +289,7 @@ export async function runCodexPostToolUseHook({
333
289
  writeMonitorState,
334
290
  ensureMonitorTask,
335
291
  buildMonitorUsage,
292
+ autoRefreshStateStore,
336
293
  });
337
294
  }
338
295
 
@@ -345,20 +302,20 @@ async function runCodexContextRefreshInstructionHook({
345
302
  writeMonitorState = null,
346
303
  ensureMonitorTask = null,
347
304
  buildMonitorUsage = null,
305
+ autoRefreshStateStore = null,
348
306
  } = {}) {
349
- const [{ evaluateCodexAutoRefreshUsage }, capturedState] = await Promise.all([
350
- import('../codex-auto-refresh.mjs'),
351
- captureCodexHookSession({
352
- args,
353
- payload,
354
- env,
355
- db,
356
- writeMonitorState,
357
- ensureMonitorTask,
358
- buildMonitorUsage,
359
- summarize: false,
360
- }),
361
- ]);
307
+ void eventName;
308
+ void autoRefreshStateStore;
309
+ const capturedState = await captureCodexHookSession({
310
+ args,
311
+ payload,
312
+ env,
313
+ db,
314
+ writeMonitorState,
315
+ ensureMonitorTask,
316
+ buildMonitorUsage,
317
+ summarize: false,
318
+ });
362
319
 
363
320
  if (capturedState.status !== 'ok') {
364
321
  return {
@@ -371,41 +328,13 @@ async function runCodexContextRefreshInstructionHook({
371
328
  };
372
329
  }
373
330
 
374
- const decision = evaluateCodexAutoRefreshUsage(capturedState.usage);
375
- if (!decision.shouldRefresh) {
376
- return {
377
- status: 'ok',
378
- reason: 'codex_rollout_captured',
379
- codexThreadIdSource: capturedState.identity.codexThreadIdSource,
380
- captured: capturedState.captured,
381
- monitorState: capturedState.monitorState,
382
- autoRefreshPrompt: {
383
- status: 'skipped',
384
- reason: decision.reason,
385
- decision,
386
- },
387
- };
388
- }
389
-
390
- const context = renderAutoRefreshContext({
391
- usage: capturedState.usage,
392
- decision,
393
- threadId: capturedState.identity.codexThreadId,
394
- eventName,
395
- });
396
331
  return {
397
332
  status: 'ok',
398
333
  reason: 'codex_rollout_captured',
399
334
  codexThreadIdSource: capturedState.identity.codexThreadIdSource,
400
335
  captured: capturedState.captured,
401
336
  monitorState: capturedState.monitorState,
402
- autoRefreshPrompt: {
403
- status: 'ready',
404
- reason: 'threshold_reached',
405
- decision,
406
- context,
407
- output: codexContextOutput(context, eventName),
408
- },
337
+ autoRefreshPrompt: codexAutoRefreshDisabledResult(),
409
338
  };
410
339
  }
411
340
 
@@ -475,10 +404,8 @@ export async function run(argv = []) {
475
404
 
476
405
  export const _internal = {
477
406
  codexHomeFromTranscriptPath,
478
- codexContextOutput,
479
407
  parseArgs,
480
408
  parsePayload,
481
- renderAutoRefreshContext,
482
409
  resolveCodexHookThreadIdentity,
483
410
  };
484
411