@link-assistant/hive-mind 1.50.15 → 1.52.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 +12 -0
- package/README.hi.md +12 -0
- package/README.md +12 -0
- package/README.ru.md +12 -0
- package/README.zh.md +12 -0
- package/package.json +2 -2
- package/src/agent.prompts.lib.mjs +4 -9
- package/src/claude.lib.mjs +3 -1
- package/src/claude.prompts.lib.mjs +5 -13
- package/src/codex.options.lib.mjs +1 -0
- package/src/codex.prompts.lib.mjs +4 -9
- package/src/config.lib.mjs +133 -31
- package/src/models/index.mjs +11 -6
- package/src/opencode.prompts.lib.mjs +4 -9
- package/src/solve.config.lib.mjs +7 -2
- package/src/telegram-bot.mjs +27 -60
- package/src/telegram-solve-command.lib.mjs +102 -0
- package/src/thinking-prompt.lib.mjs +61 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.52.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 5b24866: Add Claude Opus 4.7 model support with adaptive thinking, model-correct xhigh/max effort mapping, Opus 4.5/Mythos effort detection, and the --show-thinking-content option.
|
|
8
|
+
|
|
9
|
+
## 1.51.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- fd3c76c: Add per-tool Telegram solve aliases: /claude, /codex, /opencode, and /agent.
|
|
14
|
+
|
|
3
15
|
## 1.50.15
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/README.hi.md
CHANGED
|
@@ -444,6 +444,18 @@ Examples:
|
|
|
444
444
|
/solve https://github.com/owner/repo/issues/123 --model sonnet
|
|
445
445
|
/solve https://github.com/owner/repo/issues/123 --model opus --think max
|
|
446
446
|
|
|
447
|
+
Aliases:
|
|
448
|
+
/do और /continue /solve के बराबर हैं
|
|
449
|
+
/claude /solve --tool claude के बराबर है
|
|
450
|
+
/codex /solve --tool codex के बराबर है
|
|
451
|
+
/opencode /solve --tool opencode के बराबर है
|
|
452
|
+
/agent /solve --tool agent के बराबर है
|
|
453
|
+
|
|
454
|
+
Tool alias examples:
|
|
455
|
+
/codex https://github.com/owner/repo/issues/123 --model gpt-5.4
|
|
456
|
+
/opencode https://github.com/owner/repo/issues/123 --model grok-code-fast-1
|
|
457
|
+
/agent https://github.com/owner/repo/issues/123 --model nemotron-3-super-free
|
|
458
|
+
|
|
447
459
|
Free Models (with --tool agent):
|
|
448
460
|
/solve https://github.com/owner/repo/issues/123 --tool agent --model nemotron-3-super-free
|
|
449
461
|
/solve https://github.com/owner/repo/issues/123 --tool agent --model opencode/nemotron-3-super-free
|
package/README.md
CHANGED
|
@@ -453,6 +453,18 @@ Examples:
|
|
|
453
453
|
/solve https://github.com/owner/repo/issues/123 --model sonnet
|
|
454
454
|
/solve https://github.com/owner/repo/issues/123 --model opus --think max
|
|
455
455
|
|
|
456
|
+
Aliases:
|
|
457
|
+
/do and /continue are equivalent to /solve
|
|
458
|
+
/claude is equivalent to /solve --tool claude
|
|
459
|
+
/codex is equivalent to /solve --tool codex
|
|
460
|
+
/opencode is equivalent to /solve --tool opencode
|
|
461
|
+
/agent is equivalent to /solve --tool agent
|
|
462
|
+
|
|
463
|
+
Tool alias examples:
|
|
464
|
+
/codex https://github.com/owner/repo/issues/123 --model gpt-5.4
|
|
465
|
+
/opencode https://github.com/owner/repo/issues/123 --model grok-code-fast-1
|
|
466
|
+
/agent https://github.com/owner/repo/issues/123 --model nemotron-3-super-free
|
|
467
|
+
|
|
456
468
|
Free Models (with --tool agent):
|
|
457
469
|
/solve https://github.com/owner/repo/issues/123 --tool agent --model nemotron-3-super-free
|
|
458
470
|
/solve https://github.com/owner/repo/issues/123 --tool agent --model opencode/nemotron-3-super-free
|
package/README.ru.md
CHANGED
|
@@ -444,6 +444,18 @@ Examples:
|
|
|
444
444
|
/solve https://github.com/owner/repo/issues/123 --model sonnet
|
|
445
445
|
/solve https://github.com/owner/repo/issues/123 --model opus --think max
|
|
446
446
|
|
|
447
|
+
Aliases:
|
|
448
|
+
/do и /continue эквивалентны /solve
|
|
449
|
+
/claude эквивалентна /solve --tool claude
|
|
450
|
+
/codex эквивалентна /solve --tool codex
|
|
451
|
+
/opencode эквивалентна /solve --tool opencode
|
|
452
|
+
/agent эквивалентна /solve --tool agent
|
|
453
|
+
|
|
454
|
+
Tool alias examples:
|
|
455
|
+
/codex https://github.com/owner/repo/issues/123 --model gpt-5.4
|
|
456
|
+
/opencode https://github.com/owner/repo/issues/123 --model grok-code-fast-1
|
|
457
|
+
/agent https://github.com/owner/repo/issues/123 --model nemotron-3-super-free
|
|
458
|
+
|
|
447
459
|
Free Models (with --tool agent):
|
|
448
460
|
/solve https://github.com/owner/repo/issues/123 --tool agent --model nemotron-3-super-free
|
|
449
461
|
/solve https://github.com/owner/repo/issues/123 --tool agent --model opencode/nemotron-3-super-free
|
package/README.zh.md
CHANGED
|
@@ -444,6 +444,18 @@ Examples:
|
|
|
444
444
|
/solve https://github.com/owner/repo/issues/123 --model sonnet
|
|
445
445
|
/solve https://github.com/owner/repo/issues/123 --model opus --think max
|
|
446
446
|
|
|
447
|
+
Aliases:
|
|
448
|
+
/do 和 /continue 等同于 /solve
|
|
449
|
+
/claude 等同于 /solve --tool claude
|
|
450
|
+
/codex 等同于 /solve --tool codex
|
|
451
|
+
/opencode 等同于 /solve --tool opencode
|
|
452
|
+
/agent 等同于 /solve --tool agent
|
|
453
|
+
|
|
454
|
+
Tool alias examples:
|
|
455
|
+
/codex https://github.com/owner/repo/issues/123 --model gpt-5.4
|
|
456
|
+
/opencode https://github.com/owner/repo/issues/123 --model grok-code-fast-1
|
|
457
|
+
/agent https://github.com/owner/repo/issues/123 --model nemotron-3-super-free
|
|
458
|
+
|
|
447
459
|
Free Models (with --tool agent):
|
|
448
460
|
/solve https://github.com/owner/repo/issues/123 --tool agent --model nemotron-3-super-free
|
|
449
461
|
/solve https://github.com/owner/repo/issues/123 --tool agent --model opencode/nemotron-3-super-free
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@link-assistant/hive-mind",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.52.0",
|
|
4
4
|
"description": "AI-powered issue solver and hive mind for collaborative problem solving",
|
|
5
5
|
"main": "src/hive.mjs",
|
|
6
6
|
"type": "module",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"hive-telegram-bot": "./src/telegram-bot.mjs"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
|
-
"test": "node tests/solve-queue.test.mjs && node tests/limits-display.test.mjs && node tests/test-usage-limit.mjs && node tests/test-issue-1616-pr-issue-link-preservation.mjs && node tests/test-telegram-message-filters.mjs && node tests/test-solve-queue-command.mjs && node tests/test-queue-display-1267.mjs && node tests/test-telegram-bot-launcher.mjs",
|
|
16
|
+
"test": "node tests/solve-queue.test.mjs && node tests/limits-display.test.mjs && node tests/test-usage-limit.mjs && node tests/test-issue-1616-pr-issue-link-preservation.mjs && node tests/test-telegram-message-filters.mjs && node tests/test-telegram-bot-command-aliases.mjs && node tests/test-solve-queue-command.mjs && node tests/test-queue-display-1267.mjs && node tests/test-telegram-bot-launcher.mjs",
|
|
17
17
|
"test:queue": "node tests/solve-queue.test.mjs",
|
|
18
18
|
"test:limits-display": "node tests/limits-display.test.mjs",
|
|
19
19
|
"test:usage-limit": "node tests/test-usage-limit.mjs",
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { getArchitectureCareSubPrompt } from './architecture-care.prompts.lib.mjs';
|
|
7
7
|
import { getExperimentsExamplesSubPrompt } from './experiments-examples.prompts.lib.mjs';
|
|
8
|
+
import { getThinkingPromptInstruction } from './thinking-prompt.lib.mjs';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Build the user prompt for Agent
|
|
@@ -58,15 +59,9 @@ export const buildUserPrompt = params => {
|
|
|
58
59
|
promptLines.push('');
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
low: 'Think.',
|
|
65
|
-
medium: 'Think hard.',
|
|
66
|
-
high: 'Think harder.',
|
|
67
|
-
max: 'Ultrathink.',
|
|
68
|
-
};
|
|
69
|
-
promptLines.push(thinkMessages[argv.think]);
|
|
62
|
+
const thinkingPromptInstruction = getThinkingPromptInstruction({ tool: 'agent', argv });
|
|
63
|
+
if (thinkingPromptInstruction) {
|
|
64
|
+
promptLines.push(thinkingPromptInstruction);
|
|
70
65
|
}
|
|
71
66
|
|
|
72
67
|
// Final instruction
|
package/src/claude.lib.mjs
CHANGED
|
@@ -301,6 +301,7 @@ export const executeClaude = async params => {
|
|
|
301
301
|
owner,
|
|
302
302
|
repo,
|
|
303
303
|
argv,
|
|
304
|
+
claudeVersion: getClaudeVersion(),
|
|
304
305
|
});
|
|
305
306
|
// Build the system prompt
|
|
306
307
|
const systemPrompt = buildSystemPrompt({
|
|
@@ -784,7 +785,7 @@ export const executeClaudeCommand = async params => {
|
|
|
784
785
|
}
|
|
785
786
|
try {
|
|
786
787
|
const { thinkingBudget: resolvedThinkingBudget, thinkLevel, isNewVersion, maxBudget } = await resolveThinkingSettings(argv, log);
|
|
787
|
-
const claudeEnv = getClaudeEnv({ thinkingBudget: resolvedThinkingBudget, model: effectiveModel, thinkLevel, maxBudget, planModel: resolvedPlanModel, executionModel: resolvedExecutionModel });
|
|
788
|
+
const claudeEnv = getClaudeEnv({ thinkingBudget: resolvedThinkingBudget, model: effectiveModel, thinkLevel, maxBudget, planModel: resolvedPlanModel, executionModel: resolvedExecutionModel, showThinkingContent: argv.showThinkingContent });
|
|
788
789
|
if (argv.verbose) claudeEnv.ANTHROPIC_LOG = 'debug';
|
|
789
790
|
const modelMaxOutputTokens = getMaxOutputTokensForModel(effectiveModel);
|
|
790
791
|
if (argv.verbose) {
|
|
@@ -792,6 +793,7 @@ export const executeClaudeCommand = async params => {
|
|
|
792
793
|
if (resolvedPlanModel) await log(`📊 opusplan: plan=${resolvedPlanModel}, exec=${resolvedExecutionModel}`, { verbose: true });
|
|
793
794
|
if (resolvedThinkingBudget !== undefined) await log(`📊 MAX_THINKING_TOKENS: ${resolvedThinkingBudget}`, { verbose: true });
|
|
794
795
|
if (claudeEnv.CLAUDE_CODE_EFFORT_LEVEL) await log(`📊 CLAUDE_CODE_EFFORT_LEVEL: ${claudeEnv.CLAUDE_CODE_EFFORT_LEVEL}`, { verbose: true });
|
|
796
|
+
if (claudeEnv.CLAUDE_CODE_SHOW_THINKING) await log(`📊 CLAUDE_CODE_SHOW_THINKING: ${claudeEnv.CLAUDE_CODE_SHOW_THINKING}`, { verbose: true });
|
|
795
797
|
if (!isNewVersion && thinkLevel) await log(`📊 Thinking level (via keywords): ${thinkLevel}`, { verbose: true });
|
|
796
798
|
}
|
|
797
799
|
const simpleEscapedSystem = systemPrompt.replace(/"/g, '\\"');
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { getArchitectureCareSubPrompt } from './architecture-care.prompts.lib.mjs';
|
|
7
7
|
import { getExperimentsExamplesSubPrompt } from './experiments-examples.prompts.lib.mjs';
|
|
8
8
|
import { primaryModelNames } from './models/index.mjs';
|
|
9
|
+
import { getThinkingPromptInstruction } from './thinking-prompt.lib.mjs';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Build the user prompt for Claude
|
|
@@ -13,7 +14,7 @@ import { primaryModelNames } from './models/index.mjs';
|
|
|
13
14
|
* @returns {string} The formatted user prompt
|
|
14
15
|
*/
|
|
15
16
|
export const buildUserPrompt = params => {
|
|
16
|
-
const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, workspaceTmpDir, isContinueMode, forkedRepo, feedbackLines, owner, repo, argv, contributingGuidelines } = params;
|
|
17
|
+
const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, workspaceTmpDir, isContinueMode, forkedRepo, feedbackLines, owner, repo, argv, contributingGuidelines, claudeVersion } = params;
|
|
17
18
|
|
|
18
19
|
const promptLines = [];
|
|
19
20
|
|
|
@@ -65,18 +66,9 @@ export const buildUserPrompt = params => {
|
|
|
65
66
|
promptLines.push('');
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
// Keeping keywords for backward compatibility with older Claude Code versions
|
|
72
|
-
if (argv && argv.think) {
|
|
73
|
-
const thinkMessages = {
|
|
74
|
-
low: 'Think.',
|
|
75
|
-
medium: 'Think hard.',
|
|
76
|
-
high: 'Think harder.',
|
|
77
|
-
max: 'Ultrathink.',
|
|
78
|
-
};
|
|
79
|
-
promptLines.push(thinkMessages[argv.think]);
|
|
69
|
+
const thinkingPromptInstruction = getThinkingPromptInstruction({ tool: 'claude', argv, claudeVersion });
|
|
70
|
+
if (thinkingPromptInstruction) {
|
|
71
|
+
promptLines.push(thinkingPromptInstruction);
|
|
80
72
|
}
|
|
81
73
|
|
|
82
74
|
// Final instruction
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { getArchitectureCareSubPrompt } from './architecture-care.prompts.lib.mjs';
|
|
7
7
|
import { getExperimentsExamplesSubPrompt } from './experiments-examples.prompts.lib.mjs';
|
|
8
|
+
import { getThinkingPromptInstruction } from './thinking-prompt.lib.mjs';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Build the user prompt for Codex
|
|
@@ -58,15 +59,9 @@ export const buildUserPrompt = params => {
|
|
|
58
59
|
promptLines.push('');
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
low: 'Think.',
|
|
65
|
-
medium: 'Think hard.',
|
|
66
|
-
high: 'Think harder.',
|
|
67
|
-
max: 'Ultrathink.',
|
|
68
|
-
};
|
|
69
|
-
promptLines.push(thinkMessages[argv.think]);
|
|
62
|
+
const thinkingPromptInstruction = getThinkingPromptInstruction({ tool: 'codex', argv });
|
|
63
|
+
if (thinkingPromptInstruction) {
|
|
64
|
+
promptLines.push(thinkingPromptInstruction);
|
|
70
65
|
}
|
|
71
66
|
|
|
72
67
|
// Final instruction
|
package/src/config.lib.mjs
CHANGED
|
@@ -178,11 +178,82 @@ export const isOpus46OrLater = model => {
|
|
|
178
178
|
if (!model) return false;
|
|
179
179
|
const normalizedModel = model.toLowerCase();
|
|
180
180
|
// Check for explicit opus-4-6 or later versions, or opusplan (Issue #1223)
|
|
181
|
-
// Note: The 'opus' alias now maps to Opus 4.
|
|
181
|
+
// Note: The 'opus' alias now maps to Opus 4.7 (Issue #1620), so we also check for the alias directly
|
|
182
182
|
// opusplan uses Opus for planning, so it should get Opus-level settings
|
|
183
183
|
return normalizedModel === 'opus' || normalizedModel === 'opusplan' || normalizedModel.includes('opus-4-6') || normalizedModel.includes('opus-4-7') || normalizedModel.includes('opus-5');
|
|
184
184
|
};
|
|
185
185
|
|
|
186
|
+
const isOpus47 = model => {
|
|
187
|
+
if (!model) return false;
|
|
188
|
+
const normalizedModel = model.toLowerCase();
|
|
189
|
+
// 'opus' alias now maps to Opus 4.7 (Issue #1620)
|
|
190
|
+
// opusplan uses Opus for planning, so it gets Opus-level settings
|
|
191
|
+
return normalizedModel === 'opus' || normalizedModel === 'opusplan' || normalizedModel.includes('opus-4-7');
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Check if a model is Opus 4.7 or later (Issue #1620)
|
|
196
|
+
* These models use Opus 4.7+ adaptive thinking behavior.
|
|
197
|
+
* @param {string} model - The model name or ID
|
|
198
|
+
* @returns {boolean} True if the model is Opus 4.7 or later
|
|
199
|
+
*/
|
|
200
|
+
export const isOpus47OrLater = model => {
|
|
201
|
+
if (!model) return false;
|
|
202
|
+
const normalizedModel = model.toLowerCase();
|
|
203
|
+
return isOpus47(model) || normalizedModel.includes('opus-5');
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const isOpus45 = model => {
|
|
207
|
+
if (!model) return false;
|
|
208
|
+
const m = model.toLowerCase();
|
|
209
|
+
return m === 'opus-4-5' || m.includes('opus-4-5');
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const isOpus46 = model => {
|
|
213
|
+
if (!model) return false;
|
|
214
|
+
const m = model.toLowerCase();
|
|
215
|
+
return m === 'opus-4-6' || m.includes('opus-4-6');
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const isSonnet46OrLater = model => {
|
|
219
|
+
if (!model) return false;
|
|
220
|
+
const m = model.toLowerCase();
|
|
221
|
+
return m === 'sonnet' || m === 'sonnet-4-6' || m.includes('sonnet-4-6') || m.includes('sonnet-5');
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const isMythosPreview = model => {
|
|
225
|
+
if (!model) return false;
|
|
226
|
+
return model.toLowerCase().includes('mythos');
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Check if a model supports CLAUDE_CODE_EFFORT_LEVEL (Issue #1238, Issue #1620)
|
|
231
|
+
* Official effort support: Claude Mythos Preview, Opus 4.7, Opus 4.6, Sonnet 4.6, and Opus 4.5.
|
|
232
|
+
* Haiku 4.5 and older models use MAX_THINKING_TOKENS only.
|
|
233
|
+
* @param {string} model - The model name or ID
|
|
234
|
+
* @returns {boolean} True if the model supports effort levels
|
|
235
|
+
*/
|
|
236
|
+
export const supportsEffortLevel = model => {
|
|
237
|
+
if (!model) return false;
|
|
238
|
+
return isMythosPreview(model) || isOpus47OrLater(model) || isOpus46(model) || isSonnet46OrLater(model) || isOpus45(model);
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Check if a model supports the xhigh effort level.
|
|
243
|
+
* Official docs list xhigh only for Claude Opus 4.7.
|
|
244
|
+
* @param {string} model - The model name or ID
|
|
245
|
+
* @returns {boolean} True if the model supports xhigh effort
|
|
246
|
+
*/
|
|
247
|
+
export const supportsXHighEffortLevel = model => isOpus47(model);
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Check if a model supports the max effort level.
|
|
251
|
+
* Official docs list max for Claude Mythos Preview, Opus 4.7, Opus 4.6, and Sonnet 4.6.
|
|
252
|
+
* @param {string} model - The model name or ID
|
|
253
|
+
* @returns {boolean} True if the model supports max effort
|
|
254
|
+
*/
|
|
255
|
+
export const supportsMaxEffortLevel = model => isMythosPreview(model) || isOpus47OrLater(model) || isOpus46(model) || isSonnet46OrLater(model);
|
|
256
|
+
|
|
186
257
|
/**
|
|
187
258
|
* Get the max output tokens for a specific model (Issue #1221)
|
|
188
259
|
* @param {string} model - The model name or ID
|
|
@@ -218,6 +289,7 @@ export const getThinkingLevelToTokens = (maxBudget = DEFAULT_MAX_THINKING_BUDGET
|
|
|
218
289
|
low: Math.floor(maxBudget / 4), // ~8000 for default 31999
|
|
219
290
|
medium: Math.floor(maxBudget / 2), // ~16000 for default 31999
|
|
220
291
|
high: Math.floor((maxBudget * 3) / 4), // ~24000 for default 31999
|
|
292
|
+
xhigh: maxBudget, // same as max when represented as MAX_THINKING_TOKENS
|
|
221
293
|
max: maxBudget, // 31999 by default
|
|
222
294
|
});
|
|
223
295
|
|
|
@@ -250,56 +322,73 @@ export const getTokensToThinkingLevel = (maxBudget = DEFAULT_MAX_THINKING_BUDGET
|
|
|
250
322
|
export const tokensToThinkingLevel = getTokensToThinkingLevel(DEFAULT_MAX_THINKING_BUDGET);
|
|
251
323
|
|
|
252
324
|
/**
|
|
253
|
-
* Valid effort levels for Opus 4.6 (Issue #1238)
|
|
254
|
-
*
|
|
325
|
+
* Valid effort levels for Opus 4.6 and Sonnet 4.6 (Issue #1238, Issue #1620)
|
|
326
|
+
* These models use CLAUDE_CODE_EFFORT_LEVEL for thinking depth control
|
|
327
|
+
* @type {string[]}
|
|
328
|
+
*/
|
|
329
|
+
export const OPUS_46_EFFORT_LEVELS = ['low', 'medium', 'high', 'max'];
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Valid effort levels for Opus 4.7 (Issue #1620)
|
|
333
|
+
* Opus 4.7 supports the additional 'xhigh' level.
|
|
334
|
+
* See: https://platform.claude.com/docs/en/build-with-claude/effort
|
|
255
335
|
* @type {string[]}
|
|
256
336
|
*/
|
|
257
|
-
export const
|
|
337
|
+
export const OPUS_47_EFFORT_LEVELS = ['low', 'medium', 'high', 'xhigh', 'max'];
|
|
258
338
|
|
|
259
339
|
/**
|
|
260
|
-
* Convert thinking level to
|
|
261
|
-
* Opus 4.
|
|
262
|
-
*
|
|
263
|
-
* @
|
|
340
|
+
* Convert thinking level to effort level (Issue #1238, Issue #1620)
|
|
341
|
+
* Models with max support keep max as max. Opus 4.7 keeps xhigh as xhigh.
|
|
342
|
+
* Models with effort but without max support use high for max/xhigh.
|
|
343
|
+
* @param {string|undefined} thinkLevel - The thinking level (off/low/medium/high/xhigh/max)
|
|
344
|
+
* @param {Object} [options] - Options
|
|
345
|
+
* @param {boolean} [options.isOpus47] - Backward-compatible shorthand for supportsXHigh
|
|
346
|
+
* @param {boolean} [options.supportsXHigh] - Whether the model supports xhigh effort
|
|
347
|
+
* @param {boolean} [options.supportsMax] - Whether the model supports max effort
|
|
348
|
+
* @returns {string|undefined} The effort level or undefined if thinking is off
|
|
264
349
|
*/
|
|
265
|
-
export const thinkLevelToEffortLevel = thinkLevel => {
|
|
350
|
+
export const thinkLevelToEffortLevel = (thinkLevel, options = {}) => {
|
|
266
351
|
if (!thinkLevel || thinkLevel === 'off') {
|
|
267
|
-
// No effort level when thinking is disabled
|
|
268
352
|
return undefined;
|
|
269
353
|
}
|
|
270
354
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
355
|
+
const supportsXHigh = options.supportsXHigh ?? options.isOpus47 ?? false;
|
|
356
|
+
const supportsMax = options.supportsMax ?? true;
|
|
357
|
+
|
|
274
358
|
switch (thinkLevel) {
|
|
275
359
|
case 'low':
|
|
276
360
|
return 'low';
|
|
277
361
|
case 'medium':
|
|
278
362
|
return 'medium';
|
|
279
363
|
case 'high':
|
|
280
|
-
case 'max':
|
|
281
364
|
return 'high';
|
|
365
|
+
case 'xhigh':
|
|
366
|
+
return supportsXHigh ? 'xhigh' : supportsMax ? 'max' : 'high';
|
|
367
|
+
case 'max':
|
|
368
|
+
return supportsMax ? 'max' : 'high';
|
|
282
369
|
default:
|
|
283
370
|
return undefined;
|
|
284
371
|
}
|
|
285
372
|
};
|
|
286
373
|
|
|
287
374
|
/**
|
|
288
|
-
* Convert thinking budget (tokens) to
|
|
375
|
+
* Convert thinking budget (tokens) to effort level (Issue #1238, Issue #1620)
|
|
289
376
|
* Uses token thresholds to determine the appropriate effort level
|
|
290
377
|
* @param {number|undefined} thinkingBudget - The thinking budget in tokens
|
|
291
378
|
* @param {number} maxBudget - Maximum thinking budget (default: 31999)
|
|
292
|
-
* @
|
|
379
|
+
* @param {Object} [options] - Options
|
|
380
|
+
* @param {boolean} [options.isOpus47] - Backward-compatible shorthand for supportsXHigh
|
|
381
|
+
* @param {boolean} [options.supportsXHigh] - Whether the model supports xhigh effort
|
|
382
|
+
* @param {boolean} [options.supportsMax] - Whether the model supports max effort
|
|
383
|
+
* @returns {string|undefined} The effort level or undefined if thinking is off
|
|
293
384
|
*/
|
|
294
|
-
export const thinkingBudgetToEffortLevel = (thinkingBudget, maxBudget = DEFAULT_MAX_THINKING_BUDGET) => {
|
|
385
|
+
export const thinkingBudgetToEffortLevel = (thinkingBudget, maxBudget = DEFAULT_MAX_THINKING_BUDGET, options = {}) => {
|
|
295
386
|
if (thinkingBudget === undefined || thinkingBudget === 0) {
|
|
296
|
-
// No effort level when thinking is disabled
|
|
297
387
|
return undefined;
|
|
298
388
|
}
|
|
299
389
|
|
|
300
|
-
// Convert tokens to thinking level, then to effort level
|
|
301
390
|
const thinkLevel = getTokensToThinkingLevel(maxBudget)(thinkingBudget);
|
|
302
|
-
return thinkLevelToEffortLevel(thinkLevel);
|
|
391
|
+
return thinkLevelToEffortLevel(thinkLevel, options);
|
|
303
392
|
};
|
|
304
393
|
|
|
305
394
|
// Check if a version supports thinking budget (>= minimum version)
|
|
@@ -339,27 +428,40 @@ export const getClaudeEnv = (options = {}) => {
|
|
|
339
428
|
MCP_TOOL_TIMEOUT: String(claudeCode.mcpToolTimeout),
|
|
340
429
|
};
|
|
341
430
|
|
|
342
|
-
//
|
|
343
|
-
//
|
|
344
|
-
//
|
|
345
|
-
|
|
431
|
+
// Opus 4.7+ always uses adaptive thinking — MAX_THINKING_TOKENS has no effect (Issue #1620)
|
|
432
|
+
// For Opus 4.6 and earlier, MAX_THINKING_TOKENS controls extended thinking (Claude Code >= 2.1.12)
|
|
433
|
+
// Default is 0 (thinking disabled) per Issue #1238.
|
|
434
|
+
const opus47 = options.model && isOpus47OrLater(options.model);
|
|
435
|
+
if (opus47) {
|
|
436
|
+
// Remove any inherited MAX_THINKING_TOKENS from process.env — Opus 4.7 ignores it
|
|
437
|
+
delete env.MAX_THINKING_TOKENS;
|
|
438
|
+
} else {
|
|
439
|
+
env.MAX_THINKING_TOKENS = String(options.thinkingBudget ?? 0);
|
|
440
|
+
}
|
|
346
441
|
|
|
347
|
-
//
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
442
|
+
// Set CLAUDE_CODE_EFFORT_LEVEL for models that support it (Issue #1238, Issue #1620)
|
|
443
|
+
if (options.model && supportsEffortLevel(options.model)) {
|
|
444
|
+
const effortOptions = {
|
|
445
|
+
supportsXHigh: supportsXHighEffortLevel(options.model),
|
|
446
|
+
supportsMax: supportsMaxEffortLevel(options.model),
|
|
447
|
+
};
|
|
352
448
|
let effortLevel;
|
|
353
449
|
if (options.thinkLevel) {
|
|
354
|
-
effortLevel = thinkLevelToEffortLevel(options.thinkLevel);
|
|
450
|
+
effortLevel = thinkLevelToEffortLevel(options.thinkLevel, effortOptions);
|
|
355
451
|
} else if (options.thinkingBudget !== undefined && options.thinkingBudget > 0) {
|
|
356
|
-
effortLevel = thinkingBudgetToEffortLevel(options.thinkingBudget, options.maxBudget);
|
|
452
|
+
effortLevel = thinkingBudgetToEffortLevel(options.thinkingBudget, options.maxBudget, effortOptions);
|
|
357
453
|
}
|
|
358
454
|
|
|
359
455
|
if (effortLevel) {
|
|
360
456
|
env.CLAUDE_CODE_EFFORT_LEVEL = effortLevel;
|
|
361
457
|
}
|
|
362
458
|
}
|
|
459
|
+
|
|
460
|
+
// Opus 4.7 omits thinking content by default; opt in with --show-thinking-content (Issue #1620)
|
|
461
|
+
// Sets CLAUDE_CODE_SHOW_THINKING=1 which Claude Code uses to request display: "summarized"
|
|
462
|
+
if (options.showThinkingContent) {
|
|
463
|
+
env.CLAUDE_CODE_SHOW_THINKING = '1';
|
|
464
|
+
}
|
|
363
465
|
// Set ANTHROPIC_DEFAULT_OPUS_MODEL when planModel is specified (Issue #1223)
|
|
364
466
|
// This tells Claude Code which model to use during plan mode in opusplan
|
|
365
467
|
if (options.planModel) {
|
package/src/models/index.mjs
CHANGED
|
@@ -23,23 +23,25 @@ import { log } from '../lib.mjs';
|
|
|
23
23
|
// ─── MODEL DATA ──────────────────────────────────────────────────────────────
|
|
24
24
|
|
|
25
25
|
// Claude models (Anthropic API)
|
|
26
|
-
// Updated for Opus 4.5/4.6 and Sonnet 4.6 support (Issue #1221, Issue #1238, Issue #1329, Issue #1433)
|
|
26
|
+
// Updated for Opus 4.5/4.6/4.7 and Sonnet 4.6 support (Issue #1221, Issue #1238, Issue #1329, Issue #1433, Issue #1620)
|
|
27
27
|
export const claudeModels = {
|
|
28
28
|
sonnet: 'claude-sonnet-4-6', // Sonnet 4.6 (default, Issue #1329)
|
|
29
|
-
opus: 'claude-opus-4-
|
|
29
|
+
opus: 'claude-opus-4-7', // Opus 4.7 (Issue #1620)
|
|
30
30
|
haiku: 'claude-haiku-4-5-20251001', // Haiku 4.5
|
|
31
31
|
'haiku-3-5': 'claude-3-5-haiku-20241022', // Haiku 3.5
|
|
32
32
|
'haiku-3': 'claude-3-haiku-20240307', // Haiku 3
|
|
33
33
|
opusplan: 'opusplan', // Special mode: Opus for planning, Sonnet for execution (Issue #1223)
|
|
34
34
|
// Shorter version aliases (Issue #1221, Issue #1329 - PR comment feedback)
|
|
35
35
|
'sonnet-4-6': 'claude-sonnet-4-6', // Sonnet 4.6 short alias (Issue #1329)
|
|
36
|
-
'opus-4-
|
|
36
|
+
'opus-4-7': 'claude-opus-4-7', // Opus 4.7 short alias (Issue #1620)
|
|
37
|
+
'opus-4-6': 'claude-opus-4-6', // Opus 4.6 short alias (backward compatibility)
|
|
37
38
|
'opus-4-5': 'claude-opus-4-5-20251101', // Opus 4.5 short alias
|
|
38
39
|
'sonnet-4-5': 'claude-sonnet-4-5-20250929', // Sonnet 4.5 short alias (backward compatibility)
|
|
39
40
|
'haiku-4-5': 'claude-haiku-4-5-20251001', // Haiku 4.5 short alias
|
|
40
|
-
// Version aliases for backward compatibility (Issue #1221, Issue #1329)
|
|
41
|
+
// Version aliases for backward compatibility (Issue #1221, Issue #1329, Issue #1620)
|
|
42
|
+
'claude-opus-4-7': 'claude-opus-4-7', // Opus 4.7 (Issue #1620)
|
|
41
43
|
'claude-sonnet-4-6': 'claude-sonnet-4-6', // Sonnet 4.6 (Issue #1329)
|
|
42
|
-
'claude-opus-4-6': 'claude-opus-4-6', // Opus 4.6
|
|
44
|
+
'claude-opus-4-6': 'claude-opus-4-6', // Opus 4.6 (backward compatibility)
|
|
43
45
|
'claude-opus-4-5': 'claude-opus-4-5-20251101', // Opus 4.5
|
|
44
46
|
'claude-sonnet-4-5': 'claude-sonnet-4-5-20250929', // Sonnet 4.5 (backward compatibility)
|
|
45
47
|
'claude-haiku-4-5': 'claude-haiku-4-5-20251001', // Haiku 4.5
|
|
@@ -129,6 +131,7 @@ export const defaultModels = {
|
|
|
129
131
|
// Models that support 1M token context window via [1m] suffix (Issue #1221, Issue #1238, Issue #1329)
|
|
130
132
|
// See: https://code.claude.com/docs/en/model-config
|
|
131
133
|
export const MODELS_SUPPORTING_1M_CONTEXT = [
|
|
134
|
+
'claude-opus-4-7', // Opus 4.7 (Issue #1620)
|
|
132
135
|
'claude-opus-4-6',
|
|
133
136
|
'claude-opus-4-5-20251101',
|
|
134
137
|
'claude-sonnet-4-6', // Sonnet 4.6 (Issue #1329)
|
|
@@ -136,7 +139,8 @@ export const MODELS_SUPPORTING_1M_CONTEXT = [
|
|
|
136
139
|
'claude-sonnet-4-5',
|
|
137
140
|
'sonnet', // Now maps to Sonnet 4.6 (Issue #1329)
|
|
138
141
|
'sonnet-4-6', // Short alias (Issue #1329)
|
|
139
|
-
'opus',
|
|
142
|
+
'opus', // Now maps to Opus 4.7 (Issue #1620)
|
|
143
|
+
'opus-4-7', // Short alias (Issue #1620)
|
|
140
144
|
'opus-4-6', // Short alias (Issue #1221 - PR comment feedback)
|
|
141
145
|
'opus-4-5', // Short alias (Issue #1238)
|
|
142
146
|
'sonnet-4-5', // Short alias (Issue #1221 - PR comment feedback)
|
|
@@ -165,6 +169,7 @@ export const freeToBaseModelMap = {
|
|
|
165
169
|
|
|
166
170
|
export const CLAUDE_MODELS = {
|
|
167
171
|
...claudeModels,
|
|
172
|
+
'claude-opus-4-7': 'claude-opus-4-7', // Opus 4.7 full ID (Issue #1620)
|
|
168
173
|
'claude-sonnet-4-5-20250929': 'claude-sonnet-4-5-20250929',
|
|
169
174
|
'claude-opus-4-5-20251101': 'claude-opus-4-5-20251101',
|
|
170
175
|
'claude-haiku-4-5-20251001': 'claude-haiku-4-5-20251001',
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { getArchitectureCareSubPrompt } from './architecture-care.prompts.lib.mjs';
|
|
7
7
|
import { getExperimentsExamplesSubPrompt } from './experiments-examples.prompts.lib.mjs';
|
|
8
|
+
import { getThinkingPromptInstruction } from './thinking-prompt.lib.mjs';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Build the user prompt for OpenCode
|
|
@@ -58,15 +59,9 @@ export const buildUserPrompt = params => {
|
|
|
58
59
|
promptLines.push('');
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
low: 'Think.',
|
|
65
|
-
medium: 'Think hard.',
|
|
66
|
-
high: 'Think harder.',
|
|
67
|
-
max: 'Ultrathink.',
|
|
68
|
-
};
|
|
69
|
-
promptLines.push(thinkMessages[argv.think]);
|
|
62
|
+
const thinkingPromptInstruction = getThinkingPromptInstruction({ tool: 'opencode', argv });
|
|
63
|
+
if (thinkingPromptInstruction) {
|
|
64
|
+
promptLines.push(thinkingPromptInstruction);
|
|
70
65
|
}
|
|
71
66
|
|
|
72
67
|
// Final instruction
|
package/src/solve.config.lib.mjs
CHANGED
|
@@ -229,8 +229,8 @@ export const SOLVE_OPTION_DEFINITIONS = {
|
|
|
229
229
|
},
|
|
230
230
|
think: {
|
|
231
231
|
type: 'string',
|
|
232
|
-
description: 'Thinking level hint. For Claude, translated to --thinking-budget for Claude Code >= 2.1.12 (off=0, low=~8000, medium=~16000, high=~24000, max=31999). For Codex, mapped to reasoning effort (off=none, low=low, medium=medium, high=high, max=xhigh).',
|
|
233
|
-
choices: ['off', 'low', 'medium', 'high', 'max'],
|
|
232
|
+
description: 'Thinking level hint. For Claude, translated to --thinking-budget for Claude Code >= 2.1.12 (off=0, low=~8000, medium=~16000, high=~24000, xhigh/max=31999) and to CLAUDE_CODE_EFFORT_LEVEL when supported. Opus 4.7 supports xhigh and max; Opus 4.6/Sonnet 4.6/Mythos support max; Opus 4.5 uses high for xhigh/max. For Codex, mapped to reasoning effort (off=none, low=low, medium=medium, high=high, xhigh/max=xhigh).',
|
|
233
|
+
choices: ['off', 'low', 'medium', 'high', 'xhigh', 'max'],
|
|
234
234
|
default: undefined,
|
|
235
235
|
},
|
|
236
236
|
'thinking-budget': {
|
|
@@ -248,6 +248,11 @@ export const SOLVE_OPTION_DEFINITIONS = {
|
|
|
248
248
|
description: 'Maximum thinking budget for calculating --think level mappings (default: 31999 for Claude Code). Values: off=0, low=max/4, medium=max/2, high=max*3/4, max=max.',
|
|
249
249
|
default: 31999,
|
|
250
250
|
},
|
|
251
|
+
'show-thinking-content': {
|
|
252
|
+
type: 'boolean',
|
|
253
|
+
description: 'Show thinking content in Claude responses. Opus 4.7 omits thinking content by default; this option opts in to receive summarized thinking blocks. Disabled by default. Only affects --tool claude.',
|
|
254
|
+
default: false,
|
|
255
|
+
},
|
|
251
256
|
'prompt-plan-sub-agent': {
|
|
252
257
|
type: 'boolean',
|
|
253
258
|
description: 'Encourage AI to use a planning sub-agent or planning workflow for initial planning. Supported for --tool claude and --tool codex.',
|
package/src/telegram-bot.mjs
CHANGED
|
@@ -45,6 +45,7 @@ const { formatUsageMessage, formatCodexLimitsSection, getAllCachedLimits } = awa
|
|
|
45
45
|
const { getVersionInfo, formatVersionMessage } = await import('./version-info.lib.mjs');
|
|
46
46
|
const { escapeMarkdown, escapeMarkdownV2, cleanNonPrintableChars, makeSpecialCharsVisible } = await import('./telegram-markdown.lib.mjs');
|
|
47
47
|
const { getSolveQueue, createQueueExecuteCallback } = await import('./telegram-solve-queue.lib.mjs');
|
|
48
|
+
const { applySolveToolAlias, getSolveCommandNameFromText, getSolveToolAliasFromText, parseCommandArgs, SOLVE_COMMAND_NAMES } = await import('./telegram-solve-command.lib.mjs');
|
|
48
49
|
const { isChatStopped, getChatStopInfo, getStoppedChatRejectMessage, DEFAULT_STOP_REASON } = await import('./telegram-start-stop-command.lib.mjs');
|
|
49
50
|
const { isOldMessage: _isOldMessage, isGroupChat: _isGroupChat, isChatAuthorized: _isChatAuthorized, isForwardedOrReply: _isForwardedOrReply, extractCommandFromText, extractGitHubUrl: _extractGitHubUrl } = await import('./telegram-message-filters.lib.mjs');
|
|
50
51
|
const { launchBotWithRetry } = await import('./telegram-bot-launcher.lib.mjs');
|
|
@@ -447,49 +448,6 @@ function validateModelInArgs(args, tool = 'claude') {
|
|
|
447
448
|
return null;
|
|
448
449
|
}
|
|
449
450
|
|
|
450
|
-
function parseCommandArgs(text) {
|
|
451
|
-
// Use only first line and trim it
|
|
452
|
-
const firstLine = text.split('\n')[0].trim();
|
|
453
|
-
const argsText = firstLine.replace(/^\/\w+\s*/, '');
|
|
454
|
-
|
|
455
|
-
if (!argsText.trim()) {
|
|
456
|
-
return [];
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// Replace em-dash (—) with double-dash (--) to fix Telegram auto-replacement
|
|
460
|
-
const normalizedArgsText = argsText.replace(/—/g, '--');
|
|
461
|
-
|
|
462
|
-
const args = [];
|
|
463
|
-
let currentArg = '';
|
|
464
|
-
let inQuotes = false;
|
|
465
|
-
let quoteChar = null;
|
|
466
|
-
|
|
467
|
-
for (let i = 0; i < normalizedArgsText.length; i++) {
|
|
468
|
-
const char = normalizedArgsText[i];
|
|
469
|
-
|
|
470
|
-
if ((char === '"' || char === "'") && !inQuotes) {
|
|
471
|
-
inQuotes = true;
|
|
472
|
-
quoteChar = char;
|
|
473
|
-
} else if (char === quoteChar && inQuotes) {
|
|
474
|
-
inQuotes = false;
|
|
475
|
-
quoteChar = null;
|
|
476
|
-
} else if (char === ' ' && !inQuotes) {
|
|
477
|
-
if (currentArg) {
|
|
478
|
-
args.push(currentArg);
|
|
479
|
-
currentArg = '';
|
|
480
|
-
}
|
|
481
|
-
} else {
|
|
482
|
-
currentArg += char;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
if (currentArg) {
|
|
487
|
-
args.push(currentArg);
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
return args;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
451
|
function mergeArgsWithOverrides(userArgs, overrides) {
|
|
494
452
|
if (!overrides || overrides.length === 0) {
|
|
495
453
|
return userArgs;
|
|
@@ -651,16 +609,17 @@ bot.command('help', async ctx => {
|
|
|
651
609
|
message += '📝 *Available Commands:*\n\n';
|
|
652
610
|
|
|
653
611
|
if (solveEnabled) {
|
|
654
|
-
message += '*/solve* (aliases: */do*, */continue*) - Solve a GitHub issue\n';
|
|
612
|
+
message += '*/solve* (aliases: */do*, */continue*, */claude*, */codex*, */opencode*, */agent*) - Solve a GitHub issue\n';
|
|
655
613
|
message += 'Usage: `/solve <github-url> [options]`\n';
|
|
656
614
|
message += 'Example: `/solve https://github.com/owner/repo/issues/123 --model sonnet`\n';
|
|
615
|
+
message += 'Tool aliases imply `--tool <tool>`: `/codex <github-url>` equals `/solve <github-url> --tool codex`\n';
|
|
657
616
|
message += 'Or reply to a message with a GitHub link: `/solve`\n';
|
|
658
617
|
if (solveOverrides.length > 0) {
|
|
659
618
|
message += `🔒 Locked options: \`${solveOverrides.join(' ')}\`\n`;
|
|
660
619
|
}
|
|
661
620
|
message += '\n';
|
|
662
621
|
} else {
|
|
663
|
-
message += '*/solve* (aliases: */do*, */continue*) - ❌ Disabled\n\n';
|
|
622
|
+
message += '*/solve* (aliases: */do*, */continue*, */claude*, */codex*, */opencode*, */agent*) - ❌ Disabled\n\n';
|
|
664
623
|
}
|
|
665
624
|
|
|
666
625
|
if (hiveEnabled) {
|
|
@@ -688,11 +647,11 @@ bot.command('help', async ctx => {
|
|
|
688
647
|
message += '🔔 *Session Notifications:* The bot monitors sessions and notifies when they complete.\n';
|
|
689
648
|
if (ISOLATION_BACKEND) message += `🔒 *Isolation Mode:* \`${ISOLATION_BACKEND}\` (experimental)\n`;
|
|
690
649
|
message += '\n';
|
|
691
|
-
message += '⚠️ *Note:* /solve, /do, /continue, /hive, /solve\\_queue, /limits, /version, /accept\\_invites, /merge, /stop and /start commands only work in group chats.\n\n';
|
|
650
|
+
message += '⚠️ *Note:* /solve, /do, /continue, /claude, /codex, /opencode, /agent, /hive, /solve\\_queue, /limits, /version, /accept\\_invites, /merge, /stop and /start commands only work in group chats.\n\n';
|
|
692
651
|
message += '🔧 *Common Options:*\n';
|
|
693
652
|
message += `• \`--model <model>\` or \`-m\` - ${buildModelOptionDescription()}\n`;
|
|
694
653
|
message += '• `--base-branch <branch>` or `-b` - Target branch for PR (default: repo default branch)\n';
|
|
695
|
-
message += '• `--think <level>` - Thinking level (off/low/medium/high/max) | `--thinking-budget <num>` - Token budget (0-63999)\n';
|
|
654
|
+
message += '• `--think <level>` - Thinking level (off/low/medium/high/xhigh/max) | `--thinking-budget <num>` - Token budget (0-63999)\n';
|
|
696
655
|
message += '• `--verbose` or `-v` - Verbose output | `--attach-logs` - Attach logs to PR\n';
|
|
697
656
|
message += '\n💡 *Tip:* Many more options available. See full documentation for complete list.\n';
|
|
698
657
|
|
|
@@ -795,12 +754,14 @@ const { handleSolveQueueCommand } = registerSolveQueueCommand(bot, { ...sharedCo
|
|
|
795
754
|
|
|
796
755
|
// Named handler for /solve command - extracted for reuse by text-based fallback (issue #1207)
|
|
797
756
|
async function handleSolveCommand(ctx) {
|
|
798
|
-
|
|
757
|
+
const solveCommandName = getSolveCommandNameFromText(ctx.message?.text) || 'solve';
|
|
758
|
+
const solveCommandDisplay = `/${solveCommandName}`;
|
|
759
|
+
VERBOSE && console.log(`[VERBOSE] ${solveCommandDisplay} command received`);
|
|
799
760
|
|
|
800
761
|
// Add breadcrumb for error tracking
|
|
801
762
|
await addBreadcrumb({
|
|
802
763
|
category: 'telegram.command',
|
|
803
|
-
message:
|
|
764
|
+
message: `${solveCommandDisplay} command received`,
|
|
804
765
|
level: 'info',
|
|
805
766
|
data: {
|
|
806
767
|
chatId: ctx.chat?.id,
|
|
@@ -812,16 +773,16 @@ async function handleSolveCommand(ctx) {
|
|
|
812
773
|
|
|
813
774
|
if (!solveEnabled) {
|
|
814
775
|
if (VERBOSE) {
|
|
815
|
-
console.log(
|
|
776
|
+
console.log(`[VERBOSE] ${solveCommandDisplay} ignored: command disabled`);
|
|
816
777
|
}
|
|
817
|
-
await ctx.reply('❌ The
|
|
778
|
+
await ctx.reply('❌ The solve command is disabled on this bot instance.');
|
|
818
779
|
return;
|
|
819
780
|
}
|
|
820
781
|
|
|
821
782
|
// Ignore messages sent before bot started
|
|
822
783
|
if (isOldMessage(ctx)) {
|
|
823
784
|
if (VERBOSE) {
|
|
824
|
-
console.log(
|
|
785
|
+
console.log(`[VERBOSE] ${solveCommandDisplay} ignored: old message`);
|
|
825
786
|
}
|
|
826
787
|
return;
|
|
827
788
|
}
|
|
@@ -834,22 +795,22 @@ async function handleSolveCommand(ctx) {
|
|
|
834
795
|
|
|
835
796
|
if (isForwarded || isOldApiForwarded) {
|
|
836
797
|
if (VERBOSE) {
|
|
837
|
-
console.log(
|
|
798
|
+
console.log(`[VERBOSE] ${solveCommandDisplay} ignored: forwarded message`);
|
|
838
799
|
}
|
|
839
800
|
return;
|
|
840
801
|
}
|
|
841
802
|
|
|
842
803
|
if (!_isGroupChat(ctx)) {
|
|
843
804
|
if (VERBOSE) {
|
|
844
|
-
console.log(
|
|
805
|
+
console.log(`[VERBOSE] ${solveCommandDisplay} ignored: not a group chat`);
|
|
845
806
|
}
|
|
846
|
-
await ctx.reply(
|
|
807
|
+
await ctx.reply(`❌ The ${solveCommandDisplay} command only works in group chats. Please add this bot to a group and make it an admin.`, { reply_to_message_id: ctx.message.message_id });
|
|
847
808
|
return;
|
|
848
809
|
}
|
|
849
810
|
|
|
850
811
|
if (!isTopicAuthorized(ctx)) {
|
|
851
812
|
if (VERBOSE) {
|
|
852
|
-
console.log(
|
|
813
|
+
console.log(`[VERBOSE] ${solveCommandDisplay} ignored: not authorized`);
|
|
853
814
|
}
|
|
854
815
|
await ctx.reply(buildAuthErrorMessage(ctx), { reply_to_message_id: ctx.message.message_id });
|
|
855
816
|
return;
|
|
@@ -863,8 +824,9 @@ async function handleSolveCommand(ctx) {
|
|
|
863
824
|
return;
|
|
864
825
|
}
|
|
865
826
|
|
|
866
|
-
VERBOSE && console.log(
|
|
827
|
+
VERBOSE && console.log(`[VERBOSE] ${solveCommandDisplay} passed all checks, executing...`);
|
|
867
828
|
|
|
829
|
+
const solveToolAlias = getSolveToolAliasFromText(ctx.message.text);
|
|
868
830
|
let userArgs = parseCommandArgs(ctx.message.text);
|
|
869
831
|
|
|
870
832
|
// Check if this is a reply to a message and user didn't provide URL as first argument
|
|
@@ -911,6 +873,8 @@ async function handleSolveCommand(ctx) {
|
|
|
911
873
|
}
|
|
912
874
|
}
|
|
913
875
|
|
|
876
|
+
userArgs = applySolveToolAlias(userArgs, solveToolAlias);
|
|
877
|
+
|
|
914
878
|
const validation = validateGitHubUrl(userArgs);
|
|
915
879
|
if (!validation.valid) {
|
|
916
880
|
let errorMsg = `❌ ${validation.error}`;
|
|
@@ -1040,7 +1004,10 @@ async function handleSolveCommand(ctx) {
|
|
|
1040
1004
|
}
|
|
1041
1005
|
}
|
|
1042
1006
|
|
|
1043
|
-
bot.command(
|
|
1007
|
+
bot.command(
|
|
1008
|
+
SOLVE_COMMAND_NAMES.map(command => new RegExp(`^${command}$`, 'i')),
|
|
1009
|
+
handleSolveCommand
|
|
1010
|
+
);
|
|
1044
1011
|
|
|
1045
1012
|
// Named handler for /hive command - extracted for reuse by text-based fallback (issue #1207)
|
|
1046
1013
|
async function handleHiveCommand(ctx) {
|
|
@@ -1267,8 +1234,8 @@ bot.on('message', async (ctx, next) => {
|
|
|
1267
1234
|
}
|
|
1268
1235
|
|
|
1269
1236
|
// Check if this is a command we handle
|
|
1270
|
-
|
|
1271
|
-
const handlers = {
|
|
1237
|
+
const solveHandlers = Object.fromEntries(SOLVE_COMMAND_NAMES.map(command => [command, handleSolveCommand]));
|
|
1238
|
+
const handlers = { ...solveHandlers, hive: handleHiveCommand, solve_queue: handleSolveQueueCommand, solvequeue: handleSolveQueueCommand };
|
|
1272
1239
|
|
|
1273
1240
|
const handler = handlers[extracted.command];
|
|
1274
1241
|
if (!handler) return next();
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared parsing helpers for Telegram solve commands.
|
|
3
|
+
*
|
|
4
|
+
* Keeps /solve aliases and argument normalization testable without loading the
|
|
5
|
+
* full Telegram bot entry point.
|
|
6
|
+
*
|
|
7
|
+
* @see https://github.com/link-assistant/hive-mind/issues/525
|
|
8
|
+
* @see https://github.com/link-assistant/hive-mind/issues/1618
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const TOOL_SOLVE_COMMAND_ALIASES = Object.freeze({
|
|
12
|
+
claude: 'claude',
|
|
13
|
+
codex: 'codex',
|
|
14
|
+
opencode: 'opencode',
|
|
15
|
+
agent: 'agent',
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const SOLVE_COMMAND_NAMES = Object.freeze(['solve', 'do', 'continue', ...Object.keys(TOOL_SOLVE_COMMAND_ALIASES)]);
|
|
19
|
+
|
|
20
|
+
export function parseCommandArgs(text) {
|
|
21
|
+
const firstLine = text.split('\n')[0].trim();
|
|
22
|
+
const argsText = firstLine.replace(/^\/\w+(?:@\S+)?\s*/, '');
|
|
23
|
+
|
|
24
|
+
if (!argsText.trim()) {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Replace em-dash with double-dash to fix Telegram auto-replacement.
|
|
29
|
+
const normalizedArgsText = argsText.replace(/—/g, '--');
|
|
30
|
+
|
|
31
|
+
const args = [];
|
|
32
|
+
let currentArg = '';
|
|
33
|
+
let inQuotes = false;
|
|
34
|
+
let quoteChar = null;
|
|
35
|
+
|
|
36
|
+
for (let i = 0; i < normalizedArgsText.length; i++) {
|
|
37
|
+
const char = normalizedArgsText[i];
|
|
38
|
+
|
|
39
|
+
if ((char === '"' || char === "'") && !inQuotes) {
|
|
40
|
+
inQuotes = true;
|
|
41
|
+
quoteChar = char;
|
|
42
|
+
} else if (char === quoteChar && inQuotes) {
|
|
43
|
+
inQuotes = false;
|
|
44
|
+
quoteChar = null;
|
|
45
|
+
} else if (char === ' ' && !inQuotes) {
|
|
46
|
+
if (currentArg) {
|
|
47
|
+
args.push(currentArg);
|
|
48
|
+
currentArg = '';
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
currentArg += char;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (currentArg) {
|
|
56
|
+
args.push(currentArg);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return args;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function getSolveCommandNameFromText(text) {
|
|
63
|
+
if (!text || typeof text !== 'string') {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const firstLine = text.split('\n')[0].trim();
|
|
68
|
+
const match = firstLine.match(/^\/(\w+)(?:@\S+)?(?:\s|$)/);
|
|
69
|
+
return match ? match[1].toLowerCase() : null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function getSolveToolAliasFromText(text) {
|
|
73
|
+
const command = getSolveCommandNameFromText(text);
|
|
74
|
+
return command ? TOOL_SOLVE_COMMAND_ALIASES[command] || null : null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function applySolveToolAlias(args, toolAlias) {
|
|
78
|
+
if (!toolAlias || args.length === 0) {
|
|
79
|
+
return args;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const filteredArgs = [];
|
|
83
|
+
|
|
84
|
+
for (let i = 0; i < args.length; i++) {
|
|
85
|
+
const arg = args[i];
|
|
86
|
+
|
|
87
|
+
if (arg === '--tool') {
|
|
88
|
+
if (i + 1 < args.length && !args[i + 1].startsWith('--')) {
|
|
89
|
+
i++;
|
|
90
|
+
}
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (arg.startsWith('--tool=')) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
filteredArgs.push(arg);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return [...filteredArgs, '--tool', toolAlias];
|
|
102
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { supportsEffortLevel, supportsThinkingBudget } from './config.lib.mjs';
|
|
4
|
+
import { defaultModels, mapModelForTool } from './models/index.mjs';
|
|
5
|
+
|
|
6
|
+
export const THINK_PROMPT_MESSAGES = Object.freeze({
|
|
7
|
+
low: 'Think.',
|
|
8
|
+
medium: 'Think hard.',
|
|
9
|
+
high: 'Think harder.',
|
|
10
|
+
xhigh: 'Ultrathink.',
|
|
11
|
+
max: 'Ultrathink.',
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export const isClaudeLikeModel = model => {
|
|
15
|
+
if (!model) return false;
|
|
16
|
+
const normalized = String(model).toLowerCase();
|
|
17
|
+
return normalized === 'opusplan' || normalized.includes('claude') || normalized.startsWith('anthropic/');
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const resolvePromptModelForTool = (tool = 'claude', model) => {
|
|
21
|
+
const selectedModel = model || defaultModels[tool];
|
|
22
|
+
return selectedModel ? mapModelForTool(tool, selectedModel) : null;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const toolSupportsStructuredThinkingBudget = ({ tool = 'claude', claudeVersion, thinkingBudgetClaudeMinimumVersion } = {}) => {
|
|
26
|
+
if (tool === 'claude') {
|
|
27
|
+
return supportsThinkingBudget(claudeVersion || '0.0.0', thinkingBudgetClaudeMinimumVersion || '2.1.12');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Codex maps --think/--thinking-budget to model_reasoning_effort instead of prompt text.
|
|
31
|
+
return tool === 'codex';
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const shouldAddThinkingPromptInstruction = ({ tool = 'claude', argv, claudeVersion } = {}) => {
|
|
35
|
+
const thinkLevel = argv?.think;
|
|
36
|
+
if (!thinkLevel || !THINK_PROMPT_MESSAGES[thinkLevel]) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const resolvedModel = resolvePromptModelForTool(tool, argv?.model);
|
|
41
|
+
if (!isClaudeLikeModel(resolvedModel)) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (supportsEffortLevel(resolvedModel)) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return !toolSupportsStructuredThinkingBudget({
|
|
50
|
+
tool,
|
|
51
|
+
claudeVersion,
|
|
52
|
+
thinkingBudgetClaudeMinimumVersion: argv?.thinkingBudgetClaudeMinimumVersion,
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const getThinkingPromptInstruction = options => {
|
|
57
|
+
if (!shouldAddThinkingPromptInstruction(options)) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
return THINK_PROMPT_MESSAGES[options?.argv?.think];
|
|
61
|
+
};
|