praxis-agent 0.62.1 → 0.62.2
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/README.md +6 -1
- package/dist/cli/interactive.d.ts +1 -0
- package/dist/cli/interactive.js +6 -4
- package/dist/cli/tui/claude-style.js +10 -1
- package/dist/cli/tui/conversation-export.js +3 -1
- package/dist/cli/tui/transcript-presentation.d.ts +1 -1
- package/dist/cli/tui/transcript-viewport.js +23 -0
- package/dist/cli/tui/tui-row-ir.js +3 -1
- package/dist/cli-runtime.d.ts +1 -0
- package/dist/cli-runtime.js +36 -6
- package/dist/providers/anthropic-compatible.d.ts +1 -0
- package/dist/providers/anthropic-compatible.js +20 -8
- package/dist/providers/anthropic-model-spec.d.ts +1 -0
- package/dist/providers/anthropic-model-spec.js +1 -0
- package/dist/providers/provider-registry.d.ts +6 -1
- package/dist/providers/provider-registry.js +9 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -104,6 +104,9 @@ unknown model IDs. Add the exact terminal `[1m]` suffix (for example,
|
|
|
104
104
|
Praxis keeps that selected model public, removes the suffix on the wire, and
|
|
105
105
|
adds the `context-1m-2025-08-07` Anthropic beta once. An explicit
|
|
106
106
|
`PRAXIS_CONTEXT_WINDOW_TOKENS` value overrides either inferred window.
|
|
107
|
+
Exact `claude-sonnet-4-6` and `claude-opus-4-6` selections also support
|
|
108
|
+
`--thinking adaptive`; use `--thinking enabled --max-thinking-tokens <n>`
|
|
109
|
+
when a fixed thinking-token budget is required.
|
|
107
110
|
|
|
108
111
|
Common non-interactive operations:
|
|
109
112
|
|
|
@@ -166,7 +169,9 @@ troubleshooting. Run `praxis --help` for the authoritative command surface.
|
|
|
166
169
|
resize-safe lifecycle, Ctrl-C restoration, fullscreen `Ctrl+L` redraw, and
|
|
167
170
|
mouse-wheel/drag selection with edge autoscroll and OSC 52 copy,
|
|
168
171
|
interactive `/doctor` diagnostics, per-session model/effort/permission controls,
|
|
169
|
-
context/status/skill/task dashboards
|
|
172
|
+
context/status/skill/task dashboards whose context view previews the selected
|
|
173
|
+
provider/model capacity before the first turn and reports unavailable capacity
|
|
174
|
+
without fabricated percentages, prompt stash and continuation shortcuts,
|
|
170
175
|
filterable `@` file and agent references, composer undo, `Ctrl+G` external
|
|
171
176
|
editing, shared `/keybindings` creation/editing and supported-action remapping,
|
|
172
177
|
shared built-in and custom `/theme` profiles with immediate
|
|
@@ -85,6 +85,7 @@ interface InteractiveSessionCommands {
|
|
|
85
85
|
}
|
|
86
86
|
export interface InteractiveServiceFactory {
|
|
87
87
|
scheduledPrompts?: boolean;
|
|
88
|
+
contextWindowTokens?(modelId?: string): number | undefined;
|
|
88
89
|
createService(options: {
|
|
89
90
|
eventSink: RuntimeEventSink;
|
|
90
91
|
requireProvider: boolean;
|
package/dist/cli/interactive.js
CHANGED
|
@@ -471,7 +471,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
471
471
|
const [, setTurnDuration] = useState();
|
|
472
472
|
const [usage, setUsage] = useState();
|
|
473
473
|
const [costUsd, setCostUsd] = useState();
|
|
474
|
-
const [contextWindowTokens, setContextWindowTokens] = useState(display.contextWindowTokens);
|
|
474
|
+
const [contextWindowTokens, setContextWindowTokens] = useState(display.contextWindowTokens ?? factory.contextWindowTokens?.());
|
|
475
475
|
const [historyState, setHistoryState] = useState(() => {
|
|
476
476
|
const items = [...initialHistory];
|
|
477
477
|
return {
|
|
@@ -2237,8 +2237,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
2237
2237
|
setAvailableSlashCommands(created.slashCommands?.() ?? slashCommands);
|
|
2238
2238
|
setAvailableAgents(created.agentDefinitions?.() ?? agents);
|
|
2239
2239
|
const runtimeInfo = created.runtimeInfo?.();
|
|
2240
|
-
|
|
2241
|
-
setContextWindowTokens(runtimeInfo.contextWindowTokens);
|
|
2240
|
+
setContextWindowTokens(runtimeInfo?.contextWindowTokens);
|
|
2242
2241
|
return created;
|
|
2243
2242
|
}
|
|
2244
2243
|
finally {
|
|
@@ -2314,6 +2313,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
2314
2313
|
void loading.finally(() => onTurnChange?.(null));
|
|
2315
2314
|
};
|
|
2316
2315
|
const changeModel = (model) => {
|
|
2316
|
+
setContextWindowTokens(factory.contextWindowTokens?.(model));
|
|
2317
2317
|
updateRuntimePreferences((current) => {
|
|
2318
2318
|
if (model !== undefined)
|
|
2319
2319
|
return { ...current, model };
|
|
@@ -6726,7 +6726,9 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
6726
6726
|
const contextEntry = {
|
|
6727
6727
|
kind: 'context',
|
|
6728
6728
|
usedTokens: Math.max(measuredTokens, skillTokens),
|
|
6729
|
-
|
|
6729
|
+
...(runtimeDisplay.contextWindowTokens === undefined
|
|
6730
|
+
? {}
|
|
6731
|
+
: { contextWindowTokens: runtimeDisplay.contextWindowTokens }),
|
|
6730
6732
|
model: runtimeDisplay.model ?? 'provider default',
|
|
6731
6733
|
skills,
|
|
6732
6734
|
memoryFiles: [],
|
|
@@ -390,6 +390,15 @@ function percent(tokens, total) {
|
|
|
390
390
|
}
|
|
391
391
|
function ContextUsageBlock({ usedTokens, contextWindowTokens, model, skills, memoryFiles, screenReader, }) {
|
|
392
392
|
const theme = useTuiTheme();
|
|
393
|
+
const resourceTree = (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsx(Text, { ...theme.text.heading, bold: true, children: "Memory files \u00B7 /memory" }), memoryFiles.length === 0 ? (_jsx(Text, { dimColor: true, children: "\u2514 No memory files" })) : (memoryFiles.map((file, index) => (_jsxs(Text, { dimColor: true, children: [index === memoryFiles.length - 1 ? '└' : '├', " ", file.path, ":", ' ', file.tokens, " tokens"] }, file.path)))), _jsx(Text, { children: " " }), _jsx(Text, { ...theme.text.heading, bold: true, children: "Skills \u00B7 /skills" }), _jsx(Text, { children: " " }), _jsx(Text, { children: "Loaded" }), skills.length === 0 ? (_jsx(Text, { dimColor: true, children: "\u2514 No skills loaded" })) : (skills.map((skill, index) => (_jsxs(Text, { dimColor: true, children: [index === skills.length - 1 ? '└' : '├', " ", skill.name, ": ~", skill.tokens, " tokens"] }, skill.name))))] }));
|
|
394
|
+
if (screenReader && contextWindowTokens === undefined) {
|
|
395
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { ...theme.text.heading, children: "Context Usage" }), _jsxs(Text, { children: [model ?? 'provider default', " \u00B7 ", usedTokens.toLocaleString(), " tokens \u00B7 Context capacity unavailable"] }), _jsxs(Text, { children: ["Memory files:", ' ', memoryFiles
|
|
396
|
+
.map((file) => `${file.path} (${file.tokens} tokens)`)
|
|
397
|
+
.join(', ') || 'none'] }), _jsxs(Text, { children: ["Skills: ", skills.map(({ name }) => name).join(', ') || 'none'] })] }));
|
|
398
|
+
}
|
|
399
|
+
if (contextWindowTokens === undefined) {
|
|
400
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, marginLeft: 2, children: [_jsx(Text, { ...theme.text.heading, bold: true, children: "Context Usage" }), _jsxs(Text, { dimColor: true, children: [model ?? 'provider default', " \u00B7 ", usedTokens.toLocaleString(), " tokens \u00B7 Context capacity unavailable"] }), resourceTree] }));
|
|
401
|
+
}
|
|
393
402
|
const totalTokens = Math.max(1, contextWindowTokens);
|
|
394
403
|
const compactBuffer = Math.round(totalTokens * 0.165);
|
|
395
404
|
const usable = Math.max(1, totalTokens - compactBuffer);
|
|
@@ -399,7 +408,7 @@ function ContextUsageBlock({ usedTokens, contextWindowTokens, model, skills, mem
|
|
|
399
408
|
const usedCells = Math.min(25, Math.round((usedTokens / totalTokens) * 25));
|
|
400
409
|
const bufferCells = Math.min(25 - usedCells, Math.round((compactBuffer / totalTokens) * 25));
|
|
401
410
|
const cells = Array.from({ length: 25 }, (_, index) => index < usedCells ? '⛁' : index >= 25 - bufferCells ? '⛝' : '⛶');
|
|
402
|
-
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, marginLeft: 2, children: [_jsx(Text, { ...theme.text.heading, bold: true, children: "Context Usage" }), _jsxs(Box, { children: [_jsx(Box, { flexDirection: "column", marginRight: 2, children: Array.from({ length: 5 }, (_, row) => (_jsx(Text, { children: cells.slice(row * 5, row * 5 + 5).join(' ') }, row))) }), _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: [model ?? 'provider default', " \u00B7 ", compactTokens(usedTokens), "/", compactTokens(totalTokens), " tokens (", percent(usedTokens, totalTokens), ")"] }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, italic: true, children: "Estimated usage by category" }), _jsxs(Text, { children: ["\u26C1 Messages and other context: ", compactTokens(usedTokens), " tokens (", percent(usedTokens, totalTokens), ")"] }), _jsxs(Text, { children: ["\u26F6 Free space: ", compactTokens(Math.max(0, usable - usedTokens)), " (", percent(Math.max(0, usable - usedTokens), totalTokens), ")"] }), _jsxs(Text, { children: ["\u26DD Autocompact buffer: ", compactTokens(compactBuffer), " tokens (", percent(compactBuffer, totalTokens), ")"] })] })] }),
|
|
411
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, marginLeft: 2, children: [_jsx(Text, { ...theme.text.heading, bold: true, children: "Context Usage" }), _jsxs(Box, { children: [_jsx(Box, { flexDirection: "column", marginRight: 2, children: Array.from({ length: 5 }, (_, row) => (_jsx(Text, { children: cells.slice(row * 5, row * 5 + 5).join(' ') }, row))) }), _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: [model ?? 'provider default', " \u00B7 ", compactTokens(usedTokens), "/", compactTokens(totalTokens), " tokens (", percent(usedTokens, totalTokens), ")"] }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, italic: true, children: "Estimated usage by category" }), _jsxs(Text, { children: ["\u26C1 Messages and other context: ", compactTokens(usedTokens), " tokens (", percent(usedTokens, totalTokens), ")"] }), _jsxs(Text, { children: ["\u26F6 Free space: ", compactTokens(Math.max(0, usable - usedTokens)), " (", percent(Math.max(0, usable - usedTokens), totalTokens), ")"] }), _jsxs(Text, { children: ["\u26DD Autocompact buffer: ", compactTokens(compactBuffer), " tokens (", percent(compactBuffer, totalTokens), ")"] })] })] }), resourceTree] }));
|
|
403
412
|
}
|
|
404
413
|
const MARKDOWN_TEXT_CACHE_MAX = 2048;
|
|
405
414
|
const markdownTextCache = new Map();
|
|
@@ -32,7 +32,9 @@ export function conversationExportText(_display, items) {
|
|
|
32
32
|
output.push(...lines(item.text, ' ⎿ '));
|
|
33
33
|
}
|
|
34
34
|
else if (item.kind === 'context') {
|
|
35
|
-
output.push('',
|
|
35
|
+
output.push('', item.contextWindowTokens === undefined
|
|
36
|
+
? `Context Usage: ${item.usedTokens} tokens · Context capacity unavailable`
|
|
37
|
+
: `Context Usage: ${item.usedTokens}/${item.contextWindowTokens} tokens`);
|
|
36
38
|
}
|
|
37
39
|
else {
|
|
38
40
|
output.push(...lines(item.text, item.kind === 'warning' ? '⚠ ' : '· '));
|
|
@@ -262,6 +262,21 @@ function percent(tokens, total) {
|
|
|
262
262
|
return `${Math.round((tokens / Math.max(1, total)) * 100 * 10) / 10}%`;
|
|
263
263
|
}
|
|
264
264
|
function contextUsageLineCount(item, width) {
|
|
265
|
+
if (item.contextWindowTokens === undefined) {
|
|
266
|
+
const resourceWidth = Math.max(1, width - 2);
|
|
267
|
+
const memoryRows = item.memoryFiles.length
|
|
268
|
+
? item.memoryFiles.reduce((rows, file, index) => rows +
|
|
269
|
+
wrappedLineCount(`${index === item.memoryFiles.length - 1 ? '└' : '├'} ${file.path}: ${file.tokens} tokens`, resourceWidth), 0)
|
|
270
|
+
: wrappedLineCount('└ No memory files', resourceWidth);
|
|
271
|
+
const skillRows = item.skills.length
|
|
272
|
+
? item.skills.reduce((rows, skill, index) => rows +
|
|
273
|
+
wrappedLineCount(`${index === item.skills.length - 1 ? '└' : '├'} ${skill.name}: ~${skill.tokens} tokens`, resourceWidth), 0)
|
|
274
|
+
: wrappedLineCount('└ No skills loaded', resourceWidth);
|
|
275
|
+
return (8 +
|
|
276
|
+
wrappedLineCount(`${item.model ?? 'provider default'} · ${item.usedTokens.toLocaleString()} tokens · Context capacity unavailable`, resourceWidth) +
|
|
277
|
+
memoryRows +
|
|
278
|
+
skillRows);
|
|
279
|
+
}
|
|
265
280
|
const totalTokens = Math.max(1, item.contextWindowTokens);
|
|
266
281
|
const compactBuffer = Math.round(totalTokens * 0.165);
|
|
267
282
|
const usable = Math.max(1, totalTokens - compactBuffer);
|
|
@@ -367,6 +382,14 @@ function visibleOutputLineCount(text, width, mode) {
|
|
|
367
382
|
: 0));
|
|
368
383
|
}
|
|
369
384
|
function screenReaderContextLineCount(item, width) {
|
|
385
|
+
if (item.contextWindowTokens === undefined) {
|
|
386
|
+
return [
|
|
387
|
+
'Context Usage',
|
|
388
|
+
`${item.model ?? 'provider default'} · ${item.usedTokens.toLocaleString()} tokens · Context capacity unavailable`,
|
|
389
|
+
`Memory files: ${item.memoryFiles.map((file) => `${file.path} (${file.tokens} tokens)`).join(', ') || 'none'}`,
|
|
390
|
+
`Skills: ${item.skills.map(({ name }) => name).join(', ') || 'none'}`,
|
|
391
|
+
].reduce((rows, line) => rows + wrappedLineCount(line, width), 0);
|
|
392
|
+
}
|
|
370
393
|
const totalTokens = Math.max(1, item.contextWindowTokens);
|
|
371
394
|
const compactBuffer = Math.round(totalTokens * 0.165);
|
|
372
395
|
return [
|
|
@@ -38,7 +38,9 @@ function entryText(entry) {
|
|
|
38
38
|
};
|
|
39
39
|
if (item.kind === 'context')
|
|
40
40
|
return {
|
|
41
|
-
text:
|
|
41
|
+
text: item.contextWindowTokens === undefined
|
|
42
|
+
? `Context Usage · ${item.usedTokens} tokens · Context capacity unavailable`
|
|
43
|
+
: `Context Usage · ${item.usedTokens}/${item.contextWindowTokens} tokens`,
|
|
42
44
|
role: 'heading',
|
|
43
45
|
};
|
|
44
46
|
}
|
package/dist/cli-runtime.d.ts
CHANGED
|
@@ -232,6 +232,7 @@ export declare function resolveInteractiveProviderStartup(options: {
|
|
|
232
232
|
}): Promise<{
|
|
233
233
|
effectiveModel: string | undefined;
|
|
234
234
|
trustProjectRequestAvailable: boolean;
|
|
235
|
+
contextWindowTokensForModel: (modelId?: string) => number | undefined;
|
|
235
236
|
}>;
|
|
236
237
|
export declare function createDefaultDependencies(entrypoint?: string): CliDependencies;
|
|
237
238
|
export declare function createBackgroundWorkerRuntime(workerSink: RuntimeEventSink, dispatch: {
|
package/dist/cli-runtime.js
CHANGED
|
@@ -51,7 +51,7 @@ import { FallbackModelProvider } from './providers/fallback-provider.js';
|
|
|
51
51
|
import { ProviderCredentialVault } from './persistence/provider-credential-vault.js';
|
|
52
52
|
import { parseContextEnvironment, parseProviderEnvironment, } from './providers/environment.js';
|
|
53
53
|
import { ProviderAuthenticationError, resolveProviderCredential, } from './providers/provider-auth.js';
|
|
54
|
-
import { resolveProviderRegistry } from './providers/provider-registry.js';
|
|
54
|
+
import { resolveProviderContextWindowTokens, resolveProviderRegistry, } from './providers/provider-registry.js';
|
|
55
55
|
import { ProviderSettingsError, resolveProviderTarget, } from './providers/provider-settings.js';
|
|
56
56
|
import { ModelPricingRegistry, usageCostUsd } from './core/usage.js';
|
|
57
57
|
import { LocalToolRegistry } from './tools/local-tools.js';
|
|
@@ -2482,8 +2482,9 @@ export async function resolveInteractiveProviderStartup(options) {
|
|
|
2482
2482
|
truthyEnvironmentValue(environment.CLAUDE_CODE_SIMPLE);
|
|
2483
2483
|
if (disabled) {
|
|
2484
2484
|
let effectiveModel;
|
|
2485
|
+
let protocol;
|
|
2485
2486
|
try {
|
|
2486
|
-
|
|
2487
|
+
const target = await resolveProviderTarget({
|
|
2487
2488
|
configRoot: options.configRoot,
|
|
2488
2489
|
cwd: options.cwd,
|
|
2489
2490
|
environment,
|
|
@@ -2497,16 +2498,28 @@ export async function resolveInteractiveProviderStartup(options) {
|
|
|
2497
2498
|
? {}
|
|
2498
2499
|
: { profile: options.controls.providerProfile }),
|
|
2499
2500
|
includeSettings: false,
|
|
2500
|
-
})
|
|
2501
|
+
});
|
|
2502
|
+
effectiveModel = target.modelId;
|
|
2503
|
+
protocol = target.protocol;
|
|
2501
2504
|
}
|
|
2502
2505
|
catch (error) {
|
|
2503
2506
|
if (!(error instanceof ProviderSettingsError &&
|
|
2504
2507
|
error.code === 'model_required'))
|
|
2505
2508
|
throw error;
|
|
2506
2509
|
}
|
|
2510
|
+
const context = parseContextEnvironment(environment);
|
|
2507
2511
|
return {
|
|
2508
2512
|
effectiveModel,
|
|
2509
2513
|
trustProjectRequestAvailable: options.controls.trustProject,
|
|
2514
|
+
contextWindowTokensForModel: (modelId) => protocol === undefined
|
|
2515
|
+
? undefined
|
|
2516
|
+
: resolveProviderContextWindowTokens({
|
|
2517
|
+
protocol,
|
|
2518
|
+
modelId: modelId ?? effectiveModel ?? '',
|
|
2519
|
+
...(context.contextWindowTokens === undefined
|
|
2520
|
+
? {}
|
|
2521
|
+
: { explicitContextWindowTokens: context.contextWindowTokens }),
|
|
2522
|
+
}),
|
|
2510
2523
|
};
|
|
2511
2524
|
}
|
|
2512
2525
|
const assessment = await assessCurrentWorkspaceProviderSelection({
|
|
@@ -2531,8 +2544,9 @@ export async function resolveInteractiveProviderStartup(options) {
|
|
|
2531
2544
|
}
|
|
2532
2545
|
}
|
|
2533
2546
|
let effectiveModel;
|
|
2547
|
+
let protocol;
|
|
2534
2548
|
try {
|
|
2535
|
-
|
|
2549
|
+
const target = await resolveProviderTarget({
|
|
2536
2550
|
configRoot: options.configRoot,
|
|
2537
2551
|
cwd: options.cwd,
|
|
2538
2552
|
environment,
|
|
@@ -2547,13 +2561,28 @@ export async function resolveInteractiveProviderStartup(options) {
|
|
|
2547
2561
|
: { profile: options.controls.providerProfile }),
|
|
2548
2562
|
includeSettings: true,
|
|
2549
2563
|
includeProjectSettings,
|
|
2550
|
-
})
|
|
2564
|
+
});
|
|
2565
|
+
effectiveModel = target.modelId;
|
|
2566
|
+
protocol = target.protocol;
|
|
2551
2567
|
}
|
|
2552
2568
|
catch (error) {
|
|
2553
2569
|
if (!(error instanceof ProviderSettingsError && error.code === 'model_required'))
|
|
2554
2570
|
throw error;
|
|
2555
2571
|
}
|
|
2556
|
-
|
|
2572
|
+
const context = parseContextEnvironment(environment);
|
|
2573
|
+
return {
|
|
2574
|
+
effectiveModel,
|
|
2575
|
+
trustProjectRequestAvailable,
|
|
2576
|
+
contextWindowTokensForModel: (modelId) => protocol === undefined
|
|
2577
|
+
? undefined
|
|
2578
|
+
: resolveProviderContextWindowTokens({
|
|
2579
|
+
protocol,
|
|
2580
|
+
modelId: modelId ?? effectiveModel ?? '',
|
|
2581
|
+
...(context.contextWindowTokens === undefined
|
|
2582
|
+
? {}
|
|
2583
|
+
: { explicitContextWindowTokens: context.contextWindowTokens }),
|
|
2584
|
+
}),
|
|
2585
|
+
};
|
|
2557
2586
|
}
|
|
2558
2587
|
export function createDefaultDependencies(entrypoint = fileURLToPath(import.meta.url)) {
|
|
2559
2588
|
const dependencies = {
|
|
@@ -2629,6 +2658,7 @@ export function createDefaultDependencies(entrypoint = fileURLToPath(import.meta
|
|
|
2629
2658
|
statePath: interactiveStatePath,
|
|
2630
2659
|
factory: {
|
|
2631
2660
|
createService: createInteractiveService,
|
|
2661
|
+
contextWindowTokens: startup.contextWindowTokensForModel,
|
|
2632
2662
|
scheduledPrompts: Boolean(effectiveModel),
|
|
2633
2663
|
},
|
|
2634
2664
|
...(signal ? { signal } : {}),
|
|
@@ -37,6 +37,7 @@ export declare class AnthropicCompatibleProvider implements ModelProvider {
|
|
|
37
37
|
private readonly maxErrorBodyBytes;
|
|
38
38
|
private readonly thinking;
|
|
39
39
|
private readonly wireModel;
|
|
40
|
+
private readonly supportsAdaptiveThinking;
|
|
40
41
|
private readonly providerBetas;
|
|
41
42
|
private readonly promptCaching;
|
|
42
43
|
private readonly streaming;
|
|
@@ -50,17 +50,23 @@ function positiveInteger(value, label) {
|
|
|
50
50
|
}
|
|
51
51
|
return value;
|
|
52
52
|
}
|
|
53
|
-
function validateThinking(thinking) {
|
|
53
|
+
function validateThinking(thinking, supportsAdaptiveThinking) {
|
|
54
54
|
if (!thinking)
|
|
55
55
|
return undefined;
|
|
56
56
|
if (!['enabled', 'adaptive', 'disabled'].includes(thinking.mode)) {
|
|
57
57
|
throw new Error(`Unsupported thinking mode: ${thinking.mode}`);
|
|
58
58
|
}
|
|
59
|
+
if (thinking.mode === 'adaptive' && !supportsAdaptiveThinking) {
|
|
60
|
+
throw new Error('Adaptive thinking is only supported for explicit Claude Sonnet 4.6 or Opus 4.6 models');
|
|
61
|
+
}
|
|
59
62
|
if (thinking.maxTokens !== undefined) {
|
|
60
63
|
positiveInteger(thinking.maxTokens, 'Max thinking tokens');
|
|
61
64
|
if (thinking.mode === 'disabled') {
|
|
62
65
|
throw new Error('Max thinking tokens cannot be used when thinking is disabled');
|
|
63
66
|
}
|
|
67
|
+
if (thinking.mode === 'adaptive') {
|
|
68
|
+
throw new Error('Max thinking tokens cannot be used with adaptive thinking; use enabled thinking for a fixed budget');
|
|
69
|
+
}
|
|
64
70
|
}
|
|
65
71
|
return thinking;
|
|
66
72
|
}
|
|
@@ -645,6 +651,7 @@ export class AnthropicCompatibleProvider {
|
|
|
645
651
|
maxErrorBodyBytes;
|
|
646
652
|
thinking;
|
|
647
653
|
wireModel;
|
|
654
|
+
supportsAdaptiveThinking;
|
|
648
655
|
providerBetas;
|
|
649
656
|
promptCaching;
|
|
650
657
|
streaming;
|
|
@@ -655,6 +662,7 @@ export class AnthropicCompatibleProvider {
|
|
|
655
662
|
this.endpoint = `${options.baseUrl.replace(/\/+$/, '')}/messages`;
|
|
656
663
|
this.model = modelSpec.model;
|
|
657
664
|
this.wireModel = modelSpec.wireModel;
|
|
665
|
+
this.supportsAdaptiveThinking = modelSpec.supportsAdaptiveThinking;
|
|
658
666
|
this.providerBetas = Object.freeze([...modelSpec.betas]);
|
|
659
667
|
this.streaming = options.streaming ?? true;
|
|
660
668
|
this.fetchImplementation = options.fetchImplementation ?? fetch;
|
|
@@ -672,14 +680,16 @@ export class AnthropicCompatibleProvider {
|
|
|
672
680
|
documents: true,
|
|
673
681
|
webSearch: options.webSearch === true,
|
|
674
682
|
thinking: {
|
|
675
|
-
modes:
|
|
683
|
+
modes: modelSpec.supportsAdaptiveThinking
|
|
684
|
+
? ['enabled', 'adaptive', 'disabled']
|
|
685
|
+
: ['enabled', 'disabled'],
|
|
676
686
|
maxTokens: true,
|
|
677
687
|
},
|
|
678
688
|
contextWindowTokens: modelSpec.contextWindowTokens,
|
|
679
689
|
maxOutputTokens: this.maxOutputTokens,
|
|
680
690
|
terminalReasons: true,
|
|
681
691
|
};
|
|
682
|
-
this.thinking = validateThinking(options.thinking);
|
|
692
|
+
this.thinking = validateThinking(options.thinking, this.supportsAdaptiveThinking);
|
|
683
693
|
const promptCaching = options.promptCaching !== undefined
|
|
684
694
|
? options.promptCaching
|
|
685
695
|
: (options.promptCacheResolver?.({
|
|
@@ -708,16 +718,18 @@ export class AnthropicCompatibleProvider {
|
|
|
708
718
|
if (request.webSearch && request.tools?.length) {
|
|
709
719
|
throw new Error('Web search cannot be combined with model tools');
|
|
710
720
|
}
|
|
711
|
-
const thinking = validateThinking(request.thinking ?? this.thinking);
|
|
721
|
+
const thinking = validateThinking(request.thinking ?? this.thinking, this.supportsAdaptiveThinking);
|
|
712
722
|
const maxTokens = Math.max(this.maxOutputTokens, thinking?.maxTokens === undefined ? 0 : thinking.maxTokens + 1);
|
|
713
723
|
const thinkingPayload = thinking === undefined
|
|
714
724
|
? undefined
|
|
715
725
|
: thinking.mode === 'disabled'
|
|
716
726
|
? { type: 'disabled' }
|
|
717
|
-
:
|
|
718
|
-
type: '
|
|
719
|
-
|
|
720
|
-
|
|
727
|
+
: thinking.mode === 'adaptive'
|
|
728
|
+
? { type: 'adaptive' }
|
|
729
|
+
: {
|
|
730
|
+
type: 'enabled',
|
|
731
|
+
budget_tokens: thinking.maxTokens ?? maxTokens - 1,
|
|
732
|
+
};
|
|
721
733
|
const betas = [
|
|
722
734
|
...this.providerBetas,
|
|
723
735
|
...(request.betas ?? []),
|
|
@@ -4,6 +4,7 @@ export interface ResolvedAnthropicModelSpec {
|
|
|
4
4
|
readonly wireModel: string;
|
|
5
5
|
readonly contextWindowTokens: number;
|
|
6
6
|
readonly betas: readonly string[];
|
|
7
|
+
readonly supportsAdaptiveThinking: boolean;
|
|
7
8
|
}
|
|
8
9
|
export declare function resolveAnthropicModelSpec(model: string, explicitContextWindowTokens?: number): ResolvedAnthropicModelSpec;
|
|
9
10
|
//# sourceMappingURL=anthropic-model-spec.d.ts.map
|
|
@@ -10,6 +10,7 @@ export function resolveAnthropicModelSpec(model, explicitContextWindowTokens) {
|
|
|
10
10
|
wireModel,
|
|
11
11
|
contextWindowTokens: explicitContextWindowTokens ?? (longContext ? 1_000_000 : 200_000),
|
|
12
12
|
betas: Object.freeze(longContext ? [ANTHROPIC_LONG_CONTEXT_BETA] : []),
|
|
13
|
+
supportsAdaptiveThinking: wireModel === 'claude-sonnet-4-6' || wireModel === 'claude-opus-4-6',
|
|
13
14
|
});
|
|
14
15
|
}
|
|
15
16
|
//# sourceMappingURL=anthropic-model-spec.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ModelProvider, ModelThinkingConfig } from '../core/runtime.js';
|
|
2
2
|
import { type CodexOAuthVault } from './codex-oauth.js';
|
|
3
|
-
import { type ProviderTarget } from './provider-settings.js';
|
|
3
|
+
import { type ProviderProtocol, type ProviderTarget } from './provider-settings.js';
|
|
4
4
|
import type { ProviderCredentialSourceMetadata, ProviderCredentialReader, ResolvedProviderCredential } from './provider-auth.js';
|
|
5
5
|
import { parseProviderEnvironment, type ContextEnvironment } from './environment.js';
|
|
6
6
|
import { type AnthropicPromptCachePolicy } from './anthropic-prompt-cache.js';
|
|
@@ -47,6 +47,11 @@ export interface ProviderRegistry {
|
|
|
47
47
|
readonly credentialSource: ProviderRegistrySourceMetadata;
|
|
48
48
|
create(modelId?: string): ModelProvider;
|
|
49
49
|
}
|
|
50
|
+
export declare function resolveProviderContextWindowTokens(options: {
|
|
51
|
+
protocol: ProviderProtocol;
|
|
52
|
+
modelId: string;
|
|
53
|
+
explicitContextWindowTokens?: number;
|
|
54
|
+
}): number | undefined;
|
|
50
55
|
export declare function resolveProviderRegistry(options: ResolveProviderRegistryOptions): Promise<ProviderRegistry>;
|
|
51
56
|
export declare function createProviderRegistry(options: ProviderRegistryOptions): ProviderRegistry;
|
|
52
57
|
//# sourceMappingURL=provider-registry.d.ts.map
|
|
@@ -6,6 +6,7 @@ import { DeadlineModelProvider } from './deadline-provider.js';
|
|
|
6
6
|
import { NonStreamingFallbackModelProvider } from './non-streaming-fallback-provider.js';
|
|
7
7
|
import { CodexOAuthCredentialManager, } from './codex-oauth.js';
|
|
8
8
|
import { resolveProviderTarget, } from './provider-settings.js';
|
|
9
|
+
import { resolveAnthropicModelSpec } from './anthropic-model-spec.js';
|
|
9
10
|
import { ProviderAuthenticationError, resolveProviderCredential, } from './provider-auth.js';
|
|
10
11
|
import { parseContextEnvironment, parseProviderEnvironment, } from './environment.js';
|
|
11
12
|
import { createAnthropicPromptCachePolicyResolver, } from './anthropic-prompt-cache.js';
|
|
@@ -17,6 +18,14 @@ export class ProviderRegistryError extends Error {
|
|
|
17
18
|
this.code = code;
|
|
18
19
|
}
|
|
19
20
|
}
|
|
21
|
+
export function resolveProviderContextWindowTokens(options) {
|
|
22
|
+
if (options.protocol === 'anthropic-messages')
|
|
23
|
+
return resolveAnthropicModelSpec(options.modelId, options.explicitContextWindowTokens).contextWindowTokens;
|
|
24
|
+
if (options.protocol === 'openai-compatible' ||
|
|
25
|
+
options.protocol === 'openai-responses')
|
|
26
|
+
return options.explicitContextWindowTokens;
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
20
29
|
export async function resolveProviderRegistry(options) {
|
|
21
30
|
const environment = options.environment ?? process.env;
|
|
22
31
|
const target = await resolveProviderTarget({
|