@link-assistant/hive-mind 2.12.0 → 2.12.2

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.
package/src/codex.lib.mjs CHANGED
@@ -7,12 +7,10 @@ import { ensureUseM } from './use-m-bootstrap.lib.mjs';
7
7
  if (typeof globalThis.use === 'undefined') {
8
8
  await ensureUseM();
9
9
  }
10
-
11
10
  const { $ } = await use('command-stream');
12
11
  const fs = (await use('fs')).promises;
13
12
  const path = (await use('path')).default;
14
13
  const os = (await use('os')).default;
15
-
16
14
  // Import log from general lib
17
15
  import { log } from './lib.mjs';
18
16
  // Issues #1955 / #1990: run-health analysis lives in its own module to keep this
@@ -45,32 +43,27 @@ import { createPullRequestBaseBranchCommandIntervention } from './solve.pr-base-
45
43
  import Decimal from 'decimal.js-light';
46
44
  import { ensureAiToolScratchIgnored, filterAiToolScratchFromStatus } from './ai-tool-scratch.lib.mjs';
47
45
  import { CODEX_CACHE_READ_USAGE_PATHS, CODEX_CACHE_WRITE_USAGE_PATHS, CODEX_MODEL_DIAGNOSTIC_PATHS, CODEX_REASONING_USAGE_PATHS, CODEX_USAGE_FIELD_NAMES, createCodexTokenFieldAvailability, getFirstObservedNumber, hasAnyObservedPath, hasOwnPath } from './codex.usage-fields.lib.mjs';
48
-
49
46
  const CODEX_LONG_CONTEXT_PRICE_THRESHOLD = 272000;
50
47
  const CODEX_COMPACT_API_ENDPOINT = '/responses/compact';
51
48
  const getCodexExecEnv = (verbose = false) => (verbose ? { ...process.env, RUST_LOG: 'debug' } : { ...process.env });
52
49
 
53
50
  const escapeRegExp = value => String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
54
-
55
51
  const getCodexDiagnosticValue = (line, key) => {
56
52
  const match = line.match(new RegExp(`${escapeRegExp(key)}=(?:"([^"]*)"|([^\\s")]+))`));
57
53
  return match?.[1] ?? match?.[2] ?? null;
58
54
  };
59
-
60
55
  const getCodexDiagnosticInteger = (line, key) => {
61
56
  const value = getCodexDiagnosticValue(line, key);
62
57
  if (value === null) return null;
63
58
  const parsed = Number.parseInt(value, 10);
64
59
  return Number.isFinite(parsed) ? parsed : null;
65
60
  };
66
-
67
61
  const getCodexDiagnosticTimestamp = line => {
68
62
  const eventTimestamp = getCodexDiagnosticValue(line, 'event.timestamp');
69
63
  if (eventTimestamp) return eventTimestamp;
70
64
  const logPrefixMatch = line.match(/^\[(\d{4}-\d{2}-\d{2}T[^\]]+Z)\]/u);
71
65
  return logPrefixMatch?.[1] ?? null;
72
66
  };
73
-
74
67
  const isSuccessfulCodexCompactRequestLine = line => {
75
68
  if (!line.includes('codex_otel.log_only:')) return false;
76
69
  if (!line.includes('event.name="codex.api_request"')) return false;
@@ -90,7 +83,6 @@ const splitTokenCountEvenly = (total, partCount) => {
90
83
  return value;
91
84
  });
92
85
  };
93
-
94
86
  const splitCodexSubSessionInputTokens = (total, partCount, autoCompactTokenLimit = null) => {
95
87
  const safeTotal = Math.max(0, Math.round(total || 0));
96
88
  const safePartCount = Math.max(1, Math.round(partCount || 1));
@@ -109,13 +101,11 @@ const splitCodexSubSessionInputTokens = (total, partCount, autoCompactTokenLimit
109
101
  }
110
102
  return splitTokenCountEvenly(safeTotal, safePartCount);
111
103
  };
112
-
113
104
  const splitTokenCountByWeights = (total, weights) => {
114
105
  const safeTotal = Math.max(0, Math.round(total || 0));
115
106
  const safeWeights = Array.isArray(weights) && weights.length > 0 ? weights.map(weight => Math.max(0, weight || 0)) : [1];
116
107
  const weightTotal = safeWeights.reduce((sum, weight) => sum + weight, 0);
117
108
  if (weightTotal <= 0) return splitTokenCountEvenly(safeTotal, safeWeights.length);
118
-
119
109
  let allocated = 0;
120
110
  return safeWeights.map((weight, index) => {
121
111
  if (index === safeWeights.length - 1) return Math.max(0, safeTotal - allocated);
@@ -124,7 +114,6 @@ const splitTokenCountByWeights = (total, weights) => {
124
114
  return value;
125
115
  });
126
116
  };
127
-
128
117
  const rebuildCodexSubSessionsFromCompactifications = tokenUsage => {
129
118
  const compactifications = Array.isArray(tokenUsage.compactifications) ? tokenUsage.compactifications : [];
130
119
  if (compactifications.length === 0 || (tokenUsage.stepCount || 0) === 0) {
@@ -137,7 +126,6 @@ const rebuildCodexSubSessionsFromCompactifications = tokenUsage => {
137
126
  const cacheWriteChunks = splitTokenCountByWeights(tokenUsage.cacheWriteTokens || 0, inputChunks);
138
127
  const cacheReadChunks = splitTokenCountByWeights(tokenUsage.cacheReadTokens || 0, inputChunks);
139
128
  const outputChunks = splitTokenCountByWeights(tokenUsage.outputTokens || 0, inputChunks);
140
-
141
129
  tokenUsage.subSessions = inputChunks.map((inputTokens, index) => {
142
130
  const cacheCreationTokens = cacheWriteChunks[index] || 0;
143
131
  const outputTokens = outputChunks[index] || 0;
@@ -155,14 +143,12 @@ const rebuildCodexSubSessionsFromCompactifications = tokenUsage => {
155
143
  };
156
144
  });
157
145
  };
158
-
159
146
  const recordCodexCompactification = (line, tokenUsage) => {
160
147
  if (!isSuccessfulCodexCompactRequestLine(line)) return;
161
148
  const timestamp = getCodexDiagnosticTimestamp(line);
162
149
  const conversationId = getCodexDiagnosticValue(line, 'conversation.id');
163
150
  const existing = tokenUsage.compactifications.find(compact => compact.timestamp === timestamp && compact.conversationId === conversationId);
164
151
  if (existing) return;
165
-
166
152
  tokenUsage.compactifications.push({
167
153
  timestamp,
168
154
  preTokens: null,
@@ -171,17 +157,14 @@ const recordCodexCompactification = (line, tokenUsage) => {
171
157
  conversationId: conversationId || null,
172
158
  });
173
159
  };
174
-
175
160
  const parseCodexDiagnosticLine = (line, tokenUsage) => {
176
161
  const contextLimit = getCodexDiagnosticInteger(line, 'context_window') ?? getCodexDiagnosticInteger(line, 'model_context_window');
177
162
  if (contextLimit !== null) tokenUsage.contextLimit = contextLimit;
178
163
 
179
164
  const autoCompactTokenLimit = getCodexDiagnosticInteger(line, 'auto_compact_token_limit') ?? getCodexDiagnosticInteger(line, 'model_auto_compact_token_limit');
180
165
  if (autoCompactTokenLimit !== null) tokenUsage.autoCompactTokenLimit = autoCompactTokenLimit;
181
-
182
166
  recordCodexCompactification(line, tokenUsage);
183
167
  };
184
-
185
168
  export const createCodexTokenUsage = requestedModelId => ({
186
169
  inputTokens: 0,
187
170
  outputTokens: 0,
@@ -201,7 +184,6 @@ export const createCodexTokenUsage = requestedModelId => ({
201
184
  compactifications: [],
202
185
  tokenFieldAvailability: createCodexTokenFieldAvailability(),
203
186
  });
204
-
205
187
  const createEmptyCodexItemUsage = () => ({
206
188
  inputTokens: 0,
207
189
  cacheCreationTokens: 0,
@@ -209,7 +191,6 @@ const createEmptyCodexItemUsage = () => ({
209
191
  outputTokens: 0,
210
192
  totalTokens: null,
211
193
  });
212
-
213
194
  const upsertById = (items, nextItem) => {
214
195
  const existingIndex = items.findIndex(item => item.id === nextItem.id);
215
196
  if (existingIndex >= 0) {
@@ -231,10 +212,8 @@ const upsertCodexSubAgentCall = (subAgentCalls, item, requestedModelId = null) =
231
212
  status: item.status || null,
232
213
  usage: subAgentCalls.find(call => call.id === item.id)?.usage || createEmptyCodexItemUsage(),
233
214
  };
234
-
235
215
  upsertById(subAgentCalls, nextCall);
236
216
  };
237
-
238
217
  const upsertCodexCommandExecution = (commandExecutions, item) => {
239
218
  upsertById(commandExecutions, {
240
219
  id: item.id || null,
@@ -244,7 +223,6 @@ const upsertCodexCommandExecution = (commandExecutions, item) => {
244
223
  status: item.status || null,
245
224
  });
246
225
  };
247
-
248
226
  const upsertCodexFileChange = (fileChanges, item) => {
249
227
  upsertById(fileChanges, {
250
228
  id: item.id || null,
@@ -257,7 +235,6 @@ const upsertCodexFileChange = (fileChanges, item) => {
257
235
  : [],
258
236
  });
259
237
  };
260
-
261
238
  const upsertCodexMcpToolCall = (mcpToolCalls, item) => {
262
239
  upsertById(mcpToolCalls, {
263
240
  id: item.id || null,
@@ -278,7 +255,6 @@ const upsertCodexWebSearch = (webSearches, item) => {
278
255
  action: item.action || null,
279
256
  });
280
257
  };
281
-
282
258
  const upsertCodexTodoList = (todoLists, item) => {
283
259
  upsertById(todoLists, {
284
260
  id: item.id || null,
@@ -290,14 +266,12 @@ const upsertCodexTodoList = (todoLists, item) => {
290
266
  : [],
291
267
  });
292
268
  };
293
-
294
269
  const upsertCodexItemError = (itemErrors, item) => {
295
270
  upsertById(itemErrors, {
296
271
  id: item.id || null,
297
272
  message: item.message || '',
298
273
  });
299
274
  };
300
-
301
275
  // Issue #2136: `codex exec --json` writes its NDJSON protocol to **stdout** only.
302
276
  // Its stderr carries OTEL tracing text (RUST_LOG=debug under --verbose), and each
303
277
  // `codex.tool_result` record dumps the raw stdout of the command codex just ran —
@@ -347,7 +321,6 @@ export const parseCodexExecJsonOutput = (output, state = {}, requestedModelId =
347
321
  // identity we can check. Diagnostics only; the gate stays order-based.
348
322
  foreignThreadIds: state.foreignThreadIds || [],
349
323
  };
350
-
351
324
  nextState.tokenUsage.tokenFieldAvailability ||= createCodexTokenFieldAvailability();
352
325
  if (!Array.isArray(nextState.tokenUsage.subSessions)) nextState.tokenUsage.subSessions = [];
353
326
  if (!Array.isArray(nextState.tokenUsage.compactifications)) nextState.tokenUsage.compactifications = [];
@@ -357,9 +330,7 @@ export const parseCodexExecJsonOutput = (output, state = {}, requestedModelId =
357
330
  for (const rawLine of output.split('\n')) {
358
331
  const line = rawLine.trim();
359
332
  if (!line) continue;
360
-
361
333
  parseCodexDiagnosticLine(line, nextState.tokenUsage);
362
-
363
334
  let data;
364
335
  try {
365
336
  data = sanitizeObjectStrings(JSON.parse(line));
@@ -371,7 +342,6 @@ export const parseCodexExecJsonOutput = (output, state = {}, requestedModelId =
371
342
  if (rejection) nextState.pluginInstallRejections.push(rejection);
372
343
  continue;
373
344
  }
374
-
375
345
  // Issue #1968: a stream line that parses to a bare `null` (or any non-object
376
346
  // JSON primitive such as a number/string/boolean) must not crash the parser.
377
347
  // Codex echoes the stdout of every command it runs back into its own NDJSON
@@ -381,7 +351,6 @@ export const parseCodexExecJsonOutput = (output, state = {}, requestedModelId =
381
351
  // aborted the entire solve. Real Codex events are always JSON objects, so any
382
352
  // non-object line is safely ignored.
383
353
  if (data === null || typeof data !== 'object') continue;
384
-
385
354
  const eventType = typeof data.type === 'string' ? data.type : 'unknown';
386
355
 
387
356
  // Issue #2136: a protocol-shaped object on a non-protocol stream is echoed
@@ -390,12 +359,10 @@ export const parseCodexExecJsonOutput = (output, state = {}, requestedModelId =
390
359
  nextState.telemetryEventCounts[eventType] = (nextState.telemetryEventCounts[eventType] || 0) + 1;
391
360
  continue;
392
361
  }
393
-
394
362
  nextState.eventCounts[eventType] = (nextState.eventCounts[eventType] || 0) + 1;
395
363
  if (eventType === 'turn.started' || eventType === 'turn.completed' || eventType === 'turn.failed') {
396
364
  nextState.turnLifecycle.push(eventType);
397
365
  }
398
-
399
366
  if (eventType === 'thread.started' && typeof data.thread_id === 'string' && !nextState.sessionId) {
400
367
  nextState.sessionId = data.thread_id;
401
368
  } else if (eventType === 'thread.started' && typeof data.thread_id === 'string' && data.thread_id !== nextState.sessionId) {
@@ -406,7 +373,6 @@ export const parseCodexExecJsonOutput = (output, state = {}, requestedModelId =
406
373
  } else if (!nextState.sessionId && typeof data.session_id === 'string') {
407
374
  nextState.sessionId = data.session_id;
408
375
  }
409
-
410
376
  for (const [pathName, getter] of CODEX_MODEL_DIAGNOSTIC_PATHS) {
411
377
  if (typeof getter(data) === 'string') observedModelPaths.add(pathName);
412
378
  }
@@ -420,26 +386,22 @@ export const parseCodexExecJsonOutput = (output, state = {}, requestedModelId =
420
386
  if (streamErrorText.includes('401') || streamErrorText.includes('Unauthorized')) nextState.authError = true;
421
387
  nextState.streamErrors.push({ message: streamErrorText });
422
388
  }
423
-
424
389
  const turnFailureText = eventType === 'turn.failed' ? firstErrorText([data.error, data.message]) : '';
425
390
  if (turnFailureText) {
426
391
  if (turnFailureText.includes('401') || turnFailureText.includes('Unauthorized')) nextState.authError = true;
427
392
  nextState.turnFailures.push({ message: turnFailureText });
428
393
  }
429
-
430
394
  if (eventType === 'turn.completed' && data.usage && typeof data.usage === 'object') {
431
395
  const inputTokens = getFirstObservedNumber(data.usage, ['input_tokens']);
432
396
  const cachedInputTokens = getFirstObservedNumber(data.usage, CODEX_CACHE_READ_USAGE_PATHS);
433
397
  const cacheWriteTokens = getFirstObservedNumber(data.usage, CODEX_CACHE_WRITE_USAGE_PATHS);
434
398
  const outputTokens = getFirstObservedNumber(data.usage, ['output_tokens']);
435
399
  const reasoningTokens = getFirstObservedNumber(data.usage, CODEX_REASONING_USAGE_PATHS);
436
-
437
400
  if (hasOwnPath(data.usage, 'input_tokens')) nextState.tokenUsage.tokenFieldAvailability.inputTokens = true;
438
401
  if (hasAnyObservedPath(data.usage, CODEX_CACHE_READ_USAGE_PATHS)) nextState.tokenUsage.tokenFieldAvailability.cacheReadTokens = true;
439
402
  if (hasAnyObservedPath(data.usage, CODEX_CACHE_WRITE_USAGE_PATHS)) nextState.tokenUsage.tokenFieldAvailability.cacheWriteTokens = true;
440
403
  if (hasOwnPath(data.usage, 'output_tokens')) nextState.tokenUsage.tokenFieldAvailability.outputTokens = true;
441
404
  if (hasAnyObservedPath(data.usage, CODEX_REASONING_USAGE_PATHS)) nextState.tokenUsage.tokenFieldAvailability.reasoningTokens = true;
442
-
443
405
  const nonCachedInputTokens = Math.max(0, inputTokens - cachedInputTokens);
444
406
  nextState.tokenUsage.inputTokens += nonCachedInputTokens;
445
407
  nextState.tokenUsage.cacheReadTokens += cachedInputTokens;
@@ -463,19 +425,15 @@ export const parseCodexExecJsonOutput = (output, state = {}, requestedModelId =
463
425
  const usageFieldSet = CODEX_USAGE_FIELD_NAMES.filter(fieldName => hasOwnPath(data.usage, fieldName));
464
426
  if (usageFieldSet.length > 0) nextState.observedUsageFieldSets.push(usageFieldSet);
465
427
  }
466
-
467
428
  const item = data.item;
468
429
  const itemType = typeof item?.type === 'string' ? item.type : null;
469
430
  if (itemType) nextState.itemTypeCounts[itemType] = (nextState.itemTypeCounts[itemType] || 0) + 1;
470
-
471
431
  if ((eventType === 'item.completed' || eventType === 'item.updated') && itemType === 'agent_message' && typeof item.text === 'string' && item.text.trim()) {
472
432
  nextState.resultSummary = item.text;
473
433
  }
474
-
475
434
  if ((eventType === 'item.completed' || eventType === 'item.updated') && itemType === 'reasoning' && typeof item.text === 'string' && item.text.trim()) {
476
435
  nextState.reasoningSummaries.push(item.text);
477
436
  }
478
-
479
437
  if ((eventType === 'item.completed' || eventType === 'item.updated') && itemType === 'collab_tool_call' && item && typeof item === 'object') {
480
438
  upsertCodexSubAgentCall(nextState.subAgentCalls, item, requestedModelId);
481
439
  }
@@ -483,19 +441,15 @@ export const parseCodexExecJsonOutput = (output, state = {}, requestedModelId =
483
441
  if ((eventType === 'item.started' || eventType === 'item.updated' || eventType === 'item.completed') && itemType === 'command_execution' && item && typeof item === 'object') {
484
442
  upsertCodexCommandExecution(nextState.commandExecutions, item);
485
443
  }
486
-
487
444
  if ((eventType === 'item.started' || eventType === 'item.updated' || eventType === 'item.completed') && itemType === 'file_change' && item && typeof item === 'object') {
488
445
  upsertCodexFileChange(nextState.fileChanges, item);
489
446
  }
490
-
491
447
  if ((eventType === 'item.started' || eventType === 'item.updated' || eventType === 'item.completed') && itemType === 'mcp_tool_call' && item && typeof item === 'object') {
492
448
  upsertCodexMcpToolCall(nextState.mcpToolCalls, item);
493
449
  }
494
-
495
450
  if ((eventType === 'item.started' || eventType === 'item.updated' || eventType === 'item.completed') && itemType === 'web_search' && item && typeof item === 'object') {
496
451
  upsertCodexWebSearch(nextState.webSearches, item);
497
452
  }
498
-
499
453
  if ((eventType === 'item.started' || eventType === 'item.updated' || eventType === 'item.completed') && itemType === 'todo_list' && item && typeof item === 'object') {
500
454
  upsertCodexTodoList(nextState.todoLists, item);
501
455
  }
@@ -504,15 +458,12 @@ export const parseCodexExecJsonOutput = (output, state = {}, requestedModelId =
504
458
  upsertCodexItemError(nextState.itemErrors, item);
505
459
  }
506
460
  }
507
-
508
461
  rebuildCodexSubSessionsFromCompactifications(nextState.tokenUsage);
509
462
  nextState.observedModelDiagnosticPaths = [...observedModelPaths];
510
463
  return nextState;
511
464
  };
512
-
513
465
  export const buildCodexResultModelUsage = (modelId, tokenUsage, pricingInfo = null) => {
514
466
  if (!modelId || !tokenUsage) return null;
515
-
516
467
  return {
517
468
  [modelId]: {
518
469
  inputTokens: tokenUsage.inputTokens || 0,
@@ -527,7 +478,6 @@ export const buildCodexResultModelUsage = (modelId, tokenUsage, pricingInfo = nu
527
478
  },
528
479
  };
529
480
  };
530
-
531
481
  const toCost = (tokens, pricePerMillion) => {
532
482
  if (!Number.isFinite(tokens) || !Number.isFinite(pricePerMillion)) return 0;
533
483
  return new Decimal(tokens).mul(pricePerMillion).div(1_000_000).toNumber();
@@ -542,16 +492,13 @@ const buildCodexPricingFallback = (modelId, tokenUsage, error = null) => ({
542
492
  totalCostUSD: null,
543
493
  error,
544
494
  });
545
-
546
495
  export const calculateCodexPricingFromModelInfo = (modelId, tokenUsage, modelInfo) => {
547
496
  if (!modelId) return null;
548
497
  if (!tokenUsage) return buildCodexPricingFallback(modelId, null);
549
498
  if (!modelInfo?.cost) return buildCodexPricingFallback(modelId, tokenUsage, 'Model pricing not found in models.dev API');
550
-
551
499
  const standardCost = modelInfo.cost;
552
500
  const usesLongContextPricing = !!standardCost.context_over_200k && (tokenUsage.peakContextUsage || 0) > CODEX_LONG_CONTEXT_PRICE_THRESHOLD;
553
501
  const cost = usesLongContextPricing ? { ...standardCost, ...standardCost.context_over_200k } : standardCost;
554
-
555
502
  const pricing = {
556
503
  inputPerMillion: cost.input || 0,
557
504
  outputPerMillion: cost.output || 0,
@@ -559,7 +506,6 @@ export const calculateCodexPricingFromModelInfo = (modelId, tokenUsage, modelInf
559
506
  cacheWritePerMillion: cost.cache_write ?? cost.input ?? 0,
560
507
  reasoningPerMillion: cost.reasoning || 0,
561
508
  };
562
-
563
509
  const breakdown = {
564
510
  input: toCost(tokenUsage.inputTokens || 0, pricing.inputPerMillion),
565
511
  output: toCost(tokenUsage.outputTokens || 0, pricing.outputPerMillion),
@@ -571,7 +517,6 @@ export const calculateCodexPricingFromModelInfo = (modelId, tokenUsage, modelInf
571
517
 
572
518
  tokenUsage.contextLimit = tokenUsage.contextLimit || modelInfo.limit?.context || null;
573
519
  tokenUsage.outputLimit = tokenUsage.outputLimit || modelInfo.limit?.output || null;
574
-
575
520
  return {
576
521
  modelId,
577
522
  modelName: modelInfo.name || modelId,
@@ -585,7 +530,6 @@ export const calculateCodexPricingFromModelInfo = (modelId, tokenUsage, modelInf
585
530
  longContextThreshold: usesLongContextPricing ? CODEX_LONG_CONTEXT_PRICE_THRESHOLD : null,
586
531
  };
587
532
  };
588
-
589
533
  export const calculateCodexPricing = async (modelId, tokenUsage) => {
590
534
  if (!modelId) return null;
591
535
  // Issue #2119: a Formal AI session is served by the local Link.Assistant
@@ -598,12 +542,10 @@ export const calculateCodexPricing = async (modelId, tokenUsage) => {
598
542
  return buildCodexPricingFallback(modelId, tokenUsage, error.message);
599
543
  }
600
544
  };
601
-
602
545
  // Function to validate Codex CLI connection
603
546
  export const validateCodexConnection = async (model = defaultModels.codex, verbose = false) => {
604
547
  // Map model alias to full ID
605
548
  const mappedModel = mapModelToId(model);
606
-
607
549
  // Retry configuration
608
550
  const maxRetries = 3;
609
551
  let retryCount = 0;
@@ -615,7 +557,6 @@ export const validateCodexConnection = async (model = defaultModels.codex, verbo
615
557
  } else {
616
558
  await log(`šŸ”„ Retry attempt ${retryCount}/${maxRetries} for Codex validation...`);
617
559
  }
618
-
619
560
  // Check if Codex CLI is installed and get version
620
561
  try {
621
562
  const versionResult = await $`timeout ${Math.floor(timeouts.codexCli / 1000)} codex --version`;
@@ -630,15 +571,12 @@ export const validateCodexConnection = async (model = defaultModels.codex, verbo
630
571
  await log(`āš ļø Codex CLI version check failed (${versionError.code}), proceeding with connection test...`);
631
572
  }
632
573
  }
633
-
634
574
  // Test basic Codex functionality with a simple "echo hi" command
635
575
  // Using exec mode with JSON output for validation
636
576
  const testResult = await $({ env: getCodexExecEnv(verbose) })`printf "echo hi" | timeout ${Math.floor(timeouts.codexCli / 1000)} codex exec --model ${mappedModel} --json --skip-git-repo-check -c model_reasoning_effort="none" --dangerously-bypass-approvals-and-sandbox`;
637
-
638
577
  if (testResult.code !== 0) {
639
578
  const stderr = testResult.stderr?.toString() || '';
640
579
  const stdout = testResult.stdout?.toString() || '';
641
-
642
580
  // Check for authentication errors in both stderr and stdout
643
581
  // Codex CLI may return auth errors in JSON format on stdout
644
582
  if (stderr.includes('auth') || stderr.includes('login') || stdout.includes('Not logged in') || stdout.includes('401 Unauthorized')) {
@@ -654,7 +592,6 @@ export const validateCodexConnection = async (model = defaultModels.codex, verbo
654
592
  if (stdout && !stderr) await log(` Output: ${stdout.trim()}`, { level: 'error' });
655
593
  return false;
656
594
  }
657
-
658
595
  // Success
659
596
  await log('āœ… Codex CLI connection validated successfully');
660
597
  return true;
@@ -664,11 +601,9 @@ export const validateCodexConnection = async (model = defaultModels.codex, verbo
664
601
  return false;
665
602
  }
666
603
  };
667
-
668
604
  // Start the validation
669
605
  return await attemptValidation();
670
606
  };
671
-
672
607
  // Function to handle Codex runtime switching (if applicable)
673
608
  export const handleCodexRuntimeSwitch = async () => {
674
609
  // Codex is typically run as a CLI tool, runtime switching may not be applicable
@@ -678,11 +613,9 @@ export const handleCodexRuntimeSwitch = async () => {
678
613
 
679
614
  /** Check if Playwright MCP is available and connected to Codex @returns {Promise<boolean>} */
680
615
  export const checkPlaywrightMcpAvailability = ensureCodexPlaywrightMcpServer;
681
-
682
616
  // Main function to execute Codex with prompts and settings
683
617
  export const executeCodex = async params => {
684
618
  const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, workspaceTmpDir, isContinueMode, mergeStateStatus, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv, log, formatAligned, getResourceSnapshot, codexPath = 'codex', $ } = params;
685
-
686
619
  if (argv.promptSubagentsViaAgentCommander) {
687
620
  try {
688
621
  await $`which start-agent`;
@@ -692,13 +625,11 @@ export const executeCodex = async params => {
692
625
  await log('āš ļø agent-commander not installed; prompt guidance will be skipped (npm i -g @link-assistant/agent-commander)');
693
626
  }
694
627
  }
695
-
696
628
  // Import prompt building functions from codex.prompts.lib.mjs
697
629
  const { buildUserPrompt, buildSystemPrompt } = await import('./codex.prompts.lib.mjs');
698
630
  const { checkModelVisionCapability } = await import('./claude.lib.mjs');
699
631
  const mappedModel = mapModelToId(argv.model);
700
632
  const modelSupportsVision = await checkModelVisionCapability(mappedModel);
701
-
702
633
  if (argv.verbose) {
703
634
  await log(`šŸ‘ļø Model vision capability: ${modelSupportsVision ? 'supported' : 'not supported'}`, { verbose: true });
704
635
  }
@@ -721,7 +652,6 @@ export const executeCodex = async params => {
721
652
  repo,
722
653
  argv,
723
654
  });
724
-
725
655
  // Build the system prompt
726
656
  const systemPrompt = buildSystemPrompt({
727
657
  owner,
@@ -736,7 +666,6 @@ export const executeCodex = async params => {
736
666
  argv,
737
667
  modelSupportsVision,
738
668
  });
739
-
740
669
  // Log prompt details in verbose mode
741
670
  if (argv.verbose) {
742
671
  await log('\nšŸ“ Final prompt structure:', { verbose: true });
@@ -745,7 +674,6 @@ export const executeCodex = async params => {
745
674
  if (feedbackLines && feedbackLines.length > 0) {
746
675
  await log(' Feedback info: Included', { verbose: true });
747
676
  }
748
-
749
677
  if (argv.dryRun) {
750
678
  await log('\nšŸ“‹ User prompt content:', { verbose: true });
751
679
  await log('---BEGIN USER PROMPT---', { verbose: true });
@@ -757,7 +685,6 @@ export const executeCodex = async params => {
757
685
  await log('---END SYSTEM PROMPT---', { verbose: true });
758
686
  }
759
687
  }
760
-
761
688
  // Issue #1877: deploy the experimental HANDOFF.md Agent Skill so Codex loads
762
689
  // it natively from .agents/skills/handoff/SKILL.md (no-op unless --use-handoff).
763
690
  await deployHandoffSkill({ tempDir, argv, log, $ });
@@ -789,15 +716,12 @@ export const executeCodex = async params => {
789
716
 
790
717
  export const executeCodexCommand = async params => {
791
718
  const { tempDir, branchName, prompt, systemPrompt, argv, log, formatAligned, getResourceSnapshot, forkedRepo, feedbackLines, codexPath, $, owner, repo, prNumber, capabilityPreflight, calculatePricing = calculateCodexPricing, waitForRetryDelay = waitWithCountdown } = params;
792
-
793
719
  const shellQuote = value => `"${String(value).replaceAll('\\', '\\\\').replaceAll('"', '\\"')}"`;
794
720
  const expectedBaseBranch = String(argv?.baseBranch || '').trim();
795
-
796
721
  // Retry configuration
797
722
  let retryCount = 0;
798
723
  let baseBranchInterventionPrompt = null;
799
724
  let baseBranchInterventionResumeCount = 0;
800
-
801
725
  const executeWithRetry = async () => {
802
726
  // Execute codex command from the cloned repository directory
803
727
  if (retryCount === 0) {
@@ -805,7 +729,6 @@ export const executeCodexCommand = async params => {
805
729
  } else {
806
730
  await log(`\n${formatAligned('šŸ”„', 'Retry attempt:', `${retryCount}/${retryLimits.maxTransientErrorRetries}`)}`);
807
731
  }
808
-
809
732
  if (argv.verbose) {
810
733
  await log(` Model: ${argv.model}`, { verbose: true });
811
734
  await log(` Working directory: ${tempDir}`, { verbose: true });
@@ -824,7 +747,6 @@ export const executeCodexCommand = async params => {
824
747
  await log('šŸ“ˆ System resources before execution:', { verbose: true });
825
748
  await log(` Memory: ${resourcesBefore.memory.split('\n')[1]}`, { verbose: true });
826
749
  await log(` Load: ${resourcesBefore.load}`, { verbose: true });
827
-
828
750
  let execCommand;
829
751
  const mappedModel = mapModelToId(argv.model);
830
752
  const { reasoningEffort, source: reasoningEffortSource, rolloutTokenBudget } = resolveCodexReasoningEffort(argv);
@@ -838,18 +760,15 @@ export const executeCodexCommand = async params => {
838
760
  // Issue #2130: "run codex login" is wrong advice for a Formal-AI-served model.
839
761
  const codexAuthRemedyLines = buildAuthRemedyLines({ model: argv.model, vendorRemedy: 'Please run: codex login' });
840
762
  Object.assign(codexEnv, toolInvocation.env);
841
-
842
763
  // For Codex, we combine system and user prompts into a single message
843
764
  // Codex doesn't have separate system prompt support in CLI mode
844
765
  const promptForAttempt = baseBranchInterventionPrompt ? `${prompt}\n\n${baseBranchInterventionPrompt}\n` : prompt;
845
766
  const combinedPrompt = systemPrompt ? `${systemPrompt}\n\n${promptForAttempt}` : promptForAttempt;
846
-
847
767
  // Write the combined prompt to a file for piping
848
768
  // Use OS temporary directory instead of repository workspace to avoid polluting the repo
849
769
  const promptFile = path.join(os.tmpdir(), `codex_prompt_${Date.now()}_${process.pid}.txt`);
850
770
  const lastMessageFile = path.join(os.tmpdir(), `codex_last_message_${Date.now()}_${process.pid}.txt`);
851
771
  await fs.writeFile(promptFile, combinedPrompt);
852
-
853
772
  await log(` Resolved model ID: ${mappedModel}`, { verbose: true });
854
773
  await log(` Execution mode: ${isResumeMode ? 'resume' : 'new exec'}`, { verbose: true });
855
774
  await log(` Prompt file: ${promptFile}`, { verbose: true });
@@ -874,7 +793,6 @@ export const executeCodexCommand = async params => {
874
793
  // Issue #2027: pair GPT-5.6 Sol's multi-agent `ultra` effort with a rollout token budget cap so it stays predictable and does not run away on cost.
875
794
  if (rolloutTokenBudget) codexArgs += ` -c ${shellQuote(`rollout_token_budget=${rolloutTokenBudget}`)}`;
876
795
  codexArgs += ' --dangerously-bypass-approvals-and-sandbox';
877
-
878
796
  // Issue #1706: Append --disable-1m-context and --sub-session-size as Codex -c overrides.
879
797
  let parsedSubSessionSize;
880
798
  try {
@@ -904,14 +822,11 @@ export const executeCodexCommand = async params => {
904
822
  if (disable1mArgs.length) await log(`šŸ“Š Codex --disable-1m-context: ${disable1mArgs.join(' ')}`, { verbose: true });
905
823
  if (subSessionSizeArgs.length) await log(`šŸ“Š Codex --sub-session-size: ${subSessionSizeArgs.join(' ')}`, { verbose: true });
906
824
  }
907
-
908
825
  // Issue #2130: re-export the Formal AI environment inside the `sh -lc` script so a
909
826
  // stale `formal-ai with --global` block in the operator profile cannot override it.
910
827
  const fullCommand = `(${buildFormalAiEnvExports(toolInvocation.env)}cd ${shellQuote(tempDir)} && cat ${shellQuote(promptFile)} | ${toolInvocation.displayCommand} ${codexArgs})`;
911
-
912
828
  const preparedResult = await logPreparedToolCommand({ argv, fullCommand, log, formatAligned });
913
829
  if (preparedResult) return preparedResult;
914
-
915
830
  try {
916
831
  let interactiveHandler = null;
917
832
  if (argv.interactiveMode && owner && repo && prNumber) {
@@ -936,7 +851,6 @@ export const executeCodexCommand = async params => {
936
851
  mirror: false,
937
852
  env: codexEnv,
938
853
  })`sh -lc ${fullCommand}`;
939
-
940
854
  await log(`${formatAligned('šŸ“‹', 'Command details:', '')}`);
941
855
  await log(formatAligned('šŸ“‚', 'Working directory:', tempDir, 2));
942
856
  await log(formatAligned('🌿', 'Branch:', branchName, 2));
@@ -945,9 +859,7 @@ export const executeCodexCommand = async params => {
945
859
  if (argv.fork && forkedRepo) {
946
860
  await log(formatAligned('šŸ“', 'Fork:', forkedRepo, 2));
947
861
  }
948
-
949
862
  await log(`\n${formatAligned('ā–¶ļø', 'Streaming output:', '')}\n`);
950
-
951
863
  let exitCode = 0;
952
864
  let sessionId = null;
953
865
  let limitReached = false;
@@ -989,7 +901,6 @@ export const executeCodexCommand = async params => {
989
901
  telemetryEventCounts: {},
990
902
  turnLifecycle: [],
991
903
  };
992
-
993
904
  // Issue #2119: a process chunk boundary can fall in the middle of an
994
905
  // NDJSON record. Parsing each raw chunk dropped both halves of a split
995
906
  // record (token usage, session id, auth errors). Buffer whole lines so
@@ -1005,10 +916,8 @@ export const executeCodexCommand = async params => {
1005
916
  }
1006
917
  lastMessage = raw;
1007
918
  const output = codexStdoutLines.write(raw);
1008
-
1009
919
  codexJsonState = parseCodexExecJsonOutput(output, codexJsonState, mappedModel, { source: 'stdout' });
1010
920
  await baseBranchCommandIntervention.handleCommandExecutions(codexJsonState.commandExecutions);
1011
-
1012
921
  if (interactiveHandler || progressMonitor) {
1013
922
  for (const rawLine of output.split('\n')) {
1014
923
  const line = rawLine.trim();
@@ -1025,12 +934,10 @@ export const executeCodexCommand = async params => {
1025
934
  }
1026
935
  }
1027
936
  }
1028
-
1029
937
  if (codexJsonState.sessionId && codexJsonState.sessionId !== sessionId) {
1030
938
  sessionId = codexJsonState.sessionId;
1031
939
  await log(`šŸ“Œ Session ID: ${sessionId}`);
1032
940
  }
1033
-
1034
941
  if (codexJsonState.resultSummary) {
1035
942
  lastTextContent = codexJsonState.resultSummary;
1036
943
  }
@@ -1042,7 +949,6 @@ export const executeCodexCommand = async params => {
1042
949
  for (const line of codexAuthRemedyLines) await log(line, { level: 'error' });
1043
950
  }
1044
951
  }
1045
-
1046
952
  if (chunk.type === 'stderr') {
1047
953
  const rawError = chunk.data.toString();
1048
954
  if (rawError && argv.verbose) {
@@ -1056,7 +962,6 @@ export const executeCodexCommand = async params => {
1056
962
  exitCode = chunk.code;
1057
963
  }
1058
964
  }
1059
-
1060
965
  // Release any line that was still being assembled when the stream ended.
1061
966
  for (const [source, remaining] of [
1062
967
  ['stdout', codexStdoutLines.flush()],
@@ -1066,7 +971,6 @@ export const executeCodexCommand = async params => {
1066
971
  codexJsonState = parseCodexExecJsonOutput(remaining, codexJsonState, mappedModel, { source });
1067
972
  await baseBranchCommandIntervention.handleCommandExecutions(codexJsonState.commandExecutions);
1068
973
  }
1069
-
1070
974
  if (codexJsonState.sessionId && codexJsonState.sessionId !== sessionId) {
1071
975
  sessionId = codexJsonState.sessionId;
1072
976
  await log(`šŸ“Œ Session ID: ${sessionId}`);
@@ -1084,7 +988,6 @@ export const executeCodexCommand = async params => {
1084
988
  if (interactiveHandler) {
1085
989
  await interactiveHandler.flush();
1086
990
  }
1087
-
1088
991
  // Issue #2130: a failed run legitimately has no final message and no
1089
992
  // turn.completed usage, so those outcomes must not be logged as warnings.
1090
993
  const runFailed = codexRunAlreadyFailed({ state: codexJsonState, exitCode });
@@ -1101,11 +1004,9 @@ export const executeCodexCommand = async params => {
1101
1004
  await log(lastMessageFromFile, { verbose: true });
1102
1005
  lastTextContent = lastTextContent || lastMessageFromFile;
1103
1006
  }
1104
-
1105
1007
  for (const line of buildCodexRunDiagnostics({ state: codexJsonState, exitCode, mappedModel })) {
1106
1008
  await log(line.message, line.options);
1107
1009
  }
1108
-
1109
1010
  const baseBranchIntervention = baseBranchCommandIntervention.getIntervention();
1110
1011
  if (baseBranchIntervention) {
1111
1012
  if ((sessionId || argv.resume) && baseBranchInterventionResumeCount < 1) {
@@ -1115,7 +1016,6 @@ export const executeCodexCommand = async params => {
1115
1016
  await log('\nšŸ”„ Resuming Codex with requested base-branch correction prompt...');
1116
1017
  return await executeWithRetry();
1117
1018
  }
1118
-
1119
1019
  return {
1120
1020
  success: false,
1121
1021
  sessionId,
@@ -1157,17 +1057,14 @@ export const executeCodexCommand = async params => {
1157
1057
  resultSummary: lastTextContent || null,
1158
1058
  ...outcome,
1159
1059
  });
1160
-
1161
1060
  // Check for authentication errors first - these should never be retried
1162
1061
  if (authError) {
1163
1062
  await logCodexResourceSnapshot({ getResourceSnapshot, log });
1164
-
1165
1063
  // Throw an error to stop retries and propagate the auth failure
1166
1064
  const error = new Error(`Codex authentication failed - 401 Unauthorized.${codexAuthRemedyLines.map(line => ` ${line.replace(/^\s*šŸ’”\s*/, '')}`).join('')}`);
1167
1065
  error.isAuthError = true;
1168
1066
  throw error;
1169
1067
  }
1170
-
1171
1068
  const codexErrorSummary = getCodexErrorEventSummary(codexJsonState);
1172
1069
  if (codexErrorSummary.ignoredEvents.length > 0) {
1173
1070
  const ignoredMessages = [...new Set(codexErrorSummary.ignoredEvents.map(event => event.message))].join('; ');
@@ -1191,7 +1088,6 @@ export const executeCodexCommand = async params => {
1191
1088
  await log(`šŸ” Parsed reset time: ${JSON.stringify(limitInfo.resetTime)}, timezone: ${JSON.stringify(limitInfo.timezone)}`, { verbose: true });
1192
1089
  limitReached = true;
1193
1090
  limitResetTime = limitInfo.resetTime;
1194
-
1195
1091
  // Issue #942: build proper solve resume command (preserves tool/model/dir).
1196
1092
  const solveResumeCmd = __codexBuildSolveResumeCmd(argv, sessionId, tempDir);
1197
1093
  const messageLines = formatUsageLimitMessage({
@@ -1225,12 +1121,9 @@ export const executeCodexCommand = async params => {
1225
1121
  await log(`\n\nāŒ Codex emitted error event: ${codexErrorSummary.message}`, { level: 'error' });
1226
1122
  await log(` Error events: item=${codexErrorSummary.counts.item}, turn=${codexErrorSummary.counts.turn}, stream=${codexErrorSummary.counts.stream}`, { level: 'error' });
1227
1123
  }
1228
-
1229
1124
  await logCodexResourceSnapshot({ getResourceSnapshot, log });
1230
-
1231
1125
  return buildRunResult({ success: false, errorInfo: codexErrorSummary, result: codexErrorSummary.message });
1232
1126
  }
1233
-
1234
1127
  if (exitCode !== 0) {
1235
1128
  const retryableError = classifyRetryableError(lastMessage);
1236
1129
  if (retryableError.isRetryable) {
@@ -1251,7 +1144,6 @@ export const executeCodexCommand = async params => {
1251
1144
  }
1252
1145
  await log(`\n\nāŒ ${retryableError.label} persisted after ${maxRetries} retries`, { level: 'error' });
1253
1146
  }
1254
-
1255
1147
  // Check for usage limit errors first (more specific)
1256
1148
  const limitInfo = detectUsageLimit(lastMessage);
1257
1149
  if (limitInfo.isUsageLimit) {
@@ -1270,7 +1162,6 @@ export const executeCodexCommand = async params => {
1270
1162
  sessionId,
1271
1163
  solveResumeCommand: solveResumeCmd,
1272
1164
  });
1273
-
1274
1165
  for (const line of messageLines) {
1275
1166
  await log(line, { level: 'warning' });
1276
1167
  }
@@ -1279,12 +1170,9 @@ export const executeCodexCommand = async params => {
1279
1170
  } else {
1280
1171
  await log(`\n\nāŒ Codex command failed with exit code ${exitCode}`, { level: 'error' });
1281
1172
  }
1282
-
1283
1173
  await logCodexResourceSnapshot({ getResourceSnapshot, log });
1284
-
1285
1174
  return buildRunResult({ success: false, errorInfo: getCodexErrorEventSummary(codexJsonState) });
1286
1175
  }
1287
-
1288
1176
  // Issue #2102: a rejected `request_plugin_install` means codex asked for a
1289
1177
  // capability the preflight did not provision, and under `codex exec` that
1290
1178
  // request can never succeed — so a run that produced nothing is blocked,
@@ -1297,7 +1185,6 @@ export const executeCodexCommand = async params => {
1297
1185
 
1298
1186
  return buildRunResult({ success: false, errorInfo: getCodexErrorEventSummary(codexJsonState), pluginProvisioning, result: [pluginProvisioning.message, ...pluginProvisioning.guidance].join(' ') });
1299
1187
  }
1300
-
1301
1188
  // Issue #1990: exit code 0 and the absence of a fatal codex error event are
1302
1189
  // necessary but NOT sufficient for success. Verify the run actually
1303
1190
  // completed its turn before declaring success. A broken-but-exit-0 run (the
@@ -1307,7 +1194,6 @@ export const executeCodexCommand = async params => {
1307
1194
  const completionHealth = getCodexCompletionHealth(codexJsonState, { lastMessage });
1308
1195
  if (!completionHealth.healthy) {
1309
1196
  await reportCodexCompletionFailure({ completionHealth, log, getResourceSnapshot });
1310
-
1311
1197
  // Issue #1990: preserve the codex session so an outer full restart can
1312
1198
  // resume with context (mirrors the transient-error retry above and the
1313
1199
  // `--tool claude` behavior). We do NOT inline-retry within the same broken
@@ -1315,10 +1201,8 @@ export const executeCodexCommand = async params => {
1315
1201
  // docker isolation) the container filesystem are preserved for a clean
1316
1202
  // restart at the orchestration level.
1317
1203
  if (sessionId && !argv.resume) argv.resume = sessionId;
1318
-
1319
1204
  return buildRunResult({ success: false, errorInfo: getCodexErrorEventSummary(codexJsonState), completionHealth, pluginProvisioning, incompleteSession: completionHealth.incompleteSession, diskPressureDetected: completionHealth.diskPressureDetected, result: completionHealth.reasons.join(' ') });
1320
1205
  }
1321
-
1322
1206
  await log('\n\nāœ… Codex command completed');
1323
1207
 
1324
1208
  // Issue #1263: Log if result summary was captured
@@ -1327,7 +1211,6 @@ export const executeCodexCommand = async params => {
1327
1211
  } else {
1328
1212
  await log('āš ļø No result summary captured from Codex output or last-message file', { level: 'warning', verbose: true });
1329
1213
  }
1330
-
1331
1214
  return buildRunResult({ success: true, pluginProvisioning });
1332
1215
  } catch (error) {
1333
1216
  // Don't report auth errors to Sentry as they are user configuration issues
@@ -1339,14 +1222,11 @@ export const executeCodexCommand = async params => {
1339
1222
  operation: 'run_codex_command',
1340
1223
  });
1341
1224
  }
1342
-
1343
1225
  await log(`\n\nāŒ Error executing Codex command: ${error.message}`, { level: 'error' });
1344
-
1345
1226
  // Re-throw auth errors to stop any outer retry loops
1346
1227
  if (error.isAuthError) {
1347
1228
  throw error;
1348
1229
  }
1349
-
1350
1230
  return {
1351
1231
  success: false,
1352
1232
  sessionId: null,
@@ -1369,7 +1249,6 @@ export const executeCodexCommand = async params => {
1369
1249
  // Start the execution with retry logic
1370
1250
  return await executeWithRetry();
1371
1251
  };
1372
-
1373
1252
  export const checkForUncommittedChanges = async (tempDir, owner, repo, branchName, $, log, autoCommit = false, autoRestartEnabled = true) => {
1374
1253
  // Similar to Claude and OpenCode version, check for uncommitted changes
1375
1254
  await log('\nšŸ” Checking for uncommitted changes...');
@@ -1377,17 +1256,14 @@ export const checkForUncommittedChanges = async (tempDir, owner, repo, branchNam
1377
1256
  await ensureAiToolScratchIgnored(tempDir, log);
1378
1257
  try {
1379
1258
  const gitStatusResult = await $({ cwd: tempDir })`git status --porcelain 2>&1`;
1380
-
1381
1259
  if (gitStatusResult.code === 0) {
1382
1260
  const statusOutput = filterAiToolScratchFromStatus(gitStatusResult.stdout.toString().trim());
1383
-
1384
1261
  if (statusOutput) {
1385
1262
  await log('šŸ“ Found uncommitted changes');
1386
1263
  await log('Changes:');
1387
1264
  for (const line of statusOutput.split('\n')) {
1388
1265
  await log(` ${line}`);
1389
1266
  }
1390
-
1391
1267
  if (autoCommit) {
1392
1268
  await log('šŸ’¾ Auto-committing changes (--auto-commit-uncommitted-changes is enabled)...');
1393
1269
 
@@ -1395,12 +1271,9 @@ export const checkForUncommittedChanges = async (tempDir, owner, repo, branchNam
1395
1271
  if (addResult.code === 0) {
1396
1272
  const commitMessage = 'Auto-commit: Changes made by Codex during problem-solving session';
1397
1273
  const commitResult = await $({ cwd: tempDir })`git commit -m ${commitMessage}`;
1398
-
1399
1274
  if (commitResult.code === 0) {
1400
1275
  await log('āœ… Changes committed successfully');
1401
-
1402
1276
  const pushResult = await $({ cwd: tempDir })`git push origin ${branchName} 2>&1`;
1403
-
1404
1277
  if (pushResult.code === 0) {
1405
1278
  await log('āœ… Changes pushed successfully');
1406
1279
  } else {
@@ -1455,7 +1328,6 @@ export const checkForUncommittedChanges = async (tempDir, owner, repo, branchNam
1455
1328
  return false;
1456
1329
  }
1457
1330
  };
1458
-
1459
1331
  // Export all functions as default object too
1460
1332
  export default {
1461
1333
  validateCodexConnection,