@link-assistant/hive-mind 2.0.23 → 2.0.25
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 +25 -0
- package/package.json +16 -16
- package/src/agent-commander.lib.mjs +1 -1
- package/src/auto-language.lib.mjs +1 -1
- package/src/claude-quiet-config.lib.mjs +1 -1
- package/src/claude.lib.mjs +1 -1
- package/src/cleanup.mjs +1 -1
- package/src/cleanup.os.lib.mjs +1 -1
- package/src/codex-health.lib.mjs +191 -0
- package/src/codex.lib.mjs +57 -102
- package/src/gemini.lib.mjs +47 -0
- package/src/git.lib.mjs +2 -2
- package/src/github-repository-names.lib.mjs +1 -1
- package/src/github.lib.mjs +1 -1
- package/src/handoff-skill.lib.mjs +1 -1
- package/src/hive-screens.lib.mjs +1 -1
- package/src/i18n.lib.mjs +1 -1
- package/src/instrument.mjs +1 -1
- package/src/interactive-mode.lib.mjs +3 -3
- package/src/isolation-runner.lib.mjs +100 -3
- package/src/qwen.lib.mjs +43 -0
- package/src/session-monitor.lib.mjs +57 -15
- package/src/session-store.lib.mjs +1 -1
- package/src/solve.auto-pr.lib.mjs +2 -2
- package/src/solve.disk-diagnostics.lib.mjs +50 -29
- package/src/solve.escalate.lib.mjs +0 -3
- package/src/solve.keep-working.lib.mjs +1 -1
- package/src/solve.repository.lib.mjs +1 -1
- package/src/solve.validation.lib.mjs +1 -2
- package/src/telegram-bot-launcher.lib.mjs +2 -6
- package/src/telegram-command-execution.lib.mjs +4 -0
- package/src/telegram-isolation.lib.mjs +1 -0
- package/src/telegram-tokens-command.lib.mjs +1 -1
- package/src/telegram-top-command.lib.mjs +1 -1
- package/src/tool-run-health.lib.mjs +64 -0
- package/src/use-m-bootstrap.lib.mjs +1 -1
- package/src/useless-tools.lib.mjs +1 -1
- package/src/youtrack/solve.youtrack.lib.mjs +2 -2
- package/src/youtrack/youtrack.lib.mjs +1 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// Tool-agnostic run-health analysis (Issue #1990).
|
|
2
|
+
//
|
|
3
|
+
// Background: under docker isolation two long-running `solve --tool codex` tasks
|
|
4
|
+
// reported SUCCESS (Exit Code: 0) while their containers had run out of disk —
|
|
5
|
+
// the AI session was cut off mid-run, no commits were produced, yet the process
|
|
6
|
+
// exited 0. Reporting that as success also discarded the container filesystem we
|
|
7
|
+
// needed to inspect and retry from.
|
|
8
|
+
//
|
|
9
|
+
// codex.lib.mjs gets a bespoke gate (paired turn.started/turn.completed lifecycle
|
|
10
|
+
// — see codex-health.lib.mjs) and claude.lib.mjs already requires its final
|
|
11
|
+
// `result` event (shouldFailClaudeStreamWithoutResult). This module provides the
|
|
12
|
+
// equivalent gate for the tools whose stream-json output (adopted from the Claude
|
|
13
|
+
// Agent SDK schema) ends with a single terminal `result` event: gemini-cli and
|
|
14
|
+
// qwen-code. An exit-0 run that clearly began work but never emitted that
|
|
15
|
+
// terminal event was interrupted and must NOT be reported as success.
|
|
16
|
+
//
|
|
17
|
+
// opencode is deliberately NOT gated here. Its `run --format json` output has no
|
|
18
|
+
// single terminal completion event we have verified is always emitted before a
|
|
19
|
+
// clean exit — opencode.lib.mjs treats several event types ('text', 'assistant',
|
|
20
|
+
// 'message', 'result', 'step_finish') as best-effort and decides success purely
|
|
21
|
+
// on the exit code. Gating opencode on a terminal event without first confirming
|
|
22
|
+
// upstream that it is reliably flushed would risk converting genuine successes
|
|
23
|
+
// into failures, so it is left as follow-up. See docs/case-studies/issue-1990.
|
|
24
|
+
//
|
|
25
|
+
// Disk-exhaustion strings ("No space left on device", ENOSPC) are surfaced only
|
|
26
|
+
// as supporting *diagnostics* — never an independent failure gate — to avoid the
|
|
27
|
+
// issue #1955 class of false positive where a tool echoes a command's stdout that
|
|
28
|
+
// merely mentions the phrase.
|
|
29
|
+
|
|
30
|
+
import { isENOSPC } from './lib.mjs';
|
|
31
|
+
|
|
32
|
+
export const getTerminalEventCompletionHealth = ({ eventCounts = {}, terminalEventTypes = ['result'], hadActivity = false, diskEvidenceTexts = [] } = {}) => {
|
|
33
|
+
const terminalCount = terminalEventTypes.reduce((sum, type) => sum + (eventCounts[type] || 0), 0);
|
|
34
|
+
|
|
35
|
+
// Only flag a run that did work but never reached its terminal event. A run
|
|
36
|
+
// with no activity at all is handled separately by each tool (e.g. gemini's
|
|
37
|
+
// emittedNoEvents check) and must not be double-counted here.
|
|
38
|
+
const incompleteSession = hadActivity && terminalCount === 0;
|
|
39
|
+
|
|
40
|
+
const diskEvidence = [];
|
|
41
|
+
for (const { source, text } of diskEvidenceTexts) {
|
|
42
|
+
if (text && isENOSPC(text)) {
|
|
43
|
+
diskEvidence.push({ source, text: String(text).replace(/\s+/g, ' ').trim().slice(0, 300) });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const diskPressureDetected = diskEvidence.length > 0;
|
|
47
|
+
|
|
48
|
+
const reasons = [];
|
|
49
|
+
if (incompleteSession) {
|
|
50
|
+
reasons.push(`The tool exited 0 but never emitted its terminal completion event (${terminalEventTypes.join('/')}); the session was cut off mid-run.`);
|
|
51
|
+
if (diskPressureDetected) {
|
|
52
|
+
reasons.push(`Disk-exhaustion signals were present in ${diskEvidence.length} location(s) (e.g. "No space left on device") — the likely cause of the interrupted session.`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
healthy: !incompleteSession,
|
|
58
|
+
incompleteSession,
|
|
59
|
+
diskPressureDetected,
|
|
60
|
+
diskEvidence,
|
|
61
|
+
terminalCount,
|
|
62
|
+
reasons,
|
|
63
|
+
};
|
|
64
|
+
};
|
|
@@ -24,7 +24,7 @@ export const fetchUseMCodeFromCdn = async ({ fetcher = fetch } = {}) => {
|
|
|
24
24
|
try {
|
|
25
25
|
return await fetchUseMCodeFromUrl(USE_M_BOOTSTRAP_FALLBACK_URL, fetcher);
|
|
26
26
|
} catch (fallbackError) {
|
|
27
|
-
throw new Error(`Failed to load use-m bootstrap from primary and fallback URLs: ${primaryError.message}; ${fallbackError.message}
|
|
27
|
+
throw new Error(`Failed to load use-m bootstrap from primary and fallback URLs: ${primaryError.message}; ${fallbackError.message}`, { cause: fallbackError });
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
30
|
|
|
@@ -154,7 +154,7 @@ export const resolveClaudeSessionToolFlags = async ({ argv, log, fallbackBuildMc
|
|
|
154
154
|
export const ensureDisallowedToolsInSettings = async ({ settingsPath, log } = {}) => {
|
|
155
155
|
const resolvedPath = settingsPath || path.join(os.homedir(), '.claude', 'settings.json');
|
|
156
156
|
const toBlock = buildDisallowedToolsList();
|
|
157
|
-
let settings
|
|
157
|
+
let settings;
|
|
158
158
|
try {
|
|
159
159
|
const content = await fs.readFile(resolvedPath, 'utf-8');
|
|
160
160
|
settings = JSON.parse(content);
|
|
@@ -15,8 +15,8 @@ const { parseYouTrackIssueId, updateYouTrackIssueStage, addYouTrackComment, crea
|
|
|
15
15
|
* @returns {Object} Validation result with YouTrack info
|
|
16
16
|
*/
|
|
17
17
|
export async function validateYouTrackUrl(issueUrl) {
|
|
18
|
-
let isYouTrackUrl
|
|
19
|
-
let youTrackIssueId
|
|
18
|
+
let isYouTrackUrl;
|
|
19
|
+
let youTrackIssueId;
|
|
20
20
|
let youTrackConfig = null;
|
|
21
21
|
|
|
22
22
|
if (!issueUrl) {
|
|
@@ -128,7 +128,7 @@ async function makeYouTrackRequest(endpoint, config, options = {}) {
|
|
|
128
128
|
return await response.json();
|
|
129
129
|
} catch (error) {
|
|
130
130
|
if (error.message.includes('fetch')) {
|
|
131
|
-
throw new Error(`Failed to connect to YouTrack at ${config.url}: ${error.message}
|
|
131
|
+
throw new Error(`Failed to connect to YouTrack at ${config.url}: ${error.message}`, { cause: error });
|
|
132
132
|
}
|
|
133
133
|
throw error;
|
|
134
134
|
}
|