@link-assistant/hive-mind 1.56.4 → 1.56.5

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @link-assistant/hive-mind
2
2
 
3
+ ## 1.56.5
4
+
5
+ ### Patch Changes
6
+
7
+ - 0447110: Treat structured Codex error events as failed tool executions even when the Codex process exits with code 0.
8
+
3
9
  ## 1.56.4
4
10
 
5
11
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "1.56.4",
3
+ "version": "1.56.5",
4
4
  "description": "AI-powered issue solver and hive mind for collaborative problem solving",
5
5
  "main": "src/hive.mjs",
6
6
  "type": "module",
package/src/codex.lib.mjs CHANGED
@@ -183,6 +183,57 @@ const upsertCodexItemError = (itemErrors, item) => {
183
183
  });
184
184
  };
185
185
 
186
+ const unwrapCodexErrorMessage = value => {
187
+ if (!value) return '';
188
+ if (typeof value !== 'string') {
189
+ if (typeof value?.error?.message === 'string') return unwrapCodexErrorMessage(value.error.message);
190
+ if (typeof value?.message === 'string') return unwrapCodexErrorMessage(value.message);
191
+ return String(value);
192
+ }
193
+
194
+ let text = value.trim();
195
+ for (let i = 0; i < 3; i++) {
196
+ if (!text.startsWith('{') && !text.startsWith('[')) break;
197
+ try {
198
+ const parsed = JSON.parse(text);
199
+ if (typeof parsed?.error?.message === 'string') return unwrapCodexErrorMessage(parsed.error.message);
200
+ if (typeof parsed?.message === 'string') {
201
+ text = parsed.message.trim();
202
+ continue;
203
+ }
204
+ return JSON.stringify(parsed);
205
+ } catch {
206
+ break;
207
+ }
208
+ }
209
+ return text;
210
+ };
211
+
212
+ export const getCodexErrorEventSummary = codexJsonState => {
213
+ const events = [];
214
+ const addEvents = (type, items = []) => {
215
+ for (const item of items) {
216
+ const message = unwrapCodexErrorMessage(item?.message);
217
+ events.push({ type, message: message || 'Codex emitted an error event' });
218
+ }
219
+ };
220
+
221
+ addEvents('item', codexJsonState?.itemErrors);
222
+ addEvents('turn', codexJsonState?.turnFailures);
223
+ addEvents('stream', codexJsonState?.streamErrors);
224
+
225
+ return {
226
+ hasError: events.length > 0,
227
+ message: events[0]?.message || null,
228
+ events,
229
+ counts: {
230
+ item: codexJsonState?.itemErrors?.length || 0,
231
+ turn: codexJsonState?.turnFailures?.length || 0,
232
+ stream: codexJsonState?.streamErrors?.length || 0,
233
+ },
234
+ };
235
+ };
236
+
186
237
  export const parseCodexExecJsonOutput = (output, state = {}, requestedModelId = null) => {
187
238
  const nextState = {
188
239
  sessionId: state.sessionId || null,
@@ -597,7 +648,7 @@ export const executeCodex = async params => {
597
648
  };
598
649
 
599
650
  export const executeCodexCommand = async params => {
600
- const { tempDir, branchName, prompt, systemPrompt, argv, log, formatAligned, getResourceSnapshot, forkedRepo, feedbackLines, codexPath, $, owner, repo, prNumber } = params;
651
+ const { tempDir, branchName, prompt, systemPrompt, argv, log, formatAligned, getResourceSnapshot, forkedRepo, feedbackLines, codexPath, $, owner, repo, prNumber, calculatePricing = calculateCodexPricing } = params;
601
652
 
602
653
  const shellQuote = value => `"${String(value).replaceAll('\\', '\\\\').replaceAll('"', '\\"')}"`;
603
654
 
@@ -852,7 +903,7 @@ export const executeCodexCommand = async params => {
852
903
  }
853
904
 
854
905
  const firstActualModelId = mappedModel;
855
- const pricingInfo = firstActualModelId ? await calculateCodexPricing(firstActualModelId, codexJsonState.tokenUsage.stepCount > 0 ? codexJsonState.tokenUsage : null) : null;
906
+ const pricingInfo = firstActualModelId ? await calculatePricing(firstActualModelId, codexJsonState.tokenUsage.stepCount > 0 ? codexJsonState.tokenUsage : null) : null;
856
907
  if (pricingInfo?.totalCostUSD !== null && pricingInfo?.totalCostUSD !== undefined) {
857
908
  await log(`šŸ’° Codex public pricing estimate: $${new Decimal(pricingInfo.totalCostUSD).toFixed(6)}`, { verbose: true });
858
909
  if (pricingInfo.usesLongContextPricing) {
@@ -876,6 +927,49 @@ export const executeCodexCommand = async params => {
876
927
  throw error;
877
928
  }
878
929
 
930
+ const codexErrorSummary = getCodexErrorEventSummary(codexJsonState);
931
+ if (codexErrorSummary.hasError) {
932
+ const limitInfo = detectUsageLimit(codexErrorSummary.message || lastMessage);
933
+ if (limitInfo.isUsageLimit) {
934
+ limitReached = true;
935
+ limitResetTime = limitInfo.resetTime;
936
+
937
+ const messageLines = formatUsageLimitMessage({
938
+ tool: 'OpenAI Codex',
939
+ resetTime: limitInfo.resetTime,
940
+ sessionId,
941
+ resumeCommand: sessionId ? `${process.argv[0]} ${process.argv[1]} ${argv.url} --resume ${sessionId}` : null,
942
+ });
943
+
944
+ for (const line of messageLines) {
945
+ await log(line, { level: 'warning' });
946
+ }
947
+ } else {
948
+ await log(`\n\nāŒ Codex emitted error event: ${codexErrorSummary.message}`, { level: 'error' });
949
+ await log(` Error events: item=${codexErrorSummary.counts.item}, turn=${codexErrorSummary.counts.turn}, stream=${codexErrorSummary.counts.stream}`, { level: 'error' });
950
+ }
951
+
952
+ const resourcesAfter = await getResourceSnapshot();
953
+ await log('\nšŸ“ˆ System resources after execution:', { verbose: true });
954
+ await log(` Memory: ${resourcesAfter.memory.split('\n')[1]}`, { verbose: true });
955
+ await log(` Load: ${resourcesAfter.load}`, { verbose: true });
956
+
957
+ return {
958
+ success: false,
959
+ sessionId,
960
+ limitReached,
961
+ limitResetTime,
962
+ pricingInfo,
963
+ publicPricingEstimate: pricingInfo?.totalCostUSD ?? null,
964
+ resultModelUsage,
965
+ subAgentCalls: codexJsonState.subAgentCalls.length > 0 ? codexJsonState.subAgentCalls : null,
966
+ codexJsonDetails: codexJsonState,
967
+ errorInfo: codexErrorSummary,
968
+ result: codexErrorSummary.message,
969
+ resultSummary: lastTextContent || null, // Issue #1263: Use last text content from JSON output stream
970
+ };
971
+ }
972
+
879
973
  if (exitCode !== 0) {
880
974
  // Check for usage limit errors first (more specific)
881
975
  const limitInfo = detectUsageLimit(lastMessage);
@@ -915,6 +1009,7 @@ export const executeCodexCommand = async params => {
915
1009
  resultModelUsage,
916
1010
  subAgentCalls: codexJsonState.subAgentCalls.length > 0 ? codexJsonState.subAgentCalls : null,
917
1011
  codexJsonDetails: codexJsonState,
1012
+ errorInfo: getCodexErrorEventSummary(codexJsonState),
918
1013
  resultSummary: lastTextContent || null, // Issue #1263: Use last text content from JSON output stream
919
1014
  };
920
1015
  }
@@ -965,6 +1060,8 @@ export const executeCodexCommand = async params => {
965
1060
  limitResetTime: null,
966
1061
  pricingInfo: null,
967
1062
  publicPricingEstimate: null,
1063
+ errorInfo: { hasError: true, message: error.message, events: [{ type: 'exception', message: error.message }], counts: { item: 0, turn: 0, stream: 0 } },
1064
+ result: error.message,
968
1065
  resultSummary: null, // Issue #1263: No result summary available on error
969
1066
  };
970
1067
  } finally {