agentfootprint 9.58.0 → 9.59.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.
Files changed (182) hide show
  1. package/CLAUDE.md +1 -1
  2. package/dist/cache/cacheRecorder.js +154 -46
  3. package/dist/cache/cacheRecorder.js.map +1 -1
  4. package/dist/cache/index.js +20 -6
  5. package/dist/cache/index.js.map +1 -1
  6. package/dist/cache/portUsage.js +47 -0
  7. package/dist/cache/portUsage.js.map +1 -0
  8. package/dist/cache/strategies/AnthropicCacheStrategy.js +5 -17
  9. package/dist/cache/strategies/AnthropicCacheStrategy.js.map +1 -1
  10. package/dist/cache/strategies/BedrockCacheStrategy.js +74 -58
  11. package/dist/cache/strategies/BedrockCacheStrategy.js.map +1 -1
  12. package/dist/cache/strategies/NoOpCacheStrategy.js +2 -1
  13. package/dist/cache/strategies/NoOpCacheStrategy.js.map +1 -1
  14. package/dist/cache/strategies/OpenAICacheStrategy.js +14 -13
  15. package/dist/cache/strategies/OpenAICacheStrategy.js.map +1 -1
  16. package/dist/core/Agent.js +7 -0
  17. package/dist/core/Agent.js.map +1 -1
  18. package/dist/core/agent/AgentBuilder.js +35 -1
  19. package/dist/core/agent/AgentBuilder.js.map +1 -1
  20. package/dist/core/agent/buildAgentChart.js +27 -0
  21. package/dist/core/agent/buildAgentChart.js.map +1 -1
  22. package/dist/core/agent/buildDynamicAgentChart.js +45 -0
  23. package/dist/core/agent/buildDynamicAgentChart.js.map +1 -1
  24. package/dist/core/agent/stages/toolCalls.js +49 -3
  25. package/dist/core/agent/stages/toolCalls.js.map +1 -1
  26. package/dist/core/slots/buildToolsSlot.js +27 -1
  27. package/dist/core/slots/buildToolsSlot.js.map +1 -1
  28. package/dist/doors/maps.js +7 -2
  29. package/dist/doors/maps.js.map +1 -1
  30. package/dist/esm/cache/cacheRecorder.d.ts +82 -36
  31. package/dist/esm/cache/cacheRecorder.js +154 -46
  32. package/dist/esm/cache/cacheRecorder.js.map +1 -1
  33. package/dist/esm/cache/index.d.ts +12 -7
  34. package/dist/esm/cache/index.js +13 -5
  35. package/dist/esm/cache/index.js.map +1 -1
  36. package/dist/esm/cache/portUsage.d.ts +30 -0
  37. package/dist/esm/cache/portUsage.js +43 -0
  38. package/dist/esm/cache/portUsage.js.map +1 -0
  39. package/dist/esm/cache/strategies/AnthropicCacheStrategy.d.ts +6 -3
  40. package/dist/esm/cache/strategies/AnthropicCacheStrategy.js +5 -17
  41. package/dist/esm/cache/strategies/AnthropicCacheStrategy.js.map +1 -1
  42. package/dist/esm/cache/strategies/BedrockCacheStrategy.d.ts +48 -19
  43. package/dist/esm/cache/strategies/BedrockCacheStrategy.js +74 -58
  44. package/dist/esm/cache/strategies/BedrockCacheStrategy.js.map +1 -1
  45. package/dist/esm/cache/strategies/NoOpCacheStrategy.d.ts +3 -2
  46. package/dist/esm/cache/strategies/NoOpCacheStrategy.js +2 -1
  47. package/dist/esm/cache/strategies/NoOpCacheStrategy.js.map +1 -1
  48. package/dist/esm/cache/strategies/OpenAICacheStrategy.d.ts +13 -2
  49. package/dist/esm/cache/strategies/OpenAICacheStrategy.js +14 -13
  50. package/dist/esm/cache/strategies/OpenAICacheStrategy.js.map +1 -1
  51. package/dist/esm/cache/types.d.ts +35 -7
  52. package/dist/esm/core/Agent.js +7 -0
  53. package/dist/esm/core/Agent.js.map +1 -1
  54. package/dist/esm/core/agent/AgentBuilder.js +35 -1
  55. package/dist/esm/core/agent/AgentBuilder.js.map +1 -1
  56. package/dist/esm/core/agent/buildAgentChart.js +27 -0
  57. package/dist/esm/core/agent/buildAgentChart.js.map +1 -1
  58. package/dist/esm/core/agent/buildDynamicAgentChart.js +45 -0
  59. package/dist/esm/core/agent/buildDynamicAgentChart.js.map +1 -1
  60. package/dist/esm/core/agent/stages/toolCalls.d.ts +7 -0
  61. package/dist/esm/core/agent/stages/toolCalls.js +47 -1
  62. package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
  63. package/dist/esm/core/agent/types.d.ts +5 -0
  64. package/dist/esm/core/slots/buildToolsSlot.js +27 -1
  65. package/dist/esm/core/slots/buildToolsSlot.js.map +1 -1
  66. package/dist/esm/doors/maps.d.ts +7 -2
  67. package/dist/esm/doors/maps.js +7 -2
  68. package/dist/esm/doors/maps.js.map +1 -1
  69. package/dist/esm/events/eventTail.d.ts +18 -0
  70. package/dist/esm/events/eventTail.js +4 -1
  71. package/dist/esm/events/eventTail.js.map +1 -1
  72. package/dist/esm/events/payloads.d.ts +28 -8
  73. package/dist/esm/integrity/disposition/ledger.d.ts +60 -0
  74. package/dist/esm/integrity/disposition/ledger.js +0 -0
  75. package/dist/esm/integrity/disposition/ledger.js.map +1 -0
  76. package/dist/esm/integrity/disposition/types.d.ts +60 -0
  77. package/dist/esm/integrity/disposition/types.js +19 -0
  78. package/dist/esm/integrity/disposition/types.js.map +1 -0
  79. package/dist/esm/{maps → lib}/claim/claim.d.ts +8 -4
  80. package/dist/esm/{maps → lib}/claim/claim.js +8 -4
  81. package/dist/esm/lib/claim/claim.js.map +1 -0
  82. package/dist/esm/lib/injection-engine/buildInjectionEngineSubflow.js +45 -1
  83. package/dist/esm/lib/injection-engine/buildInjectionEngineSubflow.js.map +1 -1
  84. package/dist/esm/lib/injection-engine/evaluator.js +10 -4
  85. package/dist/esm/lib/injection-engine/evaluator.js.map +1 -1
  86. package/dist/esm/maps/engagement/evidence.d.ts +48 -7
  87. package/dist/esm/maps/engagement/evidence.js +29 -11
  88. package/dist/esm/maps/engagement/evidence.js.map +1 -1
  89. package/dist/esm/maps/engagement/lease.d.ts +28 -6
  90. package/dist/esm/maps/engagement/lease.js +148 -13
  91. package/dist/esm/maps/engagement/lease.js.map +1 -1
  92. package/dist/esm/maps/engagement/parkCard.d.ts +48 -0
  93. package/dist/esm/maps/engagement/parkCard.js +88 -0
  94. package/dist/esm/maps/engagement/parkCard.js.map +1 -0
  95. package/dist/esm/maps/engagement/types.d.ts +138 -8
  96. package/dist/esm/maps/engagement/types.js +23 -1
  97. package/dist/esm/maps/engagement/types.js.map +1 -1
  98. package/dist/esm/recorders/observability/recordRun.d.ts +4 -0
  99. package/dist/esm/recorders/observability/recordRun.js +3 -0
  100. package/dist/esm/recorders/observability/recordRun.js.map +1 -1
  101. package/dist/esm/recorders/observability/recordingEnvelope.d.ts +17 -0
  102. package/dist/esm/recorders/observability/recordingEnvelope.js +23 -0
  103. package/dist/esm/recorders/observability/recordingEnvelope.js.map +1 -1
  104. package/dist/events/eventTail.js +4 -1
  105. package/dist/events/eventTail.js.map +1 -1
  106. package/dist/integrity/disposition/ledger.js +0 -0
  107. package/dist/integrity/disposition/ledger.js.map +1 -0
  108. package/dist/integrity/disposition/types.js +20 -0
  109. package/dist/integrity/disposition/types.js.map +1 -0
  110. package/dist/{maps → lib}/claim/claim.js +8 -4
  111. package/dist/lib/claim/claim.js.map +1 -0
  112. package/dist/lib/injection-engine/buildInjectionEngineSubflow.js +45 -1
  113. package/dist/lib/injection-engine/buildInjectionEngineSubflow.js.map +1 -1
  114. package/dist/lib/injection-engine/evaluator.js +10 -4
  115. package/dist/lib/injection-engine/evaluator.js.map +1 -1
  116. package/dist/maps/engagement/evidence.js +29 -11
  117. package/dist/maps/engagement/evidence.js.map +1 -1
  118. package/dist/maps/engagement/lease.js +148 -13
  119. package/dist/maps/engagement/lease.js.map +1 -1
  120. package/dist/maps/engagement/parkCard.js +92 -0
  121. package/dist/maps/engagement/parkCard.js.map +1 -0
  122. package/dist/maps/engagement/types.js +25 -0
  123. package/dist/maps/engagement/types.js.map +1 -1
  124. package/dist/recorders/observability/recordRun.js +3 -0
  125. package/dist/recorders/observability/recordRun.js.map +1 -1
  126. package/dist/recorders/observability/recordingEnvelope.js +23 -0
  127. package/dist/recorders/observability/recordingEnvelope.js.map +1 -1
  128. package/dist/types/cache/cacheRecorder.d.ts +82 -36
  129. package/dist/types/cache/cacheRecorder.d.ts.map +1 -1
  130. package/dist/types/cache/index.d.ts +12 -7
  131. package/dist/types/cache/index.d.ts.map +1 -1
  132. package/dist/types/cache/portUsage.d.ts +31 -0
  133. package/dist/types/cache/portUsage.d.ts.map +1 -0
  134. package/dist/types/cache/strategies/AnthropicCacheStrategy.d.ts +6 -3
  135. package/dist/types/cache/strategies/AnthropicCacheStrategy.d.ts.map +1 -1
  136. package/dist/types/cache/strategies/BedrockCacheStrategy.d.ts +48 -19
  137. package/dist/types/cache/strategies/BedrockCacheStrategy.d.ts.map +1 -1
  138. package/dist/types/cache/strategies/NoOpCacheStrategy.d.ts +3 -2
  139. package/dist/types/cache/strategies/NoOpCacheStrategy.d.ts.map +1 -1
  140. package/dist/types/cache/strategies/OpenAICacheStrategy.d.ts +13 -2
  141. package/dist/types/cache/strategies/OpenAICacheStrategy.d.ts.map +1 -1
  142. package/dist/types/cache/types.d.ts +35 -7
  143. package/dist/types/cache/types.d.ts.map +1 -1
  144. package/dist/types/core/Agent.d.ts.map +1 -1
  145. package/dist/types/core/agent/AgentBuilder.d.ts.map +1 -1
  146. package/dist/types/core/agent/buildAgentChart.d.ts.map +1 -1
  147. package/dist/types/core/agent/buildDynamicAgentChart.d.ts.map +1 -1
  148. package/dist/types/core/agent/stages/toolCalls.d.ts +7 -0
  149. package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
  150. package/dist/types/core/agent/types.d.ts +5 -0
  151. package/dist/types/core/agent/types.d.ts.map +1 -1
  152. package/dist/types/core/slots/buildToolsSlot.d.ts.map +1 -1
  153. package/dist/types/doors/maps.d.ts +7 -2
  154. package/dist/types/doors/maps.d.ts.map +1 -1
  155. package/dist/types/events/eventTail.d.ts +18 -0
  156. package/dist/types/events/eventTail.d.ts.map +1 -1
  157. package/dist/types/events/payloads.d.ts +28 -8
  158. package/dist/types/events/payloads.d.ts.map +1 -1
  159. package/dist/types/integrity/disposition/ledger.d.ts +61 -0
  160. package/dist/types/integrity/disposition/ledger.d.ts.map +1 -0
  161. package/dist/types/integrity/disposition/types.d.ts +61 -0
  162. package/dist/types/integrity/disposition/types.d.ts.map +1 -0
  163. package/dist/types/{maps → lib}/claim/claim.d.ts +8 -4
  164. package/dist/types/lib/claim/claim.d.ts.map +1 -0
  165. package/dist/types/lib/injection-engine/buildInjectionEngineSubflow.d.ts.map +1 -1
  166. package/dist/types/lib/injection-engine/evaluator.d.ts.map +1 -1
  167. package/dist/types/maps/engagement/evidence.d.ts +48 -7
  168. package/dist/types/maps/engagement/evidence.d.ts.map +1 -1
  169. package/dist/types/maps/engagement/lease.d.ts +28 -6
  170. package/dist/types/maps/engagement/lease.d.ts.map +1 -1
  171. package/dist/types/maps/engagement/parkCard.d.ts +49 -0
  172. package/dist/types/maps/engagement/parkCard.d.ts.map +1 -0
  173. package/dist/types/maps/engagement/types.d.ts +138 -8
  174. package/dist/types/maps/engagement/types.d.ts.map +1 -1
  175. package/dist/types/recorders/observability/recordRun.d.ts +4 -0
  176. package/dist/types/recorders/observability/recordRun.d.ts.map +1 -1
  177. package/dist/types/recorders/observability/recordingEnvelope.d.ts +17 -0
  178. package/dist/types/recorders/observability/recordingEnvelope.d.ts.map +1 -1
  179. package/package.json +3 -2
  180. package/dist/esm/maps/claim/claim.js.map +0 -1
  181. package/dist/maps/claim/claim.js.map +0 -1
  182. package/dist/types/maps/claim/claim.d.ts.map +0 -1
package/CLAUDE.md CHANGED
@@ -43,7 +43,7 @@ not in this table, search `src/index.ts` for the nearest noun before writing cod
43
43
  | letting the UI draw an artifact without the model naming a component | `registerArtifactComponent` (in the `agentfootprint-lens` package) | — | — |
44
44
  | finding which tools the model keeps writing by hand | `agentfootprint.tools.code_run` + `codeShape` | `src/core/codeRunnerTool.ts` | 9.46.0 |
45
45
  | a skill wrongly activated by a keyword staying loaded for the whole turn — suspending/parking a map's prompt + tools when its contribution goes unused, WITHOUT touching its cursor, and re-engaging it on evidence | `.maps()` + `advanceEngagement` + `agentfootprint.map.engaged/parked` + skip reason `'parked'` | `src/maps/` | 9.58.0 |
46
- | a value that must say how it knows itself — an unknown count that can never render as zero | `Claim<T>` (`known`/`unknown`/`notApplicable`) | `src/maps/claim/claim.ts` | 9.58.0 |
46
+ | a value that must say how it knows itself — an unknown count that can never render as zero | `Claim<T>` (`known`/`unknown`/`notApplicable`) | `src/lib/claim/claim.ts` | 9.58.0 |
47
47
  | a tool answering "I looked and found nothing", routably | `absent` | `src/core/agent/coverage/absent.ts` | 9.43.0 |
48
48
  | stating what a clean answer does NOT rule out | `coverage` | `src/core/agent/coverage/ledger.ts` | 9.43.0 |
49
49
  | proving a session store really honours the port | `runSessionLifecycleConformance` | `src/hosting/conformance/` | 9.37.0 |
@@ -1,35 +1,72 @@
1
1
  "use strict";
2
2
  /**
3
- * cacheRecorder() — observability for the v2.6 cache layer.
3
+ * cacheRecorder() — the cache layer's meter.
4
4
  *
5
5
  * Subscribes to:
6
- * - `FlowRecorder.onDecision` — captures CacheGate routing decisions
7
- * (apply-markers / no-markers + the rule that fired + evidence
8
- * from `decide()`). Read directly from `event.evidence.rules[matched]`
9
- * since footprintjs already auto-captures predicate `inputs[]`.
10
- * - `agentfootprint.stream.llm_end` events read provider's `usage`
11
- * and call the agent's CacheStrategy.extractMetrics() to normalize
12
- * into CacheMetrics (cacheReadTokens / cacheWriteTokens / fresh).
6
+ * - `FlowRecorder.onDecision` — CacheGate routing decisions
7
+ * (apply-markers / no-markers + the rule that fired + evidence from
8
+ * `decide()`).
9
+ * - `agentfootprint.stream.llm_end` reads that event's `usage` (the PORT
10
+ * shape: `{ input, output, cacheRead?, cacheWrite? }`) and asks the
11
+ * agent's `CacheStrategy.extractMetrics` what is known about it.
13
12
  *
14
- * Produces:
15
- * - per-iteration `agentfootprint.cache.applied` events (markers
16
- * applied this iter or empty if skipped) — for Lens trace
17
- * - per-iteration `agentfootprint.cache.metrics` events (hit/write
18
- * token counts + estimated dollars via PricingTable) — for
19
- * dashboards
20
- * - a turn-end summary printable via `recorder.report()` —
21
- * numeric tally plus dollars saved
13
+ * Produces: a per-turn report via `recorder.report()` — token tallies, hit
14
+ * rate and dollar estimates, each carried as a `Claim` so an UNMEASURED turn
15
+ * can never render as a zero one.
22
16
  *
23
- * v2.6 LIMITATION: doesn't yet write `scope.recentHitRate` back into
24
- * agent state. CacheGate's hit-rate-floor rule won't fire automatically;
25
- * consumers can manually wire feedback via `Agent.create(...).attach(rec)`.
26
- * Full feedback loop deferred to v2.7 (needs an agent-side accessor
27
- * convention since recorders don't normally write to scope).
17
+ * ── Why every number here is a Claim (9.59.0) ─────────────────────────
18
+ * The shipped 9.58.0 meter reported `hitRate: 0` for a 20-call turn that hit
19
+ * cache on every call. Two faults, and the second is why the first went
20
+ * unnoticed for so long: (1) the strategies parsed RAW WIRE field names off a
21
+ * value that carries the PORT shape, so every field read `undefined`; (2) the
22
+ * report typed its aggregates as bare `number`, so "nobody measured" and
23
+ * "measured, and it was zero" rendered identically. Fixing (1) alone would
24
+ * have left a meter that still cannot say "unmeasured" — hence `Claim<T>`,
25
+ * plus `measuredIterations` / `unmeasuredIterations` so a rate computed from
26
+ * 3 of 20 calls states its own denominator.
27
+ *
28
+ * Read the number through `isKnown(report.hitRate)` (or `describeClaim` for a
29
+ * one-line render). There is deliberately no door that hands you a bare
30
+ * number without your having branched.
31
+ *
32
+ * ── And an unknown carries ITS OWN reason (9.59.1) ────────────────────
33
+ * 9.59.0 enforced that law per call and then broke it in `report()`, which
34
+ * hardcoded "the provider reported no cache fields" for the whole turn no
35
+ * matter what the rows said. With no strategy passed and a provider that DID
36
+ * report cache fields, the row said "nothing read the usage" and the summary
37
+ * blamed the provider — pointing the reader away from their actual mistake.
38
+ * The summary now carries the rows' own reasons, and says so plainly when the
39
+ * rows disagree rather than picking one.
40
+ *
41
+ * ── What this recorder does NOT do ────────────────────────────────────
42
+ * It emits no events. (Earlier prose here promised per-iteration
43
+ * `agentfootprint.cache.applied` / `agentfootprint.cache.metrics` events;
44
+ * neither name has ever existed in the event registry and no `typedEmit` has
45
+ * ever been in this file. The prose was the bug.)
46
+ *
47
+ * It does not write `scope.recentHitRate` back into agent state either, so
48
+ * CacheGate's hit-rate-floor rule never fires on its own — the loop is
49
+ * severed at both ends (the key is seeded `undefined` and written by
50
+ * nothing). Recorders do not write to chart scope, so closing that loop needs
51
+ * an agent-side accessor convention; it is separable work from measuring the
52
+ * number, which is what this file now does.
28
53
  */
29
54
  Object.defineProperty(exports, "__esModule", { value: true });
30
55
  exports.cacheRecorder = void 0;
31
56
  const trace_1 = require("footprintjs/trace");
32
57
  const conventions_js_1 = require("../conventions.js");
58
+ const claim_js_1 = require("../lib/claim/claim.js");
59
+ /**
60
+ * How many distinct reasons a disagreeing summary spells out before it
61
+ * switches to counting the rest. A summary sentence must stay readable.
62
+ */
63
+ const MAX_LISTED_REASONS = 3;
64
+ /** The metrics of a row already filtered by `isKnown`. Narrowing helper only. */
65
+ function valueOfMetrics(entry) {
66
+ return (0, claim_js_1.isKnown)(entry.metrics)
67
+ ? entry.metrics.value
68
+ : { cacheReadTokens: 0, cacheWriteTokens: 0, freshInputTokens: 0 };
69
+ }
33
70
  function cacheRecorder(options = {}) {
34
71
  const perIter = [];
35
72
  let lastDecision;
@@ -64,8 +101,11 @@ function cacheRecorder(options = {}) {
64
101
  if (event.type !== 'agentfootprint.stream.llm_end')
65
102
  return;
66
103
  iterationCounter++;
104
+ // The PORT shape, which is the only shape this event has ever carried.
67
105
  const usage = event.payload.usage;
68
- const metrics = options.strategy?.extractMetrics(usage);
106
+ const metrics = options.strategy === undefined
107
+ ? (0, claim_js_1.unknown)('no CacheStrategy was given to cacheRecorder(), so nothing read the usage')
108
+ : options.strategy.extractMetrics(usage);
69
109
  const branch = lastDecision?.branch ?? 'apply-markers';
70
110
  // Compute dollar math:
71
111
  // spent = freshInput * inputPrice
@@ -73,21 +113,34 @@ function cacheRecorder(options = {}) {
73
113
  // + cacheWrite * cacheWritePrice
74
114
  // no-cache cost = (freshInput + cacheRead + cacheWrite) * inputPrice
75
115
  // saved = no-cache cost - spent
76
- let dollarsSpent = 0;
77
- let savedVsNoCache = 0;
78
- if (metrics) {
79
- dollarsSpent =
80
- dollars(metrics.freshInputTokens, 'input') +
81
- dollars(metrics.cacheReadTokens, 'cacheRead') +
82
- dollars(metrics.cacheWriteTokens, 'cacheWrite');
83
- const noCacheCost = dollars(metrics.freshInputTokens + metrics.cacheReadTokens + metrics.cacheWriteTokens, 'input');
84
- savedVsNoCache = noCacheCost - dollarsSpent;
116
+ let dollarsSpent;
117
+ let savedVsNoCache;
118
+ if ((0, claim_js_1.isKnown)(metrics)) {
119
+ const m = metrics.value;
120
+ const spent = dollars(m.freshInputTokens, 'input') +
121
+ dollars(m.cacheReadTokens, 'cacheRead') +
122
+ dollars(m.cacheWriteTokens, 'cacheWrite');
123
+ const noCacheCost = dollars(m.freshInputTokens + m.cacheReadTokens + m.cacheWriteTokens, 'input');
124
+ const ev = options.pricing === undefined
125
+ ? 'measured tokens, but no PricingTable was given — every price is 0'
126
+ : 'measured tokens priced with the given PricingTable';
127
+ dollarsSpent = (0, claim_js_1.known)(spent, ev);
128
+ savedVsNoCache = (0, claim_js_1.known)(noCacheCost - spent, ev);
129
+ }
130
+ else {
131
+ // No tokens measured ⇒ no dollars. Carrying the metrics claim's own
132
+ // reason forward is what keeps a $0 estimate from reading as "free".
133
+ const why = metrics.kind === 'unknown'
134
+ ? metrics.reason
135
+ : `no cache tokens are measurable here — ${metrics.evidence}`;
136
+ dollarsSpent = (0, claim_js_1.unknown)(why);
137
+ savedVsNoCache = (0, claim_js_1.unknown)(why);
85
138
  }
86
139
  const entry = {
87
140
  iteration: iterationCounter,
88
141
  branch,
89
142
  ...(lastDecision?.rule !== undefined && { rule: lastDecision.rule }),
90
- ...(metrics !== undefined && { metrics }),
143
+ metrics,
91
144
  dollarsSpent,
92
145
  dollarsSavedVsNoCache: savedVsNoCache,
93
146
  };
@@ -97,23 +150,78 @@ function cacheRecorder(options = {}) {
97
150
  report() {
98
151
  const apply = perIter.filter((p) => p.branch === 'apply-markers').length;
99
152
  const skip = perIter.filter((p) => p.branch === 'no-markers').length;
100
- const cacheRead = perIter.reduce((s, p) => s + (p.metrics?.cacheReadTokens ?? 0), 0);
101
- const cacheWrite = perIter.reduce((s, p) => s + (p.metrics?.cacheWriteTokens ?? 0), 0);
102
- const fresh = perIter.reduce((s, p) => s + (p.metrics?.freshInputTokens ?? 0), 0);
103
- const totalRequest = cacheRead + cacheWrite + fresh;
104
- const hitRate = totalRequest > 0 ? cacheRead / totalRequest : 0;
105
- const dollarsSpent = perIter.reduce((s, p) => s + p.dollarsSpent, 0);
106
- const dollarsSaved = perIter.reduce((s, p) => s + p.dollarsSavedVsNoCache, 0);
153
+ const measured = perIter.filter((p) => (0, claim_js_1.isKnown)(p.metrics));
154
+ const n = measured.length;
155
+ const total = perIter.length;
156
+ // ONE reason, stated once, reused by every claim below — so a reader
157
+ // who prints any single field learns why the whole report is empty.
158
+ //
159
+ // ── The summary CARRIES the rows' reasons; it never invents one (9.59.1)
160
+ // 9.59.0 hardcoded 'the provider reported no cache fields' here. That
161
+ // sentence is a fabrication whenever the rows said something else — most
162
+ // painfully when no CacheStrategy was passed and the provider DID report
163
+ // cacheRead/cacheWrite: the rows said "nothing read the usage", and the
164
+ // summary blamed the provider for the caller's own omission. An unknown
165
+ // must carry ITS OWN reason. That is the law this file exists to
166
+ // enforce, and this is it applied to the summary too.
167
+ const unmeasured = perIter.filter((p) => !(0, claim_js_1.isKnown)(p.metrics));
168
+ const reasons = [];
169
+ for (const row of unmeasured) {
170
+ const m = row.metrics;
171
+ // `unmeasured` already excluded 'known'; this narrows the type.
172
+ if ((0, claim_js_1.isKnown)(m))
173
+ continue;
174
+ const stated = m.kind === 'not-applicable' ? `this provider cannot report it (${m.evidence})` : m.reason;
175
+ if (!reasons.includes(stated))
176
+ reasons.push(stated);
177
+ }
178
+ // Rows can disagree — a turn may mix an adapter that cannot report with
179
+ // calls that carried no usage at all. Picking one silently would state a
180
+ // cause for calls that stated a different one, so the summary says there
181
+ // was more than one and lists them. Bounded by construction: at most
182
+ // MAX_LISTED_REASONS are spelled out and the remainder is COUNTED, so a
183
+ // strategy whose reason varies per call cannot grow this into a wall.
184
+ const listed = reasons.slice(0, MAX_LISTED_REASONS);
185
+ const remaining = reasons.length - listed.length;
186
+ const [onlyReason] = reasons;
187
+ const carried = reasons.length === 1 && onlyReason !== undefined
188
+ ? onlyReason
189
+ : `the calls did not agree on why — ${reasons.length} different reasons were ` +
190
+ `given: ${listed.map((r, i) => `(${i + 1}) ${r}`).join('; ')}` +
191
+ (remaining > 0 ? `; and ${remaining} more not listed here` : '');
192
+ const why = total === 0
193
+ ? 'no LLM call was observed, so there is nothing to measure'
194
+ : reasons.length === 0
195
+ ? // Unreachable where `why` is USED (it is read only when nothing
196
+ // was measured, and an unmeasured row always states a reason).
197
+ `all ${total} observed call(s) reported cache usage`
198
+ : `none of the ${total} observed call(s) reported cache usage — ${carried}`;
199
+ const evidence = `summed over the ${n} of ${total} call(s) whose usage was measured`;
200
+ const claimOf = (value) => n === 0 ? (0, claim_js_1.unknown)(why) : (0, claim_js_1.known)(value, evidence);
201
+ const cacheRead = measured.reduce((s2, p) => s2 + valueOfMetrics(p).cacheReadTokens, 0);
202
+ const cacheWrite = measured.reduce((s2, p) => s2 + valueOfMetrics(p).cacheWriteTokens, 0);
203
+ const fresh = measured.reduce((s2, p) => s2 + valueOfMetrics(p).freshInputTokens, 0);
204
+ const requestTotal = cacheRead + cacheWrite + fresh;
205
+ const dollarsSpent = measured.reduce((s2, p) => s2 + ((0, claim_js_1.isKnown)(p.dollarsSpent) ? p.dollarsSpent.value : 0), 0);
206
+ const dollarsSaved = measured.reduce((s2, p) => s2 + ((0, claim_js_1.isKnown)(p.dollarsSavedVsNoCache) ? p.dollarsSavedVsNoCache.value : 0), 0);
107
207
  return Object.freeze({
108
- totalIterations: perIter.length,
208
+ totalIterations: total,
109
209
  applyMarkersIterations: apply,
110
210
  noMarkersIterations: skip,
111
- cacheReadTokensTotal: cacheRead,
112
- cacheWriteTokensTotal: cacheWrite,
113
- freshInputTokensTotal: fresh,
114
- hitRate,
115
- estimatedDollarsSpent: dollarsSpent,
116
- estimatedDollarsSavedVsNoCache: dollarsSaved,
211
+ measuredIterations: n,
212
+ unmeasuredIterations: total - n,
213
+ cacheReadTokensTotal: claimOf(cacheRead),
214
+ cacheWriteTokensTotal: claimOf(cacheWrite),
215
+ freshInputTokensTotal: claimOf(fresh),
216
+ // A measured turn with zero prompt tokens is not a thing; guard it
217
+ // anyway rather than divide by zero and hand back NaN as a rate.
218
+ hitRate: n === 0
219
+ ? (0, claim_js_1.unknown)(why)
220
+ : requestTotal === 0
221
+ ? (0, claim_js_1.unknown)(`the ${n} measured call(s) reported 0 prompt tokens in total, so a hit rate has no denominator`)
222
+ : (0, claim_js_1.known)(cacheRead / requestTotal, evidence),
223
+ estimatedDollarsSpent: claimOf(dollarsSpent),
224
+ estimatedDollarsSavedVsNoCache: claimOf(dollarsSaved),
117
225
  perIter: Object.freeze([...perIter]),
118
226
  });
119
227
  },
@@ -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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"}
@@ -10,23 +10,37 @@
10
10
  * Strategies registered as of v2.6:
11
11
  * - NoOp (wildcard '*' fallback) — always available, registered by
12
12
  * the registry module itself
13
- * - AnthropicCacheStrategy ('anthropic', 'browser-anthropic')
13
+ * - AnthropicCacheStrategy ('anthropic', 'browser-anthropic') — the one
14
+ * end-to-end strategy: the adapter sends markers AND reads cache usage
15
+ * - OpenAICacheStrategy ('openai', 'browser-openai') — pass-through
16
+ * (OpenAI auto-caches); reports `not-applicable` metrics until the
17
+ * adapter lifts `prompt_tokens_details.cached_tokens` onto the port
18
+ * - BedrockCacheStrategy ('bedrock') — `enabled: false`; the Bedrock
19
+ * adapter implements neither half of the cache contract
14
20
  *
15
- * Future strategies (Phase 8+):
16
- * - OpenAICacheStrategy
17
- * - BedrockCacheStrategy
18
- * - GeminiCacheStrategy (v2.7+, async handle-based)
21
+ * Future strategies:
22
+ * - GeminiCacheStrategy (async handle-based)
19
23
  *
20
24
  * Public types (re-exported for consumers):
21
25
  * - CachePolicy, CacheMarker, CacheStrategy, CacheCapabilities,
22
26
  * CacheMetrics, CachePolicyContext, CacheStrategyContext
23
27
  */
24
28
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.cacheRecorder = exports.BedrockCacheStrategy = exports.OpenAICacheStrategy = exports.AnthropicCacheStrategy = exports.NoOpCacheStrategy = exports.listRegisteredStrategies = exports.registerCacheStrategy = exports.getDefaultCacheStrategy = void 0;
29
+ exports.cacheRecorder = exports.BedrockCacheStrategy = exports.OpenAICacheStrategy = exports.AnthropicCacheStrategy = exports.NoOpCacheStrategy = exports.listRegisteredStrategies = exports.registerCacheStrategy = exports.getDefaultCacheStrategy = exports.describeClaim = exports.valueOr = exports.isKnown = exports.notApplicable = exports.unknown = exports.known = void 0;
26
30
  // Side-effect imports — register strategies on module load.
27
31
  require("./strategies/AnthropicCacheStrategy.js");
28
32
  require("./strategies/OpenAICacheStrategy.js");
29
33
  require("./strategies/BedrockCacheStrategy.js");
34
+ // The honesty primitive the meter is typed in (9.59.0). Re-exported
35
+ // reference-equal from `src/lib/claim/` — the SAME symbols
36
+ // `agentfootprint/maps` exports, so `known` from either door is one function.
37
+ var claim_js_1 = require("../lib/claim/claim.js");
38
+ Object.defineProperty(exports, "known", { enumerable: true, get: function () { return claim_js_1.known; } });
39
+ Object.defineProperty(exports, "unknown", { enumerable: true, get: function () { return claim_js_1.unknown; } });
40
+ Object.defineProperty(exports, "notApplicable", { enumerable: true, get: function () { return claim_js_1.notApplicable; } });
41
+ Object.defineProperty(exports, "isKnown", { enumerable: true, get: function () { return claim_js_1.isKnown; } });
42
+ Object.defineProperty(exports, "valueOr", { enumerable: true, get: function () { return claim_js_1.valueOr; } });
43
+ Object.defineProperty(exports, "describeClaim", { enumerable: true, get: function () { return claim_js_1.describeClaim; } });
30
44
  // Strategy registry
31
45
  var strategyRegistry_js_1 = require("./strategyRegistry.js");
32
46
  Object.defineProperty(exports, "getDefaultCacheStrategy", { enumerable: true, get: function () { return strategyRegistry_js_1.getDefaultCacheStrategy; } });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;AAEH,4DAA4D;AAC5D,kDAAgD;AAChD,+CAA6C;AAC7C,gDAA8C;AAa9C,oBAAoB;AACpB,6DAI+B;AAH7B,8HAAA,uBAAuB,OAAA;AACvB,4HAAA,qBAAqB,OAAA;AACrB,+HAAA,wBAAwB,OAAA;AAG1B,wEAAwE;AACxE,0EAAsE;AAA7D,yHAAA,iBAAiB,OAAA;AAC1B,oFAAgF;AAAvE,mIAAA,sBAAsB,OAAA;AAC/B,8EAA0E;AAAjE,6HAAA,mBAAmB,OAAA;AAC5B,gFAA4E;AAAnE,+HAAA,oBAAoB,OAAA;AAE7B,WAAW;AACX,uDAAmD;AAA1C,iHAAA,aAAa,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;;;AAEH,4DAA4D;AAC5D,kDAAgD;AAChD,+CAA6C;AAC7C,gDAA8C;AAc9C,oEAAoE;AACpE,2DAA2D;AAC3D,8EAA8E;AAC9E,kDAQ+B;AAP7B,iGAAA,KAAK,OAAA;AACL,mGAAA,OAAO,OAAA;AACP,yGAAA,aAAa,OAAA;AACb,mGAAA,OAAO,OAAA;AACP,mGAAA,OAAO,OAAA;AACP,yGAAA,aAAa,OAAA;AAIf,oBAAoB;AACpB,6DAI+B;AAH7B,8HAAA,uBAAuB,OAAA;AACvB,4HAAA,qBAAqB,OAAA;AACrB,+HAAA,wBAAwB,OAAA;AAG1B,wEAAwE;AACxE,0EAAsE;AAA7D,yHAAA,iBAAiB,OAAA;AAC1B,oFAAgF;AAAvE,mIAAA,sBAAsB,OAAA;AAC/B,8EAA0E;AAAjE,6HAAA,mBAAmB,OAAA;AAC5B,gFAA4E;AAAnE,+HAAA,oBAAoB,OAAA;AAE7B,WAAW;AACX,uDAAmD;AAA1C,iHAAA,aAAa,OAAA"}
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ /**
3
+ * The one reader of PORT-shaped usage for the cache meter.
4
+ *
5
+ * Pattern: pure function, one job. Given the `usage` the framework put on
6
+ * `agentfootprint.stream.llm_end`, answer what is actually known
7
+ * about cache traffic on that call.
8
+ * Role: the honest half of the 9.59.0 meter fix. Before it, three
9
+ * strategies each parsed RAW WIRE field names (`cache_read_input_tokens`,
10
+ * `prompt_tokens_details.cached_tokens`) off a value that has never
11
+ * carried them — the framework normalises the wire at the ADAPTER
12
+ * ring (`readCacheUsage`) and only ever hands a strategy the port
13
+ * shape. Every field read `undefined`, `?? 0` turned that into a
14
+ * zero, the guard tripped, and a 20-call turn that hit cache on
15
+ * every call reported hitRate 0.
16
+ *
17
+ * The distinction this function exists to preserve: an adapter sets
18
+ * `cacheRead` / `cacheWrite` ONLY when the provider reported a number, so
19
+ * • both absent → nobody measured → `unknown`
20
+ * • either present → measured → `known` (a present 0 is a real 0)
21
+ * That is exactly the line `Claim<T>` was written to hold.
22
+ */
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.readPortCacheUsage = void 0;
25
+ const claim_js_1 = require("../lib/claim/claim.js");
26
+ /**
27
+ * Read cache metrics off port usage.
28
+ *
29
+ * @param usage the `usage` field of `agentfootprint.stream.llm_end`
30
+ * @param who the adapter named in the evidence sentence, e.g. `'the Anthropic adapter'`
31
+ */
32
+ function readPortCacheUsage(usage, who) {
33
+ if (usage === undefined || usage === null || typeof usage !== 'object') {
34
+ return (0, claim_js_1.unknown)('the llm_end event carried no usage payload, so nothing about cache traffic was measured', 'agentfootprint.stream.llm_end');
35
+ }
36
+ const { cacheRead, cacheWrite, input } = usage;
37
+ if (cacheRead === undefined && cacheWrite === undefined) {
38
+ return (0, claim_js_1.unknown)(`${who} reported no cache fields on this call — that is "nobody measured", not "no cache traffic"`, 'agentfootprint.stream.llm_end usage (port shape)');
39
+ }
40
+ return (0, claim_js_1.known)({
41
+ cacheReadTokens: cacheRead ?? 0,
42
+ cacheWriteTokens: cacheWrite ?? 0,
43
+ freshInputTokens: typeof input === 'number' ? input : 0,
44
+ }, `${who} reported cache token counts on the port usage`);
45
+ }
46
+ exports.readPortCacheUsage = readPortCacheUsage;
47
+ //# sourceMappingURL=portUsage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portUsage.js","sourceRoot":"","sources":["../../src/cache/portUsage.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;;AAEH,oDAAmE;AAGnE;;;;;GAKG;AACH,SAAgB,kBAAkB,CAChC,KAA6B,EAC7B,GAAW;IAEX,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvE,OAAO,IAAA,kBAAO,EACZ,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,IAAA,kBAAO,EACZ,GAAG,GAAG,4FAA4F,EAClG,kDAAkD,CACnD,CAAC;IACJ,CAAC;IACD,OAAO,IAAA,gBAAK,EACV;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;AAzBD,gDAyBC"}
@@ -16,7 +16,9 @@
16
16
  *
17
17
  * What this strategy DOES vs DOESN'T do:
18
18
  * - DOES: clamp markers, attach to LLMRequest.cacheMarkers,
19
- * extract metrics from response.usage
19
+ * read cache metrics off the PORT usage the framework hands it
20
+ * (`{ input, output, cacheRead?, cacheWrite? }`) — never off the raw
21
+ * Anthropic wire, which the adapter has already normalised
20
22
  * - DOES NOT: rewrite the wire body. The provider
21
23
  * (BrowserAnthropicProvider) reads `cacheMarkers` and applies
22
24
  * `cache_control` blocks during body construction. Separation of
@@ -28,6 +30,7 @@
28
30
  */
29
31
  Object.defineProperty(exports, "__esModule", { value: true });
30
32
  exports.AnthropicCacheStrategy = void 0;
33
+ const portUsage_js_1 = require("../portUsage.js");
31
34
  const strategyRegistry_js_1 = require("../strategyRegistry.js");
32
35
  /** Anthropic enforces 4 cache breakpoints per request. */
33
36
  const ANTHROPIC_MAX_MARKERS = 4;
@@ -65,22 +68,7 @@ class AnthropicCacheStrategy {
65
68
  return { request, markersApplied };
66
69
  }
67
70
  extractMetrics(usage) {
68
- if (!usage || typeof usage !== 'object')
69
- return undefined;
70
- const u = usage;
71
- const cacheRead = u.cache_read_input_tokens ?? 0;
72
- const cacheWrite = u.cache_creation_input_tokens ?? 0;
73
- const fresh = u.input_tokens ?? 0;
74
- // If neither cache field present, response didn't involve caching.
75
- // Returning undefined signals "no cache info" so cacheRecorder
76
- // doesn't compute a misleading 0% hit rate.
77
- if (cacheRead === 0 && cacheWrite === 0)
78
- return undefined;
79
- return {
80
- cacheReadTokens: cacheRead,
81
- cacheWriteTokens: cacheWrite,
82
- freshInputTokens: fresh,
83
- };
71
+ return (0, portUsage_js_1.readPortCacheUsage)(usage, 'the Anthropic adapter');
84
72
  }
85
73
  }
86
74
  exports.AnthropicCacheStrategy = AnthropicCacheStrategy;
@@ -1 +1 @@
1
- {"version":3,"file":"AnthropicCacheStrategy.js","sourceRoot":"","sources":["../../../src/cache/strategies/AnthropicCacheStrategy.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;;;AAUH,gEAA+D;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,MAAa,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;AA3DD,wDA2DC;AAED,qEAAqE;AACrE,sEAAsE;AACtE,CAAC;IACC,MAAM,QAAQ,GAAG,IAAI,sBAAsB,EAAE,CAAC;IAC9C,IAAA,2CAAqB,EAAC,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,IAAA,2CAAqB,EAAC,eAAe,CAAC,CAAC;AACzC,CAAC"}
1
+ {"version":3,"file":"AnthropicCacheStrategy.js","sourceRoot":"","sources":["../../../src/cache/strategies/AnthropicCacheStrategy.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;;;AAWH,kDAAqD;AAErD,gEAA+D;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,MAAa,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,IAAA,iCAAkB,EAAC,KAAK,EAAE,uBAAuB,CAAC,CAAC;IAC5D,CAAC;CACF;AA1CD,wDA0CC;AAED,qEAAqE;AACrE,sEAAsE;AACtE,CAAC;IACC,MAAM,QAAQ,GAAG,IAAI,sBAAsB,EAAE,CAAC;IAC9C,IAAA,2CAAqB,EAAC,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,IAAA,2CAAqB,EAAC,eAAe,CAAC,CAAC;AACzC,CAAC"}
@@ -1,79 +1,95 @@
1
1
  "use strict";
2
2
  /**
3
- * BedrockCacheStrategy — model-aware strategy for AWS Bedrock.
3
+ * BedrockCacheStrategy — REGISTERED, AND HONEST ABOUT DOING NOTHING (9.59.0).
4
4
  *
5
- * Bedrock hosts multiple model families. Cache support varies:
6
- * - Claude on Bedrock identical mechanics to direct Anthropic
7
- * (`cache_control: { type: 'ephemeral' }` markers, 4-marker
8
- * limit). Strategy delegates to Anthropic-shaped behavior.
9
- * - Llama / Mistral / Cohere on Bedrock → no cache support today
10
- * (as of 2026-04-30). Strategy passes through, returns no metrics.
5
+ * What Bedrock supports and what THIS ADAPTER does are two different facts,
6
+ * and until 9.59.0 the strategy stated the first while the runtime lived
7
+ * under the second:
11
8
  *
12
- * Auto-detection: inspects `req.model` to decide. Claude model IDs
13
- * start with `'anthropic.claude'` on Bedrock (e.g.,
14
- * `anthropic.claude-3-5-sonnet-20240620-v1:0`).
9
+ * - AWS's Converse API does support prompt caching for Claude models
10
+ * (`cachePoint: { type: 'default' }` entries inserted into the `system` /
11
+ * `tools` / `messages` arrays) and does report
12
+ * `cacheReadInputTokens` / `cacheWriteInputTokens` in its usage block.
13
+ * - `BedrockProvider` implements NEITHER half. It never reads
14
+ * `req.cacheMarkers` (compare `AnthropicProvider`, which calls
15
+ * `applyCacheMarkers`) and it builds `usage` as `{ input, output }` at
16
+ * both its streaming and non-streaming sites. The string "cache" does
17
+ * not appear in that file.
15
18
  *
16
- * Auto-registers under provider name `'bedrock'`.
19
+ * So this strategy used to clamp markers onto a request field the adapter
20
+ * then discarded, and report `markersApplied` for markers that never reached
21
+ * a wire — a meter attached to a provider that cannot feed it. It now:
22
+ *
23
+ * - declares `enabled: false` (nothing here works end to end);
24
+ * - passes the request through with `markersApplied: []`, and says why
25
+ * once, in dev mode;
26
+ * - answers `extractMetrics` with `not-applicable` and the reason.
27
+ *
28
+ * It stays REGISTERED rather than being deleted so that a Bedrock consumer
29
+ * asking the registry what it got is told the truth by name, instead of
30
+ * silently falling through to the wildcard NoOp and being left to guess.
17
31
  *
18
- * Per the Phase 1 review (Reviewer 6 Provider SDK expert): for
19
- * non-Claude Bedrock models the strategy reports `enabled: false` in
20
- * its capabilities so the CacheDecision subflow can short-circuit
21
- * marker emission (potential v2.7 optimization). Today markers still
22
- * emit and we drop them silently in prepareRequest.
32
+ * Note on Converse: the send half is genuinely a DIFFERENT wire from
33
+ * Anthropic's `cache_control` block, so implementing it wants its own
34
+ * `bedrockCacheWire.ts` beside `anthropicCacheWire.ts` not a reuse. That
35
+ * is the follow-up this file is waiting for; when it lands, flip `enabled`
36
+ * and give `extractMetrics` the `readPortCacheUsage` body the Anthropic
37
+ * strategy has.
38
+ *
39
+ * Auto-registers under provider name `'bedrock'`.
23
40
  */
24
41
  Object.defineProperty(exports, "__esModule", { value: true });
25
42
  exports.BedrockCacheStrategy = void 0;
43
+ const claim_js_1 = require("../../lib/claim/claim.js");
26
44
  const strategyRegistry_js_1 = require("../strategyRegistry.js");
27
- /** Match Bedrock-Claude model ids: `anthropic.claude-...` */
28
- const BEDROCK_CLAUDE_RE = /^anthropic\.claude/i;
29
- /** Anthropic's 4-marker limit applies to Bedrock-Claude too. */
30
- const BEDROCK_MAX_MARKERS = 4;
45
+ const footprintjs_1 = require("footprintjs");
31
46
  const BEDROCK_CAPABILITIES = Object.freeze({
32
- // We say `enabled: true` at the capability level because Bedrock-
33
- // Claude DOES support caching. Bedrock-Llama/Mistral land in the
34
- // model-aware code path inside prepareRequest (no markers applied).
35
- enabled: true,
36
- maxMarkers: BEDROCK_MAX_MARKERS,
37
- ttls: ['short', 'long'],
38
- fields: ['system', 'tools', 'messages'],
47
+ // `false`, and it is a statement about the ADAPTER, not about Bedrock.
48
+ // Bedrock-Claude supports prompt caching; `BedrockProvider` implements
49
+ // neither half of the contract (never sends markers, never reads cache
50
+ // usage), so nothing this strategy could do would reach a wire. Saying
51
+ // `true` here is what let a dead meter look alive.
52
+ enabled: false,
53
+ maxMarkers: 0,
54
+ ttls: [],
55
+ fields: [],
39
56
  automatic: false,
40
57
  });
58
+ /** One warning per process, not one per call — the fact does not change. */
59
+ let warnedAboutAdapterGap = false;
41
60
  class BedrockCacheStrategy {
42
61
  providerName = 'bedrock';
43
62
  capabilities = BEDROCK_CAPABILITIES;
44
- async prepareRequest(req, candidates, ctx) {
45
- if (ctx.cachingDisabled || candidates.length === 0) {
46
- return { request: req, markersApplied: [] };
47
- }
48
- // Model-aware: only Claude on Bedrock supports cache_control.
49
- // Other model families silently drop the markers.
50
- if (!BEDROCK_CLAUDE_RE.test(req.model)) {
51
- return { request: req, markersApplied: [] };
63
+ async prepareRequest(req, candidates, _ctx) {
64
+ // Pass-through, ALWAYS. Writing `req.cacheMarkers` here would be writing
65
+ // to a field `BedrockProvider` never reads, and returning a non-empty
66
+ // `markersApplied` would put markers that never reached a wire onto the
67
+ // recorder's record.
68
+ if (candidates.length > 0 && !warnedAboutAdapterGap && (0, footprintjs_1.isDevMode)()) {
69
+ warnedAboutAdapterGap = true;
70
+ // eslint-disable-next-line no-console
71
+ console.warn(`[agentfootprint] cache: ${candidates.length} cache marker(s) were declared for ` +
72
+ `model "${req.model}", but the Bedrock adapter does not implement prompt caching — ` +
73
+ `it never sends cachePoint entries and never reads cacheReadInputTokens off the ` +
74
+ `Converse response. No markers were applied and no hit rate can be measured. ` +
75
+ `Your cache: directives stay portable and light up on the Anthropic providers.`);
52
76
  }
53
- const markersApplied = candidates.length <= BEDROCK_MAX_MARKERS
54
- ? candidates
55
- : candidates.slice(0, BEDROCK_MAX_MARKERS);
56
- return {
57
- request: { ...req, cacheMarkers: markersApplied },
58
- markersApplied,
59
- };
77
+ return { request: req, markersApplied: [] };
60
78
  }
61
- extractMetrics(usage) {
62
- // Bedrock returns the SAME usage shape as Anthropic for Claude
63
- // models same cache_creation_input_tokens / cache_read_input_tokens
64
- // fields. Reuse identical extraction.
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
- if (cacheRead === 0 && cacheWrite === 0)
71
- return undefined;
72
- return {
73
- cacheReadTokens: cacheRead,
74
- cacheWriteTokens: cacheWrite,
75
- freshInputTokens: u.input_tokens ?? 0,
76
- };
79
+ /**
80
+ * Always `not-applicable` and that is a statement about THIS adapter,
81
+ * not about Bedrock. AWS's Converse API does report
82
+ * `cacheReadInputTokens` / `cacheWriteInputTokens`, but
83
+ * `BedrockProvider` never reads them: its `usage` is built as
84
+ * `{ input, output }` at both the streaming and non-streaming sites. So
85
+ * `cacheRead`/`cacheWrite` are ALWAYS absent on this port, and an
86
+ * `unknown` per call would read as a measurement that merely failed.
87
+ * `not-applicable` says the true thing: nothing here can be measured
88
+ * until the adapter grows the read half.
89
+ */
90
+ extractMetrics(_usage) {
91
+ return (0, claim_js_1.notApplicable)('the Bedrock adapter does not read cache usage off the Converse response, so no ' +
92
+ 'cache tokens ever reach the port');
77
93
  }
78
94
  }
79
95
  exports.BedrockCacheStrategy = BedrockCacheStrategy;