pennyrouter 0.2.26 → 0.2.27

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pennyrouter",
3
- "version": "0.2.26",
3
+ "version": "0.2.27",
4
4
  "description": "Install and manage PennyRouter local coding-agent integrations.",
5
5
  "homepage": "https://pennyrouter.com",
6
6
  "bugs": {
@@ -17,15 +17,18 @@ export const CLAUDE_CODE_MODELS = {
17
17
  sonnet: "claude-sonnet-5[1m]",
18
18
  opus: "claude-opus-5[1m]",
19
19
  fable: "claude-fable-5[1m]",
20
- custom: "Penny Custom", // -> dynamic (PennyRouter cost-routing)
21
20
  };
22
21
  const CLAUDE_CODE_MODEL_PRESENTATION = {
23
- haiku: { name: "Haiku 4.5 (PennyRouter)", description: "Fast, economical Claude model · 200K context" },
24
- sonnet: { name: "Sonnet 5 (PennyRouter)", description: "Balanced Claude model · 1M context" },
25
- opus: { name: "Opus 5 (PennyRouter)", description: "Most capable Claude model · 1M context" },
26
- fable: { name: "Fable 5 (PennyRouter)", description: "Claude model · 1M context" },
22
+ haiku: { name: "Haiku 4.5", description: "Fast, economical Claude model · 200K context" },
23
+ sonnet: { name: "Sonnet 5", description: "Balanced Claude model · 1M context" },
24
+ opus: { name: "Opus 5", description: "Most capable Claude model · 1M context" },
25
+ fable: { name: "Fable 5", description: "Claude model · 1M context" },
27
26
  };
28
27
  const LEGACY_PINNED_MODEL_IDS = {
28
+ "Haiku 4.5": CLAUDE_CODE_MODELS.haiku,
29
+ "Sonnet 5": CLAUDE_CODE_MODELS.sonnet,
30
+ "Opus 5": CLAUDE_CODE_MODELS.opus,
31
+ "Fable 5": CLAUDE_CODE_MODELS.fable,
29
32
  "Haiku 4.5 (PennyRouter)": CLAUDE_CODE_MODELS.haiku,
30
33
  "Sonnet 5 (PennyRouter)": CLAUDE_CODE_MODELS.sonnet,
31
34
  "Opus 5 (PennyRouter)": CLAUDE_CODE_MODELS.opus,
@@ -86,6 +89,7 @@ export const claudeCodeHarness = {
86
89
  // Anthropic API key. Clear any stale key so it doesn't take precedence.
87
90
  current.env.ANTHROPIC_AUTH_TOKEN = apiKey;
88
91
  delete current.env.ANTHROPIC_API_KEY;
92
+ current.env.CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY = "1";
89
93
  // Claude Code displays these env values directly in its model picker; the
90
94
  // gateway decodes the friendly labels into bundle/profile overrides.
91
95
  current.env.ANTHROPIC_DEFAULT_OPUS_MODEL = CLAUDE_CODE_MODELS.opus;
@@ -93,6 +97,9 @@ export const claudeCodeHarness = {
93
97
  current.env.ANTHROPIC_DEFAULT_HAIKU_MODEL = CLAUDE_CODE_MODELS.haiku;
94
98
  current.env.ANTHROPIC_DEFAULT_FABLE_MODEL = CLAUDE_CODE_MODELS.fable;
95
99
  writeClaudeCodeModelPresentation(current.env);
100
+ delete current.env.ANTHROPIC_CUSTOM_MODEL_OPTION;
101
+ delete current.env.ANTHROPIC_CUSTOM_MODEL_OPTION_NAME;
102
+ delete current.env.ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION;
96
103
  // This is global, not per-model. Leave it unset so Claude Code uses each native id's
97
104
  // real context limit instead of incorrectly giving Haiku a 1M window.
98
105
  delete current.env.CLAUDE_CODE_MAX_CONTEXT_TOKENS;
@@ -138,11 +145,15 @@ export const claudeCodeHarness = {
138
145
  current.env.ANTHROPIC_AUTH_TOKEN = apiKey;
139
146
  // Single credential only: no stale api key, no separate Anthropic bearer.
140
147
  delete current.env.ANTHROPIC_API_KEY;
148
+ current.env.CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY = "1";
141
149
  current.env.ANTHROPIC_DEFAULT_OPUS_MODEL = CLAUDE_CODE_MODELS.opus;
142
150
  current.env.ANTHROPIC_DEFAULT_SONNET_MODEL = CLAUDE_CODE_MODELS.sonnet;
143
151
  current.env.ANTHROPIC_DEFAULT_HAIKU_MODEL = CLAUDE_CODE_MODELS.haiku;
144
152
  current.env.ANTHROPIC_DEFAULT_FABLE_MODEL = CLAUDE_CODE_MODELS.fable;
145
153
  writeClaudeCodeModelPresentation(current.env);
154
+ delete current.env.ANTHROPIC_CUSTOM_MODEL_OPTION;
155
+ delete current.env.ANTHROPIC_CUSTOM_MODEL_OPTION_NAME;
156
+ delete current.env.ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION;
146
157
  // This is global, not per-model. Leave it unset so Claude Code uses each native id's
147
158
  // real context limit instead of incorrectly giving Haiku a 1M window.
148
159
  delete current.env.CLAUDE_CODE_MAX_CONTEXT_TOKENS;
@@ -184,11 +195,15 @@ export const claudeCodeHarness = {
184
195
  // Authorization bearer token is forwarded by the gateway to Anthropic.
185
196
  current.env.ANTHROPIC_API_KEY = pennyKey;
186
197
  current.env.ANTHROPIC_AUTH_TOKEN = anthropicAuthToken;
198
+ current.env.CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY = "1";
187
199
  current.env.ANTHROPIC_DEFAULT_OPUS_MODEL = CLAUDE_CODE_MODELS.opus;
188
200
  current.env.ANTHROPIC_DEFAULT_SONNET_MODEL = CLAUDE_CODE_MODELS.sonnet;
189
201
  current.env.ANTHROPIC_DEFAULT_HAIKU_MODEL = CLAUDE_CODE_MODELS.haiku;
190
202
  current.env.ANTHROPIC_DEFAULT_FABLE_MODEL = CLAUDE_CODE_MODELS.fable;
191
203
  writeClaudeCodeModelPresentation(current.env);
204
+ delete current.env.ANTHROPIC_CUSTOM_MODEL_OPTION;
205
+ delete current.env.ANTHROPIC_CUSTOM_MODEL_OPTION_NAME;
206
+ delete current.env.ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION;
192
207
  // This is global, not per-model. Leave it unset so Claude Code uses each native id's
193
208
  // real context limit instead of incorrectly giving Haiku a 1M window.
194
209
  delete current.env.CLAUDE_CODE_MAX_CONTEXT_TOKENS;
package/src/launch.js CHANGED
@@ -433,16 +433,17 @@ export function buildPennyInvocation(harness, args, config) {
433
433
  const models = item.models || {};
434
434
  env.ANTHROPIC_BASE_URL = config.gateway_base_url;
435
435
  env.ANTHROPIC_AUTH_TOKEN = config.api_key;
436
+ // Ask Claude Code to discover the gateway's model catalog at startup. The gateway
437
+ // advertises Penny-facing ids in /v1/models; this is separate from inference routing.
438
+ env.CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY = "1";
436
439
  delete env.ANTHROPIC_API_KEY;
437
440
  if (models.haiku) env.ANTHROPIC_DEFAULT_HAIKU_MODEL = upgradeClaudeCodeModelValue(models.haiku);
438
441
  if (models.sonnet) env.ANTHROPIC_DEFAULT_SONNET_MODEL = upgradeClaudeCodeModelValue(models.sonnet);
439
442
  if (models.opus) env.ANTHROPIC_DEFAULT_OPUS_MODEL = upgradeClaudeCodeModelValue(models.opus);
440
443
  if (models.fable) env.ANTHROPIC_DEFAULT_FABLE_MODEL = upgradeClaudeCodeModelValue(models.fable);
441
- if (models.custom) {
442
- env.ANTHROPIC_CUSTOM_MODEL_OPTION = upgradeClaudeCodeModelValue(models.custom);
443
- if (models.custom_name) env.ANTHROPIC_CUSTOM_MODEL_OPTION_NAME = models.custom_name;
444
- if (models.custom_description) env.ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION = models.custom_description;
445
- }
444
+ delete env.ANTHROPIC_CUSTOM_MODEL_OPTION;
445
+ delete env.ANTHROPIC_CUSTOM_MODEL_OPTION_NAME;
446
+ delete env.ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION;
446
447
  writeClaudeCodeModelPresentation(env);
447
448
  // Per-model limits must come from Claude Code's recognized native model ids. This
448
449
  // global override would incorrectly give Haiku the 1M window.
@@ -103,12 +103,13 @@ async function main() {
103
103
  assert.ok(!claude.args.includes("--plugin-dir"));
104
104
  assert.equal(claude.env.ANTHROPIC_AUTH_TOKEN, "pr-super-secret");
105
105
  assert.equal(claude.env.ANTHROPIC_BASE_URL, "https://gateway.example/");
106
+ assert.equal(claude.env.CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY, "1");
106
107
  // 1M-capable slots carry the `[1m]` suffix; Haiku has no 1M variant and stays bare.
107
108
  // Per-slot suffixes, not the global CLAUDE_CODE_MAX_CONTEXT_TOKENS override, which
108
109
  // would wrongly hand Haiku a 1M window too.
109
110
  assert.equal(claude.env.ANTHROPIC_DEFAULT_OPUS_MODEL, "claude-opus-5[1m]");
110
111
  assert.equal(claude.env.ANTHROPIC_DEFAULT_HAIKU_MODEL, "claude-haiku-4-5-20251001");
111
- assert.equal(claude.env.ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME, "Haiku 4.5 (PennyRouter)");
112
+ assert.equal(claude.env.ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME, "Haiku 4.5");
112
113
  assert.equal(claude.env.ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION, "Fast, economical Claude model · 200K context");
113
114
  assert.equal(claude.env.CLAUDE_CODE_MAX_CONTEXT_TOKENS, undefined);
114
115
 
package/src/session.js CHANGED
@@ -97,7 +97,7 @@ export async function probeClaudeCodeGateway({ gatewayBaseUrl, apiKey }) {
97
97
  "anthropic-beta": "compact-2026-01-12",
98
98
  },
99
99
  body: JSON.stringify({
100
- model: "Sonnet 5 (PennyRouter)",
100
+ model: "Sonnet 5",
101
101
  max_tokens: 1,
102
102
  stream: false,
103
103
  messages: [{ role: "user", content: "Reply OK." }],