@link-assistant/hive-mind 1.22.1 → 1.22.3

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,30 @@
1
1
  # @link-assistant/hive-mind
2
2
 
3
+ ## 1.22.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 34a6937: Fix false positive error detection when agent recovers from transient errors (Issue #1276)
8
+ - Trust exit code 0 as authoritative indicator of success even if errors occurred during execution
9
+ - Clear streaming error detection when agent completes successfully (emits session.idle or "exiting loop")
10
+ - Fix message extraction to prefer "error" field over "message" field for agent error events
11
+ - Add tests for agent recovery scenarios and false positive prevention
12
+
13
+ ## 1.22.2
14
+
15
+ ### Patch Changes
16
+
17
+ - 5b018dc: fix: prevent CI/CD release blocking by enabling cancel-in-progress for main branch (Issue #1274)
18
+
19
+ When multiple commits are pushed to main quickly (e.g., multiple PRs merged in succession),
20
+ the old concurrency configuration would queue newer runs indefinitely until older runs complete.
21
+ This caused releases to be blocked when Docker ARM64 builds took too long.
22
+
23
+ Changes:
24
+ - Add `cancel-in-progress: true` for main branch to allow newer releases to proceed
25
+ - PR branches still queue runs to avoid cancelling checks during development
26
+ - Document the issue and solution in docs/case-studies/issue-1274/
27
+
3
28
  ## 1.22.1
4
29
 
5
30
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "1.22.1",
3
+ "version": "1.22.3",
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/agent.lib.mjs CHANGED
@@ -559,6 +559,9 @@ export const executeAgentCommand = async params => {
559
559
  // Post-hoc detection on fullOutput can miss errors if NDJSON lines get concatenated without newlines
560
560
  let streamingErrorDetected = false;
561
561
  let streamingErrorMessage = null;
562
+ // Issue #1276: Track successful completion events to clear error flags
563
+ // When agent emits session.idle or disposal events, it means it recovered and completed successfully
564
+ let agentCompletedSuccessfully = false;
562
565
  // Issue #1250: Accumulate token usage during streaming instead of parsing fullOutput later
563
566
  // This fixes the issue where NDJSON lines get concatenated without newlines, breaking JSON.parse
564
567
  const streamingTokenUsage = {
@@ -641,6 +644,12 @@ export const executeAgentCommand = async params => {
641
644
  // Explicit result message (like Claude outputs)
642
645
  lastTextContent = data.result;
643
646
  }
647
+ // Issue #1276: Detect successful completion events
648
+ // When agent emits session.idle or log with "exiting loop" message, it completed successfully
649
+ // This means any previous error events were recovered from (e.g., timeout then retry)
650
+ if (data.type === 'session.idle' || (data.type === 'log' && data.message === 'exiting loop')) {
651
+ agentCompletedSuccessfully = true;
652
+ }
644
653
  } catch {
645
654
  // Not JSON - log as plain text
646
655
  await log(line);
@@ -698,6 +707,11 @@ export const executeAgentCommand = async params => {
698
707
  } else if (stderrData.type === 'result' && stderrData.result) {
699
708
  lastTextContent = stderrData.result;
700
709
  }
710
+ // Issue #1276: Detect successful completion events (stderr)
711
+ // When agent emits session.idle or log with "exiting loop" message, it completed successfully
712
+ if (stderrData.type === 'session.idle' || (stderrData.type === 'log' && stderrData.message === 'exiting loop')) {
713
+ agentCompletedSuccessfully = true;
714
+ }
701
715
  } catch {
702
716
  // Not JSON - log as plain text
703
717
  await log(stderrLine);
@@ -745,6 +759,19 @@ export const executeAgentCommand = async params => {
745
759
  // Only check for JSON error messages, not pattern matching in output
746
760
  const outputError = detectAgentErrors(fullOutput);
747
761
 
762
+ // Issue #1276: Clear streaming error detection if agent completed successfully
763
+ // When an error occurs during execution (e.g., timeout) but the agent recovers and completes,
764
+ // we should NOT treat it as a failure. The exit code is the authoritative success indicator.
765
+ // Check for: exit code 0 AND (completion event detected OR no streaming error)
766
+ if (exitCode === 0 && (agentCompletedSuccessfully || !streamingErrorDetected)) {
767
+ // Agent exited successfully - clear any streaming errors that were recovered from
768
+ if (streamingErrorDetected && agentCompletedSuccessfully) {
769
+ await log(`ℹ️ Agent recovered from earlier error and completed successfully`, { verbose: true });
770
+ }
771
+ streamingErrorDetected = false;
772
+ streamingErrorMessage = null;
773
+ }
774
+
748
775
  // Issue #1201: Use streaming detection as primary, post-hoc as fallback
749
776
  // Streaming detection is more reliable because it parses each JSON line as it arrives,
750
777
  // avoiding issues where NDJSON lines get concatenated without newline delimiters in fullOutput
@@ -770,9 +797,16 @@ export const executeAgentCommand = async params => {
770
797
  if (fullOutput.includes(pattern)) {
771
798
  outputError.detected = true;
772
799
  outputError.type = type;
773
- // Try to extract the error message from the output
774
- const messageMatch = fullOutput.match(/"message":\s*"([^"]+)"/);
775
- outputError.match = messageMatch ? messageMatch[1] : `Error event detected in output (fallback pattern match for ${pattern})`;
800
+ // Issue #1276: Try to extract the error message from the output
801
+ // First try "error" field (agent error format), then "message" field (generic format)
802
+ // Find the error closest to the "type": "error" pattern for more accurate extraction
803
+ const patternIndex = fullOutput.indexOf(pattern);
804
+ const relevantOutput = patternIndex >= 0 ? fullOutput.substring(patternIndex) : fullOutput;
805
+ // Look for "error" or "message" field near the error type pattern
806
+ const errorFieldMatch = relevantOutput.match(/"error":\s*"([^"]+)"/);
807
+ const messageFieldMatch = relevantOutput.match(/"message":\s*"([^"]+)"/);
808
+ // Prefer "error" field over "message" for agent error events
809
+ outputError.match = errorFieldMatch ? errorFieldMatch[1] : messageFieldMatch ? messageFieldMatch[1] : `Error event detected in output (fallback pattern match for ${pattern})`;
776
810
  await log(`⚠️ Error event detected via fallback pattern match: ${outputError.match}`, { level: 'warning' });
777
811
  break;
778
812
  }