praxis-agent 0.62.0 → 0.62.1

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
@@ -98,6 +98,13 @@ cd /path/to/project
98
98
  praxis
99
99
  ```
100
100
 
101
+ Anthropic models use a 200,000-token context window by default, including
102
+ unknown model IDs. Add the exact terminal `[1m]` suffix (for example,
103
+ `claude-sonnet-4-20250514[1m]`) to request a 1,000,000-token context window;
104
+ Praxis keeps that selected model public, removes the suffix on the wire, and
105
+ adds the `context-1m-2025-08-07` Anthropic beta once. An explicit
106
+ `PRAXIS_CONTEXT_WINDOW_TOKENS` value overrides either inferred window.
107
+
101
108
  Common non-interactive operations:
102
109
 
103
110
  ```sh
@@ -177,7 +184,7 @@ troubleshooting. Run `praxis --help` for the authoritative command surface.
177
184
  patches and readable binary/conflict/transient-path notes, semantic plan/question decision panels with complete
178
185
  screen-reader actions, semantic screen projection across selectable surfaces,
179
186
  deterministic resize-aware URL/form elicitation rendering, and measured
180
- context budgets; print mode,
187
+ context budgets with base64-payload-independent image estimates; print mode,
181
188
  structured JSON/JSONL, context compaction, tool loops, and bounded execution.
182
189
  - **Built-in tools** — read, write, edit, `ApplyPatch` for bounded ordered exact
183
190
  multi-file replacements, configured plugin LSP navigation with fresh bounded
@@ -294,7 +301,9 @@ troubleshooting. Run `praxis --help` for the authoritative command surface.
294
301
  route, whether primary or fallback, stays sticky only through that logical
295
302
  Turn's tool continuations; incompatible routes fail closed, and the next
296
303
  independent Turn starts from primary. Recovery may persist only an optional
297
- selected model, never provider route or wire state.
304
+ selected model, never provider route or wire state. Anthropic uses a
305
+ 200,000-token default or exact terminal `[1m]` model syntax for 1,000,000
306
+ tokens; `PRAXIS_CONTEXT_WINDOW_TOKENS` overrides the advertised window.
298
307
  - **Transactional self-update** — `praxis update` verifies the package before
299
308
  installing it, rejects concurrent updates, and can roll back after an
300
309
  interruption or crash.
@@ -24,6 +24,12 @@ export function estimateTextTokens(value) {
24
24
  }
25
25
  return Math.ceil(ascii / 4) + nonAscii;
26
26
  }
27
+ const IMAGE_VISUAL_TOKEN_ESTIMATE = 1_600;
28
+ // This provider-neutral conservative fallback never interprets billed usage;
29
+ // observed provider usage remains authoritative at a ContextBudget watermark.
30
+ function estimateImageTokens(mediaType) {
31
+ return 8 + estimateTextTokens(mediaType) + IMAGE_VISUAL_TOKEN_ESTIMATE;
32
+ }
27
33
  function estimateMessageTokens(message) {
28
34
  let tokens = 4 + estimateTextTokens(message.role);
29
35
  if (message.role === 'tool') {
@@ -31,16 +37,14 @@ function estimateMessageTokens(message) {
31
37
  estimateTextTokens(message.toolCallId) +
32
38
  estimateTextTokens(message.content);
33
39
  for (const image of message.images ?? []) {
34
- tokens +=
35
- 8 + estimateTextTokens(image.mediaType) + estimateTextTokens(image.data);
40
+ tokens += estimateImageTokens(image.mediaType);
36
41
  }
37
42
  return tokens + (message.isError ? 1 : 0);
38
43
  }
39
44
  tokens += estimateTextTokens(message.content);
40
45
  if (message.role === 'user') {
41
46
  for (const image of message.images ?? []) {
42
- tokens +=
43
- 8 + estimateTextTokens(image.mediaType) + estimateTextTokens(image.data);
47
+ tokens += estimateImageTokens(image.mediaType);
44
48
  }
45
49
  for (const document of message.documents ?? []) {
46
50
  tokens += 8 + estimateTextTokens(document.mediaType) + 2000;
@@ -4,6 +4,10 @@ export interface AnthropicCompatibleProviderOptions {
4
4
  baseUrl: string;
5
5
  apiKey: string;
6
6
  model: string;
7
+ promptCacheResolver?: (target: {
8
+ baseUrl: string;
9
+ model: string;
10
+ }) => AnthropicPromptCachePolicy;
7
11
  maxOutputTokens?: number;
8
12
  anthropicVersion?: string;
9
13
  webSearch?: boolean;
@@ -32,6 +36,8 @@ export declare class AnthropicCompatibleProvider implements ModelProvider {
32
36
  private readonly maxToolMetadataBytes;
33
37
  private readonly maxErrorBodyBytes;
34
38
  private readonly thinking;
39
+ private readonly wireModel;
40
+ private readonly providerBetas;
35
41
  private readonly promptCaching;
36
42
  private readonly streaming;
37
43
  constructor(options: AnthropicCompatibleProviderOptions);
@@ -3,6 +3,7 @@ import { transportFailureKind } from './provider-errors.js';
3
3
  import { reportProviderTransportActivity } from './provider-transport-activity.js';
4
4
  import { markNonStreamingFallbackEligible } from './non-streaming-fallback-provider.js';
5
5
  import { createAnthropicPromptCachePolicyResolver, } from './anthropic-prompt-cache.js';
6
+ import { resolveAnthropicModelSpec } from './anthropic-model-spec.js';
6
7
  function isRecord(value) {
7
8
  return typeof value === 'object' && value !== null && !Array.isArray(value);
8
9
  }
@@ -643,21 +644,24 @@ export class AnthropicCompatibleProvider {
643
644
  maxToolMetadataBytes;
644
645
  maxErrorBodyBytes;
645
646
  thinking;
647
+ wireModel;
648
+ providerBetas;
646
649
  promptCaching;
647
650
  streaming;
648
651
  constructor(options) {
649
652
  this.options = options;
650
- if (options.contextWindowTokens !== undefined) {
651
- positiveInteger(options.contextWindowTokens, 'Context window tokens');
652
- }
653
+ const modelSpec = resolveAnthropicModelSpec(options.model, options.contextWindowTokens);
654
+ positiveInteger(modelSpec.contextWindowTokens, 'Context window tokens');
653
655
  this.endpoint = `${options.baseUrl.replace(/\/+$/, '')}/messages`;
654
- this.model = options.model;
656
+ this.model = modelSpec.model;
657
+ this.wireModel = modelSpec.wireModel;
658
+ this.providerBetas = Object.freeze([...modelSpec.betas]);
655
659
  this.streaming = options.streaming ?? true;
656
660
  this.fetchImplementation = options.fetchImplementation ?? fetch;
657
661
  this.maxOutputTokens = positiveInteger(options.maxOutputTokens ??
658
- (options.model.includes('claude-opus-4-6')
662
+ (this.wireModel.includes('claude-opus-4-6')
659
663
  ? 64_000
660
- : options.model.startsWith('claude-')
664
+ : this.wireModel.startsWith('claude-')
661
665
  ? 32_000
662
666
  : 8192), 'Max output tokens');
663
667
  this.capabilities = {
@@ -671,19 +675,20 @@ export class AnthropicCompatibleProvider {
671
675
  modes: ['enabled', 'adaptive', 'disabled'],
672
676
  maxTokens: true,
673
677
  },
674
- ...(options.contextWindowTokens === undefined
675
- ? {}
676
- : { contextWindowTokens: options.contextWindowTokens }),
678
+ contextWindowTokens: modelSpec.contextWindowTokens,
677
679
  maxOutputTokens: this.maxOutputTokens,
678
680
  terminalReasons: true,
679
681
  };
680
682
  this.thinking = validateThinking(options.thinking);
681
- const promptCaching = options.promptCaching === false
682
- ? undefined
683
- : (options.promptCaching ??
683
+ const promptCaching = options.promptCaching !== undefined
684
+ ? options.promptCaching
685
+ : (options.promptCacheResolver?.({
686
+ baseUrl: options.baseUrl,
687
+ model: this.wireModel,
688
+ }) ??
684
689
  createAnthropicPromptCachePolicyResolver({}, 'native')({
685
690
  baseUrl: options.baseUrl,
686
- model: options.model,
691
+ model: this.wireModel,
687
692
  }));
688
693
  this.promptCaching = promptCaching
689
694
  ? cacheControl(promptCaching.ttl)
@@ -714,6 +719,7 @@ export class AnthropicCompatibleProvider {
714
719
  budget_tokens: thinking.maxTokens ?? maxTokens - 1,
715
720
  };
716
721
  const betas = [
722
+ ...this.providerBetas,
717
723
  ...(request.betas ?? []),
718
724
  ...(thinking && thinking.mode !== 'disabled'
719
725
  ? ['interleaved-thinking-2025-05-14']
@@ -730,7 +736,7 @@ export class AnthropicCompatibleProvider {
730
736
  ...(betas.length ? { 'anthropic-beta': betas.join(',') } : {}),
731
737
  },
732
738
  body: JSON.stringify({
733
- model: this.options.model,
739
+ model: this.wireModel,
734
740
  max_tokens: maxTokens,
735
741
  messages: serialized.messages,
736
742
  stream: this.streaming,
@@ -0,0 +1,9 @@
1
+ export declare const ANTHROPIC_LONG_CONTEXT_BETA = "context-1m-2025-08-07";
2
+ export interface ResolvedAnthropicModelSpec {
3
+ readonly model: string;
4
+ readonly wireModel: string;
5
+ readonly contextWindowTokens: number;
6
+ readonly betas: readonly string[];
7
+ }
8
+ export declare function resolveAnthropicModelSpec(model: string, explicitContextWindowTokens?: number): ResolvedAnthropicModelSpec;
9
+ //# sourceMappingURL=anthropic-model-spec.d.ts.map
@@ -0,0 +1,15 @@
1
+ export const ANTHROPIC_LONG_CONTEXT_BETA = 'context-1m-2025-08-07';
2
+ export function resolveAnthropicModelSpec(model, explicitContextWindowTokens) {
3
+ const longContext = model.endsWith('[1m]');
4
+ const wireModel = longContext ? model.slice(0, -'[1m]'.length) : model;
5
+ if (longContext && wireModel.trim().length === 0) {
6
+ throw new Error('Anthropic [1m] model spec must include a base model name');
7
+ }
8
+ return Object.freeze({
9
+ model,
10
+ wireModel,
11
+ contextWindowTokens: explicitContextWindowTokens ?? (longContext ? 1_000_000 : 200_000),
12
+ betas: Object.freeze(longContext ? [ANTHROPIC_LONG_CONTEXT_BETA] : []),
13
+ });
14
+ }
15
+ //# sourceMappingURL=anthropic-model-spec.js.map
@@ -175,10 +175,7 @@ class NativeProviderRegistry {
175
175
  ...(this.options.anthropicPromptCacheResolver === undefined
176
176
  ? {}
177
177
  : {
178
- promptCaching: this.options.anthropicPromptCacheResolver({
179
- baseUrl: target.baseUrl,
180
- model: target.modelId,
181
- }),
178
+ promptCacheResolver: this.options.anthropicPromptCacheResolver,
182
179
  }),
183
180
  ...(this.options.providerEnvironment?.maxOutputTokens === undefined
184
181
  ? {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "praxis-agent",
3
- "version": "0.62.0",
3
+ "version": "0.62.1",
4
4
  "description": "Local-first, single-user general agent for the command line.",
5
5
  "license": "MIT",
6
6
  "author": "wuqisen",