pi-claude-billing-router 0.1.1 → 0.1.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.
@@ -1347,6 +1347,54 @@ function stripAliasStubs(payload: Record<string, unknown>): Record<string, unkno
1347
1347
  return { ...payload, tools: kept };
1348
1348
  }
1349
1349
 
1350
+ // Project request-local transcript state BEFORE providers serialize/cache/seal it.
1351
+ // In particular pi-background-tasks protects both cache positions and lineage;
1352
+ // changing the serialized tool list in before_provider_request is too late.
1353
+ function transformTranscript(messages: unknown[], plan: boolean, disableFilter: boolean): unknown[] {
1354
+ const active = new Map<string, Record<string, unknown>>();
1355
+ let previous = new Map<string, Record<string, unknown>>();
1356
+ return messages.map((message) => {
1357
+ if (!isPlainObject(message)) return message;
1358
+ if (message.role !== "system") {
1359
+ if (!plan || message.role !== "assistant" || !Array.isArray(message.content)) return message;
1360
+ const content = remapBlockNames(message.content, "toolCall", (name) => {
1361
+ const alias = FLAT_TO_MCP.get(lower(name));
1362
+ return alias && registeredMcpAliases.has(lower(alias)) ? alias : undefined;
1363
+ });
1364
+ return content === message.content ? message : { ...message, content };
1365
+ }
1366
+ if (Array.isArray(message.toolsRemoved)) {
1367
+ for (const tool of message.toolsRemoved) {
1368
+ if (isPlainObject(tool) && typeof tool.name === "string") active.delete(lower(tool.name));
1369
+ }
1370
+ }
1371
+ if (Array.isArray(message.toolsAdded)) {
1372
+ for (const tool of message.toolsAdded) {
1373
+ if (isPlainObject(tool) && typeof tool.name === "string") active.set(lower(tool.name), tool);
1374
+ }
1375
+ }
1376
+ const tools = [...active.values()];
1377
+ const projected = plan
1378
+ ? filterAndRemapTools(tools, disableFilter)!
1379
+ : (stripAliasStubs({ tools }).tools as unknown[]);
1380
+ const next = collectToolsByName(projected);
1381
+ const toolsRemoved = [...previous.keys()].filter((name) => !next.has(name))
1382
+ .map((name) => ({ name: previous.get(name)!.name }));
1383
+ const toolsAdded = [...next.entries()].filter(([name, tool]) =>
1384
+ JSON.stringify(previous.get(name)) !== JSON.stringify(tool)).map(([, tool]) => tool);
1385
+ previous = next;
1386
+ const result: Record<string, unknown> = { ...message, toolsAdded, toolsRemoved };
1387
+ if (plan) {
1388
+ result.content = rewriteSystemField(message.content);
1389
+ if (isPlainObject(message.sections)) {
1390
+ result.sections = Object.fromEntries(Object.entries(message.sections).map(([key, value]) =>
1391
+ [key, typeof value === "string" ? rewritePromptText(value) : value]));
1392
+ }
1393
+ }
1394
+ return result;
1395
+ });
1396
+ }
1397
+
1350
1398
  // ============================================================================
1351
1399
  // Extension entry point
1352
1400
  // ============================================================================
@@ -1377,6 +1425,18 @@ export default async function piClaudeCodeUse(pi: ExtensionAPI): Promise<void> {
1377
1425
  return { message: rewritten as typeof event.message };
1378
1426
  });
1379
1427
 
1428
+ pi.on("context_with_system", (event, ctx) => {
1429
+ const model = ctx.model;
1430
+ if (model?.provider !== "anthropic" || !ctx.modelRegistry.isUsingOAuth(model)) return;
1431
+ // Refresh before serialization, including tools registered after our
1432
+ // before_agent_start handler. Keep the existing best-effort semantics.
1433
+ try {
1434
+ registerMcpAliases(pi, { cwd: typeof ctx.cwd === "string" ? ctx.cwd : undefined });
1435
+ } catch { /* The existing aliases remain usable. */ }
1436
+ return { messages: transformTranscript(event.messages, useSubscription(),
1437
+ process.env.PI_CLAUDE_CODE_USE_DISABLE_TOOL_FILTER === "1") as typeof event.messages };
1438
+ });
1439
+
1380
1440
  pi.on("before_provider_request", (event, ctx) => {
1381
1441
  const model = ctx.model;
1382
1442
  if (model?.provider !== "anthropic" || !ctx.modelRegistry.isUsingOAuth(model)) {
@@ -1452,6 +1512,8 @@ export const _test = {
1452
1512
  },
1453
1513
  syncAliasActivation,
1454
1514
  transformPayload,
1515
+ transformTranscript,
1516
+ stripAliasStubs,
1455
1517
  unaliasDisplayedProse,
1456
1518
  unaliasToolCalls,
1457
1519
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-claude-billing-router",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Route Claude Pro/Max OAuth requests in pi between your plan and extra-usage credits: plan first, automatic fallback to credits when the 5h window is exhausted, /billing override, live usage in the footer.",
5
5
  "keywords": [
6
6
  "pi-package",