@skein-code/cli 0.3.21 → 0.3.22

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
@@ -57,8 +57,10 @@ inspectable index for code retrieval and no retrieval service dependency.
57
57
  artifact for bounded readback.
58
58
  - **Measurable token economy:** every model request records a privacy-safe
59
59
  receipt for stable, dynamic, history, retrieval, tool-result, and tool-schema
60
- estimates, alongside actual provider usage when available. Receipts retain
61
- only counts and runtime decisions, never prompt or source content.
60
+ estimates, alongside actual provider usage when available. OpenAI, Anthropic,
61
+ and Gemini cache/reasoning counters are normalized into session totals and
62
+ structured events when the provider reports them. Receipts retain only counts
63
+ and runtime decisions, never prompt or source content.
62
64
  - **Reversible work:** Skein snapshots affected files before mutation without
63
65
  touching your Git history.
64
66
  - **Resumable by default:** conversations, tasks, usage, and changed files live
@@ -94,7 +96,7 @@ To build, verify, and install a local package artifact from this checkout:
94
96
 
95
97
  ```bash
96
98
  npm run verify:package -- --output-dir artifacts/package
97
- npm install -g ./artifacts/package/skein-code-cli-0.3.21.tgz
99
+ npm install -g ./artifacts/package/skein-code-cli-0.3.22.tgz
98
100
  ```
99
101
 
100
102
  To install the published package from npm:
package/dist/cli.js CHANGED
@@ -220,7 +220,7 @@ function isSqliteBusy(error) {
220
220
  // package.json
221
221
  var package_default = {
222
222
  name: "@skein-code/cli",
223
- version: "0.3.21",
223
+ version: "0.3.22",
224
224
  description: "A context-first, model-agnostic coding agent with an auditable terminal workspace.",
225
225
  type: "module",
226
226
  license: "MIT",
@@ -236,6 +236,9 @@ var package_default = {
236
236
  },
237
237
  skein: {
238
238
  releaseNotes: [
239
+ "OpenAI, Anthropic, and Gemini usage now normalizes cached input, cache-write input, and reasoning tokens when reported",
240
+ "Token Ledger, session totals, and structured usage events preserve provider cache and reasoning counts without prompt content",
241
+ "Streaming, non-streaming, explicit-zero, and legacy-session compatibility paths are covered without claiming cache activation",
239
242
  "Failed configured verification output yields bounded current-run diagnostic ranking without persisting paths",
240
243
  "Diagnostic hints cannot create zero-relevance hits and are cleared on success or the next agent run",
241
244
  "Python absolute and relative module imports now contribute deterministic offline graph adjacency",
@@ -5349,10 +5352,7 @@ var AnthropicProvider = class {
5349
5352
  name: block.name ?? "unknown",
5350
5353
  arguments: safeJsonArguments(block.input)
5351
5354
  })),
5352
- usage: {
5353
- ...data.usage?.input_tokens !== void 0 ? { inputTokens: data.usage.input_tokens } : {},
5354
- ...data.usage?.output_tokens !== void 0 ? { outputTokens: data.usage.output_tokens } : {}
5355
- },
5355
+ usage: normalizeAnthropicUsage(data.usage),
5356
5356
  ...data.stop_reason ? { stopReason: data.stop_reason } : {}
5357
5357
  };
5358
5358
  }
@@ -5393,6 +5393,8 @@ var AnthropicProvider = class {
5393
5393
  let content = "";
5394
5394
  let inputTokens;
5395
5395
  let outputTokens;
5396
+ let cachedInputTokens;
5397
+ let cacheWriteInputTokens;
5396
5398
  let stopReason;
5397
5399
  const calls = /* @__PURE__ */ new Map();
5398
5400
  for await (const event of parseServerSentEvents(response)) {
@@ -5402,6 +5404,13 @@ var AnthropicProvider = class {
5402
5404
  }
5403
5405
  if (chunk.usage?.input_tokens !== void 0) inputTokens = chunk.usage.input_tokens;
5404
5406
  if (chunk.usage?.output_tokens !== void 0) outputTokens = chunk.usage.output_tokens;
5407
+ const usage = chunk.usage ?? chunk.message?.usage;
5408
+ if (usage?.cache_read_input_tokens !== void 0) {
5409
+ cachedInputTokens = usage.cache_read_input_tokens;
5410
+ }
5411
+ if (usage?.cache_creation_input_tokens !== void 0) {
5412
+ cacheWriteInputTokens = usage.cache_creation_input_tokens;
5413
+ }
5405
5414
  if (chunk.delta?.stop_reason) stopReason = chunk.delta.stop_reason;
5406
5415
  const index = chunk.index ?? 0;
5407
5416
  if (chunk.type === "content_block_start" && chunk.content_block?.type === "text") {
@@ -5440,7 +5449,9 @@ var AnthropicProvider = class {
5440
5449
  })),
5441
5450
  usage: {
5442
5451
  ...inputTokens !== void 0 ? { inputTokens } : {},
5443
- ...outputTokens !== void 0 ? { outputTokens } : {}
5452
+ ...outputTokens !== void 0 ? { outputTokens } : {},
5453
+ ...cachedInputTokens !== void 0 ? { cachedInputTokens } : {},
5454
+ ...cacheWriteInputTokens !== void 0 ? { cacheWriteInputTokens } : {}
5444
5455
  },
5445
5456
  ...stopReason ? { stopReason } : {}
5446
5457
  }
@@ -5456,13 +5467,18 @@ function normalizeAnthropicResponse(data) {
5456
5467
  name: block.name ?? "unknown",
5457
5468
  arguments: safeJsonArguments(block.input)
5458
5469
  })),
5459
- usage: {
5460
- ...data.usage?.input_tokens !== void 0 ? { inputTokens: data.usage.input_tokens } : {},
5461
- ...data.usage?.output_tokens !== void 0 ? { outputTokens: data.usage.output_tokens } : {}
5462
- },
5470
+ usage: normalizeAnthropicUsage(data.usage),
5463
5471
  ...data.stop_reason ? { stopReason: data.stop_reason } : {}
5464
5472
  };
5465
5473
  }
5474
+ function normalizeAnthropicUsage(usage) {
5475
+ return {
5476
+ ...usage?.input_tokens !== void 0 ? { inputTokens: usage.input_tokens } : {},
5477
+ ...usage?.output_tokens !== void 0 ? { outputTokens: usage.output_tokens } : {},
5478
+ ...usage?.cache_read_input_tokens !== void 0 ? { cachedInputTokens: usage.cache_read_input_tokens } : {},
5479
+ ...usage?.cache_creation_input_tokens !== void 0 ? { cacheWriteInputTokens: usage.cache_creation_input_tokens } : {}
5480
+ };
5481
+ }
5466
5482
  function toAnthropicMessage(message2) {
5467
5483
  if (message2.role === "tool") {
5468
5484
  return {
@@ -5536,10 +5552,7 @@ var GeminiProvider = class {
5536
5552
  name: part.functionCall?.name ?? "unknown",
5537
5553
  arguments: safeJsonArguments(part.functionCall?.args)
5538
5554
  })),
5539
- usage: {
5540
- ...data.usageMetadata?.promptTokenCount !== void 0 ? { inputTokens: data.usageMetadata.promptTokenCount } : {},
5541
- ...data.usageMetadata?.candidatesTokenCount !== void 0 ? { outputTokens: data.usageMetadata.candidatesTokenCount } : {}
5542
- },
5555
+ usage: normalizeGeminiUsage(data.usageMetadata),
5543
5556
  ...candidate?.finishReason ? { stopReason: candidate.finishReason } : {}
5544
5557
  };
5545
5558
  }
@@ -5579,6 +5592,8 @@ var GeminiProvider = class {
5579
5592
  let content = "";
5580
5593
  let inputTokens;
5581
5594
  let outputTokens;
5595
+ let cachedInputTokens;
5596
+ let reasoningTokens;
5582
5597
  let stopReason;
5583
5598
  const toolCalls = [];
5584
5599
  for await (const event of parseServerSentEvents(response)) {
@@ -5590,6 +5605,12 @@ var GeminiProvider = class {
5590
5605
  if (chunk.usageMetadata?.candidatesTokenCount !== void 0) {
5591
5606
  outputTokens = chunk.usageMetadata.candidatesTokenCount;
5592
5607
  }
5608
+ if (chunk.usageMetadata?.cachedContentTokenCount !== void 0) {
5609
+ cachedInputTokens = chunk.usageMetadata.cachedContentTokenCount;
5610
+ }
5611
+ if (chunk.usageMetadata?.thoughtsTokenCount !== void 0) {
5612
+ reasoningTokens = chunk.usageMetadata.thoughtsTokenCount;
5613
+ }
5593
5614
  const candidate = chunk.candidates?.[0];
5594
5615
  if (candidate?.finishReason) stopReason = candidate.finishReason;
5595
5616
  for (const part of candidate?.content?.parts ?? []) {
@@ -5613,7 +5634,9 @@ var GeminiProvider = class {
5613
5634
  toolCalls,
5614
5635
  usage: {
5615
5636
  ...inputTokens !== void 0 ? { inputTokens } : {},
5616
- ...outputTokens !== void 0 ? { outputTokens } : {}
5637
+ ...outputTokens !== void 0 ? { outputTokens } : {},
5638
+ ...cachedInputTokens !== void 0 ? { cachedInputTokens } : {},
5639
+ ...reasoningTokens !== void 0 ? { reasoningTokens } : {}
5617
5640
  },
5618
5641
  ...stopReason ? { stopReason } : {}
5619
5642
  }
@@ -5630,13 +5653,18 @@ function normalizeGeminiResponse(data) {
5630
5653
  name: part.functionCall?.name ?? "unknown",
5631
5654
  arguments: safeJsonArguments(part.functionCall?.args)
5632
5655
  })),
5633
- usage: {
5634
- ...data.usageMetadata?.promptTokenCount !== void 0 ? { inputTokens: data.usageMetadata.promptTokenCount } : {},
5635
- ...data.usageMetadata?.candidatesTokenCount !== void 0 ? { outputTokens: data.usageMetadata.candidatesTokenCount } : {}
5636
- },
5656
+ usage: normalizeGeminiUsage(data.usageMetadata),
5637
5657
  ...candidate?.finishReason ? { stopReason: candidate.finishReason } : {}
5638
5658
  };
5639
5659
  }
5660
+ function normalizeGeminiUsage(usage) {
5661
+ return {
5662
+ ...usage?.promptTokenCount !== void 0 ? { inputTokens: usage.promptTokenCount } : {},
5663
+ ...usage?.candidatesTokenCount !== void 0 ? { outputTokens: usage.candidatesTokenCount } : {},
5664
+ ...usage?.cachedContentTokenCount !== void 0 ? { cachedInputTokens: usage.cachedContentTokenCount } : {},
5665
+ ...usage?.thoughtsTokenCount !== void 0 ? { reasoningTokens: usage.thoughtsTokenCount } : {}
5666
+ };
5667
+ }
5640
5668
  function toGeminiMessage(message2) {
5641
5669
  if (message2.role === "tool") {
5642
5670
  return {
@@ -5764,6 +5792,8 @@ var OpenAIProvider = class {
5764
5792
  let content = "";
5765
5793
  let inputTokens;
5766
5794
  let outputTokens;
5795
+ let cachedInputTokens;
5796
+ let reasoningTokens;
5767
5797
  let stopReason;
5768
5798
  const calls = /* @__PURE__ */ new Map();
5769
5799
  for await (const event of parseServerSentEvents(response)) {
@@ -5771,6 +5801,12 @@ var OpenAIProvider = class {
5771
5801
  const chunk = JSON.parse(event.data);
5772
5802
  if (chunk.usage?.prompt_tokens !== void 0) inputTokens = chunk.usage.prompt_tokens;
5773
5803
  if (chunk.usage?.completion_tokens !== void 0) outputTokens = chunk.usage.completion_tokens;
5804
+ if (chunk.usage?.prompt_tokens_details?.cached_tokens !== void 0) {
5805
+ cachedInputTokens = chunk.usage.prompt_tokens_details.cached_tokens;
5806
+ }
5807
+ if (chunk.usage?.completion_tokens_details?.reasoning_tokens !== void 0) {
5808
+ reasoningTokens = chunk.usage.completion_tokens_details.reasoning_tokens;
5809
+ }
5774
5810
  const choice = chunk.choices?.[0];
5775
5811
  if (choice?.finish_reason) stopReason = choice.finish_reason;
5776
5812
  const delta = choice?.delta;
@@ -5798,7 +5834,9 @@ var OpenAIProvider = class {
5798
5834
  })),
5799
5835
  usage: {
5800
5836
  ...inputTokens !== void 0 ? { inputTokens } : {},
5801
- ...outputTokens !== void 0 ? { outputTokens } : {}
5837
+ ...outputTokens !== void 0 ? { outputTokens } : {},
5838
+ ...cachedInputTokens !== void 0 ? { cachedInputTokens } : {},
5839
+ ...reasoningTokens !== void 0 ? { reasoningTokens } : {}
5802
5840
  },
5803
5841
  ...stopReason ? { stopReason } : {}
5804
5842
  }
@@ -5816,13 +5854,18 @@ function normalizeOpenAIResponse(data) {
5816
5854
  name: call.function?.name ?? "unknown",
5817
5855
  arguments: safeJsonArguments(call.function?.arguments)
5818
5856
  })),
5819
- usage: {
5820
- ...data.usage?.prompt_tokens !== void 0 ? { inputTokens: data.usage.prompt_tokens } : {},
5821
- ...data.usage?.completion_tokens !== void 0 ? { outputTokens: data.usage.completion_tokens } : {}
5822
- },
5857
+ usage: normalizeOpenAIUsage(data.usage),
5823
5858
  ...choice?.finish_reason ? { stopReason: choice.finish_reason } : {}
5824
5859
  };
5825
5860
  }
5861
+ function normalizeOpenAIUsage(usage) {
5862
+ return {
5863
+ ...usage?.prompt_tokens !== void 0 ? { inputTokens: usage.prompt_tokens } : {},
5864
+ ...usage?.completion_tokens !== void 0 ? { outputTokens: usage.completion_tokens } : {},
5865
+ ...usage?.prompt_tokens_details?.cached_tokens !== void 0 ? { cachedInputTokens: usage.prompt_tokens_details.cached_tokens } : {},
5866
+ ...usage?.completion_tokens_details?.reasoning_tokens !== void 0 ? { reasoningTokens: usage.completion_tokens_details.reasoning_tokens } : {}
5867
+ };
5868
+ }
5826
5869
  function toOpenAIMessage(message2) {
5827
5870
  if (message2.role === "tool") {
5828
5871
  return {
@@ -6399,6 +6442,9 @@ var sessionSchema = z5.object({
6399
6442
  outputSource: z5.enum(["actual", "estimated", "mixed", "unknown"]).optional(),
6400
6443
  actualInputTokens: z5.number().nonnegative().optional(),
6401
6444
  actualOutputTokens: z5.number().nonnegative().optional(),
6445
+ actualCachedInputTokens: z5.number().nonnegative().optional(),
6446
+ actualCacheWriteInputTokens: z5.number().nonnegative().optional(),
6447
+ actualReasoningTokens: z5.number().nonnegative().optional(),
6402
6448
  estimatedInputTokens: z5.number().nonnegative().optional(),
6403
6449
  estimatedOutputTokens: z5.number().nonnegative().optional()
6404
6450
  }).strict(),
@@ -6419,7 +6465,10 @@ var sessionSchema = z5.object({
6419
6465
  }).strict(),
6420
6466
  actual: z5.object({
6421
6467
  inputTokens: z5.number().nonnegative().optional(),
6422
- outputTokens: z5.number().nonnegative().optional()
6468
+ outputTokens: z5.number().nonnegative().optional(),
6469
+ cachedInputTokens: z5.number().nonnegative().optional(),
6470
+ cacheWriteInputTokens: z5.number().nonnegative().optional(),
6471
+ reasoningTokens: z5.number().nonnegative().optional()
6423
6472
  }).strict(),
6424
6473
  inputSource: z5.enum(["actual", "estimated"]),
6425
6474
  outputSource: z5.enum(["actual", "estimated"]),
@@ -10830,6 +10879,9 @@ var AgentRunner = class {
10830
10879
  const { inputTokens, outputTokens } = turnUsage;
10831
10880
  const actualInputTokens = validTokenCount(response.usage?.inputTokens);
10832
10881
  const actualOutputTokens = validTokenCount(response.usage?.outputTokens);
10882
+ const actualCachedInputTokens = validTokenCount(response.usage?.cachedInputTokens);
10883
+ const actualCacheWriteInputTokens = validTokenCount(response.usage?.cacheWriteInputTokens);
10884
+ const actualReasoningTokens = validTokenCount(response.usage?.reasoningTokens);
10833
10885
  const receipt = recordTokenLedger(this.session, {
10834
10886
  requestId: userMessage.id,
10835
10887
  turn,
@@ -10840,7 +10892,10 @@ var AgentRunner = class {
10840
10892
  },
10841
10893
  actual: {
10842
10894
  ...actualInputTokens === void 0 ? {} : { inputTokens: actualInputTokens },
10843
- ...actualOutputTokens === void 0 ? {} : { outputTokens: actualOutputTokens }
10895
+ ...actualOutputTokens === void 0 ? {} : { outputTokens: actualOutputTokens },
10896
+ ...actualCachedInputTokens === void 0 ? {} : { cachedInputTokens: actualCachedInputTokens },
10897
+ ...actualCacheWriteInputTokens === void 0 ? {} : { cacheWriteInputTokens: actualCacheWriteInputTokens },
10898
+ ...actualReasoningTokens === void 0 ? {} : { reasoningTokens: actualReasoningTokens }
10844
10899
  },
10845
10900
  inputSource: actualInputTokens === void 0 ? "estimated" : "actual",
10846
10901
  outputSource: actualOutputTokens === void 0 ? "estimated" : "actual",
@@ -10859,7 +10914,10 @@ var AgentRunner = class {
10859
10914
  outputSource: this.session.usage.outputSource ?? "unknown",
10860
10915
  actual: {
10861
10916
  inputTokens: this.session.usage.actualInputTokens ?? 0,
10862
- outputTokens: this.session.usage.actualOutputTokens ?? 0
10917
+ outputTokens: this.session.usage.actualOutputTokens ?? 0,
10918
+ ...this.session.usage.actualCachedInputTokens === void 0 ? {} : { cachedInputTokens: this.session.usage.actualCachedInputTokens },
10919
+ ...this.session.usage.actualCacheWriteInputTokens === void 0 ? {} : { cacheWriteInputTokens: this.session.usage.actualCacheWriteInputTokens },
10920
+ ...this.session.usage.actualReasoningTokens === void 0 ? {} : { reasoningTokens: this.session.usage.actualReasoningTokens }
10863
10921
  },
10864
10922
  estimated: {
10865
10923
  inputTokens: this.session.usage.estimatedInputTokens ?? 0,
@@ -11560,6 +11618,18 @@ function recordTokenUsage(session, providerUsage, estimatedInputTokens, estimate
11560
11618
  } else {
11561
11619
  session.usage.estimatedOutputTokens = (session.usage.estimatedOutputTokens ?? 0) + outputTokens;
11562
11620
  }
11621
+ const cachedInputActual = validTokenCount(providerUsage?.cachedInputTokens);
11622
+ const cacheWriteInputActual = validTokenCount(providerUsage?.cacheWriteInputTokens);
11623
+ const reasoningActual = validTokenCount(providerUsage?.reasoningTokens);
11624
+ if (cachedInputActual !== void 0) {
11625
+ session.usage.actualCachedInputTokens = (session.usage.actualCachedInputTokens ?? 0) + cachedInputActual;
11626
+ }
11627
+ if (cacheWriteInputActual !== void 0) {
11628
+ session.usage.actualCacheWriteInputTokens = (session.usage.actualCacheWriteInputTokens ?? 0) + cacheWriteInputActual;
11629
+ }
11630
+ if (reasoningActual !== void 0) {
11631
+ session.usage.actualReasoningTokens = (session.usage.actualReasoningTokens ?? 0) + reasoningActual;
11632
+ }
11563
11633
  session.usage.inputSource = mergeMeasurementSource(
11564
11634
  priorInputSource,
11565
11635
  inputActual === void 0 ? "estimated" : "actual",