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 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, prompt stash and continuation shortcuts,
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;
@@ -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
- if (runtimeInfo?.contextWindowTokens !== undefined)
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
- contextWindowTokens: runtimeDisplay.contextWindowTokens ?? 200_000,
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), ")"] })] })] }), _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))))] }));
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('', `Context Usage: ${item.usedTokens}/${item.contextWindowTokens} tokens`);
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' ? '⚠ ' : '· '));
@@ -22,7 +22,7 @@ export type TranscriptItem = {
22
22
  } | {
23
23
  kind: 'context';
24
24
  usedTokens: number;
25
- contextWindowTokens: number;
25
+ contextWindowTokens?: number;
26
26
  model?: string;
27
27
  skills: readonly {
28
28
  name: string;
@@ -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: `Context Usage · ${item.usedTokens}/${item.contextWindowTokens} tokens`,
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
  }
@@ -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: {
@@ -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
- effectiveModel = (await resolveProviderTarget({
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
- })).modelId;
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
- effectiveModel = (await resolveProviderTarget({
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
- })).modelId;
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
- return { effectiveModel, trustProjectRequestAvailable };
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: ['enabled', 'adaptive', 'disabled'],
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: 'enabled',
719
- budget_tokens: thinking.maxTokens ?? maxTokens - 1,
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({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "praxis-agent",
3
- "version": "0.62.1",
3
+ "version": "0.62.2",
4
4
  "description": "Local-first, single-user general agent for the command line.",
5
5
  "license": "MIT",
6
6
  "author": "wuqisen",