@link-assistant/hive-mind 2.8.4 → 2.8.6
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 +16 -0
- package/package.json +3 -3
- package/src/codex-capability-preflight.lib.mjs +63 -0
- package/src/config.lib.mjs +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 2.8.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 0140e87: Verify required Codex Agent Skills against the catalog the model actually receives instead of trusting plugin enablement, so a plugin reported as installed but whose skills are invisible is reported with an actionable diagnostic rather than passing the preflight.
|
|
8
|
+
|
|
9
|
+
## 2.8.5
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 9a1cbf4: Fix false positives and false negatives in CI/CD (issue #2082). Release and Helm scripts now fail on non-zero exit codes instead of silently continuing, npm publish verification retries registry propagation without republishing, version bumps retry a rejected push on top of the new remote HEAD, shell test assertions fail the build, every workflow job declares a timeout, and jobs use `!cancelled()` so concurrency cancellation is respected. Releases on main now queue rather than cancel, so a run cannot be interrupted between publishing to npm and pushing the version bump.
|
|
14
|
+
|
|
15
|
+
Linting now also covers the `tests/` tree, which `npm run lint` and `eslint.config.mjs` both excluded — the lint job reported success while never reading the largest source tree in the repository. Enabling it surfaced real defects across 59 files, including `assert.match` regexes with unescaped literal indentation that made assertions pass for the wrong reason.
|
|
16
|
+
|
|
17
|
+
Stops leaking `CLAUDE_CODE_EFFORT_LEVEL` from the parent shell into the Claude process. `getClaudeEnv()` builds the child environment from `process.env` and sanitised an inherited `MAX_THINKING_TOKENS`, but not the effort level, so any path that computed no level passed the parent's value through — giving `haiku` an effort level it does not support, and `--think off` one despite thinking being disabled. Since Claude Code exports this variable, the leak applied whenever hive-mind ran under it. The emitted effort level is now a function of the selected model and think level only.
|
|
18
|
+
|
|
3
19
|
## 2.8.4
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@link-assistant/hive-mind",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.6",
|
|
4
4
|
"description": "AI-powered issue solver and hive mind for collaborative problem solving",
|
|
5
5
|
"main": "src/hive.mjs",
|
|
6
6
|
"type": "module",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"test:queue": "node tests/solve-queue.test.mjs",
|
|
22
22
|
"test:limits-display": "node tests/limits-display.test.mjs",
|
|
23
23
|
"test:usage-limit": "node tests/test-usage-limit.mjs",
|
|
24
|
-
"lint": "eslint 'src/**/*.{js,mjs,cjs}' 'scripts/**/*.{js,mjs,cjs}' 'eslint-rules/**/*.{js,mjs,cjs}'",
|
|
25
|
-
"lint:fix": "eslint 'src/**/*.{js,mjs,cjs}' 'scripts/**/*.{js,mjs,cjs}' 'eslint-rules/**/*.{js,mjs,cjs}' --fix",
|
|
24
|
+
"lint": "eslint 'src/**/*.{js,mjs,cjs}' 'scripts/**/*.{js,mjs,cjs}' 'eslint-rules/**/*.{js,mjs,cjs}' 'tests/**/*.{js,mjs,cjs}'",
|
|
25
|
+
"lint:fix": "eslint 'src/**/*.{js,mjs,cjs}' 'scripts/**/*.{js,mjs,cjs}' 'eslint-rules/**/*.{js,mjs,cjs}' 'tests/**/*.{js,mjs,cjs}' --fix",
|
|
26
26
|
"check:duplication": "jscpd .",
|
|
27
27
|
"format": "prettier --write \"**/*.{js,mjs,json,md}\" --ignore-path .prettierignore",
|
|
28
28
|
"format:check": "prettier --check \"**/*.{js,mjs,json,md}\" --ignore-path .prettierignore",
|
|
@@ -155,6 +155,61 @@ const parseJsonCommand = (result, label) => {
|
|
|
155
155
|
|
|
156
156
|
const catalogEntries = catalog => [...(catalog?.installed || []), ...(catalog?.available || [])];
|
|
157
157
|
|
|
158
|
+
// Issue #2084: `codex plugin list` reports enablement, not model visibility.
|
|
159
|
+
//
|
|
160
|
+
// Codex renders the skills the model can actually see into a
|
|
161
|
+
// `<skills_instructions>` block in the prompt. `codex debug prompt-input`
|
|
162
|
+
// prints that prompt, so parsing the block is the only client-side signal that
|
|
163
|
+
// matches what the model receives. Entries are rendered as
|
|
164
|
+
// `- <name>: <description> (file: <path>)`, where `<name>` is bare for skills
|
|
165
|
+
// under `$CODEX_HOME/skills` and `<plugin>:<skill>` for plugin-provided ones.
|
|
166
|
+
const SKILLS_INSTRUCTIONS_BLOCK = /<skills_instructions>([\s\S]*?)<\/skills_instructions>/u;
|
|
167
|
+
const SKILL_CATALOG_ENTRY = /(?:^|\\n)\s*-\s+([a-z0-9][a-z0-9_-]*(?::[a-z0-9][a-z0-9_-]*)?)\s*:/giu;
|
|
168
|
+
|
|
169
|
+
export const parseModelVisibleSkills = (promptInput = '') => {
|
|
170
|
+
// `codex debug prompt-input` emits JSON, so newlines inside the prompt arrive
|
|
171
|
+
// as the two-character escape `\n`. Match both that and real newlines.
|
|
172
|
+
const block = SKILLS_INSTRUCTIONS_BLOCK.exec(String(promptInput).replace(/\r\n/gu, '\n').replace(/\n/gu, '\\n'));
|
|
173
|
+
if (!block) return null;
|
|
174
|
+
const skills = new Set();
|
|
175
|
+
for (const match of block[1].matchAll(SKILL_CATALOG_ENTRY)) skills.add(match[1].toLowerCase());
|
|
176
|
+
return skills;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// Verification is advisory when the probe itself cannot run: an older Codex
|
|
180
|
+
// without `debug prompt-input`, or a sandbox that blocks it, must not fail a
|
|
181
|
+
// run that the previous verification would have allowed.
|
|
182
|
+
const readModelVisibleSkills = async ({ command, env, runCommand, log }) => {
|
|
183
|
+
const result = await runCommand({ command, args: ['debug', 'prompt-input', 'hive-mind capability probe'], env });
|
|
184
|
+
if (result.code !== 0) {
|
|
185
|
+
await log(
|
|
186
|
+
` ⚠️ Could not read the model-visible skill catalog: ${String(result.stderr || result.stdout)
|
|
187
|
+
.trim()
|
|
188
|
+
.slice(0, 200)}`,
|
|
189
|
+
{ verbose: true }
|
|
190
|
+
);
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
const skills = parseModelVisibleSkills(result.stdout);
|
|
194
|
+
if (!skills) await log(' ⚠️ Codex prompt did not contain a <skills_instructions> block; skipping skill visibility verification', { verbose: true });
|
|
195
|
+
return skills;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const verifyModelVisibleSkills = async ({ command, env, runCommand, log, requiredSkills }) => {
|
|
199
|
+
if (!requiredSkills || requiredSkills.length === 0) return;
|
|
200
|
+
const visible = await readModelVisibleSkills({ command, env, runCommand, log });
|
|
201
|
+
if (!visible) return;
|
|
202
|
+
|
|
203
|
+
await log(` 🔎 Model-visible skills (${visible.size}): ${[...visible].sort().join(', ') || 'none'}`, { verbose: true });
|
|
204
|
+
const invisible = requiredSkills.filter(skill => !visible.has(skill.toLowerCase()));
|
|
205
|
+
if (invisible.length === 0) {
|
|
206
|
+
await log(` ✅ Verified ${requiredSkills.length} required skill(s) are visible to the model`);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
throw new CodexCapabilityPreflightError(`Codex reports the required plugins as installed, but the model cannot see: ${invisible.join(', ')}. ` + `Codex exposes a plugin's skills only while its payload is materialized under ` + `CODEX_HOME/plugins/cache/<marketplace>/<plugin>/<version>/skills. ` + `Visible skills were: ${[...visible].sort().join(', ') || 'none'}.`, { missing: invisible });
|
|
211
|
+
};
|
|
212
|
+
|
|
158
213
|
const skillParts = skill => {
|
|
159
214
|
const separator = skill.indexOf(':');
|
|
160
215
|
return separator === -1 ? { namespace: null, name: skill } : { namespace: skill.slice(0, separator), name: skill.slice(separator + 1) };
|
|
@@ -341,6 +396,7 @@ async function provisionCodexCapabilities({ owner, repo, issueNumber, projectDir
|
|
|
341
396
|
}
|
|
342
397
|
if (plugins.length === 0) {
|
|
343
398
|
await log(' ✅ Required Agent Skills are already available from standard skill directories');
|
|
399
|
+
await verifyModelVisibleSkills({ command, env: baseEnv, runCommand, log, requiredSkills: requirements.skills });
|
|
344
400
|
return { required: true, plugins, skills: requirements.skills, codexHome: null, baseCodexHome };
|
|
345
401
|
}
|
|
346
402
|
|
|
@@ -365,6 +421,13 @@ async function provisionCodexCapabilities({ owner, repo, issueNumber, projectDir
|
|
|
365
421
|
const unverified = plugins.filter(plugin => !verified.has(plugin));
|
|
366
422
|
if (unverified.length > 0) throw new CodexCapabilityPreflightError(`Codex capability installation did not verify successfully: ${unverified.join(', ')}`, { missing: unverified });
|
|
367
423
|
|
|
424
|
+
// Issue #2084: enablement is not exposure. The failing run reached this point
|
|
425
|
+
// with `superpowers@openai-curated` reported as "installed, enabled" while
|
|
426
|
+
// the model saw zero `superpowers:*` skills, so the run proceeded and then
|
|
427
|
+
// stalled on the repository's mandatory preflight. Confirm the requirement
|
|
428
|
+
// against the catalog the model actually receives.
|
|
429
|
+
await verifyModelVisibleSkills({ command, env: scopedEnv, runCommand, log, requiredSkills: requirements.skills });
|
|
430
|
+
|
|
368
431
|
await log(` Codex capability state: ${codexHome}`, { verbose: true });
|
|
369
432
|
return { required: true, plugins, skills: requirements.skills, codexHome, baseCodexHome };
|
|
370
433
|
}
|
package/src/config.lib.mjs
CHANGED
|
@@ -627,6 +627,16 @@ export const getClaudeEnv = (options = {}) => {
|
|
|
627
627
|
env.MAX_THINKING_TOKENS = String(options.thinkingBudget ?? 0);
|
|
628
628
|
}
|
|
629
629
|
|
|
630
|
+
// Remove any inherited CLAUDE_CODE_EFFORT_LEVEL from process.env, mirroring the
|
|
631
|
+
// MAX_THINKING_TOKENS sanitisation above (Issue #2082). Without this, an effort
|
|
632
|
+
// level exported by the parent shell survives into the child whenever the logic
|
|
633
|
+
// below does not compute one — e.g. a model that supports no effort levels at all
|
|
634
|
+
// (haiku), or --think off. hive-mind's own agents run under Claude Code, which
|
|
635
|
+
// exports this variable, so the leak fired in exactly the common case: `haiku
|
|
636
|
+
// --think high` inherited effort=max from the parent. The value below must be a
|
|
637
|
+
// function of the selected model and think level, never of the ambient shell.
|
|
638
|
+
delete env.CLAUDE_CODE_EFFORT_LEVEL;
|
|
639
|
+
|
|
630
640
|
// Set CLAUDE_CODE_EFFORT_LEVEL for models that support it (Issue #1238, Issue #1620)
|
|
631
641
|
if (options.model && supportsEffortLevel(options.model)) {
|
|
632
642
|
const effortOptions = {
|