@link-assistant/hive-mind 1.17.1 → 1.18.0
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 +38 -0
- package/package.json +1 -1
- package/src/claude.lib.mjs +7 -10
- package/src/config.lib.mjs +85 -11
- package/src/limits.lib.mjs +72 -25
- package/src/model-mapping.lib.mjs +2 -2
- package/src/model-validation.lib.mjs +6 -4
- package/src/queue-config.lib.mjs +104 -0
- package/src/solve.config.lib.mjs +1 -1
- package/src/telegram-solve-queue.lib.mjs +5 -33
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.18.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 6b7f026: Add threshold markers to /limits command progress bars
|
|
8
|
+
|
|
9
|
+
This change implements visual threshold markers in the progress bars displayed by the /limits command. Users can now see:
|
|
10
|
+
- **Threshold position marker (│)**: Shows where queue behavior changes (e.g., blocking, one-at-a-time mode)
|
|
11
|
+
- **Warning emoji (⚠️)**: Appears when usage exceeds the threshold
|
|
12
|
+
|
|
13
|
+
Thresholds displayed:
|
|
14
|
+
- RAM: 65% (blocks new commands)
|
|
15
|
+
- CPU: 65% (blocks new commands)
|
|
16
|
+
- Disk: 90% (one-at-a-time mode)
|
|
17
|
+
- Claude 5-hour session: 65% (one-at-a-time mode)
|
|
18
|
+
- Claude weekly: 97% (one-at-a-time mode)
|
|
19
|
+
- GitHub API: 75% (blocks parallel claude commands)
|
|
20
|
+
|
|
21
|
+
Example output:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
CPU
|
|
25
|
+
▓▓▓▓▓▓▓░░░░░░░░░░░░│░░░░░░░░░░ 25%
|
|
26
|
+
0.04/6 CPU cores used
|
|
27
|
+
|
|
28
|
+
Claude 5 hour session
|
|
29
|
+
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓│▓ 98% ⚠️
|
|
30
|
+
Resets in 2h 10m (Dec 6, 12:00pm UTC)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Fixes #1242
|
|
34
|
+
|
|
35
|
+
## 1.17.2
|
|
36
|
+
|
|
37
|
+
### Patch Changes
|
|
38
|
+
|
|
39
|
+
- ae013b3: Default thinking budget to zero (thinking disabled by default), align Opus 4.6 max thinking budget with standard models (31999), change `opus` alias to map to Opus 4.5 by default (supports both `opus-4-5` and `opus-4-6` aliases)
|
|
40
|
+
|
|
3
41
|
## 1.17.1
|
|
4
42
|
|
|
5
43
|
### Patch Changes
|
package/package.json
CHANGED
package/src/claude.lib.mjs
CHANGED
|
@@ -911,23 +911,20 @@ export const executeClaudeCommand = async params => {
|
|
|
911
911
|
try {
|
|
912
912
|
// Resolve thinking settings (handles translation between --think and --thinking-budget based on Claude version)
|
|
913
913
|
// See issue #1146 for details on thinking budget translation
|
|
914
|
-
const { thinkingBudget: resolvedThinkingBudget, thinkLevel, isNewVersion } = await resolveThinkingSettings(argv, log);
|
|
914
|
+
const { thinkingBudget: resolvedThinkingBudget, thinkLevel, isNewVersion, maxBudget } = await resolveThinkingSettings(argv, log);
|
|
915
915
|
|
|
916
916
|
// Set CLAUDE_CODE_MAX_OUTPUT_TOKENS (see issue #1076), MAX_THINKING_TOKENS (see issue #1146),
|
|
917
|
-
//
|
|
917
|
+
// MCP timeout configurations (see issue #1066), and CLAUDE_CODE_EFFORT_LEVEL for Opus 4.6 (Issue #1238)
|
|
918
918
|
// Pass model for model-specific max output tokens (Issue #1221)
|
|
919
|
-
|
|
919
|
+
// Pass thinkLevel and maxBudget for Opus 4.6 effort level conversion (Issue #1238)
|
|
920
|
+
const claudeEnv = getClaudeEnv({ thinkingBudget: resolvedThinkingBudget, model: mappedModel, thinkLevel, maxBudget });
|
|
920
921
|
const modelMaxOutputTokens = getMaxOutputTokensForModel(mappedModel);
|
|
921
922
|
if (argv.verbose) await log(`📊 CLAUDE_CODE_MAX_OUTPUT_TOKENS: ${modelMaxOutputTokens}`, { verbose: true });
|
|
922
923
|
if (argv.verbose) await log(`📊 MCP_TIMEOUT: ${claudeCode.mcpTimeout}ms (server startup)`, { verbose: true });
|
|
923
924
|
if (argv.verbose) await log(`📊 MCP_TOOL_TIMEOUT: ${claudeCode.mcpToolTimeout}ms (tool execution)`, { verbose: true });
|
|
924
|
-
if (resolvedThinkingBudget !== undefined) {
|
|
925
|
-
|
|
926
|
-
}
|
|
927
|
-
// Log thinking level for older Claude Code versions that use thinking keywords
|
|
928
|
-
if (!isNewVersion && thinkLevel) {
|
|
929
|
-
await log(`📊 Thinking level (via keywords): ${thinkLevel}`, { verbose: true });
|
|
930
|
-
}
|
|
925
|
+
if (resolvedThinkingBudget !== undefined) await log(`📊 MAX_THINKING_TOKENS: ${resolvedThinkingBudget}`, { verbose: true });
|
|
926
|
+
if (claudeEnv.CLAUDE_CODE_EFFORT_LEVEL) await log(`📊 CLAUDE_CODE_EFFORT_LEVEL: ${claudeEnv.CLAUDE_CODE_EFFORT_LEVEL}`, { verbose: true });
|
|
927
|
+
if (!isNewVersion && thinkLevel) await log(`📊 Thinking level (via keywords): ${thinkLevel}`, { verbose: true });
|
|
931
928
|
if (argv.resume) {
|
|
932
929
|
// When resuming, pass prompt directly with -p flag. Escape double quotes for shell.
|
|
933
930
|
const simpleEscapedPrompt = prompt.replace(/"/g, '\\"');
|
package/src/config.lib.mjs
CHANGED
|
@@ -119,21 +119,24 @@ export const claudeCode = {
|
|
|
119
119
|
// Can be overridden via --max-thinking-budget option
|
|
120
120
|
export const DEFAULT_MAX_THINKING_BUDGET = 31999;
|
|
121
121
|
|
|
122
|
-
// Default max thinking budget for Opus 4.6 (Issue #1221)
|
|
123
|
-
//
|
|
122
|
+
// Default max thinking budget for Opus 4.6 (Issue #1221, updated in Issue #1238)
|
|
123
|
+
// Aligned with standard models (31999) for consistency.
|
|
124
|
+
// Opus 4.6 uses CLAUDE_CODE_EFFORT_LEVEL for thinking depth instead of MAX_THINKING_TOKENS
|
|
125
|
+
// (MAX_THINKING_TOKENS is ignored for Opus 4.6 unless set to 0 to disable thinking).
|
|
124
126
|
// Can be overridden via --max-thinking-budget option or HIVE_MIND_MAX_THINKING_BUDGET_OPUS_46
|
|
125
|
-
export const DEFAULT_MAX_THINKING_BUDGET_OPUS_46 = parseIntWithDefault('HIVE_MIND_MAX_THINKING_BUDGET_OPUS_46',
|
|
127
|
+
export const DEFAULT_MAX_THINKING_BUDGET_OPUS_46 = parseIntWithDefault('HIVE_MIND_MAX_THINKING_BUDGET_OPUS_46', 31999);
|
|
126
128
|
|
|
127
129
|
/**
|
|
128
|
-
* Check if a model is Opus 4.6 or later (Issue #1221)
|
|
130
|
+
* Check if a model is Opus 4.6 or later (Issue #1221, updated in Issue #1238)
|
|
129
131
|
* @param {string} model - The model name or ID
|
|
130
132
|
* @returns {boolean} True if the model is Opus 4.6 or later
|
|
131
133
|
*/
|
|
132
134
|
export const isOpus46OrLater = model => {
|
|
133
135
|
if (!model) return false;
|
|
134
136
|
const normalizedModel = model.toLowerCase();
|
|
135
|
-
// Check for opus
|
|
136
|
-
|
|
137
|
+
// Check for explicit opus-4-6 or later versions
|
|
138
|
+
// Note: The 'opus' alias now maps to Opus 4.5 (Issue #1238), so we only check explicit version identifiers
|
|
139
|
+
return normalizedModel.includes('opus-4-6') || normalizedModel.includes('opus-4-7') || normalizedModel.includes('opus-5');
|
|
137
140
|
};
|
|
138
141
|
|
|
139
142
|
/**
|
|
@@ -202,6 +205,59 @@ export const getTokensToThinkingLevel = (maxBudget = DEFAULT_MAX_THINKING_BUDGET
|
|
|
202
205
|
// Default tokens to thinking level function (using default max budget)
|
|
203
206
|
export const tokensToThinkingLevel = getTokensToThinkingLevel(DEFAULT_MAX_THINKING_BUDGET);
|
|
204
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Valid effort levels for Opus 4.6 (Issue #1238)
|
|
210
|
+
* Opus 4.6 uses CLAUDE_CODE_EFFORT_LEVEL for thinking depth control
|
|
211
|
+
* @type {string[]}
|
|
212
|
+
*/
|
|
213
|
+
export const OPUS_46_EFFORT_LEVELS = ['low', 'medium', 'high'];
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Convert thinking level to Opus 4.6 effort level (Issue #1238)
|
|
217
|
+
* Opus 4.6 uses CLAUDE_CODE_EFFORT_LEVEL (low/medium/high) instead of MAX_THINKING_TOKENS
|
|
218
|
+
* @param {string|undefined} thinkLevel - The thinking level (off/low/medium/high/max)
|
|
219
|
+
* @returns {string|undefined} The effort level (low/medium/high) or undefined if thinking is off
|
|
220
|
+
*/
|
|
221
|
+
export const thinkLevelToEffortLevel = thinkLevel => {
|
|
222
|
+
if (!thinkLevel || thinkLevel === 'off') {
|
|
223
|
+
// No effort level when thinking is disabled
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Map hive-mind thinking levels to Opus 4.6 effort levels
|
|
228
|
+
// Note: Opus 4.6 only supports low/medium/high, not 'max'
|
|
229
|
+
// We map 'max' to 'high' as it's the highest available level
|
|
230
|
+
switch (thinkLevel) {
|
|
231
|
+
case 'low':
|
|
232
|
+
return 'low';
|
|
233
|
+
case 'medium':
|
|
234
|
+
return 'medium';
|
|
235
|
+
case 'high':
|
|
236
|
+
case 'max':
|
|
237
|
+
return 'high';
|
|
238
|
+
default:
|
|
239
|
+
return undefined;
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Convert thinking budget (tokens) to Opus 4.6 effort level (Issue #1238)
|
|
245
|
+
* Uses token thresholds to determine the appropriate effort level
|
|
246
|
+
* @param {number|undefined} thinkingBudget - The thinking budget in tokens
|
|
247
|
+
* @param {number} maxBudget - Maximum thinking budget (default: 31999)
|
|
248
|
+
* @returns {string|undefined} The effort level (low/medium/high) or undefined if thinking is off
|
|
249
|
+
*/
|
|
250
|
+
export const thinkingBudgetToEffortLevel = (thinkingBudget, maxBudget = DEFAULT_MAX_THINKING_BUDGET) => {
|
|
251
|
+
if (thinkingBudget === undefined || thinkingBudget === 0) {
|
|
252
|
+
// No effort level when thinking is disabled
|
|
253
|
+
return undefined;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Convert tokens to thinking level, then to effort level
|
|
257
|
+
const thinkLevel = getTokensToThinkingLevel(maxBudget)(thinkingBudget);
|
|
258
|
+
return thinkLevelToEffortLevel(thinkLevel);
|
|
259
|
+
};
|
|
260
|
+
|
|
205
261
|
// Check if a version supports thinking budget (>= minimum version)
|
|
206
262
|
// Uses semver npm package for reliable version comparison (see issue #1146)
|
|
207
263
|
export const supportsThinkingBudget = (version, minVersion = '2.1.12') => {
|
|
@@ -221,6 +277,7 @@ export const supportsThinkingBudget = (version, minVersion = '2.1.12') => {
|
|
|
221
277
|
// Optionally sets MAX_THINKING_TOKENS when thinkingBudget is provided (see issue #1146)
|
|
222
278
|
// Also sets MCP_TIMEOUT and MCP_TOOL_TIMEOUT for MCP tool execution (see issue #1066)
|
|
223
279
|
// Supports model-specific max output tokens for Opus 4.6 (Issue #1221)
|
|
280
|
+
// Sets CLAUDE_CODE_EFFORT_LEVEL for Opus 4.6 models (Issue #1238)
|
|
224
281
|
export const getClaudeEnv = (options = {}) => {
|
|
225
282
|
// Get max output tokens based on model (Issue #1221)
|
|
226
283
|
const maxOutputTokens = options.model ? getMaxOutputTokensForModel(options.model) : claudeCode.maxOutputTokens;
|
|
@@ -235,12 +292,29 @@ export const getClaudeEnv = (options = {}) => {
|
|
|
235
292
|
MCP_TIMEOUT: String(claudeCode.mcpTimeout),
|
|
236
293
|
MCP_TOOL_TIMEOUT: String(claudeCode.mcpToolTimeout),
|
|
237
294
|
};
|
|
238
|
-
|
|
239
|
-
//
|
|
240
|
-
// Default is
|
|
241
|
-
|
|
242
|
-
|
|
295
|
+
|
|
296
|
+
// Set MAX_THINKING_TOKENS to control Claude Code's extended thinking feature (Claude Code >= 2.1.12)
|
|
297
|
+
// Default is 0 (thinking disabled) per Issue #1238. Set to 0 to disable thinking.
|
|
298
|
+
// Users can explicitly enable thinking via --think or --thinking-budget options.
|
|
299
|
+
env.MAX_THINKING_TOKENS = String(options.thinkingBudget ?? 0);
|
|
300
|
+
|
|
301
|
+
// For Opus 4.6+, also set CLAUDE_CODE_EFFORT_LEVEL to control thinking depth (Issue #1238)
|
|
302
|
+
// Opus 4.6 uses effort level (low/medium/high) instead of MAX_THINKING_TOKENS for thinking depth.
|
|
303
|
+
// MAX_THINKING_TOKENS is only used to disable thinking (when set to 0).
|
|
304
|
+
if (options.model && isOpus46OrLater(options.model)) {
|
|
305
|
+
// Convert thinkLevel or thinkingBudget to effort level
|
|
306
|
+
let effortLevel;
|
|
307
|
+
if (options.thinkLevel) {
|
|
308
|
+
effortLevel = thinkLevelToEffortLevel(options.thinkLevel);
|
|
309
|
+
} else if (options.thinkingBudget !== undefined && options.thinkingBudget > 0) {
|
|
310
|
+
effortLevel = thinkingBudgetToEffortLevel(options.thinkingBudget, options.maxBudget);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (effortLevel) {
|
|
314
|
+
env.CLAUDE_CODE_EFFORT_LEVEL = effortLevel;
|
|
315
|
+
}
|
|
243
316
|
}
|
|
317
|
+
|
|
244
318
|
return env;
|
|
245
319
|
};
|
|
246
320
|
|
package/src/limits.lib.mjs
CHANGED
|
@@ -18,6 +18,12 @@ dayjs.extend(utc);
|
|
|
18
18
|
// Import cache TTL configuration
|
|
19
19
|
import { cacheTtl } from './config.lib.mjs';
|
|
20
20
|
|
|
21
|
+
// Import centralized queue thresholds for progress bar visualization
|
|
22
|
+
// This ensures thresholds are consistent between queue logic and display formatting
|
|
23
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1242
|
|
24
|
+
export { DISPLAY_THRESHOLDS } from './queue-config.lib.mjs';
|
|
25
|
+
import { DISPLAY_THRESHOLDS } from './queue-config.lib.mjs';
|
|
26
|
+
|
|
21
27
|
const execAsync = promisify(exec);
|
|
22
28
|
|
|
23
29
|
/**
|
|
@@ -626,13 +632,35 @@ export async function getClaudeUsageLimits(verbose = false, credentialsPath = DE
|
|
|
626
632
|
/**
|
|
627
633
|
* Generate a text-based progress bar for usage percentage
|
|
628
634
|
* @param {number} percentage - Usage percentage (0-100)
|
|
635
|
+
* @param {number|null} thresholdPercentage - Optional threshold position to show in the bar (0-100)
|
|
629
636
|
* @returns {string} Text-based progress bar
|
|
637
|
+
* @see https://github.com/link-assistant/hive-mind/issues/1242
|
|
630
638
|
*/
|
|
631
|
-
export function getProgressBar(percentage) {
|
|
639
|
+
export function getProgressBar(percentage, thresholdPercentage = null) {
|
|
632
640
|
const totalBlocks = 30;
|
|
633
641
|
const filledBlocks = Math.round((percentage / 100) * totalBlocks);
|
|
634
|
-
|
|
635
|
-
|
|
642
|
+
|
|
643
|
+
if (thresholdPercentage === null) {
|
|
644
|
+
// No threshold - original behavior
|
|
645
|
+
const emptyBlocks = totalBlocks - filledBlocks;
|
|
646
|
+
return '\u2593'.repeat(filledBlocks) + '\u2591'.repeat(emptyBlocks);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// With threshold marker
|
|
650
|
+
const thresholdPos = Math.round((thresholdPercentage / 100) * totalBlocks);
|
|
651
|
+
let bar = '';
|
|
652
|
+
|
|
653
|
+
for (let i = 0; i < totalBlocks; i++) {
|
|
654
|
+
if (i === thresholdPos) {
|
|
655
|
+
bar += '│'; // Threshold marker (U+2502 Box Drawings Light Vertical)
|
|
656
|
+
} else if (i < filledBlocks) {
|
|
657
|
+
bar += '▓'; // Filled (U+2593)
|
|
658
|
+
} else {
|
|
659
|
+
bar += '░'; // Empty (U+2591)
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
return bar;
|
|
636
664
|
}
|
|
637
665
|
|
|
638
666
|
/**
|
|
@@ -664,12 +692,15 @@ export function calculateTimePassedPercentage(resetsAt, periodHours) {
|
|
|
664
692
|
|
|
665
693
|
/**
|
|
666
694
|
* Format Claude usage data into a Telegram-friendly message
|
|
695
|
+
* Shows threshold markers in progress bars to indicate where queue behavior changes.
|
|
696
|
+
*
|
|
667
697
|
* @param {Object} usage - The usage object from getClaudeUsageLimits
|
|
668
698
|
* @param {Object} diskSpace - Optional disk space info from getDiskSpaceInfo
|
|
669
699
|
* @param {Object} githubRateLimit - Optional GitHub rate limit info from getGitHubRateLimits
|
|
670
700
|
* @param {Object} cpuLoad - Optional CPU load info from getCpuLoadInfo
|
|
671
701
|
* @param {Object} memory - Optional memory info from getMemoryInfo
|
|
672
702
|
* @returns {string} Formatted message
|
|
703
|
+
* @see https://github.com/link-assistant/hive-mind/issues/1242
|
|
673
704
|
*/
|
|
674
705
|
export function formatUsageMessage(usage, diskSpace = null, githubRateLimit = null, cpuLoad = null, memory = null) {
|
|
675
706
|
// Use code block for monospace font to align progress bars properly
|
|
@@ -679,38 +710,46 @@ export function formatUsageMessage(usage, diskSpace = null, githubRateLimit = nu
|
|
|
679
710
|
message += `Current time: ${formatCurrentTime()}\n\n`;
|
|
680
711
|
|
|
681
712
|
// CPU load section (if provided)
|
|
713
|
+
// Threshold: Blocks new commands when usage >= 65%
|
|
682
714
|
if (cpuLoad) {
|
|
683
715
|
message += 'CPU\n';
|
|
684
|
-
const usedBar = getProgressBar(cpuLoad.usagePercentage);
|
|
685
|
-
|
|
716
|
+
const usedBar = getProgressBar(cpuLoad.usagePercentage, DISPLAY_THRESHOLDS.CPU);
|
|
717
|
+
const warning = cpuLoad.usagePercentage >= DISPLAY_THRESHOLDS.CPU ? ' ⚠️' : '';
|
|
718
|
+
message += `${usedBar} ${cpuLoad.usagePercentage}%${warning}\n`;
|
|
686
719
|
// Show cores used based on 5m load average (e.g., "0.04/6 CPU cores used" or "3/6 CPU cores used")
|
|
687
720
|
// Use parseFloat to strip unnecessary trailing zeros (3.00 -> 3, 0.10 -> 0.1, 0.04 -> 0.04)
|
|
688
721
|
message += `${parseFloat(cpuLoad.loadAvg5.toFixed(2))}/${cpuLoad.cpuCount} CPU cores used\n\n`;
|
|
689
722
|
}
|
|
690
723
|
|
|
691
724
|
// Memory section (if provided)
|
|
725
|
+
// Threshold: Blocks new commands when usage >= 65%
|
|
692
726
|
if (memory) {
|
|
693
727
|
message += 'RAM\n';
|
|
694
|
-
const usedBar = getProgressBar(memory.usedPercentage);
|
|
695
|
-
|
|
728
|
+
const usedBar = getProgressBar(memory.usedPercentage, DISPLAY_THRESHOLDS.RAM);
|
|
729
|
+
const warning = memory.usedPercentage >= DISPLAY_THRESHOLDS.RAM ? ' ⚠️' : '';
|
|
730
|
+
message += `${usedBar} ${memory.usedPercentage}%${warning}\n`;
|
|
696
731
|
message += `${formatBytesRange(memory.usedBytes, memory.totalBytes)}\n\n`;
|
|
697
732
|
}
|
|
698
733
|
|
|
699
734
|
// Disk space section (if provided)
|
|
735
|
+
// Threshold: One-at-a-time mode when usage >= 90%
|
|
700
736
|
if (diskSpace) {
|
|
701
737
|
message += 'Disk space\n';
|
|
702
|
-
// Show used percentage with progress bar
|
|
703
|
-
const usedBar = getProgressBar(diskSpace.usedPercentage);
|
|
704
|
-
|
|
738
|
+
// Show used percentage with progress bar and threshold marker
|
|
739
|
+
const usedBar = getProgressBar(diskSpace.usedPercentage, DISPLAY_THRESHOLDS.DISK);
|
|
740
|
+
const warning = diskSpace.usedPercentage >= DISPLAY_THRESHOLDS.DISK ? ' ⚠️' : '';
|
|
741
|
+
message += `${usedBar} ${diskSpace.usedPercentage}%${warning}\n`;
|
|
705
742
|
message += `${formatBytesRange(diskSpace.usedBytes, diskSpace.totalBytes)}\n\n`;
|
|
706
743
|
}
|
|
707
744
|
|
|
708
745
|
// GitHub API rate limits section (if provided)
|
|
746
|
+
// Threshold: Blocks parallel claude commands when >= 75%
|
|
709
747
|
if (githubRateLimit) {
|
|
710
748
|
message += 'GitHub API\n';
|
|
711
|
-
// Show used percentage with progress bar
|
|
712
|
-
const usedBar = getProgressBar(githubRateLimit.usedPercentage);
|
|
713
|
-
|
|
749
|
+
// Show used percentage with progress bar and threshold marker
|
|
750
|
+
const usedBar = getProgressBar(githubRateLimit.usedPercentage, DISPLAY_THRESHOLDS.GITHUB_API);
|
|
751
|
+
const warning = githubRateLimit.usedPercentage >= DISPLAY_THRESHOLDS.GITHUB_API ? ' ⚠️' : '';
|
|
752
|
+
message += `${usedBar} ${githubRateLimit.usedPercentage}%${warning}\n`;
|
|
714
753
|
message += `${githubRateLimit.used}/${githubRateLimit.limit} requests used\n`;
|
|
715
754
|
if (githubRateLimit.relativeReset) {
|
|
716
755
|
message += `Resets in ${githubRateLimit.relativeReset} (${githubRateLimit.resetTime})\n`;
|
|
@@ -721,21 +760,23 @@ export function formatUsageMessage(usage, diskSpace = null, githubRateLimit = nu
|
|
|
721
760
|
}
|
|
722
761
|
|
|
723
762
|
// Claude 5 hour session (five_hour)
|
|
763
|
+
// Threshold: One-at-a-time mode when usage >= 65%
|
|
724
764
|
message += 'Claude 5 hour session\n';
|
|
725
765
|
if (usage.currentSession.percentage !== null) {
|
|
726
|
-
// Add time passed progress bar first
|
|
766
|
+
// Add time passed progress bar first (no threshold marker for time)
|
|
727
767
|
const timePassed = calculateTimePassedPercentage(usage.currentSession.resetsAt, 5);
|
|
728
768
|
if (timePassed !== null) {
|
|
729
769
|
const timeBar = getProgressBar(timePassed);
|
|
730
770
|
message += `${timeBar} ${timePassed}% passed\n`;
|
|
731
771
|
}
|
|
732
772
|
|
|
733
|
-
// Add usage progress bar second
|
|
773
|
+
// Add usage progress bar second with threshold marker
|
|
734
774
|
// Use Math.floor so 100% only appears when usage is exactly 100%
|
|
735
775
|
// See: https://github.com/link-assistant/hive-mind/issues/1133
|
|
736
776
|
const pct = Math.floor(usage.currentSession.percentage);
|
|
737
|
-
const bar = getProgressBar(pct);
|
|
738
|
-
|
|
777
|
+
const bar = getProgressBar(pct, DISPLAY_THRESHOLDS.CLAUDE_5_HOUR_SESSION);
|
|
778
|
+
const warning = pct >= DISPLAY_THRESHOLDS.CLAUDE_5_HOUR_SESSION ? ' ⚠️' : '';
|
|
779
|
+
message += `${bar} ${pct}%${warning}\n`;
|
|
739
780
|
|
|
740
781
|
if (usage.currentSession.resetTime) {
|
|
741
782
|
const relativeTime = formatRelativeTime(usage.currentSession.resetsAt);
|
|
@@ -751,21 +792,23 @@ export function formatUsageMessage(usage, diskSpace = null, githubRateLimit = nu
|
|
|
751
792
|
message += '\n';
|
|
752
793
|
|
|
753
794
|
// Current week (all models / seven_day)
|
|
795
|
+
// Threshold: One-at-a-time mode when usage >= 97%
|
|
754
796
|
message += 'Current week (all models)\n';
|
|
755
797
|
if (usage.allModels.percentage !== null) {
|
|
756
|
-
// Add time passed progress bar first (
|
|
798
|
+
// Add time passed progress bar first (no threshold marker for time)
|
|
757
799
|
const timePassed = calculateTimePassedPercentage(usage.allModels.resetsAt, 168);
|
|
758
800
|
if (timePassed !== null) {
|
|
759
801
|
const timeBar = getProgressBar(timePassed);
|
|
760
802
|
message += `${timeBar} ${timePassed}% passed\n`;
|
|
761
803
|
}
|
|
762
804
|
|
|
763
|
-
// Add usage progress bar second
|
|
805
|
+
// Add usage progress bar second with threshold marker
|
|
764
806
|
// Use Math.floor so 100% only appears when usage is exactly 100%
|
|
765
807
|
// See: https://github.com/link-assistant/hive-mind/issues/1133
|
|
766
808
|
const pct = Math.floor(usage.allModels.percentage);
|
|
767
|
-
const bar = getProgressBar(pct);
|
|
768
|
-
|
|
809
|
+
const bar = getProgressBar(pct, DISPLAY_THRESHOLDS.CLAUDE_WEEKLY);
|
|
810
|
+
const warning = pct >= DISPLAY_THRESHOLDS.CLAUDE_WEEKLY ? ' ⚠️' : '';
|
|
811
|
+
message += `${bar} ${pct}%${warning}\n`;
|
|
769
812
|
|
|
770
813
|
if (usage.allModels.resetTime) {
|
|
771
814
|
const relativeTime = formatRelativeTime(usage.allModels.resetsAt);
|
|
@@ -781,21 +824,23 @@ export function formatUsageMessage(usage, diskSpace = null, githubRateLimit = nu
|
|
|
781
824
|
message += '\n';
|
|
782
825
|
|
|
783
826
|
// Current week (Sonnet only / seven_day_sonnet)
|
|
827
|
+
// Threshold: One-at-a-time mode when usage >= 97% (same as all models)
|
|
784
828
|
message += 'Current week (Sonnet only)\n';
|
|
785
829
|
if (usage.sonnetOnly.percentage !== null) {
|
|
786
|
-
// Add time passed progress bar first (
|
|
830
|
+
// Add time passed progress bar first (no threshold marker for time)
|
|
787
831
|
const timePassed = calculateTimePassedPercentage(usage.sonnetOnly.resetsAt, 168);
|
|
788
832
|
if (timePassed !== null) {
|
|
789
833
|
const timeBar = getProgressBar(timePassed);
|
|
790
834
|
message += `${timeBar} ${timePassed}% passed\n`;
|
|
791
835
|
}
|
|
792
836
|
|
|
793
|
-
// Add usage progress bar second
|
|
837
|
+
// Add usage progress bar second with threshold marker
|
|
794
838
|
// Use Math.floor so 100% only appears when usage is exactly 100%
|
|
795
839
|
// See: https://github.com/link-assistant/hive-mind/issues/1133
|
|
796
840
|
const pct = Math.floor(usage.sonnetOnly.percentage);
|
|
797
|
-
const bar = getProgressBar(pct);
|
|
798
|
-
|
|
841
|
+
const bar = getProgressBar(pct, DISPLAY_THRESHOLDS.CLAUDE_WEEKLY);
|
|
842
|
+
const warning = pct >= DISPLAY_THRESHOLDS.CLAUDE_WEEKLY ? ' ⚠️' : '';
|
|
843
|
+
message += `${bar} ${pct}%${warning}\n`;
|
|
799
844
|
|
|
800
845
|
if (usage.sonnetOnly.resetTime) {
|
|
801
846
|
const relativeTime = formatRelativeTime(usage.sonnetOnly.resetsAt);
|
|
@@ -973,6 +1018,8 @@ export default {
|
|
|
973
1018
|
getProgressBar,
|
|
974
1019
|
calculateTimePassedPercentage,
|
|
975
1020
|
formatUsageMessage,
|
|
1021
|
+
// Threshold constants for progress bar visualization
|
|
1022
|
+
DISPLAY_THRESHOLDS,
|
|
976
1023
|
// Cache management
|
|
977
1024
|
CACHE_TTL,
|
|
978
1025
|
getLimitCache,
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
// Claude models (Anthropic API)
|
|
9
|
-
// Updated for Opus 4.6 support (Issue #1221)
|
|
9
|
+
// Updated for Opus 4.5/4.6 support (Issue #1221, Issue #1238)
|
|
10
10
|
export const claudeModels = {
|
|
11
11
|
sonnet: 'claude-sonnet-4-5-20250929', // Sonnet 4.5
|
|
12
|
-
opus: 'claude-opus-4-
|
|
12
|
+
opus: 'claude-opus-4-5-20251101', // Opus 4.5 (default, Issue #1238)
|
|
13
13
|
haiku: 'claude-haiku-4-5-20251001', // Haiku 4.5
|
|
14
14
|
'haiku-3-5': 'claude-3-5-haiku-20241022', // Haiku 3.5
|
|
15
15
|
'haiku-3': 'claude-3-haiku-20240307', // Haiku 3
|
|
@@ -15,7 +15,7 @@ import { log } from './lib.mjs';
|
|
|
15
15
|
export const CLAUDE_MODELS = {
|
|
16
16
|
// Short aliases (single word)
|
|
17
17
|
sonnet: 'claude-sonnet-4-5-20250929',
|
|
18
|
-
opus: 'claude-opus-4-
|
|
18
|
+
opus: 'claude-opus-4-5-20251101', // Changed to Opus 4.5 (Issue #1238)
|
|
19
19
|
haiku: 'claude-haiku-4-5-20251001',
|
|
20
20
|
'haiku-3-5': 'claude-3-5-haiku-20241022',
|
|
21
21
|
'haiku-3': 'claude-3-haiku-20240307',
|
|
@@ -25,8 +25,8 @@ export const CLAUDE_MODELS = {
|
|
|
25
25
|
'sonnet-4-5': 'claude-sonnet-4-5-20250929', // Sonnet 4.5 short alias
|
|
26
26
|
'haiku-4-5': 'claude-haiku-4-5-20251001', // Haiku 4.5 short alias
|
|
27
27
|
// Opus version aliases (Issue #1221)
|
|
28
|
-
'claude-opus-4-6': 'claude-opus-4-6', //
|
|
29
|
-
'claude-opus-4-5': 'claude-opus-4-5-20251101', //
|
|
28
|
+
'claude-opus-4-6': 'claude-opus-4-6', // Opus 4.6
|
|
29
|
+
'claude-opus-4-5': 'claude-opus-4-5-20251101', // Opus 4.5
|
|
30
30
|
// Sonnet version aliases
|
|
31
31
|
'claude-sonnet-4-5': 'claude-sonnet-4-5-20250929',
|
|
32
32
|
// Haiku version aliases
|
|
@@ -39,15 +39,17 @@ export const CLAUDE_MODELS = {
|
|
|
39
39
|
'claude-3-haiku-20240307': 'claude-3-haiku-20240307',
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
-
// Models that support 1M token context window via [1m] suffix (Issue #1221)
|
|
42
|
+
// Models that support 1M token context window via [1m] suffix (Issue #1221, Issue #1238)
|
|
43
43
|
// See: https://code.claude.com/docs/en/model-config
|
|
44
44
|
export const MODELS_SUPPORTING_1M_CONTEXT = [
|
|
45
45
|
'claude-opus-4-6',
|
|
46
|
+
'claude-opus-4-5-20251101',
|
|
46
47
|
'claude-sonnet-4-5-20250929',
|
|
47
48
|
'claude-sonnet-4-5',
|
|
48
49
|
'sonnet',
|
|
49
50
|
'opus',
|
|
50
51
|
'opus-4-6', // Short alias (Issue #1221 - PR comment feedback)
|
|
52
|
+
'opus-4-5', // Short alias (Issue #1238)
|
|
51
53
|
'sonnet-4-5', // Short alias (Issue #1221 - PR comment feedback)
|
|
52
54
|
];
|
|
53
55
|
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Queue Configuration Module
|
|
5
|
+
*
|
|
6
|
+
* Centralized configuration for queue throttling and resource thresholds.
|
|
7
|
+
* This module is used by both telegram-solve-queue.lib.mjs (queue logic)
|
|
8
|
+
* and limits.lib.mjs (display formatting).
|
|
9
|
+
*
|
|
10
|
+
* @see https://github.com/link-assistant/hive-mind/issues/1242
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// Use use-m to dynamically import modules
|
|
14
|
+
if (typeof globalThis.use === 'undefined') {
|
|
15
|
+
try {
|
|
16
|
+
globalThis.use = (await eval(await (await fetch('https://unpkg.com/use-m/use.js')).text())).use;
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.error('❌ Fatal error: Failed to load dependencies for queue configuration');
|
|
19
|
+
console.error(` ${error.message}`);
|
|
20
|
+
console.error(' This might be due to network issues or missing dependencies.');
|
|
21
|
+
console.error(' Please check your internet connection and try again.');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const getenv = await use('getenv');
|
|
27
|
+
|
|
28
|
+
// Helper function to safely parse floats with fallback
|
|
29
|
+
const parseFloatWithDefault = (envVar, defaultValue) => {
|
|
30
|
+
const value = getenv(envVar, defaultValue.toString());
|
|
31
|
+
const parsed = parseFloat(value);
|
|
32
|
+
return isNaN(parsed) ? defaultValue : parsed;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Helper function to safely parse integers with fallback
|
|
36
|
+
const parseIntWithDefault = (envVar, defaultValue) => {
|
|
37
|
+
const value = getenv(envVar, defaultValue.toString());
|
|
38
|
+
const parsed = parseInt(value);
|
|
39
|
+
return isNaN(parsed) ? defaultValue : parsed;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Configuration constants for queue throttling
|
|
44
|
+
* All thresholds use ratios (0.0 - 1.0) representing usage percentage
|
|
45
|
+
*
|
|
46
|
+
* IMPORTANT: Running claude processes is NOT a blocking limit by itself.
|
|
47
|
+
* Commands can run in parallel as long as actual limits (CPU, API, etc.) are not exceeded.
|
|
48
|
+
* See: https://github.com/link-assistant/hive-mind/issues/1078
|
|
49
|
+
*/
|
|
50
|
+
export const QUEUE_CONFIG = {
|
|
51
|
+
// Resource thresholds (usage ratios: 0.0 - 1.0)
|
|
52
|
+
// All thresholds use >= comparison (inclusive)
|
|
53
|
+
RAM_THRESHOLD: parseFloatWithDefault('HIVE_MIND_RAM_THRESHOLD', 0.65), // Enqueue if RAM usage >= 65%
|
|
54
|
+
// CPU threshold uses 5-minute load average, not instantaneous CPU usage
|
|
55
|
+
CPU_THRESHOLD: parseFloatWithDefault('HIVE_MIND_CPU_THRESHOLD', 0.65), // Enqueue if 5-minute load average >= 65% of CPU count
|
|
56
|
+
DISK_THRESHOLD: parseFloatWithDefault('HIVE_MIND_DISK_THRESHOLD', 0.9), // One-at-a-time if disk usage >= 90%, tuned for VM with 100 GB drive
|
|
57
|
+
|
|
58
|
+
// API limit thresholds (usage ratios: 0.0 - 1.0)
|
|
59
|
+
// All thresholds use >= comparison (inclusive)
|
|
60
|
+
// Fine-tuned for Claude MAX $200 subscription
|
|
61
|
+
CLAUDE_5_HOUR_SESSION_THRESHOLD: parseFloatWithDefault('HIVE_MIND_CLAUDE_5_HOUR_SESSION_THRESHOLD', 0.65), // One-at-a-time if 5-hour limit >= 65%
|
|
62
|
+
CLAUDE_WEEKLY_THRESHOLD: parseFloatWithDefault('HIVE_MIND_CLAUDE_WEEKLY_THRESHOLD', 0.97), // One-at-a-time if weekly limit >= 97%
|
|
63
|
+
GITHUB_API_THRESHOLD: parseFloatWithDefault('HIVE_MIND_GITHUB_API_THRESHOLD', 0.75), // Enqueue if GitHub >= 75% with parallel claude
|
|
64
|
+
|
|
65
|
+
// Timing
|
|
66
|
+
// MIN_START_INTERVAL_MS: Time to allow solve command to start actual claude process
|
|
67
|
+
// This ensures that when API limits are checked, the running process is counted
|
|
68
|
+
MIN_START_INTERVAL_MS: parseIntWithDefault('HIVE_MIND_MIN_START_INTERVAL_MS', 60000), // 1 minute between starts
|
|
69
|
+
CONSUMER_POLL_INTERVAL_MS: parseIntWithDefault('HIVE_MIND_CONSUMER_POLL_INTERVAL_MS', 60000), // 1 minute between queue checks
|
|
70
|
+
MESSAGE_UPDATE_INTERVAL_MS: parseIntWithDefault('HIVE_MIND_MESSAGE_UPDATE_INTERVAL_MS', 60000), // 1 minute between status message updates
|
|
71
|
+
|
|
72
|
+
// Process detection
|
|
73
|
+
CLAUDE_PROCESS_NAMES: ['claude'], // Process names to detect
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Convert threshold ratio to percentage for display
|
|
78
|
+
* @param {number} ratio - Threshold ratio (0.0 - 1.0)
|
|
79
|
+
* @returns {number} Percentage value (0 - 100)
|
|
80
|
+
*/
|
|
81
|
+
export function thresholdToPercent(ratio) {
|
|
82
|
+
return Math.round(ratio * 100);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Display threshold constants for progress bar visualization
|
|
87
|
+
* These are derived from QUEUE_CONFIG and converted to percentages (0-100)
|
|
88
|
+
*
|
|
89
|
+
* @see https://github.com/link-assistant/hive-mind/issues/1242
|
|
90
|
+
*/
|
|
91
|
+
export const DISPLAY_THRESHOLDS = {
|
|
92
|
+
RAM: thresholdToPercent(QUEUE_CONFIG.RAM_THRESHOLD), // Blocks at 65%
|
|
93
|
+
CPU: thresholdToPercent(QUEUE_CONFIG.CPU_THRESHOLD), // Blocks at 65%
|
|
94
|
+
DISK: thresholdToPercent(QUEUE_CONFIG.DISK_THRESHOLD), // One-at-a-time at 90%
|
|
95
|
+
CLAUDE_5_HOUR_SESSION: thresholdToPercent(QUEUE_CONFIG.CLAUDE_5_HOUR_SESSION_THRESHOLD), // One-at-a-time at 65%
|
|
96
|
+
CLAUDE_WEEKLY: thresholdToPercent(QUEUE_CONFIG.CLAUDE_WEEKLY_THRESHOLD), // One-at-a-time at 97%
|
|
97
|
+
GITHUB_API: thresholdToPercent(QUEUE_CONFIG.GITHUB_API_THRESHOLD), // Blocks parallel claude at 75%
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export default {
|
|
101
|
+
QUEUE_CONFIG,
|
|
102
|
+
DISPLAY_THRESHOLDS,
|
|
103
|
+
thresholdToPercent,
|
|
104
|
+
};
|
package/src/solve.config.lib.mjs
CHANGED
|
@@ -220,7 +220,7 @@ export const SOLVE_OPTION_DEFINITIONS = {
|
|
|
220
220
|
},
|
|
221
221
|
'thinking-budget': {
|
|
222
222
|
type: 'number',
|
|
223
|
-
description: 'Thinking token budget for Claude Code (0-
|
|
223
|
+
description: 'Thinking token budget for Claude Code (0-31999). Controls MAX_THINKING_TOKENS. Default: 0 (thinking disabled). For older Claude Code versions, translated back to --think level.',
|
|
224
224
|
default: undefined,
|
|
225
225
|
},
|
|
226
226
|
'thinking-budget-claude-minimum-version': {
|
|
@@ -23,39 +23,11 @@ const execAsync = promisify(exec);
|
|
|
23
23
|
// Import centralized limits and caching
|
|
24
24
|
import { getCachedClaudeLimits, getCachedGitHubLimits, getCachedMemoryInfo, getCachedCpuInfo, getCachedDiskInfo, getLimitCache } from './limits.lib.mjs';
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
* Commands can run in parallel as long as actual limits (CPU, API, etc.) are not exceeded.
|
|
32
|
-
* See: https://github.com/link-assistant/hive-mind/issues/1078
|
|
33
|
-
*/
|
|
34
|
-
export const QUEUE_CONFIG = {
|
|
35
|
-
// Resource thresholds (usage ratios: 0.0 - 1.0)
|
|
36
|
-
// All thresholds use >= comparison (inclusive)
|
|
37
|
-
RAM_THRESHOLD: 0.65, // Enqueue if RAM usage >= 65%
|
|
38
|
-
// CPU threshold uses 5-minute load average, not instantaneous CPU usage
|
|
39
|
-
CPU_THRESHOLD: 0.65, // Enqueue if 5-minute load average >= 65% of CPU count
|
|
40
|
-
DISK_THRESHOLD: 0.9, // One-at-a-time if disk usage >= 90%, tuned for VM with 100 GB drive
|
|
41
|
-
|
|
42
|
-
// API limit thresholds (usage ratios: 0.0 - 1.0)
|
|
43
|
-
// All thresholds use >= comparison (inclusive)
|
|
44
|
-
// Fine-tuned for Claude MAX $200 subscription
|
|
45
|
-
CLAUDE_5_HOUR_SESSION_THRESHOLD: 0.65, // One-at-a-time if 5-hour limit >= 65%
|
|
46
|
-
CLAUDE_WEEKLY_THRESHOLD: 0.97, // One-at-a-time if weekly limit >= 97%
|
|
47
|
-
GITHUB_API_THRESHOLD: 0.75, // Enqueue if GitHub >= 75% with parallel claude
|
|
48
|
-
|
|
49
|
-
// Timing
|
|
50
|
-
// MIN_START_INTERVAL_MS: Time to allow solve command to start actual claude process
|
|
51
|
-
// This ensures that when API limits are checked, the running process is counted
|
|
52
|
-
MIN_START_INTERVAL_MS: 60000, // 1 minutes between starts
|
|
53
|
-
CONSUMER_POLL_INTERVAL_MS: 60000, // 1 minute between queue checks
|
|
54
|
-
MESSAGE_UPDATE_INTERVAL_MS: 60000, // 1 minute between status message updates
|
|
55
|
-
|
|
56
|
-
// Process detection
|
|
57
|
-
CLAUDE_PROCESS_NAMES: ['claude'], // Process names to detect
|
|
58
|
-
};
|
|
26
|
+
// Import centralized queue configuration
|
|
27
|
+
// This ensures thresholds are consistent between queue logic and display formatting
|
|
28
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1242
|
|
29
|
+
export { QUEUE_CONFIG } from './queue-config.lib.mjs';
|
|
30
|
+
import { QUEUE_CONFIG } from './queue-config.lib.mjs';
|
|
59
31
|
|
|
60
32
|
/**
|
|
61
33
|
* Status enum for queue items
|