agentfootprint 9.57.0 → 9.59.0

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.
Files changed (228) hide show
  1. package/AGENTS.md +1 -1
  2. package/CLAUDE.md +5 -2
  3. package/ai-instructions/claude-code/SKILL.md +1 -1
  4. package/dist/adapters/llm/AnthropicProvider.js +33 -4
  5. package/dist/adapters/llm/AnthropicProvider.js.map +1 -1
  6. package/dist/adapters/llm/BrowserAnthropicProvider.js +12 -78
  7. package/dist/adapters/llm/BrowserAnthropicProvider.js.map +1 -1
  8. package/dist/adapters/llm/anthropicCacheWire.js +101 -0
  9. package/dist/adapters/llm/anthropicCacheWire.js.map +1 -0
  10. package/dist/cache/cacheRecorder.js +105 -46
  11. package/dist/cache/cacheRecorder.js.map +1 -1
  12. package/dist/cache/index.js +20 -6
  13. package/dist/cache/index.js.map +1 -1
  14. package/dist/cache/portUsage.js +47 -0
  15. package/dist/cache/portUsage.js.map +1 -0
  16. package/dist/cache/strategies/AnthropicCacheStrategy.js +5 -17
  17. package/dist/cache/strategies/AnthropicCacheStrategy.js.map +1 -1
  18. package/dist/cache/strategies/BedrockCacheStrategy.js +74 -58
  19. package/dist/cache/strategies/BedrockCacheStrategy.js.map +1 -1
  20. package/dist/cache/strategies/NoOpCacheStrategy.js +2 -1
  21. package/dist/cache/strategies/NoOpCacheStrategy.js.map +1 -1
  22. package/dist/cache/strategies/OpenAICacheStrategy.js +14 -13
  23. package/dist/cache/strategies/OpenAICacheStrategy.js.map +1 -1
  24. package/dist/core/Agent.js +20 -1
  25. package/dist/core/Agent.js.map +1 -1
  26. package/dist/core/agent/AgentBuilder.js +125 -1
  27. package/dist/core/agent/AgentBuilder.js.map +1 -1
  28. package/dist/core/agent/buildAgentChart.js +42 -0
  29. package/dist/core/agent/buildAgentChart.js.map +1 -1
  30. package/dist/core/agent/buildDynamicAgentChart.js +68 -0
  31. package/dist/core/agent/buildDynamicAgentChart.js.map +1 -1
  32. package/dist/core/agent/stages/toolCalls.js +49 -3
  33. package/dist/core/agent/stages/toolCalls.js.map +1 -1
  34. package/dist/core/agent/window/notice.js +5 -2
  35. package/dist/core/agent/window/notice.js.map +1 -1
  36. package/dist/core/slots/buildToolsSlot.js +27 -1
  37. package/dist/core/slots/buildToolsSlot.js.map +1 -1
  38. package/dist/doors/maps.js +56 -0
  39. package/dist/doors/maps.js.map +1 -0
  40. package/dist/esm/adapters/llm/AnthropicProvider.d.ts +2 -0
  41. package/dist/esm/adapters/llm/AnthropicProvider.js +33 -4
  42. package/dist/esm/adapters/llm/AnthropicProvider.js.map +1 -1
  43. package/dist/esm/adapters/llm/BrowserAnthropicProvider.js +11 -77
  44. package/dist/esm/adapters/llm/BrowserAnthropicProvider.js.map +1 -1
  45. package/dist/esm/adapters/llm/anthropicCacheWire.d.ts +96 -0
  46. package/dist/esm/adapters/llm/anthropicCacheWire.js +96 -0
  47. package/dist/esm/adapters/llm/anthropicCacheWire.js.map +1 -0
  48. package/dist/esm/cache/cacheRecorder.d.ts +73 -36
  49. package/dist/esm/cache/cacheRecorder.js +105 -46
  50. package/dist/esm/cache/cacheRecorder.js.map +1 -1
  51. package/dist/esm/cache/index.d.ts +12 -7
  52. package/dist/esm/cache/index.js +13 -5
  53. package/dist/esm/cache/index.js.map +1 -1
  54. package/dist/esm/cache/portUsage.d.ts +30 -0
  55. package/dist/esm/cache/portUsage.js +43 -0
  56. package/dist/esm/cache/portUsage.js.map +1 -0
  57. package/dist/esm/cache/strategies/AnthropicCacheStrategy.d.ts +6 -3
  58. package/dist/esm/cache/strategies/AnthropicCacheStrategy.js +5 -17
  59. package/dist/esm/cache/strategies/AnthropicCacheStrategy.js.map +1 -1
  60. package/dist/esm/cache/strategies/BedrockCacheStrategy.d.ts +48 -19
  61. package/dist/esm/cache/strategies/BedrockCacheStrategy.js +74 -58
  62. package/dist/esm/cache/strategies/BedrockCacheStrategy.js.map +1 -1
  63. package/dist/esm/cache/strategies/NoOpCacheStrategy.d.ts +3 -2
  64. package/dist/esm/cache/strategies/NoOpCacheStrategy.js +2 -1
  65. package/dist/esm/cache/strategies/NoOpCacheStrategy.js.map +1 -1
  66. package/dist/esm/cache/strategies/OpenAICacheStrategy.d.ts +13 -2
  67. package/dist/esm/cache/strategies/OpenAICacheStrategy.js +14 -13
  68. package/dist/esm/cache/strategies/OpenAICacheStrategy.js.map +1 -1
  69. package/dist/esm/cache/types.d.ts +35 -7
  70. package/dist/esm/core/Agent.d.ts +3 -1
  71. package/dist/esm/core/Agent.js +20 -1
  72. package/dist/esm/core/Agent.js.map +1 -1
  73. package/dist/esm/core/agent/AgentBuilder.d.ts +45 -0
  74. package/dist/esm/core/agent/AgentBuilder.js +125 -1
  75. package/dist/esm/core/agent/AgentBuilder.js.map +1 -1
  76. package/dist/esm/core/agent/buildAgentChart.d.ts +8 -0
  77. package/dist/esm/core/agent/buildAgentChart.js +42 -0
  78. package/dist/esm/core/agent/buildAgentChart.js.map +1 -1
  79. package/dist/esm/core/agent/buildDynamicAgentChart.js +68 -0
  80. package/dist/esm/core/agent/buildDynamicAgentChart.js.map +1 -1
  81. package/dist/esm/core/agent/stages/toolCalls.d.ts +7 -0
  82. package/dist/esm/core/agent/stages/toolCalls.js +47 -1
  83. package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
  84. package/dist/esm/core/agent/types.d.ts +16 -0
  85. package/dist/esm/core/agent/window/notice.d.ts +5 -2
  86. package/dist/esm/core/agent/window/notice.js +5 -2
  87. package/dist/esm/core/agent/window/notice.js.map +1 -1
  88. package/dist/esm/core/slots/buildToolsSlot.js +27 -1
  89. package/dist/esm/core/slots/buildToolsSlot.js.map +1 -1
  90. package/dist/esm/doors/maps.d.ts +41 -0
  91. package/dist/esm/doors/maps.js +41 -0
  92. package/dist/esm/doors/maps.js.map +1 -0
  93. package/dist/esm/events/dispatcher.d.ts +1 -1
  94. package/dist/esm/events/dispatcher.js.map +1 -1
  95. package/dist/esm/events/payloads.d.ts +52 -2
  96. package/dist/esm/events/registry.d.ts +7 -1
  97. package/dist/esm/events/registry.js +6 -0
  98. package/dist/esm/events/registry.js.map +1 -1
  99. package/dist/esm/lib/claim/claim.d.ts +64 -0
  100. package/dist/esm/lib/claim/claim.js +66 -0
  101. package/dist/esm/lib/claim/claim.js.map +1 -0
  102. package/dist/esm/lib/injection-engine/buildInjectionEngineSubflow.d.ts +10 -0
  103. package/dist/esm/lib/injection-engine/buildInjectionEngineSubflow.js +94 -3
  104. package/dist/esm/lib/injection-engine/buildInjectionEngineSubflow.js.map +1 -1
  105. package/dist/esm/lib/injection-engine/evaluator.d.ts +9 -0
  106. package/dist/esm/lib/injection-engine/evaluator.js +29 -0
  107. package/dist/esm/lib/injection-engine/evaluator.js.map +1 -1
  108. package/dist/esm/lib/injection-engine/skillEntryEvidence.d.ts +59 -0
  109. package/dist/esm/lib/injection-engine/skillEntryEvidence.js +86 -0
  110. package/dist/esm/lib/injection-engine/skillEntryEvidence.js.map +1 -0
  111. package/dist/esm/lib/injection-engine/skillGraph.js +15 -0
  112. package/dist/esm/lib/injection-engine/skillGraph.js.map +1 -1
  113. package/dist/esm/lib/injection-engine/skillGraphCheckup.d.ts +1 -1
  114. package/dist/esm/lib/injection-engine/skillGraphCheckup.js.map +1 -1
  115. package/dist/esm/lib/injection-engine/startRuleClaim.d.ts +6 -4
  116. package/dist/esm/lib/injection-engine/startRuleClaim.js.map +1 -1
  117. package/dist/esm/lib/injection-engine/types.d.ts +14 -1
  118. package/dist/esm/lib/injection-engine/types.js.map +1 -1
  119. package/dist/esm/maps/engagement/evidence.d.ts +111 -0
  120. package/dist/esm/maps/engagement/evidence.js +123 -0
  121. package/dist/esm/maps/engagement/evidence.js.map +1 -0
  122. package/dist/esm/maps/engagement/lease.d.ts +73 -0
  123. package/dist/esm/maps/engagement/lease.js +272 -0
  124. package/dist/esm/maps/engagement/lease.js.map +1 -0
  125. package/dist/esm/maps/engagement/parkCard.d.ts +48 -0
  126. package/dist/esm/maps/engagement/parkCard.js +88 -0
  127. package/dist/esm/maps/engagement/parkCard.js.map +1 -0
  128. package/dist/esm/maps/engagement/types.d.ts +238 -0
  129. package/dist/esm/maps/engagement/types.js +35 -0
  130. package/dist/esm/maps/engagement/types.js.map +1 -0
  131. package/dist/esm/recorders/core/MapRecorder.d.ts +20 -0
  132. package/dist/esm/recorders/core/MapRecorder.js +25 -0
  133. package/dist/esm/recorders/core/MapRecorder.js.map +1 -0
  134. package/dist/events/dispatcher.js.map +1 -1
  135. package/dist/events/registry.js +6 -0
  136. package/dist/events/registry.js.map +1 -1
  137. package/dist/lib/claim/claim.js +75 -0
  138. package/dist/lib/claim/claim.js.map +1 -0
  139. package/dist/lib/injection-engine/buildInjectionEngineSubflow.js +93 -2
  140. package/dist/lib/injection-engine/buildInjectionEngineSubflow.js.map +1 -1
  141. package/dist/lib/injection-engine/evaluator.js +29 -0
  142. package/dist/lib/injection-engine/evaluator.js.map +1 -1
  143. package/dist/lib/injection-engine/skillEntryEvidence.js +90 -0
  144. package/dist/lib/injection-engine/skillEntryEvidence.js.map +1 -0
  145. package/dist/lib/injection-engine/skillGraph.js +15 -0
  146. package/dist/lib/injection-engine/skillGraph.js.map +1 -1
  147. package/dist/lib/injection-engine/skillGraphCheckup.js.map +1 -1
  148. package/dist/lib/injection-engine/startRuleClaim.js.map +1 -1
  149. package/dist/lib/injection-engine/types.js.map +1 -1
  150. package/dist/maps/engagement/evidence.js +132 -0
  151. package/dist/maps/engagement/evidence.js.map +1 -0
  152. package/dist/maps/engagement/lease.js +276 -0
  153. package/dist/maps/engagement/lease.js.map +1 -0
  154. package/dist/maps/engagement/parkCard.js +92 -0
  155. package/dist/maps/engagement/parkCard.js.map +1 -0
  156. package/dist/maps/engagement/types.js +39 -0
  157. package/dist/maps/engagement/types.js.map +1 -0
  158. package/dist/recorders/core/MapRecorder.js +29 -0
  159. package/dist/recorders/core/MapRecorder.js.map +1 -0
  160. package/dist/types/adapters/llm/AnthropicProvider.d.ts +2 -0
  161. package/dist/types/adapters/llm/AnthropicProvider.d.ts.map +1 -1
  162. package/dist/types/adapters/llm/BrowserAnthropicProvider.d.ts.map +1 -1
  163. package/dist/types/adapters/llm/anthropicCacheWire.d.ts +97 -0
  164. package/dist/types/adapters/llm/anthropicCacheWire.d.ts.map +1 -0
  165. package/dist/types/cache/cacheRecorder.d.ts +73 -36
  166. package/dist/types/cache/cacheRecorder.d.ts.map +1 -1
  167. package/dist/types/cache/index.d.ts +12 -7
  168. package/dist/types/cache/index.d.ts.map +1 -1
  169. package/dist/types/cache/portUsage.d.ts +31 -0
  170. package/dist/types/cache/portUsage.d.ts.map +1 -0
  171. package/dist/types/cache/strategies/AnthropicCacheStrategy.d.ts +6 -3
  172. package/dist/types/cache/strategies/AnthropicCacheStrategy.d.ts.map +1 -1
  173. package/dist/types/cache/strategies/BedrockCacheStrategy.d.ts +48 -19
  174. package/dist/types/cache/strategies/BedrockCacheStrategy.d.ts.map +1 -1
  175. package/dist/types/cache/strategies/NoOpCacheStrategy.d.ts +3 -2
  176. package/dist/types/cache/strategies/NoOpCacheStrategy.d.ts.map +1 -1
  177. package/dist/types/cache/strategies/OpenAICacheStrategy.d.ts +13 -2
  178. package/dist/types/cache/strategies/OpenAICacheStrategy.d.ts.map +1 -1
  179. package/dist/types/cache/types.d.ts +35 -7
  180. package/dist/types/cache/types.d.ts.map +1 -1
  181. package/dist/types/core/Agent.d.ts +3 -1
  182. package/dist/types/core/Agent.d.ts.map +1 -1
  183. package/dist/types/core/agent/AgentBuilder.d.ts +45 -0
  184. package/dist/types/core/agent/AgentBuilder.d.ts.map +1 -1
  185. package/dist/types/core/agent/buildAgentChart.d.ts +8 -0
  186. package/dist/types/core/agent/buildAgentChart.d.ts.map +1 -1
  187. package/dist/types/core/agent/buildDynamicAgentChart.d.ts.map +1 -1
  188. package/dist/types/core/agent/stages/toolCalls.d.ts +7 -0
  189. package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
  190. package/dist/types/core/agent/types.d.ts +16 -0
  191. package/dist/types/core/agent/types.d.ts.map +1 -1
  192. package/dist/types/core/agent/window/notice.d.ts +5 -2
  193. package/dist/types/core/agent/window/notice.d.ts.map +1 -1
  194. package/dist/types/core/slots/buildToolsSlot.d.ts.map +1 -1
  195. package/dist/types/doors/maps.d.ts +42 -0
  196. package/dist/types/doors/maps.d.ts.map +1 -0
  197. package/dist/types/events/dispatcher.d.ts +1 -1
  198. package/dist/types/events/dispatcher.d.ts.map +1 -1
  199. package/dist/types/events/payloads.d.ts +52 -2
  200. package/dist/types/events/payloads.d.ts.map +1 -1
  201. package/dist/types/events/registry.d.ts +7 -1
  202. package/dist/types/events/registry.d.ts.map +1 -1
  203. package/dist/types/lib/claim/claim.d.ts +65 -0
  204. package/dist/types/lib/claim/claim.d.ts.map +1 -0
  205. package/dist/types/lib/injection-engine/buildInjectionEngineSubflow.d.ts +10 -0
  206. package/dist/types/lib/injection-engine/buildInjectionEngineSubflow.d.ts.map +1 -1
  207. package/dist/types/lib/injection-engine/evaluator.d.ts +9 -0
  208. package/dist/types/lib/injection-engine/evaluator.d.ts.map +1 -1
  209. package/dist/types/lib/injection-engine/skillEntryEvidence.d.ts +60 -0
  210. package/dist/types/lib/injection-engine/skillEntryEvidence.d.ts.map +1 -0
  211. package/dist/types/lib/injection-engine/skillGraph.d.ts.map +1 -1
  212. package/dist/types/lib/injection-engine/skillGraphCheckup.d.ts +1 -1
  213. package/dist/types/lib/injection-engine/skillGraphCheckup.d.ts.map +1 -1
  214. package/dist/types/lib/injection-engine/startRuleClaim.d.ts +6 -4
  215. package/dist/types/lib/injection-engine/startRuleClaim.d.ts.map +1 -1
  216. package/dist/types/lib/injection-engine/types.d.ts +14 -1
  217. package/dist/types/lib/injection-engine/types.d.ts.map +1 -1
  218. package/dist/types/maps/engagement/evidence.d.ts +112 -0
  219. package/dist/types/maps/engagement/evidence.d.ts.map +1 -0
  220. package/dist/types/maps/engagement/lease.d.ts +74 -0
  221. package/dist/types/maps/engagement/lease.d.ts.map +1 -0
  222. package/dist/types/maps/engagement/parkCard.d.ts +49 -0
  223. package/dist/types/maps/engagement/parkCard.d.ts.map +1 -0
  224. package/dist/types/maps/engagement/types.d.ts +239 -0
  225. package/dist/types/maps/engagement/types.d.ts.map +1 -0
  226. package/dist/types/recorders/core/MapRecorder.d.ts +21 -0
  227. package/dist/types/recorders/core/MapRecorder.d.ts.map +1 -0
  228. package/package.json +16 -2
@@ -1,58 +1,96 @@
1
1
  /**
2
- * cacheRecorder() — observability for the v2.6 cache layer.
2
+ * cacheRecorder() — the cache layer's meter.
3
3
  *
4
4
  * Subscribes to:
5
- * - `FlowRecorder.onDecision` — captures CacheGate routing decisions
6
- * (apply-markers / no-markers + the rule that fired + evidence
7
- * from `decide()`). Read directly from `event.evidence.rules[matched]`
8
- * since footprintjs already auto-captures predicate `inputs[]`.
9
- * - `agentfootprint.stream.llm_end` events — read provider's `usage`
10
- * and call the agent's CacheStrategy.extractMetrics() to normalize
11
- * into CacheMetrics (cacheReadTokens / cacheWriteTokens / fresh).
5
+ * - `FlowRecorder.onDecision` — CacheGate routing decisions
6
+ * (apply-markers / no-markers + the rule that fired + evidence from
7
+ * `decide()`).
8
+ * - `agentfootprint.stream.llm_end` — reads that event's `usage` (the PORT
9
+ * shape: `{ input, output, cacheRead?, cacheWrite? }`) and asks the
10
+ * agent's `CacheStrategy.extractMetrics` what is known about it.
12
11
  *
13
- * Produces:
14
- * - per-iteration `agentfootprint.cache.applied` events (markers
15
- * applied this iter or empty if skipped) — for Lens trace
16
- * - per-iteration `agentfootprint.cache.metrics` events (hit/write
17
- * token counts + estimated dollars via PricingTable) — for
18
- * dashboards
19
- * - a turn-end summary printable via `recorder.report()` —
20
- * numeric tally plus dollars saved
12
+ * Produces: a per-turn report via `recorder.report()` — token tallies, hit
13
+ * rate and dollar estimates, each carried as a `Claim` so an UNMEASURED turn
14
+ * can never render as a zero one.
21
15
  *
22
- * v2.6 LIMITATION: doesn't yet write `scope.recentHitRate` back into
23
- * agent state. CacheGate's hit-rate-floor rule won't fire automatically;
24
- * consumers can manually wire feedback via `Agent.create(...).attach(rec)`.
25
- * Full feedback loop deferred to v2.7 (needs an agent-side accessor
26
- * convention since recorders don't normally write to scope).
16
+ * ── Why every number here is a Claim (9.59.0) ─────────────────────────
17
+ * The shipped 9.58.0 meter reported `hitRate: 0` for a 20-call turn that hit
18
+ * cache on every call. Two faults, and the second is why the first went
19
+ * unnoticed for so long: (1) the strategies parsed RAW WIRE field names off a
20
+ * value that carries the PORT shape, so every field read `undefined`; (2) the
21
+ * report typed its aggregates as bare `number`, so "nobody measured" and
22
+ * "measured, and it was zero" rendered identically. Fixing (1) alone would
23
+ * have left a meter that still cannot say "unmeasured" — hence `Claim<T>`,
24
+ * plus `measuredIterations` / `unmeasuredIterations` so a rate computed from
25
+ * 3 of 20 calls states its own denominator.
26
+ *
27
+ * Read the number through `isKnown(report.hitRate)` (or `describeClaim` for a
28
+ * one-line render). There is deliberately no door that hands you a bare
29
+ * number without your having branched.
30
+ *
31
+ * ── What this recorder does NOT do ────────────────────────────────────
32
+ * It emits no events. (Earlier prose here promised per-iteration
33
+ * `agentfootprint.cache.applied` / `agentfootprint.cache.metrics` events;
34
+ * neither name has ever existed in the event registry and no `typedEmit` has
35
+ * ever been in this file. The prose was the bug.)
36
+ *
37
+ * It does not write `scope.recentHitRate` back into agent state either, so
38
+ * CacheGate's hit-rate-floor rule never fires on its own — the loop is
39
+ * severed at both ends (the key is seeded `undefined` and written by
40
+ * nothing). Recorders do not write to chart scope, so closing that loop needs
41
+ * an agent-side accessor convention; it is separable work from measuring the
42
+ * number, which is what this file now does.
27
43
  */
28
44
  import type { CombinedRecorder } from 'footprintjs';
29
45
  import type { CacheMetrics, CacheStrategy } from './types.js';
30
46
  import type { PricingTable } from '../adapters/types.js';
31
- interface PerIterEntry {
47
+ import { type Claim } from '../lib/claim/claim.js';
48
+ /** One LLM call's row on the record. */
49
+ export interface PerIterEntry {
32
50
  readonly iteration: number;
33
51
  readonly branch: 'apply-markers' | 'no-markers';
34
52
  readonly rule?: string;
35
- readonly metrics?: CacheMetrics;
36
- readonly dollarsSpent: number;
37
- readonly dollarsSavedVsNoCache: number;
53
+ /**
54
+ * What the strategy could say about this call's cache traffic. `known` =
55
+ * the provider reported counts; `unknown` = nothing was measured;
56
+ * `not-applicable` = this adapter cannot report cache usage at all.
57
+ */
58
+ readonly metrics: Claim<CacheMetrics>;
59
+ /** Dollar estimates, `unknown` on any call whose metrics were not known. */
60
+ readonly dollarsSpent: Claim<number>;
61
+ readonly dollarsSavedVsNoCache: Claim<number>;
38
62
  }
39
- interface CacheReportSummary {
63
+ /**
64
+ * The turn's tally. Every quantity derived from provider usage is a
65
+ * {@link Claim}: unmeasured is a first-class answer, never a zero.
66
+ */
67
+ export interface CacheReportSummary {
68
+ /** LLM calls seen. Always known — the recorder counted them itself. */
40
69
  readonly totalIterations: number;
41
70
  readonly applyMarkersIterations: number;
42
71
  readonly noMarkersIterations: number;
43
- readonly cacheReadTokensTotal: number;
44
- readonly cacheWriteTokensTotal: number;
45
- readonly freshInputTokensTotal: number;
46
- readonly hitRate: number;
47
- readonly estimatedDollarsSpent: number;
48
- readonly estimatedDollarsSavedVsNoCache: number;
72
+ /**
73
+ * Calls whose cache traffic the provider actually reported. The DENOMINATOR
74
+ * every claim below is computed over — a rate from 3 of 20 calls is not the
75
+ * turn's rate, and this is how a reader can tell.
76
+ */
77
+ readonly measuredIterations: number;
78
+ /** Calls that reported nothing (no usage, no cache fields, or an adapter that cannot). */
79
+ readonly unmeasuredIterations: number;
80
+ readonly cacheReadTokensTotal: Claim<number>;
81
+ readonly cacheWriteTokensTotal: Claim<number>;
82
+ readonly freshInputTokensTotal: Claim<number>;
83
+ /** cacheRead / (cacheRead + cacheWrite + fresh), over MEASURED calls only. */
84
+ readonly hitRate: Claim<number>;
85
+ readonly estimatedDollarsSpent: Claim<number>;
86
+ readonly estimatedDollarsSavedVsNoCache: Claim<number>;
49
87
  readonly perIter: readonly PerIterEntry[];
50
88
  }
51
89
  export interface CacheRecorderOptions {
52
90
  /**
53
- * The agent's CacheStrategy. Required for `extractMetrics` —
54
- * normalizes provider-specific `usage` shapes into CacheMetrics.
55
- * If not provided, recorder logs the raw usage and skips dollar math.
91
+ * The agent's CacheStrategy — the thing that reads the port usage. Without
92
+ * one every row's metrics are `unknown` (with that as the stated reason),
93
+ * which is the honest answer: nothing read the usage.
56
94
  */
57
95
  readonly strategy?: CacheStrategy;
58
96
  /**
@@ -82,4 +120,3 @@ export interface CacheRecorderHandle extends CombinedRecorder {
82
120
  reset(): void;
83
121
  }
84
122
  export declare function cacheRecorder(options?: CacheRecorderOptions): CacheRecorderHandle;
85
- export {};
@@ -1,32 +1,55 @@
1
1
  /**
2
- * cacheRecorder() — observability for the v2.6 cache layer.
2
+ * cacheRecorder() — the cache layer's meter.
3
3
  *
4
4
  * Subscribes to:
5
- * - `FlowRecorder.onDecision` — captures CacheGate routing decisions
6
- * (apply-markers / no-markers + the rule that fired + evidence
7
- * from `decide()`). Read directly from `event.evidence.rules[matched]`
8
- * since footprintjs already auto-captures predicate `inputs[]`.
9
- * - `agentfootprint.stream.llm_end` events — read provider's `usage`
10
- * and call the agent's CacheStrategy.extractMetrics() to normalize
11
- * into CacheMetrics (cacheReadTokens / cacheWriteTokens / fresh).
5
+ * - `FlowRecorder.onDecision` — CacheGate routing decisions
6
+ * (apply-markers / no-markers + the rule that fired + evidence from
7
+ * `decide()`).
8
+ * - `agentfootprint.stream.llm_end` — reads that event's `usage` (the PORT
9
+ * shape: `{ input, output, cacheRead?, cacheWrite? }`) and asks the
10
+ * agent's `CacheStrategy.extractMetrics` what is known about it.
12
11
  *
13
- * Produces:
14
- * - per-iteration `agentfootprint.cache.applied` events (markers
15
- * applied this iter or empty if skipped) — for Lens trace
16
- * - per-iteration `agentfootprint.cache.metrics` events (hit/write
17
- * token counts + estimated dollars via PricingTable) — for
18
- * dashboards
19
- * - a turn-end summary printable via `recorder.report()` —
20
- * numeric tally plus dollars saved
12
+ * Produces: a per-turn report via `recorder.report()` — token tallies, hit
13
+ * rate and dollar estimates, each carried as a `Claim` so an UNMEASURED turn
14
+ * can never render as a zero one.
21
15
  *
22
- * v2.6 LIMITATION: doesn't yet write `scope.recentHitRate` back into
23
- * agent state. CacheGate's hit-rate-floor rule won't fire automatically;
24
- * consumers can manually wire feedback via `Agent.create(...).attach(rec)`.
25
- * Full feedback loop deferred to v2.7 (needs an agent-side accessor
26
- * convention since recorders don't normally write to scope).
16
+ * ── Why every number here is a Claim (9.59.0) ─────────────────────────
17
+ * The shipped 9.58.0 meter reported `hitRate: 0` for a 20-call turn that hit
18
+ * cache on every call. Two faults, and the second is why the first went
19
+ * unnoticed for so long: (1) the strategies parsed RAW WIRE field names off a
20
+ * value that carries the PORT shape, so every field read `undefined`; (2) the
21
+ * report typed its aggregates as bare `number`, so "nobody measured" and
22
+ * "measured, and it was zero" rendered identically. Fixing (1) alone would
23
+ * have left a meter that still cannot say "unmeasured" — hence `Claim<T>`,
24
+ * plus `measuredIterations` / `unmeasuredIterations` so a rate computed from
25
+ * 3 of 20 calls states its own denominator.
26
+ *
27
+ * Read the number through `isKnown(report.hitRate)` (or `describeClaim` for a
28
+ * one-line render). There is deliberately no door that hands you a bare
29
+ * number without your having branched.
30
+ *
31
+ * ── What this recorder does NOT do ────────────────────────────────────
32
+ * It emits no events. (Earlier prose here promised per-iteration
33
+ * `agentfootprint.cache.applied` / `agentfootprint.cache.metrics` events;
34
+ * neither name has ever existed in the event registry and no `typedEmit` has
35
+ * ever been in this file. The prose was the bug.)
36
+ *
37
+ * It does not write `scope.recentHitRate` back into agent state either, so
38
+ * CacheGate's hit-rate-floor rule never fires on its own — the loop is
39
+ * severed at both ends (the key is seeded `undefined` and written by
40
+ * nothing). Recorders do not write to chart scope, so closing that loop needs
41
+ * an agent-side accessor convention; it is separable work from measuring the
42
+ * number, which is what this file now does.
27
43
  */
28
44
  import { splitStageId } from 'footprintjs/trace';
29
45
  import { STAGE_IDS } from '../conventions.js';
46
+ import { known, unknown, isKnown } from '../lib/claim/claim.js';
47
+ /** The metrics of a row already filtered by `isKnown`. Narrowing helper only. */
48
+ function valueOfMetrics(entry) {
49
+ return isKnown(entry.metrics)
50
+ ? entry.metrics.value
51
+ : { cacheReadTokens: 0, cacheWriteTokens: 0, freshInputTokens: 0 };
52
+ }
30
53
  export function cacheRecorder(options = {}) {
31
54
  const perIter = [];
32
55
  let lastDecision;
@@ -61,8 +84,11 @@ export function cacheRecorder(options = {}) {
61
84
  if (event.type !== 'agentfootprint.stream.llm_end')
62
85
  return;
63
86
  iterationCounter++;
87
+ // The PORT shape, which is the only shape this event has ever carried.
64
88
  const usage = event.payload.usage;
65
- const metrics = options.strategy?.extractMetrics(usage);
89
+ const metrics = options.strategy === undefined
90
+ ? unknown('no CacheStrategy was given to cacheRecorder(), so nothing read the usage')
91
+ : options.strategy.extractMetrics(usage);
66
92
  const branch = lastDecision?.branch ?? 'apply-markers';
67
93
  // Compute dollar math:
68
94
  // spent = freshInput * inputPrice
@@ -70,21 +96,34 @@ export function cacheRecorder(options = {}) {
70
96
  // + cacheWrite * cacheWritePrice
71
97
  // no-cache cost = (freshInput + cacheRead + cacheWrite) * inputPrice
72
98
  // saved = no-cache cost - spent
73
- let dollarsSpent = 0;
74
- let savedVsNoCache = 0;
75
- if (metrics) {
76
- dollarsSpent =
77
- dollars(metrics.freshInputTokens, 'input') +
78
- dollars(metrics.cacheReadTokens, 'cacheRead') +
79
- dollars(metrics.cacheWriteTokens, 'cacheWrite');
80
- const noCacheCost = dollars(metrics.freshInputTokens + metrics.cacheReadTokens + metrics.cacheWriteTokens, 'input');
81
- savedVsNoCache = noCacheCost - dollarsSpent;
99
+ let dollarsSpent;
100
+ let savedVsNoCache;
101
+ if (isKnown(metrics)) {
102
+ const m = metrics.value;
103
+ const spent = dollars(m.freshInputTokens, 'input') +
104
+ dollars(m.cacheReadTokens, 'cacheRead') +
105
+ dollars(m.cacheWriteTokens, 'cacheWrite');
106
+ const noCacheCost = dollars(m.freshInputTokens + m.cacheReadTokens + m.cacheWriteTokens, 'input');
107
+ const ev = options.pricing === undefined
108
+ ? 'measured tokens, but no PricingTable was given — every price is 0'
109
+ : 'measured tokens priced with the given PricingTable';
110
+ dollarsSpent = known(spent, ev);
111
+ savedVsNoCache = known(noCacheCost - spent, ev);
112
+ }
113
+ else {
114
+ // No tokens measured ⇒ no dollars. Carrying the metrics claim's own
115
+ // reason forward is what keeps a $0 estimate from reading as "free".
116
+ const why = metrics.kind === 'unknown'
117
+ ? metrics.reason
118
+ : `no cache tokens are measurable here — ${metrics.evidence}`;
119
+ dollarsSpent = unknown(why);
120
+ savedVsNoCache = unknown(why);
82
121
  }
83
122
  const entry = {
84
123
  iteration: iterationCounter,
85
124
  branch,
86
125
  ...(lastDecision?.rule !== undefined && { rule: lastDecision.rule }),
87
- ...(metrics !== undefined && { metrics }),
126
+ metrics,
88
127
  dollarsSpent,
89
128
  dollarsSavedVsNoCache: savedVsNoCache,
90
129
  };
@@ -94,23 +133,43 @@ export function cacheRecorder(options = {}) {
94
133
  report() {
95
134
  const apply = perIter.filter((p) => p.branch === 'apply-markers').length;
96
135
  const skip = perIter.filter((p) => p.branch === 'no-markers').length;
97
- const cacheRead = perIter.reduce((s, p) => s + (p.metrics?.cacheReadTokens ?? 0), 0);
98
- const cacheWrite = perIter.reduce((s, p) => s + (p.metrics?.cacheWriteTokens ?? 0), 0);
99
- const fresh = perIter.reduce((s, p) => s + (p.metrics?.freshInputTokens ?? 0), 0);
100
- const totalRequest = cacheRead + cacheWrite + fresh;
101
- const hitRate = totalRequest > 0 ? cacheRead / totalRequest : 0;
102
- const dollarsSpent = perIter.reduce((s, p) => s + p.dollarsSpent, 0);
103
- const dollarsSaved = perIter.reduce((s, p) => s + p.dollarsSavedVsNoCache, 0);
136
+ const measured = perIter.filter((p) => isKnown(p.metrics));
137
+ const n = measured.length;
138
+ const total = perIter.length;
139
+ // ONE reason, stated once, reused by every claim below — so a reader
140
+ // who prints any single field learns why the whole report is empty.
141
+ const why = total === 0
142
+ ? 'no LLM call was observed, so there is nothing to measure'
143
+ : `none of the ${total} observed call(s) reported cache usage — ` +
144
+ (perIter[0]?.metrics.kind === 'not-applicable'
145
+ ? `this provider cannot report it (${perIter[0].metrics.evidence})`
146
+ : 'the provider reported no cache fields');
147
+ const evidence = `summed over the ${n} of ${total} call(s) whose usage was measured`;
148
+ const claimOf = (value) => n === 0 ? unknown(why) : known(value, evidence);
149
+ const cacheRead = measured.reduce((s2, p) => s2 + valueOfMetrics(p).cacheReadTokens, 0);
150
+ const cacheWrite = measured.reduce((s2, p) => s2 + valueOfMetrics(p).cacheWriteTokens, 0);
151
+ const fresh = measured.reduce((s2, p) => s2 + valueOfMetrics(p).freshInputTokens, 0);
152
+ const requestTotal = cacheRead + cacheWrite + fresh;
153
+ const dollarsSpent = measured.reduce((s2, p) => s2 + (isKnown(p.dollarsSpent) ? p.dollarsSpent.value : 0), 0);
154
+ const dollarsSaved = measured.reduce((s2, p) => s2 + (isKnown(p.dollarsSavedVsNoCache) ? p.dollarsSavedVsNoCache.value : 0), 0);
104
155
  return Object.freeze({
105
- totalIterations: perIter.length,
156
+ totalIterations: total,
106
157
  applyMarkersIterations: apply,
107
158
  noMarkersIterations: skip,
108
- cacheReadTokensTotal: cacheRead,
109
- cacheWriteTokensTotal: cacheWrite,
110
- freshInputTokensTotal: fresh,
111
- hitRate,
112
- estimatedDollarsSpent: dollarsSpent,
113
- estimatedDollarsSavedVsNoCache: dollarsSaved,
159
+ measuredIterations: n,
160
+ unmeasuredIterations: total - n,
161
+ cacheReadTokensTotal: claimOf(cacheRead),
162
+ cacheWriteTokensTotal: claimOf(cacheWrite),
163
+ freshInputTokensTotal: claimOf(fresh),
164
+ // A measured turn with zero prompt tokens is not a thing; guard it
165
+ // anyway rather than divide by zero and hand back NaN as a rate.
166
+ hitRate: n === 0
167
+ ? unknown(why)
168
+ : requestTotal === 0
169
+ ? unknown(`the ${n} measured call(s) reported 0 prompt tokens in total, so a hit rate has no denominator`)
170
+ : known(cacheRead / requestTotal, evidence),
171
+ estimatedDollarsSpent: claimOf(dollarsSpent),
172
+ estimatedDollarsSavedVsNoCache: claimOf(dollarsSaved),
114
173
  perIter: Object.freeze([...perIter]),
115
174
  });
116
175
  },
@@ -1 +1 @@
1
- {"version":3,"file":"cacheRecorder.js","sourceRoot":"","sources":["../../../src/cache/cacheRecorder.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cacheRecorder.js","sourceRoot":"","sources":["../../../src/cache/cacheRecorder.ts"],"names":[],"mappings":"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"}
@@ -9,12 +9,16 @@
9
9
  * Strategies registered as of v2.6:
10
10
  * - NoOp (wildcard '*' fallback) — always available, registered by
11
11
  * the registry module itself
12
- * - AnthropicCacheStrategy ('anthropic', 'browser-anthropic')
12
+ * - AnthropicCacheStrategy ('anthropic', 'browser-anthropic') — the one
13
+ * end-to-end strategy: the adapter sends markers AND reads cache usage
14
+ * - OpenAICacheStrategy ('openai', 'browser-openai') — pass-through
15
+ * (OpenAI auto-caches); reports `not-applicable` metrics until the
16
+ * adapter lifts `prompt_tokens_details.cached_tokens` onto the port
17
+ * - BedrockCacheStrategy ('bedrock') — `enabled: false`; the Bedrock
18
+ * adapter implements neither half of the cache contract
13
19
  *
14
- * Future strategies (Phase 8+):
15
- * - OpenAICacheStrategy
16
- * - BedrockCacheStrategy
17
- * - GeminiCacheStrategy (v2.7+, async handle-based)
20
+ * Future strategies:
21
+ * - GeminiCacheStrategy (async handle-based)
18
22
  *
19
23
  * Public types (re-exported for consumers):
20
24
  * - CachePolicy, CacheMarker, CacheStrategy, CacheCapabilities,
@@ -23,11 +27,12 @@
23
27
  import './strategies/AnthropicCacheStrategy.js';
24
28
  import './strategies/OpenAICacheStrategy.js';
25
29
  import './strategies/BedrockCacheStrategy.js';
26
- export type { CachePolicy, CachePolicyContext, CacheMarker, CacheStrategy, CacheStrategyContext, CacheCapabilities, CacheMetrics, } from './types.js';
30
+ export type { CachePolicy, CachePolicyContext, CacheMarker, CacheStrategy, CacheStrategyContext, CacheCapabilities, CacheMetrics, CacheUsage, } from './types.js';
31
+ export { known, unknown, notApplicable, isKnown, valueOr, describeClaim, type Claim, } from '../lib/claim/claim.js';
27
32
  export { getDefaultCacheStrategy, registerCacheStrategy, listRegisteredStrategies, } from './strategyRegistry.js';
28
33
  export { NoOpCacheStrategy } from './strategies/NoOpCacheStrategy.js';
29
34
  export { AnthropicCacheStrategy } from './strategies/AnthropicCacheStrategy.js';
30
35
  export { OpenAICacheStrategy } from './strategies/OpenAICacheStrategy.js';
31
36
  export { BedrockCacheStrategy } from './strategies/BedrockCacheStrategy.js';
32
37
  export { cacheRecorder } from './cacheRecorder.js';
33
- export type { CacheRecorderOptions, CacheRecorderHandle } from './cacheRecorder.js';
38
+ export type { CacheRecorderOptions, CacheRecorderHandle, CacheReportSummary, PerIterEntry, } from './cacheRecorder.js';
@@ -9,12 +9,16 @@
9
9
  * Strategies registered as of v2.6:
10
10
  * - NoOp (wildcard '*' fallback) — always available, registered by
11
11
  * the registry module itself
12
- * - AnthropicCacheStrategy ('anthropic', 'browser-anthropic')
12
+ * - AnthropicCacheStrategy ('anthropic', 'browser-anthropic') — the one
13
+ * end-to-end strategy: the adapter sends markers AND reads cache usage
14
+ * - OpenAICacheStrategy ('openai', 'browser-openai') — pass-through
15
+ * (OpenAI auto-caches); reports `not-applicable` metrics until the
16
+ * adapter lifts `prompt_tokens_details.cached_tokens` onto the port
17
+ * - BedrockCacheStrategy ('bedrock') — `enabled: false`; the Bedrock
18
+ * adapter implements neither half of the cache contract
13
19
  *
14
- * Future strategies (Phase 8+):
15
- * - OpenAICacheStrategy
16
- * - BedrockCacheStrategy
17
- * - GeminiCacheStrategy (v2.7+, async handle-based)
20
+ * Future strategies:
21
+ * - GeminiCacheStrategy (async handle-based)
18
22
  *
19
23
  * Public types (re-exported for consumers):
20
24
  * - CachePolicy, CacheMarker, CacheStrategy, CacheCapabilities,
@@ -24,6 +28,10 @@
24
28
  import './strategies/AnthropicCacheStrategy.js';
25
29
  import './strategies/OpenAICacheStrategy.js';
26
30
  import './strategies/BedrockCacheStrategy.js';
31
+ // The honesty primitive the meter is typed in (9.59.0). Re-exported
32
+ // reference-equal from `src/lib/claim/` — the SAME symbols
33
+ // `agentfootprint/maps` exports, so `known` from either door is one function.
34
+ export { known, unknown, notApplicable, isKnown, valueOr, describeClaim, } from '../lib/claim/claim.js';
27
35
  // Strategy registry
28
36
  export { getDefaultCacheStrategy, registerCacheStrategy, listRegisteredStrategies, } from './strategyRegistry.js';
29
37
  // Built-in strategy classes (for consumers who want explicit overrides)
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cache/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,4DAA4D;AAC5D,OAAO,wCAAwC,CAAC;AAChD,OAAO,qCAAqC,CAAC;AAC7C,OAAO,sCAAsC,CAAC;AAa9C,oBAAoB;AACpB,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAE/B,wEAAwE;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAE5E,WAAW;AACX,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cache/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,4DAA4D;AAC5D,OAAO,wCAAwC,CAAC;AAChD,OAAO,qCAAqC,CAAC;AAC7C,OAAO,sCAAsC,CAAC;AAc9C,oEAAoE;AACpE,2DAA2D;AAC3D,8EAA8E;AAC9E,OAAO,EACL,KAAK,EACL,OAAO,EACP,aAAa,EACb,OAAO,EACP,OAAO,EACP,aAAa,GAEd,MAAM,uBAAuB,CAAC;AAE/B,oBAAoB;AACpB,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAE/B,wEAAwE;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAE5E,WAAW;AACX,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The one reader of PORT-shaped usage for the cache meter.
3
+ *
4
+ * Pattern: pure function, one job. Given the `usage` the framework put on
5
+ * `agentfootprint.stream.llm_end`, answer what is actually known
6
+ * about cache traffic on that call.
7
+ * Role: the honest half of the 9.59.0 meter fix. Before it, three
8
+ * strategies each parsed RAW WIRE field names (`cache_read_input_tokens`,
9
+ * `prompt_tokens_details.cached_tokens`) off a value that has never
10
+ * carried them — the framework normalises the wire at the ADAPTER
11
+ * ring (`readCacheUsage`) and only ever hands a strategy the port
12
+ * shape. Every field read `undefined`, `?? 0` turned that into a
13
+ * zero, the guard tripped, and a 20-call turn that hit cache on
14
+ * every call reported hitRate 0.
15
+ *
16
+ * The distinction this function exists to preserve: an adapter sets
17
+ * `cacheRead` / `cacheWrite` ONLY when the provider reported a number, so
18
+ * • both absent → nobody measured → `unknown`
19
+ * • either present → measured → `known` (a present 0 is a real 0)
20
+ * That is exactly the line `Claim<T>` was written to hold.
21
+ */
22
+ import { type Claim } from '../lib/claim/claim.js';
23
+ import type { CacheMetrics, CacheUsage } from './types.js';
24
+ /**
25
+ * Read cache metrics off port usage.
26
+ *
27
+ * @param usage the `usage` field of `agentfootprint.stream.llm_end`
28
+ * @param who the adapter named in the evidence sentence, e.g. `'the Anthropic adapter'`
29
+ */
30
+ export declare function readPortCacheUsage(usage: CacheUsage | undefined, who: string): Claim<CacheMetrics>;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The one reader of PORT-shaped usage for the cache meter.
3
+ *
4
+ * Pattern: pure function, one job. Given the `usage` the framework put on
5
+ * `agentfootprint.stream.llm_end`, answer what is actually known
6
+ * about cache traffic on that call.
7
+ * Role: the honest half of the 9.59.0 meter fix. Before it, three
8
+ * strategies each parsed RAW WIRE field names (`cache_read_input_tokens`,
9
+ * `prompt_tokens_details.cached_tokens`) off a value that has never
10
+ * carried them — the framework normalises the wire at the ADAPTER
11
+ * ring (`readCacheUsage`) and only ever hands a strategy the port
12
+ * shape. Every field read `undefined`, `?? 0` turned that into a
13
+ * zero, the guard tripped, and a 20-call turn that hit cache on
14
+ * every call reported hitRate 0.
15
+ *
16
+ * The distinction this function exists to preserve: an adapter sets
17
+ * `cacheRead` / `cacheWrite` ONLY when the provider reported a number, so
18
+ * • both absent → nobody measured → `unknown`
19
+ * • either present → measured → `known` (a present 0 is a real 0)
20
+ * That is exactly the line `Claim<T>` was written to hold.
21
+ */
22
+ import { known, unknown } from '../lib/claim/claim.js';
23
+ /**
24
+ * Read cache metrics off port usage.
25
+ *
26
+ * @param usage the `usage` field of `agentfootprint.stream.llm_end`
27
+ * @param who the adapter named in the evidence sentence, e.g. `'the Anthropic adapter'`
28
+ */
29
+ export function readPortCacheUsage(usage, who) {
30
+ if (usage === undefined || usage === null || typeof usage !== 'object') {
31
+ return unknown('the llm_end event carried no usage payload, so nothing about cache traffic was measured', 'agentfootprint.stream.llm_end');
32
+ }
33
+ const { cacheRead, cacheWrite, input } = usage;
34
+ if (cacheRead === undefined && cacheWrite === undefined) {
35
+ return unknown(`${who} reported no cache fields on this call — that is "nobody measured", not "no cache traffic"`, 'agentfootprint.stream.llm_end usage (port shape)');
36
+ }
37
+ return known({
38
+ cacheReadTokens: cacheRead ?? 0,
39
+ cacheWriteTokens: cacheWrite ?? 0,
40
+ freshInputTokens: typeof input === 'number' ? input : 0,
41
+ }, `${who} reported cache token counts on the port usage`);
42
+ }
43
+ //# sourceMappingURL=portUsage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portUsage.js","sourceRoot":"","sources":["../../../src/cache/portUsage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,KAAK,EAAE,OAAO,EAAc,MAAM,uBAAuB,CAAC;AAGnE;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,KAA6B,EAC7B,GAAW;IAEX,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvE,OAAO,OAAO,CACZ,yFAAyF,EACzF,+BAA+B,CAChC,CAAC;IACJ,CAAC;IACD,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAC/C,IAAI,SAAS,KAAK,SAAS,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QACxD,OAAO,OAAO,CACZ,GAAG,GAAG,4FAA4F,EAClG,kDAAkD,CACnD,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CACV;QACE,eAAe,EAAE,SAAS,IAAI,CAAC;QAC/B,gBAAgB,EAAE,UAAU,IAAI,CAAC;QACjC,gBAAgB,EAAE,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;KACxD,EACD,GAAG,GAAG,gDAAgD,CACvD,CAAC;AACJ,CAAC"}
@@ -15,7 +15,9 @@
15
15
  *
16
16
  * What this strategy DOES vs DOESN'T do:
17
17
  * - DOES: clamp markers, attach to LLMRequest.cacheMarkers,
18
- * extract metrics from response.usage
18
+ * read cache metrics off the PORT usage the framework hands it
19
+ * (`{ input, output, cacheRead?, cacheWrite? }`) — never off the raw
20
+ * Anthropic wire, which the adapter has already normalised
19
21
  * - DOES NOT: rewrite the wire body. The provider
20
22
  * (BrowserAnthropicProvider) reads `cacheMarkers` and applies
21
23
  * `cache_control` blocks during body construction. Separation of
@@ -25,7 +27,8 @@
25
27
  * Auto-registers in the strategy registry on module import for
26
28
  * provider names: 'anthropic', 'browser-anthropic'.
27
29
  */
28
- import type { CacheCapabilities, CacheMarker, CacheMetrics, CacheStrategy, CacheStrategyContext } from '../types.js';
30
+ import type { CacheCapabilities, CacheMarker, CacheMetrics, CacheStrategy, CacheStrategyContext, CacheUsage } from '../types.js';
31
+ import type { Claim } from '../../lib/claim/claim.js';
29
32
  import type { LLMRequest } from '../../adapters/types.js';
30
33
  export declare class AnthropicCacheStrategy implements CacheStrategy {
31
34
  readonly providerName = "anthropic";
@@ -34,5 +37,5 @@ export declare class AnthropicCacheStrategy implements CacheStrategy {
34
37
  readonly request: LLMRequest;
35
38
  readonly markersApplied: readonly CacheMarker[];
36
39
  }>;
37
- extractMetrics(usage: unknown): CacheMetrics | undefined;
40
+ extractMetrics(usage: CacheUsage | undefined): Claim<CacheMetrics>;
38
41
  }
@@ -15,7 +15,9 @@
15
15
  *
16
16
  * What this strategy DOES vs DOESN'T do:
17
17
  * - DOES: clamp markers, attach to LLMRequest.cacheMarkers,
18
- * extract metrics from response.usage
18
+ * read cache metrics off the PORT usage the framework hands it
19
+ * (`{ input, output, cacheRead?, cacheWrite? }`) — never off the raw
20
+ * Anthropic wire, which the adapter has already normalised
19
21
  * - DOES NOT: rewrite the wire body. The provider
20
22
  * (BrowserAnthropicProvider) reads `cacheMarkers` and applies
21
23
  * `cache_control` blocks during body construction. Separation of
@@ -25,6 +27,7 @@
25
27
  * Auto-registers in the strategy registry on module import for
26
28
  * provider names: 'anthropic', 'browser-anthropic'.
27
29
  */
30
+ import { readPortCacheUsage } from '../portUsage.js';
28
31
  import { registerCacheStrategy } from '../strategyRegistry.js';
29
32
  /** Anthropic enforces 4 cache breakpoints per request. */
30
33
  const ANTHROPIC_MAX_MARKERS = 4;
@@ -62,22 +65,7 @@ export class AnthropicCacheStrategy {
62
65
  return { request, markersApplied };
63
66
  }
64
67
  extractMetrics(usage) {
65
- if (!usage || typeof usage !== 'object')
66
- return undefined;
67
- const u = usage;
68
- const cacheRead = u.cache_read_input_tokens ?? 0;
69
- const cacheWrite = u.cache_creation_input_tokens ?? 0;
70
- const fresh = u.input_tokens ?? 0;
71
- // If neither cache field present, response didn't involve caching.
72
- // Returning undefined signals "no cache info" so cacheRecorder
73
- // doesn't compute a misleading 0% hit rate.
74
- if (cacheRead === 0 && cacheWrite === 0)
75
- return undefined;
76
- return {
77
- cacheReadTokens: cacheRead,
78
- cacheWriteTokens: cacheWrite,
79
- freshInputTokens: fresh,
80
- };
68
+ return readPortCacheUsage(usage, 'the Anthropic adapter');
81
69
  }
82
70
  }
83
71
  // Auto-register on module import. Both 'anthropic' (server-side) and
@@ -1 +1 @@
1
- {"version":3,"file":"AnthropicCacheStrategy.js","sourceRoot":"","sources":["../../../../src/cache/strategies/AnthropicCacheStrategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAUH,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,0DAA0D;AAC1D,MAAM,qBAAqB,GAAG,CAAC,CAAC;AAEhC,MAAM,sBAAsB,GAAsB,MAAM,CAAC,MAAM,CAAC;IAC9D,OAAO,EAAE,IAAI;IACb,UAAU,EAAE,qBAAqB;IACjC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,CAAkC;IACxD,MAAM,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAiD;IACvF,SAAS,EAAE,KAAK;CACjB,CAAC,CAAC;AAEH,MAAM,OAAO,sBAAsB;IACxB,YAAY,GAAG,WAAW,CAAC;IAC3B,YAAY,GAAG,sBAAsB,CAAC;IAE/C,KAAK,CAAC,cAAc,CAClB,GAAe,EACf,UAAkC,EAClC,GAAyB;QAKzB,8DAA8D;QAC9D,kEAAkE;QAClE,gEAAgE;QAChE,kEAAkE;QAClE,IAAI,GAAG,CAAC,eAAe,EAAE,CAAC;YACxB,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC;QAC9C,CAAC;QAED,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC;QAC9C,CAAC;QAED,2DAA2D;QAC3D,4DAA4D;QAC5D,kEAAkE;QAClE,MAAM,cAAc,GAClB,UAAU,CAAC,MAAM,IAAI,qBAAqB;YACxC,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,qBAAqB,CAAC,CAAC;QAEjD,MAAM,OAAO,GAAe;YAC1B,GAAG,GAAG;YACN,YAAY,EAAE,cAAc;SAC7B,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC;IACrC,CAAC;IAED,cAAc,CAAC,KAAc;QAC3B,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QAC1D,MAAM,CAAC,GAAG,KAIT,CAAC;QACF,MAAM,SAAS,GAAG,CAAC,CAAC,uBAAuB,IAAI,CAAC,CAAC;QACjD,MAAM,UAAU,GAAG,CAAC,CAAC,2BAA2B,IAAI,CAAC,CAAC;QACtD,MAAM,KAAK,GAAG,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC;QAClC,mEAAmE;QACnE,+DAA+D;QAC/D,4CAA4C;QAC5C,IAAI,SAAS,KAAK,CAAC,IAAI,UAAU,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QAC1D,OAAO;YACL,eAAe,EAAE,SAAS;YAC1B,gBAAgB,EAAE,UAAU;YAC5B,gBAAgB,EAAE,KAAK;SACxB,CAAC;IACJ,CAAC;CACF;AAED,qEAAqE;AACrE,sEAAsE;AACtE,CAAC;IACC,MAAM,QAAQ,GAAG,IAAI,sBAAsB,EAAE,CAAC;IAC9C,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IAChC,2EAA2E;IAC3E,oDAAoD;IACpD,MAAM,eAAe,GAAkB;QACrC,YAAY,EAAE,mBAAmB;QACjC,YAAY,EAAE,QAAQ,CAAC,YAAY;QACnC,cAAc,EAAE,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC;QACtD,cAAc,EAAE,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC;KACvD,CAAC;IACF,qBAAqB,CAAC,eAAe,CAAC,CAAC;AACzC,CAAC"}
1
+ {"version":3,"file":"AnthropicCacheStrategy.js","sourceRoot":"","sources":["../../../../src/cache/strategies/AnthropicCacheStrategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAWH,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,0DAA0D;AAC1D,MAAM,qBAAqB,GAAG,CAAC,CAAC;AAEhC,MAAM,sBAAsB,GAAsB,MAAM,CAAC,MAAM,CAAC;IAC9D,OAAO,EAAE,IAAI;IACb,UAAU,EAAE,qBAAqB;IACjC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,CAAkC;IACxD,MAAM,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAiD;IACvF,SAAS,EAAE,KAAK;CACjB,CAAC,CAAC;AAEH,MAAM,OAAO,sBAAsB;IACxB,YAAY,GAAG,WAAW,CAAC;IAC3B,YAAY,GAAG,sBAAsB,CAAC;IAE/C,KAAK,CAAC,cAAc,CAClB,GAAe,EACf,UAAkC,EAClC,GAAyB;QAKzB,8DAA8D;QAC9D,kEAAkE;QAClE,gEAAgE;QAChE,kEAAkE;QAClE,IAAI,GAAG,CAAC,eAAe,EAAE,CAAC;YACxB,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC;QAC9C,CAAC;QAED,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC;QAC9C,CAAC;QAED,2DAA2D;QAC3D,4DAA4D;QAC5D,kEAAkE;QAClE,MAAM,cAAc,GAClB,UAAU,CAAC,MAAM,IAAI,qBAAqB;YACxC,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,qBAAqB,CAAC,CAAC;QAEjD,MAAM,OAAO,GAAe;YAC1B,GAAG,GAAG;YACN,YAAY,EAAE,cAAc;SAC7B,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC;IACrC,CAAC;IAED,cAAc,CAAC,KAA6B;QAC1C,OAAO,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,CAAC,CAAC;IAC5D,CAAC;CACF;AAED,qEAAqE;AACrE,sEAAsE;AACtE,CAAC;IACC,MAAM,QAAQ,GAAG,IAAI,sBAAsB,EAAE,CAAC;IAC9C,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IAChC,2EAA2E;IAC3E,oDAAoD;IACpD,MAAM,eAAe,GAAkB;QACrC,YAAY,EAAE,mBAAmB;QACjC,YAAY,EAAE,QAAQ,CAAC,YAAY;QACnC,cAAc,EAAE,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC;QACtD,cAAc,EAAE,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC;KACvD,CAAC;IACF,qBAAqB,CAAC,eAAe,CAAC,CAAC;AACzC,CAAC"}