@link-assistant/hive-mind 2.3.0 → 2.4.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 +7 -0
- package/package.json +1 -1
- package/src/claude.lib.mjs +3 -1
- package/src/codex.options.lib.mjs +2 -0
- package/src/config.lib.mjs +26 -0
- package/src/hive.config.lib.mjs +7 -1
- package/src/locales/en.lino +1 -1
- package/src/locales/hi.lino +1 -1
- package/src/locales/ru.lino +1 -1
- package/src/locales/zh.lino +1 -1
- package/src/solve.config.lib.mjs +69 -2
- package/src/think-level.lib.mjs +140 -0
- package/src/thinking-prompt.lib.mjs +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 2.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- dc28004: Fix `--think` validation asymmetry between `solve` and `hive` (Issue #2041). After the #2038 vocabulary refactor, `solve.config` only validated `--think` in the CLI `parseArguments()` path, so consumers that parse solve options directly through the yargs config — most notably the Telegram bot — silently accepted invalid `--think` values (a CI false-negative that failed `test-telegram-options-before-url`). `createYargsConfig` now runs the same `normalizeAndValidateThink` `.check()` that `hive.config` already used, and `parseArguments` propagates that validation error verbatim instead of swallowing it. Invalid `--think` values are now rejected consistently on the CLI and Telegram paths.
|
|
8
|
+
- d92c772: `--think` now accepts a richer, provider-neutral vocabulary (Issue #2038): the off synonyms `off`/`disable`/`disabled`/`no`/`none` all mean disabled (or the closest safe equivalent when a model cannot truly disable thinking), a new `minimal` tier below `low` (Codex `minimal` reasoning; Claude lowest effort with a ~4000-token budget), a first-class `adaptive` mode that requests provider-managed adaptive thinking and fails fast for `solve`/`hive` on models/tools that do not support it (only adaptive-only Claude models: Opus 4.7+, Fable 5, Mythos 5, Sonnet 5), and numeric intensities for precision — percentages `0%`..`100%`, fractions `0.0`..`1.0`, and the integers `0` (off) and `1` (max). Normalization is applied consistently for both `solve` and `hive`.
|
|
9
|
+
|
|
3
10
|
## 2.3.0
|
|
4
11
|
|
|
5
12
|
### Minor Changes
|
package/package.json
CHANGED
package/src/claude.lib.mjs
CHANGED
|
@@ -206,7 +206,9 @@ export const resolveThinkingSettings = async (argv, log) => {
|
|
|
206
206
|
let thinkLevel = argv.think;
|
|
207
207
|
let translation = null;
|
|
208
208
|
if (isNewVersion) {
|
|
209
|
-
|
|
209
|
+
// Issue #2038: `adaptive` is provider-managed and has no explicit token
|
|
210
|
+
// budget; skip the budget translation and let the model manage thinking.
|
|
211
|
+
if (thinkLevel !== undefined && thinkLevel !== 'adaptive' && thinkingBudget === undefined) {
|
|
210
212
|
thinkingBudget = thinkingLevelToTokens[thinkLevel];
|
|
211
213
|
translation = `--think ${thinkLevel} → --thinking-budget ${thinkingBudget}`;
|
|
212
214
|
if (argv.verbose) {
|
|
@@ -14,6 +14,8 @@ export const mapModelToId = model => codexModels[model] || model;
|
|
|
14
14
|
// deepest single-agent effort. `off` disables reasoning (`none`). See docs/case-studies/issue-2027.
|
|
15
15
|
const THINK_LEVEL_TO_CODEX_REASONING = {
|
|
16
16
|
off: 'none',
|
|
17
|
+
// Issue #2038: Codex/GPT-5.x natively exposes a `minimal` reasoning effort below `low`.
|
|
18
|
+
minimal: 'minimal',
|
|
17
19
|
low: 'low',
|
|
18
20
|
medium: 'medium',
|
|
19
21
|
high: 'high',
|
package/src/config.lib.mjs
CHANGED
|
@@ -341,6 +341,20 @@ export const supportsEffortLevel = model => {
|
|
|
341
341
|
return isFable5OrMythos5(model) || isMythosPreview(model) || isOpus47OrLater(model) || isOpus46(model) || isSonnet46OrLater(model) || isOpus45(model);
|
|
342
342
|
};
|
|
343
343
|
|
|
344
|
+
/**
|
|
345
|
+
* Issue #2038: Check whether a model uses provider-managed adaptive thinking.
|
|
346
|
+
* Adaptive-only Claude models (Opus 4.7+, Fable 5, Mythos 5, Sonnet 5) manage
|
|
347
|
+
* their own thinking depth and accept an unset effort/budget as "adaptive".
|
|
348
|
+
* These are exactly the models for which `--think adaptive` is meaningful; all
|
|
349
|
+
* other Claude models and non-Claude tools do not expose an adaptive mode.
|
|
350
|
+
* @param {string} model - The model name or ID
|
|
351
|
+
* @returns {boolean} True if the model supports adaptive thinking
|
|
352
|
+
*/
|
|
353
|
+
export const supportsAdaptiveThinking = model => {
|
|
354
|
+
if (!model) return false;
|
|
355
|
+
return isOpus47OrLater(model) || isFable5OrMythos5(model) || isSonnet5(model);
|
|
356
|
+
};
|
|
357
|
+
|
|
344
358
|
/**
|
|
345
359
|
* Check if a model supports the xhigh effort level.
|
|
346
360
|
* Official docs list xhigh for Claude Fable 5, Claude Mythos 5, Claude Opus 4.7,
|
|
@@ -393,6 +407,7 @@ export const getDefaultMaxThinkingBudgetForModel = model => {
|
|
|
393
407
|
*/
|
|
394
408
|
export const getThinkingLevelToTokens = (maxBudget = DEFAULT_MAX_THINKING_BUDGET) => ({
|
|
395
409
|
off: 0,
|
|
410
|
+
minimal: Math.floor(maxBudget / 8), // ~4000 for default 31999 (Issue #2038: below `low`)
|
|
396
411
|
low: Math.floor(maxBudget / 4), // ~8000 for default 31999
|
|
397
412
|
medium: Math.floor(maxBudget / 2), // ~16000 for default 31999
|
|
398
413
|
high: Math.floor((maxBudget * 3) / 4), // ~24000 for default 31999
|
|
@@ -413,12 +428,14 @@ export const thinkingLevelToTokens = getThinkingLevelToTokens(DEFAULT_MAX_THINKI
|
|
|
413
428
|
export const getTokensToThinkingLevel = (maxBudget = DEFAULT_MAX_THINKING_BUDGET) => {
|
|
414
429
|
const levels = getThinkingLevelToTokens(maxBudget);
|
|
415
430
|
// Calculate midpoints between levels for range determination
|
|
431
|
+
const minimalLowMidpoint = Math.floor((levels.minimal + levels.low) / 2);
|
|
416
432
|
const lowMediumMidpoint = Math.floor((levels.low + levels.medium) / 2);
|
|
417
433
|
const mediumHighMidpoint = Math.floor((levels.medium + levels.high) / 2);
|
|
418
434
|
const highMaxMidpoint = Math.floor((levels.high + levels.max) / 2);
|
|
419
435
|
|
|
420
436
|
return tokens => {
|
|
421
437
|
if (tokens === 0) return 'off';
|
|
438
|
+
if (tokens <= minimalLowMidpoint) return 'minimal'; // Issue #2038
|
|
422
439
|
if (tokens <= lowMediumMidpoint) return 'low';
|
|
423
440
|
if (tokens <= mediumHighMidpoint) return 'medium';
|
|
424
441
|
if (tokens <= highMaxMidpoint) return 'high';
|
|
@@ -505,6 +522,15 @@ export const thinkLevelToEffortLevel = (thinkLevel, options = {}) => {
|
|
|
505
522
|
const supportsMax = options.supportsMax ?? true;
|
|
506
523
|
|
|
507
524
|
switch (thinkLevel) {
|
|
525
|
+
case 'adaptive':
|
|
526
|
+
// Issue #2038: adaptive requests provider-managed thinking. Claude Code has
|
|
527
|
+
// no explicit `adaptive` effort value; leaving the effort unset lets the
|
|
528
|
+
// model manage its own thinking depth (its native adaptive behaviour).
|
|
529
|
+
return undefined;
|
|
530
|
+
case 'minimal':
|
|
531
|
+
// Issue #2038: Claude effort levels start at `low`; `minimal` maps to the
|
|
532
|
+
// lowest real effort so it stays strictly below `low` in intent but valid.
|
|
533
|
+
return 'low';
|
|
508
534
|
case 'low':
|
|
509
535
|
return 'low';
|
|
510
536
|
case 'medium':
|
package/src/hive.config.lib.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// when only the yargs configuration is needed (e.g., in telegram-bot.mjs)
|
|
4
4
|
// This module has no heavy dependencies to allow fast loading for --help
|
|
5
5
|
|
|
6
|
-
import { SOLVE_OPTION_DEFINITIONS } from './solve.config.lib.mjs';
|
|
6
|
+
import { SOLVE_OPTION_DEFINITIONS, normalizeAndValidateThink } from './solve.config.lib.mjs';
|
|
7
7
|
import { buildModelOptionDescription, defaultModels } from './models/index.mjs';
|
|
8
8
|
|
|
9
9
|
// Hive-only options that are NOT solve options (hive-specific functionality).
|
|
@@ -224,6 +224,12 @@ export const createYargsConfig = yargsInstance => {
|
|
|
224
224
|
'strip-aliased': false,
|
|
225
225
|
'populate--': false,
|
|
226
226
|
})
|
|
227
|
+
// Issue #2038: normalize/validate --think identically to solve (off synonyms,
|
|
228
|
+
// minimal, adaptive, percentages/fractions/0|1; fail fast on unsupported adaptive).
|
|
229
|
+
.check(argv => {
|
|
230
|
+
normalizeAndValidateThink(argv);
|
|
231
|
+
return true;
|
|
232
|
+
})
|
|
227
233
|
.showHelpOnFail(false) // Don't show help on validation failures
|
|
228
234
|
.strict()
|
|
229
235
|
.help('h')
|
package/src/locales/en.lino
CHANGED
|
@@ -578,7 +578,7 @@ en
|
|
|
578
578
|
branch
|
|
579
579
|
option "• `--base-branch <branch>` or `-b` - Target branch for PR (default: repo default branch)"
|
|
580
580
|
think
|
|
581
|
-
option "• `--think <level>` - Thinking level (off/low/medium/high/xhigh/ultra/max) | `--thinking-budget <num>` - Token budget (0-63999)"
|
|
581
|
+
option "• `--think <level>` - Thinking level (off/minimal/low/medium/high/xhigh/ultra/max/adaptive) | `--thinking-budget <num>` - Token budget (0-63999)"
|
|
582
582
|
verbose
|
|
583
583
|
option "• `--verbose` or `-v` - Verbose output | `--attach-logs` - Attach logs to PR"
|
|
584
584
|
show
|
package/src/locales/hi.lino
CHANGED
|
@@ -578,7 +578,7 @@ hi
|
|
|
578
578
|
branch
|
|
579
579
|
option "• `--base-branch <branch>` या `-b` - PR के लिए target branch (default: repo default branch)"
|
|
580
580
|
think
|
|
581
|
-
option "• `--think <level>` - thinking level (off/low/medium/high/xhigh/ultra/max) | `--thinking-budget <num>` - token budget (0-63999)"
|
|
581
|
+
option "• `--think <level>` - thinking level (off/minimal/low/medium/high/xhigh/ultra/max/adaptive) | `--thinking-budget <num>` - token budget (0-63999)"
|
|
582
582
|
verbose
|
|
583
583
|
option "• `--verbose` या `-v` - verbose output | `--attach-logs` - logs को PR से attach करें"
|
|
584
584
|
show
|
package/src/locales/ru.lino
CHANGED
|
@@ -578,7 +578,7 @@ ru
|
|
|
578
578
|
branch
|
|
579
579
|
option "• `--base-branch <branch>` или `-b` - Целевая ветка для PR (по умолчанию ветка репозитория)"
|
|
580
580
|
think
|
|
581
|
-
option "• `--think <level>` - уровень размышления (off/low/medium/high/xhigh/ultra/max) | `--thinking-budget <num>` - бюджет токенов (0-63999)"
|
|
581
|
+
option "• `--think <level>` - уровень размышления (off/minimal/low/medium/high/xhigh/ultra/max/adaptive) | `--thinking-budget <num>` - бюджет токенов (0-63999)"
|
|
582
582
|
verbose
|
|
583
583
|
option "• `--verbose` или `-v` - подробный вывод | `--attach-logs` - прикрепить логи к PR"
|
|
584
584
|
show
|
package/src/locales/zh.lino
CHANGED
|
@@ -578,7 +578,7 @@ zh
|
|
|
578
578
|
branch
|
|
579
579
|
option "• `--base-branch <branch>` 或 `-b` - PR 目标分支(默认:仓库默认分支)"
|
|
580
580
|
think
|
|
581
|
-
option "• `--think <level>` - 思考级别(off/low/medium/high/xhigh/ultra/max)| `--thinking-budget <num>` - token 预算(0-63999)"
|
|
581
|
+
option "• `--think <level>` - 思考级别(off/minimal/low/medium/high/xhigh/ultra/max/adaptive)| `--thinking-budget <num>` - token 预算(0-63999)"
|
|
582
582
|
verbose
|
|
583
583
|
option "• `--verbose` 或 `-v` - 详细输出 | `--attach-logs` - 将日志附加到 PR"
|
|
584
584
|
show
|
package/src/solve.config.lib.mjs
CHANGED
|
@@ -12,6 +12,9 @@ import { defaultModels, buildModelOptionDescription, resolveDefaultFallbackModel
|
|
|
12
12
|
import { validateBranchName } from './solve.branch.lib.mjs';
|
|
13
13
|
import { resolveEscalationConfig, isEscalateEnabled, DEFAULT_ESCALATE_RANGE } from './solve.escalate.lib.mjs';
|
|
14
14
|
import { getLinoYargsFactory, hideBin, normalizeCliArgs, parseCliArgumentsWithLino } from './cli-arguments.lib.mjs';
|
|
15
|
+
import { normalizeThinkLevel, ADAPTIVE_THINK_LEVEL } from './think-level.lib.mjs';
|
|
16
|
+
import { supportsAdaptiveThinking } from './config.lib.mjs';
|
|
17
|
+
import { resolvePromptModelForTool } from './thinking-prompt.lib.mjs';
|
|
15
18
|
|
|
16
19
|
// Re-export for use by telegram-bot.mjs (avoids extra import lines there)
|
|
17
20
|
export { detectMalformedFlags };
|
|
@@ -299,8 +302,14 @@ export const SOLVE_OPTION_DEFINITIONS = {
|
|
|
299
302
|
},
|
|
300
303
|
think: {
|
|
301
304
|
type: 'string',
|
|
302
|
-
description:
|
|
303
|
-
|
|
305
|
+
description:
|
|
306
|
+
'Thinking level hint. Levels (ascending): off, minimal, low, medium, high, xhigh, ultra, max, plus the special `adaptive` mode. ' +
|
|
307
|
+
'Off synonyms (all mean disabled, or the closest safe equivalent): off/disable/disabled/no/none. An omitted --think means off. ' +
|
|
308
|
+
'Numeric intensities are accepted for precision: percentages 0%..100%, fractions 0.0..1.0, and the integers 0 (off) and 1 (max). ' +
|
|
309
|
+
'`adaptive` requests provider-managed adaptive thinking and fails immediately for models/tools that do not support it (only adaptive-only Claude models: Opus 4.7+, Fable 5, Mythos 5, Sonnet 5). ' +
|
|
310
|
+
'For Claude, levels translate to --thinking-budget for Claude Code >= 2.1.12 (off=0, minimal=~4000, low=~8000, medium=~16000, high=~24000, xhigh/ultra/max=31999) and to CLAUDE_CODE_EFFORT_LEVEL when supported (minimal→low). Adaptive-only models that cannot disable thinking use their lowest effort for off. ' +
|
|
311
|
+
'`ultra` maps to the highest supported Claude effort (Claude "ultracode"-class reasoning). ' +
|
|
312
|
+
'For Codex (GPT-5.6 Sol), mapped 1:1 to reasoning effort (off=none, minimal=minimal, low=low, medium=medium, high=high, xhigh=xhigh, ultra=ultra, max=max); ultra runs the multi-agent mode paired with a rollout token budget cap. Default: off.',
|
|
304
313
|
default: 'off',
|
|
305
314
|
},
|
|
306
315
|
'thinking-budget': {
|
|
@@ -736,6 +745,14 @@ export const createYargsConfig = yargsInstance => {
|
|
|
736
745
|
.parserConfiguration({
|
|
737
746
|
'boolean-negation': true,
|
|
738
747
|
})
|
|
748
|
+
// Issue #2038 + #2041: normalize/validate --think during parsing (off synonyms,
|
|
749
|
+
// minimal, adaptive, percentages/fractions/0|1; fail fast on unsupported adaptive)
|
|
750
|
+
// so callers that parse via yargs directly (e.g. the Telegram bot) reject invalid
|
|
751
|
+
// --think values instead of silently accepting them. Mirrors hive.config.
|
|
752
|
+
.check(argv => {
|
|
753
|
+
normalizeAndValidateThink(argv);
|
|
754
|
+
return true;
|
|
755
|
+
})
|
|
739
756
|
// Use yargs built-in strict mode to reject unrecognized options
|
|
740
757
|
// This prevents issues like #453 and #482 where unknown options are silently ignored
|
|
741
758
|
.strict()
|
|
@@ -745,6 +762,42 @@ export const createYargsConfig = yargsInstance => {
|
|
|
745
762
|
return config;
|
|
746
763
|
};
|
|
747
764
|
|
|
765
|
+
/**
|
|
766
|
+
* Issue #2038: Normalize the parsed `--think` value into a single canonical
|
|
767
|
+
* vocabulary and validate the special `adaptive` mode. Mutates `argv.think` in
|
|
768
|
+
* place and throws (with `_enhanced` set so the error message is shown verbatim)
|
|
769
|
+
* for invalid values or unsupported adaptive requests. Shared by solve and hive
|
|
770
|
+
* so both commands fail fast identically.
|
|
771
|
+
* @param {Object} argv - Parsed CLI arguments (reads/writes `think`, reads `tool`/`model`)
|
|
772
|
+
*/
|
|
773
|
+
export const normalizeAndValidateThink = argv => {
|
|
774
|
+
if (!argv) return;
|
|
775
|
+
|
|
776
|
+
if (argv.think !== undefined) {
|
|
777
|
+
try {
|
|
778
|
+
argv.think = normalizeThinkLevel(argv.think);
|
|
779
|
+
} catch (thinkError) {
|
|
780
|
+
const err = new Error(thinkError.message);
|
|
781
|
+
err._enhanced = true;
|
|
782
|
+
throw err;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// `--think adaptive` requests provider-managed adaptive thinking and must fail
|
|
787
|
+
// immediately when the selected tool/model does not support it. Only
|
|
788
|
+
// adaptive-only Claude models expose an adaptive mode.
|
|
789
|
+
if (argv.think === ADAPTIVE_THINK_LEVEL) {
|
|
790
|
+
const tool = argv.tool || 'claude';
|
|
791
|
+
const resolvedModel = resolvePromptModelForTool(tool, argv.model);
|
|
792
|
+
const adaptiveSupported = tool === 'claude' && supportsAdaptiveThinking(resolvedModel);
|
|
793
|
+
if (!adaptiveSupported) {
|
|
794
|
+
const err = new Error(`--think adaptive is not supported by ${tool}${resolvedModel ? ` model "${resolvedModel}"` : ''}. ` + 'Adaptive thinking is only available on adaptive-only Claude models (Opus 4.7+, Fable 5, Mythos 5, Sonnet 5). ' + 'Use an explicit level instead (off, minimal, low, medium, high, xhigh, ultra, max).');
|
|
795
|
+
err._enhanced = true;
|
|
796
|
+
throw err;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
|
|
748
801
|
// Parse command line arguments - now needs yargs and hideBin passed in
|
|
749
802
|
export const parseArguments = async (yargs = getLinoYargsFactory(), hideBinFn = hideBin) => {
|
|
750
803
|
const rawArgs = normalizeCliArgs(hideBinFn(process.argv));
|
|
@@ -804,6 +857,14 @@ export const parseArguments = async (yargs = getLinoYargsFactory(), hideBinFn =
|
|
|
804
857
|
}
|
|
805
858
|
}
|
|
806
859
|
} catch (error) {
|
|
860
|
+
// Issue #2041: the yargs `.check()` for --think (added to createYargsConfig so
|
|
861
|
+
// non-CLI consumers like the Telegram bot reject invalid values) throws an
|
|
862
|
+
// already-enhanced error. Propagate it verbatim instead of swallowing it into
|
|
863
|
+
// `error.argv`, otherwise the CLI would silently drop the invalid --think and
|
|
864
|
+
// crash later during normalization.
|
|
865
|
+
if (error && error._enhanced && !(error.message && /Unknown argument/.test(error.message))) {
|
|
866
|
+
throw error;
|
|
867
|
+
}
|
|
807
868
|
// Yargs throws errors for validation issues
|
|
808
869
|
// If the error is about unknown arguments (strict mode), enhance it with suggestions
|
|
809
870
|
// Check if this error has already been enhanced to avoid re-processing
|
|
@@ -852,6 +913,12 @@ export const parseArguments = async (yargs = getLinoYargsFactory(), hideBinFn =
|
|
|
852
913
|
argv.think = undefined;
|
|
853
914
|
}
|
|
854
915
|
|
|
916
|
+
// Issue #2038: normalize the --think value into a single canonical vocabulary
|
|
917
|
+
// (off synonyms, minimal, adaptive, percentages/fractions/0|1) and fail fast on
|
|
918
|
+
// an unsupported `adaptive` request. Shared by solve and hive so both commands
|
|
919
|
+
// behave identically.
|
|
920
|
+
normalizeAndValidateThink(argv);
|
|
921
|
+
|
|
855
922
|
// --plan flag expansion (Issue #1223)
|
|
856
923
|
// When --plan is set, it acts as a shortcut for --plan-model opus --worker-model sonnet
|
|
857
924
|
// Explicit --plan-model and --model/--worker-model values take precedence
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Issue #2038: Canonical normalization of the `--think` option.
|
|
5
|
+
*
|
|
6
|
+
* The `--think` flag historically accepted only a small fixed set of keyword
|
|
7
|
+
* levels (off/low/medium/high/xhigh/ultra/max). Issue #2038 requires a much
|
|
8
|
+
* richer and more forgiving surface, mapped consistently across Claude and
|
|
9
|
+
* Codex:
|
|
10
|
+
*
|
|
11
|
+
* 1. A family of synonyms that all mean "off" (thinking disabled, or the
|
|
12
|
+
* closest safe equivalent when a model cannot truly disable thinking):
|
|
13
|
+
* `off`, `disable`, `disabled`, `no`, `none`, `false`.
|
|
14
|
+
* 2. A `minimal` level (below `low`) mapped to the lowest real reasoning
|
|
15
|
+
* effort each tool supports (Codex `minimal`, Claude lowest effort).
|
|
16
|
+
* 3. An explicit `adaptive` level that requests provider-managed adaptive
|
|
17
|
+
* thinking and MUST fail fast for models/tools that do not support it.
|
|
18
|
+
* 4. Numeric intensities so users can dial precision:
|
|
19
|
+
* - percentages `0%` .. `100%`
|
|
20
|
+
* - fractions `0.0` .. `1.0`
|
|
21
|
+
* - the integers `0` and `1`
|
|
22
|
+
* `0`/`0%`/`0.0` == off and `1`/`100%`/`1.0` == max.
|
|
23
|
+
*
|
|
24
|
+
* `normalizeThinkLevel()` folds every accepted spelling into one canonical
|
|
25
|
+
* level so the rest of the codebase keeps operating on a single vocabulary.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// Canonical think levels in ascending intensity order. `adaptive` is a distinct
|
|
29
|
+
// mode (provider-managed), not a point on the numeric intensity scale, so it is
|
|
30
|
+
// listed separately and never produced by numeric coercion.
|
|
31
|
+
export const CANONICAL_THINK_LEVELS = Object.freeze(['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'ultra', 'max']);
|
|
32
|
+
|
|
33
|
+
export const ADAPTIVE_THINK_LEVEL = 'adaptive';
|
|
34
|
+
|
|
35
|
+
// Keyword synonyms → canonical level. All the "off" spellings are synonyms per
|
|
36
|
+
// the issue title, and an omitted `--think` is treated as `off` elsewhere.
|
|
37
|
+
const THINK_LEVEL_SYNONYMS = Object.freeze({
|
|
38
|
+
off: 'off',
|
|
39
|
+
disable: 'off',
|
|
40
|
+
disabled: 'off',
|
|
41
|
+
no: 'off',
|
|
42
|
+
none: 'off',
|
|
43
|
+
false: 'off',
|
|
44
|
+
min: 'minimal',
|
|
45
|
+
minimal: 'minimal',
|
|
46
|
+
low: 'low',
|
|
47
|
+
medium: 'medium',
|
|
48
|
+
med: 'medium',
|
|
49
|
+
high: 'high',
|
|
50
|
+
xhigh: 'xhigh',
|
|
51
|
+
'x-high': 'xhigh',
|
|
52
|
+
ultra: 'ultra',
|
|
53
|
+
max: 'max',
|
|
54
|
+
maximum: 'max',
|
|
55
|
+
full: 'max',
|
|
56
|
+
adaptive: 'adaptive',
|
|
57
|
+
auto: 'adaptive',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Map a fraction in [0, 1] to a canonical intensity level.
|
|
62
|
+
* 0 → off, 1 → max, with evenly spaced bands in between. Never returns the
|
|
63
|
+
* out-of-band `ultra` (multi-agent) or `adaptive` modes.
|
|
64
|
+
* @param {number} fraction
|
|
65
|
+
* @returns {string}
|
|
66
|
+
*/
|
|
67
|
+
export const fractionToThinkLevel = fraction => {
|
|
68
|
+
if (!Number.isFinite(fraction) || fraction <= 0) return 'off';
|
|
69
|
+
if (fraction < 0.2) return 'minimal';
|
|
70
|
+
if (fraction < 0.4) return 'low';
|
|
71
|
+
if (fraction < 0.6) return 'medium';
|
|
72
|
+
if (fraction < 0.8) return 'high';
|
|
73
|
+
if (fraction < 1) return 'xhigh';
|
|
74
|
+
return 'max';
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Parse a numeric think value (percentage, fraction, or 0/1 integer) into a
|
|
79
|
+
* fraction in [0, 1], or return null when the value is not numeric.
|
|
80
|
+
* @param {string} raw
|
|
81
|
+
* @returns {number|null}
|
|
82
|
+
*/
|
|
83
|
+
export const parseNumericThinkValue = raw => {
|
|
84
|
+
const text = String(raw).trim();
|
|
85
|
+
const percentMatch = /^([0-9]+(?:\.[0-9]+)?)\s*%$/.exec(text);
|
|
86
|
+
if (percentMatch) {
|
|
87
|
+
return Math.min(1, Math.max(0, Number(percentMatch[1]) / 100));
|
|
88
|
+
}
|
|
89
|
+
if (/^[0-9]+(?:\.[0-9]+)?$/.exec(text)) {
|
|
90
|
+
const num = Number(text);
|
|
91
|
+
// Bare integers greater than 1 are treated as a 0..100 style percentage so
|
|
92
|
+
// `--think 50` behaves like `--think 50%`; 0 and 1 stay canonical fraction
|
|
93
|
+
// endpoints (off and max).
|
|
94
|
+
if (num > 1) return Math.min(1, num / 100);
|
|
95
|
+
return Math.min(1, Math.max(0, num));
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Normalize any accepted `--think` spelling into a canonical level.
|
|
102
|
+
* @param {string|number|undefined|null} raw
|
|
103
|
+
* @returns {string|undefined} canonical level, ADAPTIVE_THINK_LEVEL, or
|
|
104
|
+
* undefined when input is empty. Throws for unrecognized values.
|
|
105
|
+
*/
|
|
106
|
+
export const normalizeThinkLevel = raw => {
|
|
107
|
+
if (raw === undefined || raw === null || raw === '') return undefined;
|
|
108
|
+
|
|
109
|
+
// Already-canonical (e.g. re-normalization) short circuit.
|
|
110
|
+
if (typeof raw === 'string') {
|
|
111
|
+
const lowered = raw.trim().toLowerCase();
|
|
112
|
+
if (lowered === '') return undefined;
|
|
113
|
+
|
|
114
|
+
if (Object.prototype.hasOwnProperty.call(THINK_LEVEL_SYNONYMS, lowered)) {
|
|
115
|
+
return THINK_LEVEL_SYNONYMS[lowered];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const fraction = parseNumericThinkValue(lowered);
|
|
119
|
+
if (fraction !== null) {
|
|
120
|
+
return fractionToThinkLevel(fraction);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
throw new Error(`Invalid --think value: "${raw}". Use a level (${CANONICAL_THINK_LEVELS.join(', ')}, adaptive), ` + `an off synonym (off/disable/disabled/no/none), a percentage (0%..100%), or a fraction (0.0..1.0).`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (typeof raw === 'number') {
|
|
127
|
+
const fraction = raw > 1 ? Math.min(1, raw / 100) : Math.min(1, Math.max(0, raw));
|
|
128
|
+
return fractionToThinkLevel(fraction);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
throw new Error(`Invalid --think value: "${raw}".`);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export default {
|
|
135
|
+
CANONICAL_THINK_LEVELS,
|
|
136
|
+
ADAPTIVE_THINK_LEVEL,
|
|
137
|
+
normalizeThinkLevel,
|
|
138
|
+
fractionToThinkLevel,
|
|
139
|
+
parseNumericThinkValue,
|
|
140
|
+
};
|
|
@@ -4,6 +4,7 @@ import { supportsEffortLevel, supportsThinkingBudget } from './config.lib.mjs';
|
|
|
4
4
|
import { defaultModels, mapModelForTool } from './models/index.mjs';
|
|
5
5
|
|
|
6
6
|
export const THINK_PROMPT_MESSAGES = Object.freeze({
|
|
7
|
+
minimal: 'Think.',
|
|
7
8
|
low: 'Think.',
|
|
8
9
|
medium: 'Think hard.',
|
|
9
10
|
high: 'Think harder.',
|