agentfootprint 7.14.0 → 7.16.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 (203) hide show
  1. package/dist/adapters/hosting/agentcore.js +350 -0
  2. package/dist/adapters/hosting/agentcore.js.map +1 -0
  3. package/dist/adapters/llm/BrowserOpenAIProvider.js +7 -3
  4. package/dist/adapters/llm/BrowserOpenAIProvider.js.map +1 -1
  5. package/dist/adapters/llm/OpenAIProvider.js +11 -3
  6. package/dist/adapters/llm/OpenAIProvider.js.map +1 -1
  7. package/dist/adapters/memory/agentcore.js +125 -1
  8. package/dist/adapters/memory/agentcore.js.map +1 -1
  9. package/dist/adapters/security/agentcore.js +0 -0
  10. package/dist/adapters/security/agentcore.js.map +1 -0
  11. package/dist/conventions.js +7 -0
  12. package/dist/conventions.js.map +1 -1
  13. package/dist/core/Agent.js +56 -2
  14. package/dist/core/Agent.js.map +1 -1
  15. package/dist/core/agent/AgentBuilder.js +86 -1
  16. package/dist/core/agent/AgentBuilder.js.map +1 -1
  17. package/dist/core/agent/buildAgentChart.js +12 -1
  18. package/dist/core/agent/buildAgentChart.js.map +1 -1
  19. package/dist/core/agent/buildDynamicAgentChart.js +10 -1
  20. package/dist/core/agent/buildDynamicAgentChart.js.map +1 -1
  21. package/dist/core/agent/compaction/errors.js +40 -0
  22. package/dist/core/agent/compaction/errors.js.map +1 -0
  23. package/dist/core/agent/compaction/index.js +24 -0
  24. package/dist/core/agent/compaction/index.js.map +1 -0
  25. package/dist/core/agent/compaction/strategy.js +152 -0
  26. package/dist/core/agent/compaction/strategy.js.map +1 -0
  27. package/dist/core/agent/compaction/summarize.js +103 -0
  28. package/dist/core/agent/compaction/summarize.js.map +1 -0
  29. package/dist/core/agent/compaction/turns.js +153 -0
  30. package/dist/core/agent/compaction/turns.js.map +1 -0
  31. package/dist/core/agent/compaction/types.js +21 -0
  32. package/dist/core/agent/compaction/types.js.map +1 -0
  33. package/dist/core/agent/stages/compact.js +130 -0
  34. package/dist/core/agent/stages/compact.js.map +1 -0
  35. package/dist/esm/adapters/hosting/agentcore.d.ts +199 -0
  36. package/dist/esm/adapters/hosting/agentcore.js +321 -0
  37. package/dist/esm/adapters/hosting/agentcore.js.map +1 -0
  38. package/dist/esm/adapters/llm/BrowserOpenAIProvider.js +7 -3
  39. package/dist/esm/adapters/llm/BrowserOpenAIProvider.js.map +1 -1
  40. package/dist/esm/adapters/llm/OpenAIProvider.js +11 -3
  41. package/dist/esm/adapters/llm/OpenAIProvider.js.map +1 -1
  42. package/dist/esm/adapters/memory/agentcore.d.ts +88 -2
  43. package/dist/esm/adapters/memory/agentcore.js +125 -1
  44. package/dist/esm/adapters/memory/agentcore.js.map +1 -1
  45. package/dist/esm/adapters/security/agentcore.d.ts +157 -0
  46. package/dist/esm/adapters/security/agentcore.js +0 -0
  47. package/dist/esm/adapters/security/agentcore.js.map +1 -0
  48. package/dist/esm/conventions.d.ts +7 -0
  49. package/dist/esm/conventions.js +7 -0
  50. package/dist/esm/conventions.js.map +1 -1
  51. package/dist/esm/core/Agent.d.ts +8 -1
  52. package/dist/esm/core/Agent.js +56 -2
  53. package/dist/esm/core/Agent.js.map +1 -1
  54. package/dist/esm/core/agent/AgentBuilder.d.ts +43 -0
  55. package/dist/esm/core/agent/AgentBuilder.js +86 -1
  56. package/dist/esm/core/agent/AgentBuilder.js.map +1 -1
  57. package/dist/esm/core/agent/buildAgentChart.d.ts +13 -0
  58. package/dist/esm/core/agent/buildAgentChart.js +12 -1
  59. package/dist/esm/core/agent/buildAgentChart.js.map +1 -1
  60. package/dist/esm/core/agent/buildDynamicAgentChart.js +10 -1
  61. package/dist/esm/core/agent/buildDynamicAgentChart.js.map +1 -1
  62. package/dist/esm/core/agent/compaction/errors.d.ts +26 -0
  63. package/dist/esm/core/agent/compaction/errors.js +36 -0
  64. package/dist/esm/core/agent/compaction/errors.js.map +1 -0
  65. package/dist/esm/core/agent/compaction/index.d.ts +18 -0
  66. package/dist/esm/core/agent/compaction/index.js +18 -0
  67. package/dist/esm/core/agent/compaction/index.js.map +1 -0
  68. package/dist/esm/core/agent/compaction/strategy.d.ts +103 -0
  69. package/dist/esm/core/agent/compaction/strategy.js +148 -0
  70. package/dist/esm/core/agent/compaction/strategy.js.map +1 -0
  71. package/dist/esm/core/agent/compaction/summarize.d.ts +61 -0
  72. package/dist/esm/core/agent/compaction/summarize.js +96 -0
  73. package/dist/esm/core/agent/compaction/summarize.js.map +1 -0
  74. package/dist/esm/core/agent/compaction/turns.d.ts +85 -0
  75. package/dist/esm/core/agent/compaction/turns.js +145 -0
  76. package/dist/esm/core/agent/compaction/turns.js.map +1 -0
  77. package/dist/esm/core/agent/compaction/types.d.ts +148 -0
  78. package/dist/esm/core/agent/compaction/types.js +20 -0
  79. package/dist/esm/core/agent/compaction/types.js.map +1 -0
  80. package/dist/esm/core/agent/stages/compact.d.ts +59 -0
  81. package/dist/esm/core/agent/stages/compact.js +126 -0
  82. package/dist/esm/core/agent/stages/compact.js.map +1 -0
  83. package/dist/esm/core/agent/types.d.ts +11 -0
  84. package/dist/esm/hosting/httpHost.d.ts +121 -0
  85. package/dist/esm/hosting/httpHost.js +248 -0
  86. package/dist/esm/hosting/httpHost.js.map +1 -0
  87. package/dist/esm/hosting/index.d.ts +8 -1
  88. package/dist/esm/hosting/index.js +7 -1
  89. package/dist/esm/hosting/index.js.map +1 -1
  90. package/dist/esm/hosting/nodeHost.d.ts +23 -16
  91. package/dist/esm/hosting/nodeHost.js +32 -199
  92. package/dist/esm/hosting/nodeHost.js.map +1 -1
  93. package/dist/esm/hosting-providers.d.ts +50 -0
  94. package/dist/esm/hosting-providers.js +50 -0
  95. package/dist/esm/hosting-providers.js.map +1 -0
  96. package/dist/esm/index.d.ts +1 -0
  97. package/dist/esm/index.js +5 -0
  98. package/dist/esm/index.js.map +1 -1
  99. package/dist/esm/lib/mcp/gatewayTransport.d.ts +103 -0
  100. package/dist/esm/lib/mcp/gatewayTransport.js +123 -0
  101. package/dist/esm/lib/mcp/gatewayTransport.js.map +1 -0
  102. package/dist/esm/lib/mcp/index.d.ts +2 -1
  103. package/dist/esm/lib/mcp/index.js +1 -0
  104. package/dist/esm/lib/mcp/index.js.map +1 -1
  105. package/dist/esm/lib/mcp/mcpClient.js +10 -2
  106. package/dist/esm/lib/mcp/mcpClient.js.map +1 -1
  107. package/dist/esm/lib/mcp/types.d.ts +45 -1
  108. package/dist/esm/memory/store/types.d.ts +22 -0
  109. package/dist/esm/memory-providers.d.ts +1 -1
  110. package/dist/esm/memory-providers.js.map +1 -1
  111. package/dist/esm/recorders/core/CompactionMeter.d.ts +78 -0
  112. package/dist/esm/recorders/core/CompactionMeter.js +101 -0
  113. package/dist/esm/recorders/core/CompactionMeter.js.map +1 -0
  114. package/dist/esm/security/index.d.ts +10 -1
  115. package/dist/esm/security/index.js +14 -1
  116. package/dist/esm/security/index.js.map +1 -1
  117. package/dist/esm/tool-providers/index.d.ts +6 -2
  118. package/dist/esm/tool-providers/index.js +5 -1
  119. package/dist/esm/tool-providers/index.js.map +1 -1
  120. package/dist/hosting/httpHost.js +276 -0
  121. package/dist/hosting/httpHost.js.map +1 -0
  122. package/dist/hosting/index.js +10 -1
  123. package/dist/hosting/index.js.map +1 -1
  124. package/dist/hosting/nodeHost.js +34 -224
  125. package/dist/hosting/nodeHost.js.map +1 -1
  126. package/dist/hosting-providers.js +57 -0
  127. package/dist/hosting-providers.js.map +1 -0
  128. package/dist/index.js +13 -5
  129. package/dist/index.js.map +1 -1
  130. package/dist/lib/mcp/gatewayTransport.js +129 -0
  131. package/dist/lib/mcp/gatewayTransport.js.map +1 -0
  132. package/dist/lib/mcp/index.js +4 -1
  133. package/dist/lib/mcp/index.js.map +1 -1
  134. package/dist/lib/mcp/mcpClient.js +10 -2
  135. package/dist/lib/mcp/mcpClient.js.map +1 -1
  136. package/dist/memory-providers.js.map +1 -1
  137. package/dist/recorders/core/CompactionMeter.js +105 -0
  138. package/dist/recorders/core/CompactionMeter.js.map +1 -0
  139. package/dist/security/index.js +16 -2
  140. package/dist/security/index.js.map +1 -1
  141. package/dist/tool-providers/index.js +7 -1
  142. package/dist/tool-providers/index.js.map +1 -1
  143. package/dist/types/adapters/hosting/agentcore.d.ts +200 -0
  144. package/dist/types/adapters/hosting/agentcore.d.ts.map +1 -0
  145. package/dist/types/adapters/llm/BrowserOpenAIProvider.d.ts.map +1 -1
  146. package/dist/types/adapters/llm/OpenAIProvider.d.ts.map +1 -1
  147. package/dist/types/adapters/memory/agentcore.d.ts +88 -2
  148. package/dist/types/adapters/memory/agentcore.d.ts.map +1 -1
  149. package/dist/types/adapters/security/agentcore.d.ts +158 -0
  150. package/dist/types/adapters/security/agentcore.d.ts.map +1 -0
  151. package/dist/types/conventions.d.ts +7 -0
  152. package/dist/types/conventions.d.ts.map +1 -1
  153. package/dist/types/core/Agent.d.ts +8 -1
  154. package/dist/types/core/Agent.d.ts.map +1 -1
  155. package/dist/types/core/agent/AgentBuilder.d.ts +43 -0
  156. package/dist/types/core/agent/AgentBuilder.d.ts.map +1 -1
  157. package/dist/types/core/agent/buildAgentChart.d.ts +13 -0
  158. package/dist/types/core/agent/buildAgentChart.d.ts.map +1 -1
  159. package/dist/types/core/agent/buildDynamicAgentChart.d.ts.map +1 -1
  160. package/dist/types/core/agent/compaction/errors.d.ts +27 -0
  161. package/dist/types/core/agent/compaction/errors.d.ts.map +1 -0
  162. package/dist/types/core/agent/compaction/index.d.ts +19 -0
  163. package/dist/types/core/agent/compaction/index.d.ts.map +1 -0
  164. package/dist/types/core/agent/compaction/strategy.d.ts +104 -0
  165. package/dist/types/core/agent/compaction/strategy.d.ts.map +1 -0
  166. package/dist/types/core/agent/compaction/summarize.d.ts +62 -0
  167. package/dist/types/core/agent/compaction/summarize.d.ts.map +1 -0
  168. package/dist/types/core/agent/compaction/turns.d.ts +86 -0
  169. package/dist/types/core/agent/compaction/turns.d.ts.map +1 -0
  170. package/dist/types/core/agent/compaction/types.d.ts +149 -0
  171. package/dist/types/core/agent/compaction/types.d.ts.map +1 -0
  172. package/dist/types/core/agent/stages/compact.d.ts +60 -0
  173. package/dist/types/core/agent/stages/compact.d.ts.map +1 -0
  174. package/dist/types/core/agent/types.d.ts +11 -0
  175. package/dist/types/core/agent/types.d.ts.map +1 -1
  176. package/dist/types/hosting/httpHost.d.ts +122 -0
  177. package/dist/types/hosting/httpHost.d.ts.map +1 -0
  178. package/dist/types/hosting/index.d.ts +8 -1
  179. package/dist/types/hosting/index.d.ts.map +1 -1
  180. package/dist/types/hosting/nodeHost.d.ts +23 -16
  181. package/dist/types/hosting/nodeHost.d.ts.map +1 -1
  182. package/dist/types/hosting-providers.d.ts +51 -0
  183. package/dist/types/hosting-providers.d.ts.map +1 -0
  184. package/dist/types/index.d.ts +1 -0
  185. package/dist/types/index.d.ts.map +1 -1
  186. package/dist/types/lib/mcp/gatewayTransport.d.ts +104 -0
  187. package/dist/types/lib/mcp/gatewayTransport.d.ts.map +1 -0
  188. package/dist/types/lib/mcp/index.d.ts +2 -1
  189. package/dist/types/lib/mcp/index.d.ts.map +1 -1
  190. package/dist/types/lib/mcp/mcpClient.d.ts.map +1 -1
  191. package/dist/types/lib/mcp/types.d.ts +45 -1
  192. package/dist/types/lib/mcp/types.d.ts.map +1 -1
  193. package/dist/types/memory/store/types.d.ts +22 -0
  194. package/dist/types/memory/store/types.d.ts.map +1 -1
  195. package/dist/types/memory-providers.d.ts +1 -1
  196. package/dist/types/memory-providers.d.ts.map +1 -1
  197. package/dist/types/recorders/core/CompactionMeter.d.ts +79 -0
  198. package/dist/types/recorders/core/CompactionMeter.d.ts.map +1 -0
  199. package/dist/types/security/index.d.ts +10 -1
  200. package/dist/types/security/index.d.ts.map +1 -1
  201. package/dist/types/tool-providers/index.d.ts +6 -2
  202. package/dist/types/tool-providers/index.d.ts.map +1 -1
  203. package/package.json +14 -1
@@ -0,0 +1,145 @@
1
+ /**
2
+ * compaction/turns — where a turn boundary is, and which turns may fold.
3
+ *
4
+ * Pattern: Pure functions over the window (no scope, no I/O, no clock).
5
+ * Role: core/ layer. The whole safety argument of compaction lives here:
6
+ * a fold that splits an assistant's `tool_use` from its
7
+ * `tool_result` produces a request the vendor rejects, and a fold
8
+ * that swallows an unanswered question destroys the referent of the
9
+ * answer that has not arrived yet.
10
+ * Emits: N/A.
11
+ *
12
+ * Testable on its own — see `test/core/agent/compaction-turns.test.ts`.
13
+ */
14
+ /**
15
+ * Segment a window into turns.
16
+ *
17
+ * A new turn starts at any non-`tool` message; `tool` messages join the turn
18
+ * in progress. A leading `tool` message (only reachable from a hand-built
19
+ * history) starts its own turn rather than being silently dropped.
20
+ */
21
+ export function segmentTurns(history) {
22
+ const turns = [];
23
+ let current = [];
24
+ let start = 0;
25
+ const flush = () => {
26
+ if (current.length === 0)
27
+ return;
28
+ turns.push({ index: turns.length, start, length: current.length, messages: current });
29
+ current = [];
30
+ };
31
+ for (let i = 0; i < history.length; i++) {
32
+ const msg = history[i];
33
+ if (msg.role !== 'tool' || current.length === 0) {
34
+ flush();
35
+ start = i;
36
+ }
37
+ current.push(msg);
38
+ }
39
+ flush();
40
+ return turns;
41
+ }
42
+ /** Every tool_call id that has a matching `role: 'tool'` message. */
43
+ export function answeredCallIds(history) {
44
+ const answered = new Set();
45
+ for (const msg of history) {
46
+ if (msg.role === 'tool' && msg.toolCallId)
47
+ answered.add(msg.toolCallId);
48
+ }
49
+ return answered;
50
+ }
51
+ /**
52
+ * Why this turn may NOT fold, or `undefined` when it may.
53
+ *
54
+ * Order matters only for which reason gets reported first; every check is
55
+ * independent. `paused-tool` / `pending-check-in` are separated from
56
+ * `unresolved-tool-call` on purpose: they are the same shape but a different
57
+ * fact about the world, and "we are waiting on a human" is what the person
58
+ * reading the trace needs to see.
59
+ */
60
+ export function refusalFor(turn, ctx) {
61
+ for (const msg of turn.messages) {
62
+ if (msg.role === 'system')
63
+ return 'system-envelope';
64
+ }
65
+ const paused = ctx.pausedToolCallId;
66
+ if (paused !== undefined && paused.length > 0) {
67
+ for (const msg of turn.messages) {
68
+ const holdsPaused = msg.toolCallId === paused || (msg.toolCalls ?? []).some((tc) => tc.id === paused);
69
+ if (holdsPaused)
70
+ return ctx.pausedCheckIn === true ? 'pending-check-in' : 'paused-tool';
71
+ }
72
+ }
73
+ for (const msg of turn.messages) {
74
+ for (const call of msg.toolCalls ?? []) {
75
+ if (!ctx.answeredCallIds.has(call.id))
76
+ return 'unresolved-tool-call';
77
+ }
78
+ }
79
+ return undefined;
80
+ }
81
+ /**
82
+ * Choose the fold span: the LONGEST CONTIGUOUS run of foldable candidate
83
+ * turns, starting at the oldest foldable one.
84
+ *
85
+ * Contiguity is not fussiness — it is what keeps the conversation in order.
86
+ * A fold replaces its span with ONE summary message; if the span skipped over
87
+ * an unfoldable turn, that turn would end up after a summary of things that
88
+ * happened before it. So an unfoldable turn at the front is stepped over (the
89
+ * fold "takes the next oldest instead") and an unfoldable turn in the middle
90
+ * ends the span. Everything not folded keeps its position.
91
+ *
92
+ * @param turns the window's turn segmentation
93
+ * @param keepRecent how many trailing turns are off-limits
94
+ * @param ctx foldability inputs
95
+ * @param isSummaryTurn true for a turn that is a summary a prior fold wrote
96
+ */
97
+ export function planFold(turns, keepRecent, ctx, isSummaryTurn) {
98
+ const refusals = [];
99
+ const candidateCount = Math.max(0, turns.length - keepRecent);
100
+ for (let i = candidateCount; i < turns.length; i++) {
101
+ const turn = turns[i];
102
+ refusals.push({ reason: 'inside-keep-window', turnIndex: i, messageIndex: turn.start });
103
+ }
104
+ if (candidateCount === 0)
105
+ return { from: -1, to: -1, refusals };
106
+ const before = [];
107
+ let from = -1;
108
+ let to = -1;
109
+ for (let i = 0; i < candidateCount; i++) {
110
+ const turn = turns[i];
111
+ const reason = refusalFor(turn, ctx);
112
+ if (reason === undefined) {
113
+ if (from === -1)
114
+ from = i;
115
+ to = i;
116
+ continue;
117
+ }
118
+ before.push({ reason, turnIndex: i, messageIndex: turn.start });
119
+ if (from !== -1)
120
+ break; // an unfoldable turn ENDS the span
121
+ }
122
+ // A span that is nothing but one existing summary is not worth a call:
123
+ // re-summarizing a summary spends tokens to lose detail and names nothing
124
+ // new. It becomes foldable again as soon as a real turn joins it.
125
+ if (from !== -1 && from === to && isSummaryTurn(turns[from])) {
126
+ return {
127
+ from: -1,
128
+ to: -1,
129
+ refusals: [
130
+ ...before,
131
+ { reason: 'only-existing-summary', turnIndex: from, messageIndex: turns[from].start },
132
+ ...refusals,
133
+ ],
134
+ };
135
+ }
136
+ return { from, to, refusals: [...before, ...refusals] };
137
+ }
138
+ /** Total characters of message content in a window. Exact; not tokens. */
139
+ export function windowChars(history) {
140
+ let total = 0;
141
+ for (const msg of history)
142
+ total += msg.content.length;
143
+ return total;
144
+ }
145
+ //# sourceMappingURL=turns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turns.js","sourceRoot":"","sources":["../../../../../src/core/agent/compaction/turns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAoBH;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,OAA8B;IACzD,MAAM,KAAK,GAAW,EAAE,CAAC;IACzB,IAAI,OAAO,GAAiB,EAAE,CAAC;IAC/B,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,MAAM,KAAK,GAAG,GAAS,EAAE;QACvB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACjC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QACtF,OAAO,GAAG,EAAE,CAAC;IACf,CAAC,CAAC;IAEF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,CAAE,CAAC;QACxB,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChD,KAAK,EAAE,CAAC;YACR,KAAK,GAAG,CAAC,CAAC;QACZ,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpB,CAAC;IACD,KAAK,EAAE,CAAC;IACR,OAAO,KAAK,CAAC;AACf,CAAC;AAYD,qEAAqE;AACrE,MAAM,UAAU,eAAe,CAAC,OAA8B;IAC5D,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,GAAG,CAAC,UAAU;YAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,IAAU,EAAE,GAAuB;IAC5D,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChC,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,iBAAiB,CAAC;IACtD,CAAC;IACD,MAAM,MAAM,GAAG,GAAG,CAAC,gBAAgB,CAAC;IACpC,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChC,MAAM,WAAW,GACf,GAAG,CAAC,UAAU,KAAK,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;YACpF,IAAI,WAAW;gBAAE,OAAO,GAAG,CAAC,aAAa,KAAK,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,aAAa,CAAC;QAC1F,CAAC;IACH,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChC,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC;YACvC,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAAE,OAAO,sBAAsB,CAAC;QACvE,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAWD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,QAAQ,CACtB,KAAsB,EACtB,UAAkB,EAClB,GAAuB,EACvB,aAAsC;IAEtC,MAAM,QAAQ,GAAkB,EAAE,CAAC;IACnC,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC;IAE9D,KAAK,IAAI,CAAC,GAAG,cAAc,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACnD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,oBAAoB,EAAE,SAAS,EAAE,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IAC1F,CAAC;IACD,IAAI,cAAc,KAAK,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC;IAEhE,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC;IACd,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,cAAc,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QACvB,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACrC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,IAAI,IAAI,KAAK,CAAC,CAAC;gBAAE,IAAI,GAAG,CAAC,CAAC;YAC1B,EAAE,GAAG,CAAC,CAAC;YACP,SAAS;QACX,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAChE,IAAI,IAAI,KAAK,CAAC,CAAC;YAAE,MAAM,CAAC,mCAAmC;IAC7D,CAAC;IAED,uEAAuE;IACvE,0EAA0E;IAC1E,kEAAkE;IAClE,IAAI,IAAI,KAAK,CAAC,CAAC,IAAI,IAAI,KAAK,EAAE,IAAI,aAAa,CAAC,KAAK,CAAC,IAAI,CAAE,CAAC,EAAE,CAAC;QAC9D,OAAO;YACL,IAAI,EAAE,CAAC,CAAC;YACR,EAAE,EAAE,CAAC,CAAC;YACN,QAAQ,EAAE;gBACR,GAAG,MAAM;gBACT,EAAE,MAAM,EAAE,uBAAuB,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,CAAC,IAAI,CAAE,CAAC,KAAK,EAAE;gBACtF,GAAG,QAAQ;aACZ;SACF,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,GAAG,MAAM,EAAE,GAAG,QAAQ,CAAC,EAAE,CAAC;AAC1D,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,WAAW,CAAC,OAA8B;IACxD,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,GAAG,IAAI,OAAO;QAAE,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;IACvD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,148 @@
1
+ /**
2
+ * compaction/types — the public shape of `.compaction()` and the record it
3
+ * writes into the ledger.
4
+ *
5
+ * Pattern: Value objects (no behavior) + one resolved-config type.
6
+ * Role: core/ layer. The law this feature exists to keep is stated once,
7
+ * here, because every other file in this folder implements a piece
8
+ * of it: **compaction edits the WINDOW, never the LEDGER.**
9
+ * Emits: N/A (types only).
10
+ *
11
+ * The window is `scope.history` — the array `call-llm` hands the provider.
12
+ * The ledger is footprintjs's commit log, which is append-only: the turns a
13
+ * fold removes from the window were committed by `seed#0` / `tool-calls#N`
14
+ * BEFORE the fold and stay in those bundles byte-identical forever. A fold
15
+ * therefore cannot destroy history even in principle; it can only stop
16
+ * re-sending it. The summary is a CLAIM about the past, so it is filed as a
17
+ * claim — its own recorded step, naming every `runtimeStageId` it folded.
18
+ */
19
+ import type { LLMProvider } from '../../../adapters/types.js';
20
+ /**
21
+ * What `.compaction({...})` accepts.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * const agent = Agent.create({ provider: anthropic(), model: 'claude-sonnet-4-5' })
26
+ * .compaction({
27
+ * thresholdTokens: 120_000,
28
+ * summarizer: anthropic(), // usually the cheap one
29
+ * model: 'claude-haiku-4-5',
30
+ * keepRecentTurns: 6,
31
+ * })
32
+ * .build();
33
+ * ```
34
+ */
35
+ export interface CompactionOptions {
36
+ /**
37
+ * Fold when the LAST call's adapter-reported input tokens exceed this.
38
+ *
39
+ * REQUIRED, with no default. A default budget here would be a number the
40
+ * library invented for a window whose size only the consumer's model and
41
+ * wallet know — and every run would silently inherit it.
42
+ */
43
+ readonly thresholdTokens: number;
44
+ /**
45
+ * How many of the most recent turns are never folded. Default 6.
46
+ *
47
+ * The recent turns are what the model is actually reasoning over; folding
48
+ * them is how a compacting agent loses the thread.
49
+ */
50
+ readonly keepRecentTurns?: number;
51
+ /**
52
+ * The provider that writes the summary. Explicitly chosen — the library
53
+ * never quietly bills your main model for compaction.
54
+ */
55
+ readonly summarizer: LLMProvider;
56
+ /**
57
+ * Model id for the summarizer call. Defaults to the agent's own model, so
58
+ * `summarizer: anthropic()` alone works; name a cheap model to spend less.
59
+ */
60
+ readonly model?: string;
61
+ }
62
+ /** Resolved form — defaults applied at build time, validated once. */
63
+ export interface ResolvedCompaction {
64
+ readonly thresholdTokens: number;
65
+ readonly keepRecentTurns: number;
66
+ readonly summarizer: LLMProvider;
67
+ readonly model: string | undefined;
68
+ }
69
+ /**
70
+ * Why a turn refused to fold. Every one of these is NAMED in the commit —
71
+ * a fold that took less than it could have has to say why, or the next
72
+ * person debugging an over-budget window has to guess.
73
+ */
74
+ export type FoldRefusalReason =
75
+ /** The turn holds a `role: 'system'` message. The envelope never folds. */
76
+ 'system-envelope'
77
+ /**
78
+ * An assistant `tool_use` in this turn has no matching `tool_result` in the
79
+ * window. Folding an unanswered question destroys the answer's referent —
80
+ * and the referent may still arrive (a paused run resumes).
81
+ */
82
+ | 'unresolved-tool-call'
83
+ /** The turn holds the tool call this run is currently paused on. */
84
+ | 'paused-tool'
85
+ /** The turn holds a tool call waiting on a human check-in decision. */
86
+ | 'pending-check-in'
87
+ /** Inside `keepRecentTurns` — the recent window is never a candidate. */
88
+ | 'inside-keep-window'
89
+ /**
90
+ * The only foldable candidate is a summary a previous fold wrote. Folding a
91
+ * summary of a summary with nothing new to add spends a call to lose detail.
92
+ */
93
+ | 'only-existing-summary'
94
+ /** The summarizer threw. No fold this iteration; the window stays big. */
95
+ | 'summarizer-failed'
96
+ /**
97
+ * The summary came back no smaller than the span it would replace, so the
98
+ * fold was abandoned. Measured in chars on both sides — the same unit, an
99
+ * exact comparison, not a token guess. Folding here would spend a call to
100
+ * make the window BIGGER and lose the detail as well.
101
+ */
102
+ | 'summary-not-smaller';
103
+ /** One named refusal, positioned so a reader can find the turn. */
104
+ export interface FoldRefusal {
105
+ readonly reason: FoldRefusalReason;
106
+ /** Index of the turn in this iteration's turn segmentation. */
107
+ readonly turnIndex: number;
108
+ /** Index of the turn's first message in the pre-fold window. */
109
+ readonly messageIndex: number;
110
+ }
111
+ /**
112
+ * What one visit to the compaction stage put in the ledger. Appended to
113
+ * `scope.compactions`, so the run's whole compaction story is one array in
114
+ * the commit log — including the visits that folded NOTHING, which are the
115
+ * interesting ones.
116
+ *
117
+ * On `windowChars*` vs tokens: the char counts are EXACT and measured here.
118
+ * There is deliberately no `tokensAfter` — nothing can count the tokens of a
119
+ * window that has not been sent yet, and inventing one would be exactly the
120
+ * guess this feature exists to refuse. The honest "after" is the NEXT call's
121
+ * `stream.llm_end` usage.
122
+ */
123
+ export interface CompactionRecord {
124
+ /** ReAct iteration this visit belongs to. */
125
+ readonly iteration: number;
126
+ /** Adapter-reported input tokens of the last call — what tripped the check. */
127
+ readonly measuredTokens: number;
128
+ /** The budget it was compared against. */
129
+ readonly thresholdTokens: number;
130
+ /** True when the measurement was over budget (a fold was attempted). */
131
+ readonly overBudget: boolean;
132
+ /** `runtimeStageId`s of the stages that appended the folded messages. */
133
+ readonly foldedStageIds: readonly string[];
134
+ /** How many messages left the window. */
135
+ readonly foldedMessageCount: number;
136
+ /** Window size in chars before / after this visit. Exact, and not tokens. */
137
+ readonly windowCharsBefore: number;
138
+ readonly windowCharsAfter: number;
139
+ /** Length of the summary text the summarizer produced (0 when none). */
140
+ readonly summaryChars: number;
141
+ /** What the summarizer call itself cost, when it reported usage. */
142
+ readonly summarizerTokens?: {
143
+ readonly input: number;
144
+ readonly output: number;
145
+ };
146
+ /** Every turn that refused to fold, named. */
147
+ readonly refusals: readonly FoldRefusal[];
148
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * compaction/types — the public shape of `.compaction()` and the record it
3
+ * writes into the ledger.
4
+ *
5
+ * Pattern: Value objects (no behavior) + one resolved-config type.
6
+ * Role: core/ layer. The law this feature exists to keep is stated once,
7
+ * here, because every other file in this folder implements a piece
8
+ * of it: **compaction edits the WINDOW, never the LEDGER.**
9
+ * Emits: N/A (types only).
10
+ *
11
+ * The window is `scope.history` — the array `call-llm` hands the provider.
12
+ * The ledger is footprintjs's commit log, which is append-only: the turns a
13
+ * fold removes from the window were committed by `seed#0` / `tool-calls#N`
14
+ * BEFORE the fold and stay in those bundles byte-identical forever. A fold
15
+ * therefore cannot destroy history even in principle; it can only stop
16
+ * re-sending it. The summary is a CLAIM about the past, so it is filed as a
17
+ * claim — its own recorded step, naming every `runtimeStageId` it folded.
18
+ */
19
+ export {};
20
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../src/core/agent/compaction/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * compact — the loop-head stage that keeps the window inside budget.
3
+ *
4
+ * Reads the last call's ADAPTER-REPORTED input tokens from the compaction
5
+ * meter and, when they exceed `thresholdTokens`, hands the window to a window
6
+ * strategy. Runs once per ReAct iteration boundary, as the loop target, so
7
+ * everything downstream in the turn — the injection engine's triggers, all
8
+ * three context slots, and the wire itself — sees ONE window. No component
9
+ * gets a different past than the model does.
10
+ *
11
+ * The law: **it edits the window, never the ledger.** The turns a fold removes
12
+ * were committed by `seed#0` / `tool-calls#N` before this stage ran and stay
13
+ * in those bundles byte-identical; footprintjs's commit log is append-only, so
14
+ * a fold cannot erase history even in principle. What it can do is stop
15
+ * re-sending it — and say so, in its own commit, naming every runtimeStageId
16
+ * it folded. A compacted run is still a provable run.
17
+ *
18
+ * This file is deliberately all WIRING. The decision — what the window should
19
+ * become — lives behind `WindowStrategy` (compaction/strategy.ts, internal),
20
+ * so the duty to record, emit and cost what happened stays in exactly one
21
+ * place instead of being re-implemented per strategy. `.compaction()` is the
22
+ * only strategy that ships.
23
+ *
24
+ * Emits (existing vocabulary only — no new event types):
25
+ * agentfootprint.context.evicted — one per message leaving the window
26
+ * agentfootprint.context.budget_pressure — one per over-budget visit
27
+ * agentfootprint.cost.tick — when a pricingTable is set: the
28
+ * summarizer's call is a real
29
+ * billed call and counts
30
+ *
31
+ * The summarizer call is deliberately NOT bracketed with
32
+ * `stream.llm_start` / `llm_end`: those carry an `iteration` and every
33
+ * consumer pairs them by it, so a second bracket inside one iteration would
34
+ * corrupt the pairing. Its cost rides the cost channel and its tokens are
35
+ * recorded in the commit instead.
36
+ */
37
+ import type { TypedScope } from 'footprintjs';
38
+ import type { PricingTable } from '../../../adapters/types.js';
39
+ import type { CompactionMeterHandle } from '../../../recorders/core/CompactionMeter.js';
40
+ import type { ResolvedCompaction } from '../compaction/types.js';
41
+ import type { AgentState } from '../types.js';
42
+ export interface CompactStageDeps {
43
+ /** Resolved `.compaction()` config (defaults already applied). */
44
+ readonly config: ResolvedCompaction;
45
+ /** The meter this stage reads. Attached inline by `Agent.createExecutor`. */
46
+ readonly meter: CompactionMeterHandle;
47
+ /** The agent's own model — the summarizer's default. */
48
+ readonly defaultModel: string;
49
+ /** The MAIN provider's name, for the unmeasurable refusal. */
50
+ readonly providerName: string;
51
+ /** Optional pricing adapter, so a summarizer call is costed like any other. */
52
+ readonly pricingTable?: PricingTable;
53
+ /** Optional cumulative USD cap per run. */
54
+ readonly costBudget?: number;
55
+ /** Injectable clock (tests pin survivalMs). */
56
+ readonly now?: () => number;
57
+ }
58
+ /** Build the compaction stage function. */
59
+ export declare function buildCompactStage(deps: CompactStageDeps): (scope: TypedScope<AgentState>) => Promise<void>;
@@ -0,0 +1,126 @@
1
+ /**
2
+ * compact — the loop-head stage that keeps the window inside budget.
3
+ *
4
+ * Reads the last call's ADAPTER-REPORTED input tokens from the compaction
5
+ * meter and, when they exceed `thresholdTokens`, hands the window to a window
6
+ * strategy. Runs once per ReAct iteration boundary, as the loop target, so
7
+ * everything downstream in the turn — the injection engine's triggers, all
8
+ * three context slots, and the wire itself — sees ONE window. No component
9
+ * gets a different past than the model does.
10
+ *
11
+ * The law: **it edits the window, never the ledger.** The turns a fold removes
12
+ * were committed by `seed#0` / `tool-calls#N` before this stage ran and stay
13
+ * in those bundles byte-identical; footprintjs's commit log is append-only, so
14
+ * a fold cannot erase history even in principle. What it can do is stop
15
+ * re-sending it — and say so, in its own commit, naming every runtimeStageId
16
+ * it folded. A compacted run is still a provable run.
17
+ *
18
+ * This file is deliberately all WIRING. The decision — what the window should
19
+ * become — lives behind `WindowStrategy` (compaction/strategy.ts, internal),
20
+ * so the duty to record, emit and cost what happened stays in exactly one
21
+ * place instead of being re-implemented per strategy. `.compaction()` is the
22
+ * only strategy that ships.
23
+ *
24
+ * Emits (existing vocabulary only — no new event types):
25
+ * agentfootprint.context.evicted — one per message leaving the window
26
+ * agentfootprint.context.budget_pressure — one per over-budget visit
27
+ * agentfootprint.cost.tick — when a pricingTable is set: the
28
+ * summarizer's call is a real
29
+ * billed call and counts
30
+ *
31
+ * The summarizer call is deliberately NOT bracketed with
32
+ * `stream.llm_start` / `llm_end`: those carry an `iteration` and every
33
+ * consumer pairs them by it, so a second bracket inside one iteration would
34
+ * corrupt the pairing. Its cost rides the cost channel and its tokens are
35
+ * recorded in the commit instead.
36
+ */
37
+ import { typedEmit } from '../../../recorders/core/typedEmit.js';
38
+ import { fnv1a } from '../../slots/helpers.js';
39
+ import { emitCostTick } from '../../cost.js';
40
+ import { CompactionUnmeasurableError } from '../compaction/errors.js';
41
+ import { summarizeOldestStrategy } from '../compaction/strategy.js';
42
+ import { answeredCallIds, segmentTurns } from '../compaction/turns.js';
43
+ /** Build the compaction stage function. */
44
+ export function buildCompactStage(deps) {
45
+ const { config, meter } = deps;
46
+ const now = deps.now ?? (() => Date.now());
47
+ const strategy = summarizeOldestStrategy(config, deps.defaultModel);
48
+ // Dedup latch for the strategy's warning: a summarizer that is down is down
49
+ // for the whole run, and one warning is a warning while ten is noise.
50
+ let warned = false;
51
+ return async (scope) => {
52
+ const metered = meter.lastCall();
53
+ // Iteration 1: nothing has been sent, so nothing has been counted. A
54
+ // compactor that acted here would be guessing, which is the one thing
55
+ // this feature refuses to do.
56
+ if (metered === undefined)
57
+ return;
58
+ if (metered.input === 0 && metered.output === 0) {
59
+ throw new CompactionUnmeasurableError(deps.providerName);
60
+ }
61
+ if (metered.input <= config.thresholdTokens)
62
+ return;
63
+ // ── Over budget. Ask the strategy what the window should become. ──
64
+ const history = (scope.history ?? []).slice();
65
+ const pausedToolCallId = scope.pausedToolCallId;
66
+ const foldability = {
67
+ answeredCallIds: answeredCallIds(history),
68
+ ...(pausedToolCallId !== undefined && { pausedToolCallId }),
69
+ ...(scope.pausedCheckIn === true && { pausedCheckIn: true }),
70
+ };
71
+ const result = await strategy.plan({
72
+ history,
73
+ turns: segmentTurns(history),
74
+ origins: meter.origins(),
75
+ measuredTokens: metered.input,
76
+ foldability,
77
+ iteration: scope.iteration ?? 1,
78
+ signal: scope.$getEnv?.()?.signal,
79
+ now,
80
+ });
81
+ // ── Apply. The stage owns every side effect. ─────────────────────
82
+ if (result.window !== undefined && result.rebase !== undefined) {
83
+ // Tell the meter before writing: the fold is the one window shape it
84
+ // cannot infer (the set trap JSON-round-trips every write).
85
+ meter.rebaseForFold(result.rebase.headCount, result.rebase.keptTailCount, now());
86
+ scope.history = result.window;
87
+ }
88
+ const prior = scope.compactions ?? [];
89
+ scope.compactions = [...prior, result.record];
90
+ // One eviction per message that left, in the slot vocabulary consumers
91
+ // already subscribe to. The hash uses the SAME formula the messages slot
92
+ // used to report the piece as injected, so evicted and injected refer to
93
+ // the same piece by the same name.
94
+ for (const eviction of result.evictions) {
95
+ const msg = history[eviction.index];
96
+ if (msg === undefined)
97
+ continue;
98
+ typedEmit(scope, 'agentfootprint.context.evicted', {
99
+ slot: 'messages',
100
+ contentHash: fnv1a(`${msg.role}:${eviction.index}:${msg.content}`),
101
+ reason: 'budget',
102
+ survivalMs: eviction.survivalMs,
103
+ });
104
+ }
105
+ // `capTokens` / `projectedTokens` are the historical names on
106
+ // `BudgetPressureRecord`; the slots measure them in chars, and this
107
+ // measures them in the tokens the decision was actually made on.
108
+ typedEmit(scope, 'agentfootprint.context.budget_pressure', {
109
+ slot: 'messages',
110
+ capTokens: config.thresholdTokens,
111
+ projectedTokens: metered.input,
112
+ overflowBy: Math.max(0, metered.input - config.thresholdTokens),
113
+ planAction: result.planAction,
114
+ });
115
+ // A summarizer call is real money. It counts against the same budget.
116
+ if (result.spend !== undefined) {
117
+ emitCostTick(scope, deps.pricingTable, deps.costBudget, result.spend.model, result.spend.usage);
118
+ }
119
+ if (result.warning !== undefined && !warned) {
120
+ warned = true;
121
+ // eslint-disable-next-line no-console
122
+ console.warn(`[agentfootprint compaction] ${result.warning}`);
123
+ }
124
+ };
125
+ }
126
+ //# sourceMappingURL=compact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compact.js","sourceRoot":"","sources":["../../../../../src/core/agent/stages/compact.ts"],"names":[],"mappings":"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"}
@@ -18,6 +18,7 @@ import type { CredentialProvider } from '../../identity/types.js';
18
18
  import type { ToolArgValidationMode } from './toolArgsValidation.js';
19
19
  import type { ThinkingBlock } from '../../thinking/types.js';
20
20
  import type { ReliabilityScope } from '../../reliability/types.js';
21
+ import type { CompactionRecord } from './compaction/types.js';
21
22
  /**
22
23
  * Dials for the deferred observer queue (RFC-001) — only meaningful with
23
24
  * `observerDelivery: 'deferred'` (passing them without it throws at
@@ -407,6 +408,16 @@ export interface AgentState {
407
408
  /** The base system prompt `.configure()` resolved for THIS run, replacing
408
409
  * `.system(...)`. Same rule: absent unless a resolver returned one. */
409
410
  resolvedInstructions?: string;
411
+ /** One record per OVER-BUDGET visit to the compaction stage — including the
412
+ * visits that folded nothing, which are the interesting ones. Written only
413
+ * by an agent built with `.compaction()`; absent otherwise, so an agent
414
+ * without it commits exactly the keys it always did.
415
+ *
416
+ * This array is the fold's half of the law: the window shrank, and the
417
+ * ledger says which stages' messages left it, how many, and what the last
418
+ * call actually measured. The messages themselves are untouched in the
419
+ * commit bundles that wrote them. */
420
+ compactions?: readonly CompactionRecord[];
410
421
  /** Set when a `PermissionChecker` returns `{ result: 'halt', ... }`.
411
422
  * `Agent.run()` reads these at the API boundary and throws a typed
412
423
  * `PolicyHaltError` carrying the same context — the chart $break's