@link-assistant/hive-mind 2.0.22 → 2.0.23
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 +6 -0
- package/package.json +1 -1
- package/src/agent-commander.lib.mjs +2 -1
- package/src/claude.lib.mjs +4 -2
- package/src/config.lib.mjs +8 -0
- package/src/hive.mjs +4 -1
- package/src/models/index.mjs +96 -0
- package/src/solve.config.lib.mjs +5 -0
- package/src/solve.mjs +2 -1
- package/src/telegram-bot.mjs +7 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 2.0.23
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 4778963: Add `--sub-agent-model` for Claude Code subagents and agent teams. The option is accepted by solve, hive, and Telegram command parsing, validates Claude aliases/full IDs plus `inherit`, and maps to `CLAUDE_CODE_SUBAGENT_MODEL` only when explicitly provided so Claude Code defaults remain unchanged.
|
|
8
|
+
|
|
3
9
|
## 2.0.22
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { resolveCodexReasoningEffort } from './codex.options.lib.mjs';
|
|
11
|
-
import { mapModelForTool } from './models/index.mjs';
|
|
11
|
+
import { mapClaudeSubAgentModelToEnvValue, mapModelForTool } from './models/index.mjs';
|
|
12
12
|
import { buildCodexDisable1mContextConfigArgs, buildCodexSubSessionSizeConfigArgs, parseSubSessionSize } from './sub-session-size.lib.mjs';
|
|
13
13
|
import { detectUsageLimit } from './usage-limit.lib.mjs';
|
|
14
14
|
import { getCacheReadTokenCount, getCumulativeContextInputTokens, getOutputTokenCount } from './context-fill.lib.mjs';
|
|
@@ -54,6 +54,7 @@ const buildClaudeToolOptions = (argv = {}) => {
|
|
|
54
54
|
if (argv.disable1mContext) extraEnv.CLAUDE_CODE_DISABLE_1M_CONTEXT = '1';
|
|
55
55
|
if (argv.showThinkingContent) extraEnv.CLAUDE_CODE_SHOW_THINKING = '1';
|
|
56
56
|
if (argv.planModel) extraEnv.ANTHROPIC_DEFAULT_OPUS_MODEL = argv.planModel;
|
|
57
|
+
if (argv.subAgentModel) extraEnv.CLAUDE_CODE_SUBAGENT_MODEL = mapClaudeSubAgentModelToEnvValue(argv.subAgentModel);
|
|
57
58
|
appendExtraEnv(options, extraEnv);
|
|
58
59
|
|
|
59
60
|
return options;
|
package/src/claude.lib.mjs
CHANGED
|
@@ -21,7 +21,7 @@ import { seedCumulativeAnthropicCost, addAnthropicRunCost } from './anthropic-co
|
|
|
21
21
|
import { buildSolveResumeCommand } from './solve.resume-command.lib.mjs'; // Issue #942
|
|
22
22
|
import { SESSION_FORCE_KILLED_MARKER, postTrackedComment } from './tool-comments.lib.mjs'; // Issue #1625
|
|
23
23
|
import { handleClaudeRuntimeSwitch } from './claude.runtime-switch.lib.mjs'; // see issue #1141
|
|
24
|
-
import { CLAUDE_MODELS as availableModels } from './models/index.mjs'; // Issue #1221
|
|
24
|
+
import { CLAUDE_MODELS as availableModels, mapClaudeSubAgentModelToEnvValue } from './models/index.mjs'; // Issue #1221, #1978
|
|
25
25
|
import { buildMcpConfigWithoutPlaywright, ensureClaudePlaywrightMcpServer } from './playwright-mcp.lib.mjs';
|
|
26
26
|
import { resolveClaudeSessionToolFlags } from './useless-tools.lib.mjs';
|
|
27
27
|
import { ensureClaudeQuietConfig } from './claude-quiet-config.lib.mjs';
|
|
@@ -658,6 +658,7 @@ export const executeClaudeCommand = async params => {
|
|
|
658
658
|
let execCommand;
|
|
659
659
|
const mappedModel = mapModelToId(argv.model);
|
|
660
660
|
const resolvedPlanModel = argv.planModel ? mapModelToId(argv.planModel) : undefined; // Issue #1223
|
|
661
|
+
const resolvedSubAgentModel = argv.subAgentModel ? mapClaudeSubAgentModelToEnvValue(argv.subAgentModel) : undefined; // Issue #1978
|
|
661
662
|
const effectiveModel = resolvedPlanModel ? 'opusplan' : mappedModel;
|
|
662
663
|
const resolvedExecutionModel = resolvedPlanModel ? mappedModel : undefined;
|
|
663
664
|
// Issue #1949: Let Claude Code handle transient overload (529) fallback via its own
|
|
@@ -706,12 +707,13 @@ export const executeClaudeCommand = async params => {
|
|
|
706
707
|
// Issue #1706: --sub-session-size + --disable-1m-context. Resolve here, then pass into getClaudeEnv along with the rest.
|
|
707
708
|
const { parsed: parsedSubSessionSize, contextWindowTokens } = await resolveSubSessionSize({ rawValue: argv.subSessionSize, tool: 'claude', modelId: effectiveModel, fetchModelInfo, log });
|
|
708
709
|
// Issue #817: streaming mode sets exitAfterStopDelayMs=60000 so the headless Claude process stays alive between NDJSON turns.
|
|
709
|
-
const claudeEnv = getClaudeEnv({ thinkingBudget: resolvedThinkingBudget, model: effectiveModel, thinkLevel, maxBudget, planModel: resolvedPlanModel, executionModel: resolvedExecutionModel, showThinkingContent: argv.showThinkingContent, exitAfterStopDelayMs: streamingInput ? 60_000 : undefined, disable1mContext: !!argv.disable1mContext, subSessionSize: parsedSubSessionSize, contextWindowTokens });
|
|
710
|
+
const claudeEnv = getClaudeEnv({ thinkingBudget: resolvedThinkingBudget, model: effectiveModel, thinkLevel, maxBudget, planModel: resolvedPlanModel, executionModel: resolvedExecutionModel, subAgentModel: resolvedSubAgentModel, showThinkingContent: argv.showThinkingContent, exitAfterStopDelayMs: streamingInput ? 60_000 : undefined, disable1mContext: !!argv.disable1mContext, subSessionSize: parsedSubSessionSize, contextWindowTokens });
|
|
710
711
|
if (argv.verbose) claudeEnv.ANTHROPIC_LOG = 'debug';
|
|
711
712
|
const modelMaxOutputTokens = getMaxOutputTokensForModel(effectiveModel);
|
|
712
713
|
if (argv.verbose) {
|
|
713
714
|
await log(`📊 CLAUDE_CODE_MAX_OUTPUT_TOKENS: ${modelMaxOutputTokens}, MCP_TIMEOUT: ${claudeCode.mcpTimeout}ms, MCP_TOOL_TIMEOUT: ${claudeCode.mcpToolTimeout}ms, ANTHROPIC_LOG: debug`, { verbose: true });
|
|
714
715
|
if (resolvedPlanModel) await log(`📊 opusplan: plan=${resolvedPlanModel}, exec=${resolvedExecutionModel}`, { verbose: true });
|
|
716
|
+
if (claudeEnv.CLAUDE_CODE_SUBAGENT_MODEL) await log(`📊 CLAUDE_CODE_SUBAGENT_MODEL: ${claudeEnv.CLAUDE_CODE_SUBAGENT_MODEL}`, { verbose: true });
|
|
715
717
|
if (resolvedThinkingBudget !== undefined) await log(`📊 MAX_THINKING_TOKENS: ${resolvedThinkingBudget}`, { verbose: true });
|
|
716
718
|
if (claudeEnv.CLAUDE_CODE_EFFORT_LEVEL) await log(`📊 CLAUDE_CODE_EFFORT_LEVEL: ${claudeEnv.CLAUDE_CODE_EFFORT_LEVEL}`, { verbose: true });
|
|
717
719
|
if (claudeEnv.CLAUDE_CODE_SHOW_THINKING) await log(`📊 CLAUDE_CODE_SHOW_THINKING: ${claudeEnv.CLAUDE_CODE_SHOW_THINKING}`, { verbose: true });
|
package/src/config.lib.mjs
CHANGED
|
@@ -543,9 +543,11 @@ export const supportsThinkingBudget = (version, minVersion = '2.1.12') => {
|
|
|
543
543
|
// Supports planModel/executionModel for opusplan mode (Issue #1223)
|
|
544
544
|
// Issue #1706: supports subSessionSize (parsed) + disable1mContext to cap
|
|
545
545
|
// auto-compaction sub-session size and opt out of the 1M extended context.
|
|
546
|
+
// Issue #1978: supports subAgentModel for Claude Code native subagents and agent teams.
|
|
546
547
|
// See: https://code.claude.com/docs/en/env-vars and https://code.claude.com/docs/en/model-config
|
|
547
548
|
// ANTHROPIC_DEFAULT_OPUS_MODEL → model used in plan mode (and for 'opus' alias)
|
|
548
549
|
// ANTHROPIC_DEFAULT_SONNET_MODEL → model used in execution mode (and for 'sonnet' alias)
|
|
550
|
+
// CLAUDE_CODE_SUBAGENT_MODEL → model used by all subagents and agent teams
|
|
549
551
|
// CLAUDE_CODE_DISABLE_1M_CONTEXT, CLAUDE_CODE_AUTO_COMPACT_WINDOW, CLAUDE_AUTOCOMPACT_PCT_OVERRIDE
|
|
550
552
|
export const getClaudeEnv = (options = {}) => {
|
|
551
553
|
// Get max output tokens based on model (Issue #1221)
|
|
@@ -619,6 +621,12 @@ export const getClaudeEnv = (options = {}) => {
|
|
|
619
621
|
env.ANTHROPIC_DEFAULT_SONNET_MODEL = String(options.executionModel);
|
|
620
622
|
}
|
|
621
623
|
|
|
624
|
+
// Issue #1978: Set Claude Code native subagent/agent-team model only when
|
|
625
|
+
// explicitly requested. Leaving this unset preserves Claude Code defaults.
|
|
626
|
+
if (options.subAgentModel) {
|
|
627
|
+
env.CLAUDE_CODE_SUBAGENT_MODEL = String(options.subAgentModel);
|
|
628
|
+
}
|
|
629
|
+
|
|
622
630
|
// Issue #1706: --disable-1m-context. Sets CLAUDE_CODE_DISABLE_1M_CONTEXT=1.
|
|
623
631
|
if (options.disable1mContext) {
|
|
624
632
|
env.CLAUDE_CODE_DISABLE_1M_CONTEXT = '1';
|
package/src/hive.mjs
CHANGED
|
@@ -74,7 +74,7 @@ if (isRunningDirectly) {
|
|
|
74
74
|
const { validateClaudeConnection } = claudeLib;
|
|
75
75
|
// Import model validation library
|
|
76
76
|
const modelValidation = await import('./models/index.mjs');
|
|
77
|
-
const { validateAndExitOnInvalidModel, defaultModels, resolveRuntimeDefaultModel } = modelValidation;
|
|
77
|
+
const { validateAndExitOnInvalidClaudeSubAgentModel, validateAndExitOnInvalidModel, defaultModels, resolveRuntimeDefaultModel } = modelValidation;
|
|
78
78
|
const githubLib = await import('./github.lib.mjs');
|
|
79
79
|
const { checkGitHubPermissions, fetchAllIssuesWithPagination, fetchProjectIssues, isRateLimitError, batchCheckPullRequestsForIssues, parseGitHubUrl, batchCheckArchivedRepositories } = githubLib;
|
|
80
80
|
// Import YouTrack-related functions
|
|
@@ -473,6 +473,9 @@ if (isRunningDirectly) {
|
|
|
473
473
|
}
|
|
474
474
|
await validateAndExitOnInvalidModel(argv.planModel, tool, safeExit);
|
|
475
475
|
}
|
|
476
|
+
if (argv.subAgentModel) {
|
|
477
|
+
await validateAndExitOnInvalidClaudeSubAgentModel(argv.subAgentModel, tool, safeExit);
|
|
478
|
+
}
|
|
476
479
|
|
|
477
480
|
// Handle -s (--skip-issues-with-prs) and --auto-continue interaction
|
|
478
481
|
const hasExplicitAutoContinue = rawArgs.includes('--auto-continue');
|
package/src/models/index.mjs
CHANGED
|
@@ -742,6 +742,62 @@ export const validateModelName = (model, tool = 'claude') => {
|
|
|
742
742
|
};
|
|
743
743
|
};
|
|
744
744
|
|
|
745
|
+
export const CLAUDE_SUB_AGENT_MODEL_INHERIT = 'inherit';
|
|
746
|
+
|
|
747
|
+
export const normalizeClaudeSubAgentModelName = model => {
|
|
748
|
+
if (model === undefined || model === null) return model;
|
|
749
|
+
if (typeof model !== 'string') return model;
|
|
750
|
+
|
|
751
|
+
const trimmed = model.trim();
|
|
752
|
+
return trimmed.toLowerCase() === CLAUDE_SUB_AGENT_MODEL_INHERIT ? CLAUDE_SUB_AGENT_MODEL_INHERIT : trimmed;
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
const looksLikeClaudeProviderModelId = model => {
|
|
756
|
+
if (typeof model !== 'string') return false;
|
|
757
|
+
const normalized = model.toLowerCase();
|
|
758
|
+
return normalized.startsWith('claude-') || normalized.startsWith('anthropic/') || normalized.startsWith('anthropic.') || normalized.includes('.anthropic.');
|
|
759
|
+
};
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Validate the Claude Code subagent/agent-team model override.
|
|
763
|
+
*
|
|
764
|
+
* Claude Code documents CLAUDE_CODE_SUBAGENT_MODEL as accepting full provider
|
|
765
|
+
* model IDs, normal Claude model aliases, and the special value "inherit".
|
|
766
|
+
* Keep "inherit" scoped to this option so it does not become a valid main
|
|
767
|
+
* session model.
|
|
768
|
+
*
|
|
769
|
+
* @param {string} model - The subagent model override value
|
|
770
|
+
* @returns {{ valid: boolean, message?: string, suggestions?: string[], mappedModel?: string }}
|
|
771
|
+
*/
|
|
772
|
+
export const validateClaudeSubAgentModelName = model => {
|
|
773
|
+
const normalized = normalizeClaudeSubAgentModelName(model);
|
|
774
|
+
|
|
775
|
+
if (normalized === CLAUDE_SUB_AGENT_MODEL_INHERIT) {
|
|
776
|
+
return {
|
|
777
|
+
valid: true,
|
|
778
|
+
mappedModel: CLAUDE_SUB_AGENT_MODEL_INHERIT,
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
const validation = validateModelName(normalized, 'claude');
|
|
783
|
+
if (validation.valid) return validation;
|
|
784
|
+
if (looksLikeClaudeProviderModelId(normalized)) {
|
|
785
|
+
return {
|
|
786
|
+
valid: true,
|
|
787
|
+
mappedModel: normalized,
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
return validation;
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
export const mapClaudeSubAgentModelToEnvValue = model => {
|
|
795
|
+
const result = validateClaudeSubAgentModelName(model);
|
|
796
|
+
if (result.valid && result.mappedModel) return result.mappedModel;
|
|
797
|
+
const normalized = normalizeClaudeSubAgentModelName(model);
|
|
798
|
+
return mapModelForTool('claude', normalized);
|
|
799
|
+
};
|
|
800
|
+
|
|
745
801
|
/**
|
|
746
802
|
* Validate model name and exit with error if invalid
|
|
747
803
|
* This is the main entry point for model validation in solve.mjs, hive.mjs, etc.
|
|
@@ -767,6 +823,46 @@ export const validateAndExitOnInvalidModel = async (model, tool = 'claude', exit
|
|
|
767
823
|
return true;
|
|
768
824
|
};
|
|
769
825
|
|
|
826
|
+
/**
|
|
827
|
+
* Validate --sub-agent-model and exit with error if invalid.
|
|
828
|
+
*
|
|
829
|
+
* This option maps to Claude Code's CLAUDE_CODE_SUBAGENT_MODEL, so it is only
|
|
830
|
+
* meaningful for the Claude tool even when solve/hive supports multiple tools.
|
|
831
|
+
*
|
|
832
|
+
* @param {string} model - The subagent model override value
|
|
833
|
+
* @param {string} tool - The selected tool
|
|
834
|
+
* @param {Function} exitFn - Function to call for exiting (default: process.exit)
|
|
835
|
+
* @returns {Promise<boolean>} True if valid, exits process if invalid
|
|
836
|
+
*/
|
|
837
|
+
export const validateAndExitOnInvalidClaudeSubAgentModel = async (model, tool = 'claude', exitFn = null) => {
|
|
838
|
+
if (model === undefined || model === null || model === '') return true;
|
|
839
|
+
|
|
840
|
+
if (tool !== 'claude') {
|
|
841
|
+
await log(`❌ --sub-agent-model is only supported with --tool claude (current tool: ${tool})`, { level: 'error' });
|
|
842
|
+
if (exitFn) {
|
|
843
|
+
await exitFn(1, '--sub-agent-model requires --tool claude');
|
|
844
|
+
} else {
|
|
845
|
+
process.exit(1);
|
|
846
|
+
}
|
|
847
|
+
return false;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
const result = validateClaudeSubAgentModelName(model);
|
|
851
|
+
|
|
852
|
+
if (!result.valid) {
|
|
853
|
+
await log(`❌ Invalid --sub-agent-model: ${result.message}`, { level: 'error' });
|
|
854
|
+
|
|
855
|
+
if (exitFn) {
|
|
856
|
+
await exitFn(1, 'Invalid sub-agent model name');
|
|
857
|
+
} else {
|
|
858
|
+
process.exit(1);
|
|
859
|
+
}
|
|
860
|
+
return false;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
return true;
|
|
864
|
+
};
|
|
865
|
+
|
|
770
866
|
/**
|
|
771
867
|
* Format the list of available models for help text
|
|
772
868
|
* @param {string} tool - The tool name
|
package/src/solve.config.lib.mjs
CHANGED
|
@@ -333,6 +333,11 @@ export const SOLVE_OPTION_DEFINITIONS = {
|
|
|
333
333
|
description: 'Fallback model to switch to on model capacity/overload errors (and, for Fable 5, on safety-classifier refusals). When supported, retries resume the same session with this model. Defaults: claude fable/claude-fable-5 -> opus (Opus 4.8); claude mythos-5/claude-mythos-5 -> fable; claude opus/opus-4-8 -> opus-4-7; claude opus-4-7 -> opus-4-6; codex gpt-5.5 -> gpt-5.4; all others unset.',
|
|
334
334
|
default: undefined,
|
|
335
335
|
},
|
|
336
|
+
'sub-agent-model': {
|
|
337
|
+
type: 'string',
|
|
338
|
+
description: 'Claude Code subagent/agent-team model override. Sets CLAUDE_CODE_SUBAGENT_MODEL only when provided. Accepts Claude model aliases, full model IDs, or "inherit" to use normal Claude Code subagent model resolution. Only works with --tool claude.',
|
|
339
|
+
default: undefined,
|
|
340
|
+
},
|
|
336
341
|
'show-thinking-content': {
|
|
337
342
|
type: 'boolean',
|
|
338
343
|
description: 'Show thinking content in Claude responses. Opus 4.7+ omits thinking content by default (applies to Opus 4.8 as well); this option opts in to receive summarized thinking blocks. Disabled by default. Only affects --tool claude.',
|
package/src/solve.mjs
CHANGED
|
@@ -64,7 +64,7 @@ const { attachFinalLogIfMissing } = await import('./attach-logs-guarantee.lib.mj
|
|
|
64
64
|
// "did the AI post anything?" check in --auto-attach-solution-summary).
|
|
65
65
|
const { postTrackedComment, USAGE_LIMIT_REACHED_MARKER } = await import('./tool-comments.lib.mjs');
|
|
66
66
|
const { prepareFeedbackAndTimestamps, checkUncommittedChanges, checkForkActions } = await import('./solve.preparation.lib.mjs');
|
|
67
|
-
const { validateAndExitOnInvalidModel } = await import('./models/index.mjs');
|
|
67
|
+
const { validateAndExitOnInvalidClaudeSubAgentModel, validateAndExitOnInvalidModel } = await import('./models/index.mjs');
|
|
68
68
|
const { autoAcceptInviteForRepo } = await import('./solve.accept-invite.lib.mjs');
|
|
69
69
|
const { handleAutoForkOption, handleMaintainerForkAccess } = await import('./solve.fork-detection.lib.mjs');
|
|
70
70
|
// Initialize log file early (before argument parsing) to capture all output
|
|
@@ -243,6 +243,7 @@ if (argv.planModel) {
|
|
|
243
243
|
}
|
|
244
244
|
await validateAndExitOnInvalidModel(argv.planModel, tool, safeExit);
|
|
245
245
|
}
|
|
246
|
+
if (argv.subAgentModel) await validateAndExitOnInvalidClaudeSubAgentModel(argv.subAgentModel, tool, safeExit);
|
|
246
247
|
|
|
247
248
|
// Perform all system checks (skip tool connection check in dry-run or when --skip-tool-connection-check; model validation always runs)
|
|
248
249
|
const skipToolConnectionCheck = argv.dryRun || argv.skipToolConnectionCheck || argv.toolConnectionCheck === false;
|
package/src/telegram-bot.mjs
CHANGED
|
@@ -316,7 +316,7 @@ if (config.dryRun) {
|
|
|
316
316
|
const { buildUserMention } = await import('./buildUserMention.lib.mjs');
|
|
317
317
|
const { reportError, initializeSentry, addBreadcrumb } = await import('./sentry.lib.mjs');
|
|
318
318
|
const { parseGitHubUrl, validateGitHubEntityExistence } = await import('./github.lib.mjs');
|
|
319
|
-
const { validateModelName, buildModelOptionDescription } = await import('./models/index.mjs');
|
|
319
|
+
const { validateClaudeSubAgentModelName, validateModelName, buildModelOptionDescription } = await import('./models/index.mjs');
|
|
320
320
|
const { resolveIsolation, createIsolationAwareQueueCallback } = await import('./telegram-isolation.lib.mjs');
|
|
321
321
|
const limitsLib = await import('./limits.lib.mjs');
|
|
322
322
|
const { formatUsageMessage, formatCodexLimitsSection, getAllCachedLimits } = limitsLib;
|
|
@@ -435,6 +435,12 @@ function validateModelInArgs(args, tool = 'claude') {
|
|
|
435
435
|
if (!validation.valid) {
|
|
436
436
|
return validation.message;
|
|
437
437
|
}
|
|
438
|
+
} else if (args[i] === '--sub-agent-model' || args[i].startsWith('--sub-agent-model=')) {
|
|
439
|
+
const modelName = args[i] === '--sub-agent-model' ? args[i + 1] : args[i].substring('--sub-agent-model='.length);
|
|
440
|
+
if (!modelName) continue;
|
|
441
|
+
if (tool !== 'claude') return `--sub-agent-model is only supported with --tool claude (current tool: ${tool})`;
|
|
442
|
+
const validation = validateClaudeSubAgentModelName(modelName);
|
|
443
|
+
if (!validation.valid) return `Invalid --sub-agent-model: ${validation.message}`;
|
|
438
444
|
}
|
|
439
445
|
}
|
|
440
446
|
return null;
|