agentfootprint 7.16.0 → 7.17.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 (187) hide show
  1. package/dist/conventions.js +8 -6
  2. package/dist/conventions.js.map +1 -1
  3. package/dist/core/Agent.js +31 -26
  4. package/dist/core/Agent.js.map +1 -1
  5. package/dist/core/agent/AgentBuilder.js +93 -43
  6. package/dist/core/agent/AgentBuilder.js.map +1 -1
  7. package/dist/core/agent/buildAgentChart.js +8 -8
  8. package/dist/core/agent/buildAgentChart.js.map +1 -1
  9. package/dist/core/agent/buildDynamicAgentChart.js +6 -6
  10. package/dist/core/agent/buildDynamicAgentChart.js.map +1 -1
  11. package/dist/core/agent/stages/window.js +141 -0
  12. package/dist/core/agent/stages/window.js.map +1 -0
  13. package/dist/core/agent/window/errors.js.map +1 -0
  14. package/dist/core/agent/window/index.js +36 -0
  15. package/dist/core/agent/window/index.js.map +1 -0
  16. package/dist/core/agent/window/notice.js +57 -0
  17. package/dist/core/agent/window/notice.js.map +1 -0
  18. package/dist/core/agent/window/options.js +93 -0
  19. package/dist/core/agent/window/options.js.map +1 -0
  20. package/dist/core/agent/window/removal.js +45 -0
  21. package/dist/core/agent/window/removal.js.map +1 -0
  22. package/dist/core/agent/window/strategies/drop.js +106 -0
  23. package/dist/core/agent/window/strategies/drop.js.map +1 -0
  24. package/dist/core/agent/window/strategies/slidingWindow.js +87 -0
  25. package/dist/core/agent/window/strategies/slidingWindow.js.map +1 -0
  26. package/dist/core/agent/window/strategies/summarizeOldest.js +178 -0
  27. package/dist/core/agent/window/strategies/summarizeOldest.js.map +1 -0
  28. package/dist/core/agent/window/strategies/tokenBudget.js +95 -0
  29. package/dist/core/agent/window/strategies/tokenBudget.js.map +1 -0
  30. package/dist/core/agent/window/strategy.js +42 -0
  31. package/dist/core/agent/window/strategy.js.map +1 -0
  32. package/dist/core/agent/window/summarize.js.map +1 -0
  33. package/dist/core/agent/{compaction → window}/turns.js +50 -28
  34. package/dist/core/agent/window/turns.js.map +1 -0
  35. package/dist/core/agent/window/types.js +23 -0
  36. package/dist/core/agent/window/types.js.map +1 -0
  37. package/dist/esm/conventions.d.ts +8 -6
  38. package/dist/esm/conventions.js +8 -6
  39. package/dist/esm/conventions.js.map +1 -1
  40. package/dist/esm/core/Agent.d.ts +6 -6
  41. package/dist/esm/core/Agent.js +31 -26
  42. package/dist/esm/core/Agent.js.map +1 -1
  43. package/dist/esm/core/agent/AgentBuilder.d.ts +60 -4
  44. package/dist/esm/core/agent/AgentBuilder.js +93 -43
  45. package/dist/esm/core/agent/AgentBuilder.js.map +1 -1
  46. package/dist/esm/core/agent/buildAgentChart.d.ts +14 -7
  47. package/dist/esm/core/agent/buildAgentChart.js +8 -8
  48. package/dist/esm/core/agent/buildAgentChart.js.map +1 -1
  49. package/dist/esm/core/agent/buildDynamicAgentChart.js +6 -6
  50. package/dist/esm/core/agent/buildDynamicAgentChart.js.map +1 -1
  51. package/dist/esm/core/agent/stages/window.d.ts +63 -0
  52. package/dist/esm/core/agent/stages/window.js +137 -0
  53. package/dist/esm/core/agent/stages/window.js.map +1 -0
  54. package/dist/esm/core/agent/types.d.ts +15 -8
  55. package/dist/esm/core/agent/window/errors.js.map +1 -0
  56. package/dist/esm/core/agent/window/index.d.ts +27 -0
  57. package/dist/esm/core/agent/window/index.js +25 -0
  58. package/dist/esm/core/agent/window/index.js.map +1 -0
  59. package/dist/esm/core/agent/window/notice.d.ts +46 -0
  60. package/dist/esm/core/agent/window/notice.js +52 -0
  61. package/dist/esm/core/agent/window/notice.js.map +1 -0
  62. package/dist/esm/core/agent/window/options.d.ts +33 -0
  63. package/dist/esm/core/agent/window/options.js +87 -0
  64. package/dist/esm/core/agent/window/options.js.map +1 -0
  65. package/dist/esm/core/agent/window/removal.d.ts +22 -0
  66. package/dist/esm/core/agent/window/removal.js +40 -0
  67. package/dist/esm/core/agent/window/removal.js.map +1 -0
  68. package/dist/esm/core/agent/window/strategies/drop.d.ts +54 -0
  69. package/dist/esm/core/agent/window/strategies/drop.js +102 -0
  70. package/dist/esm/core/agent/window/strategies/drop.js.map +1 -0
  71. package/dist/esm/core/agent/window/strategies/slidingWindow.d.ts +48 -0
  72. package/dist/esm/core/agent/window/strategies/slidingWindow.js +83 -0
  73. package/dist/esm/core/agent/window/strategies/slidingWindow.js.map +1 -0
  74. package/dist/esm/core/agent/window/strategies/summarizeOldest.d.ts +42 -0
  75. package/dist/esm/core/agent/window/strategies/summarizeOldest.js +174 -0
  76. package/dist/esm/core/agent/window/strategies/summarizeOldest.js.map +1 -0
  77. package/dist/esm/core/agent/window/strategies/tokenBudget.d.ts +47 -0
  78. package/dist/esm/core/agent/window/strategies/tokenBudget.js +91 -0
  79. package/dist/esm/core/agent/window/strategies/tokenBudget.js.map +1 -0
  80. package/dist/esm/core/agent/window/strategy.d.ts +181 -0
  81. package/dist/esm/core/agent/window/strategy.js +41 -0
  82. package/dist/esm/core/agent/window/strategy.js.map +1 -0
  83. package/dist/esm/core/agent/window/summarize.js.map +1 -0
  84. package/dist/esm/core/agent/window/turns.d.ts +108 -0
  85. package/dist/esm/core/agent/{compaction → window}/turns.js +48 -26
  86. package/dist/esm/core/agent/window/turns.js.map +1 -0
  87. package/dist/esm/core/agent/window/types.d.ts +263 -0
  88. package/dist/esm/core/agent/window/types.js +22 -0
  89. package/dist/esm/core/agent/window/types.js.map +1 -0
  90. package/dist/esm/index.d.ts +1 -1
  91. package/dist/esm/index.js +10 -5
  92. package/dist/esm/index.js.map +1 -1
  93. package/dist/esm/recorders/core/CompactionMeter.d.ts +25 -19
  94. package/dist/esm/recorders/core/CompactionMeter.js +27 -19
  95. package/dist/esm/recorders/core/CompactionMeter.js.map +1 -1
  96. package/dist/index.js +17 -7
  97. package/dist/index.js.map +1 -1
  98. package/dist/recorders/core/CompactionMeter.js +27 -19
  99. package/dist/recorders/core/CompactionMeter.js.map +1 -1
  100. package/dist/types/conventions.d.ts +8 -6
  101. package/dist/types/conventions.d.ts.map +1 -1
  102. package/dist/types/core/Agent.d.ts +6 -6
  103. package/dist/types/core/Agent.d.ts.map +1 -1
  104. package/dist/types/core/agent/AgentBuilder.d.ts +60 -4
  105. package/dist/types/core/agent/AgentBuilder.d.ts.map +1 -1
  106. package/dist/types/core/agent/buildAgentChart.d.ts +14 -7
  107. package/dist/types/core/agent/buildAgentChart.d.ts.map +1 -1
  108. package/dist/types/core/agent/stages/window.d.ts +64 -0
  109. package/dist/types/core/agent/stages/window.d.ts.map +1 -0
  110. package/dist/types/core/agent/types.d.ts +15 -8
  111. package/dist/types/core/agent/types.d.ts.map +1 -1
  112. package/dist/types/core/agent/window/errors.d.ts.map +1 -0
  113. package/dist/types/core/agent/window/index.d.ts +28 -0
  114. package/dist/types/core/agent/window/index.d.ts.map +1 -0
  115. package/dist/types/core/agent/window/notice.d.ts +47 -0
  116. package/dist/types/core/agent/window/notice.d.ts.map +1 -0
  117. package/dist/types/core/agent/window/options.d.ts +34 -0
  118. package/dist/types/core/agent/window/options.d.ts.map +1 -0
  119. package/dist/types/core/agent/window/removal.d.ts +23 -0
  120. package/dist/types/core/agent/window/removal.d.ts.map +1 -0
  121. package/dist/types/core/agent/window/strategies/drop.d.ts +55 -0
  122. package/dist/types/core/agent/window/strategies/drop.d.ts.map +1 -0
  123. package/dist/types/core/agent/window/strategies/slidingWindow.d.ts +49 -0
  124. package/dist/types/core/agent/window/strategies/slidingWindow.d.ts.map +1 -0
  125. package/dist/types/core/agent/window/strategies/summarizeOldest.d.ts +43 -0
  126. package/dist/types/core/agent/window/strategies/summarizeOldest.d.ts.map +1 -0
  127. package/dist/types/core/agent/window/strategies/tokenBudget.d.ts +48 -0
  128. package/dist/types/core/agent/window/strategies/tokenBudget.d.ts.map +1 -0
  129. package/dist/types/core/agent/window/strategy.d.ts +182 -0
  130. package/dist/types/core/agent/window/strategy.d.ts.map +1 -0
  131. package/dist/types/core/agent/window/summarize.d.ts.map +1 -0
  132. package/dist/types/core/agent/window/turns.d.ts +109 -0
  133. package/dist/types/core/agent/window/turns.d.ts.map +1 -0
  134. package/dist/types/core/agent/window/types.d.ts +264 -0
  135. package/dist/types/core/agent/window/types.d.ts.map +1 -0
  136. package/dist/types/index.d.ts +1 -1
  137. package/dist/types/index.d.ts.map +1 -1
  138. package/dist/types/recorders/core/CompactionMeter.d.ts +25 -19
  139. package/dist/types/recorders/core/CompactionMeter.d.ts.map +1 -1
  140. package/package.json +1 -1
  141. package/dist/core/agent/compaction/errors.js.map +0 -1
  142. package/dist/core/agent/compaction/index.js +0 -24
  143. package/dist/core/agent/compaction/index.js.map +0 -1
  144. package/dist/core/agent/compaction/strategy.js +0 -152
  145. package/dist/core/agent/compaction/strategy.js.map +0 -1
  146. package/dist/core/agent/compaction/summarize.js.map +0 -1
  147. package/dist/core/agent/compaction/turns.js.map +0 -1
  148. package/dist/core/agent/compaction/types.js +0 -21
  149. package/dist/core/agent/compaction/types.js.map +0 -1
  150. package/dist/core/agent/stages/compact.js +0 -130
  151. package/dist/core/agent/stages/compact.js.map +0 -1
  152. package/dist/esm/core/agent/compaction/errors.js.map +0 -1
  153. package/dist/esm/core/agent/compaction/index.d.ts +0 -18
  154. package/dist/esm/core/agent/compaction/index.js +0 -18
  155. package/dist/esm/core/agent/compaction/index.js.map +0 -1
  156. package/dist/esm/core/agent/compaction/strategy.d.ts +0 -103
  157. package/dist/esm/core/agent/compaction/strategy.js +0 -148
  158. package/dist/esm/core/agent/compaction/strategy.js.map +0 -1
  159. package/dist/esm/core/agent/compaction/summarize.js.map +0 -1
  160. package/dist/esm/core/agent/compaction/turns.d.ts +0 -85
  161. package/dist/esm/core/agent/compaction/turns.js.map +0 -1
  162. package/dist/esm/core/agent/compaction/types.d.ts +0 -148
  163. package/dist/esm/core/agent/compaction/types.js +0 -20
  164. package/dist/esm/core/agent/compaction/types.js.map +0 -1
  165. package/dist/esm/core/agent/stages/compact.d.ts +0 -59
  166. package/dist/esm/core/agent/stages/compact.js +0 -126
  167. package/dist/esm/core/agent/stages/compact.js.map +0 -1
  168. package/dist/types/core/agent/compaction/errors.d.ts.map +0 -1
  169. package/dist/types/core/agent/compaction/index.d.ts +0 -19
  170. package/dist/types/core/agent/compaction/index.d.ts.map +0 -1
  171. package/dist/types/core/agent/compaction/strategy.d.ts +0 -104
  172. package/dist/types/core/agent/compaction/strategy.d.ts.map +0 -1
  173. package/dist/types/core/agent/compaction/summarize.d.ts.map +0 -1
  174. package/dist/types/core/agent/compaction/turns.d.ts +0 -86
  175. package/dist/types/core/agent/compaction/turns.d.ts.map +0 -1
  176. package/dist/types/core/agent/compaction/types.d.ts +0 -149
  177. package/dist/types/core/agent/compaction/types.d.ts.map +0 -1
  178. package/dist/types/core/agent/stages/compact.d.ts +0 -60
  179. package/dist/types/core/agent/stages/compact.d.ts.map +0 -1
  180. /package/dist/core/agent/{compaction → window}/errors.js +0 -0
  181. /package/dist/core/agent/{compaction → window}/summarize.js +0 -0
  182. /package/dist/esm/core/agent/{compaction → window}/errors.d.ts +0 -0
  183. /package/dist/esm/core/agent/{compaction → window}/errors.js +0 -0
  184. /package/dist/esm/core/agent/{compaction → window}/summarize.d.ts +0 -0
  185. /package/dist/esm/core/agent/{compaction → window}/summarize.js +0 -0
  186. /package/dist/types/core/agent/{compaction → window}/errors.d.ts +0 -0
  187. /package/dist/types/core/agent/{compaction → window}/summarize.d.ts +0 -0
@@ -0,0 +1,52 @@
1
+ /**
2
+ * window/notice — the authored message a DROP leaves behind, and why it has
3
+ * to exist at all.
4
+ *
5
+ * Pattern: Authored envelope with no untrusted payload whatsoever.
6
+ * Role: core/ layer. Shared by both drop strategies.
7
+ * Emits: N/A.
8
+ *
9
+ * The first reason for this message is the WIRE, not the prose. An agent
10
+ * window looks like `user, assistant+tool, assistant+tool, …`, so dropping
11
+ * the oldest turns leaves an ASSISTANT message at the head — and the
12
+ * providers that care (Anthropic) require the window to open on a user turn.
13
+ * A silent drop of the window's head therefore produces a request the vendor
14
+ * rejects. Something must occupy that position.
15
+ *
16
+ * Given that we have to author a message there anyway, it should say what
17
+ * happened rather than be filler. So it does: how many messages left, at
18
+ * which iteration, by which strategy, and where they still are. Unlike the
19
+ * compaction frame there is no model output involved at ALL — every character
20
+ * below is written by this library, which is why a drop has no prompt-
21
+ * injection surface to speak of.
22
+ *
23
+ * It appears ONLY when the removal reaches the window's head. A removal in
24
+ * the middle leaves the original opening turn in place, so there is no wire
25
+ * problem to solve — and splicing a lone `user` message between two assistant
26
+ * turns is its own risk. The ledger names that removal either way.
27
+ *
28
+ * It does not accumulate: the notice is an ordinary oldest turn next time
29
+ * round, so the next drop absorbs it and files a fresh one.
30
+ */
31
+ /** Opening of the authored notice. Stable — tests and readers match on it. */
32
+ export const DROP_NOTICE_PREFIX = '[dropped history';
33
+ /**
34
+ * Build the message that takes the head position after a drop.
35
+ *
36
+ * `role: 'user'` for the same reason the compaction frame is: it is the head
37
+ * of the window, and the head of the window must be a user turn.
38
+ */
39
+ export function buildDropNotice(facts) {
40
+ return {
41
+ role: 'user',
42
+ content: `${DROP_NOTICE_PREFIX} — ${facts.droppedMessageCount} earlier message(s) were dropped ` +
43
+ `from this window at iteration ${facts.iteration} by the '${facts.strategy}' window ` +
44
+ `strategy. Nothing was summarized: those turns are simply not being re-sent. They are ` +
45
+ `retained verbatim in this run's commit log.]`,
46
+ };
47
+ }
48
+ /** True when this message is a notice a previous drop wrote. */
49
+ export function isDropNotice(msg) {
50
+ return msg !== undefined && msg.role === 'user' && msg.content.startsWith(DROP_NOTICE_PREFIX);
51
+ }
52
+ //# sourceMappingURL=notice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notice.js","sourceRoot":"","sources":["../../../../../src/core/agent/window/notice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAIH,8EAA8E;AAC9E,MAAM,CAAC,MAAM,kBAAkB,GAAG,kBAAkB,CAAC;AAErD;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,KAI/B;IACC,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,OAAO,EACL,GAAG,kBAAkB,MAAM,KAAK,CAAC,mBAAmB,mCAAmC;YACvF,iCAAiC,KAAK,CAAC,SAAS,YAAY,KAAK,CAAC,QAAQ,WAAW;YACrF,uFAAuF;YACvF,8CAA8C;KACjD,CAAC;AACJ,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,YAAY,CAAC,GAA2B;IACtD,OAAO,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC;AAChG,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * window/options — validate a window strategy's options ONCE, at build time.
3
+ *
4
+ * Pattern: Guard functions returning a resolved config.
5
+ * Role: core/ layer. Every message here takes a `label`, because the same
6
+ * option bag arrives through two doors — `.compaction({...})` and
7
+ * `summarizeOldest({...})` — and an error should name the door the
8
+ * caller actually used. One implementation, so the two doors can
9
+ * never drift into validating differently.
10
+ * Emits: N/A.
11
+ *
12
+ * Everything fails at `.build()`, never mid-run. A window policy that turns
13
+ * out to be nonsense on iteration 40 of a paid run is a policy that cost you
14
+ * money to discover.
15
+ */
16
+ import type { CompactionOptions, ResolvedCompaction, SlidingWindowOptions, TokenBudgetOptions } from './types.js';
17
+ /** Default depth of the "never touch this" recent window. */
18
+ export declare const DEFAULT_KEEP_RECENT_TURNS = 6;
19
+ /**
20
+ * Validate `.compaction()` / `summarizeOldest()` options.
21
+ *
22
+ * @param label the door being used, so the error names it
23
+ */
24
+ export declare function resolveCompactionOptions(options: CompactionOptions, label: string): ResolvedCompaction;
25
+ /** Validate `slidingWindow()` options. */
26
+ export declare function resolveSlidingWindowOptions(options: SlidingWindowOptions, label: string): {
27
+ readonly keepRecentTurns: number;
28
+ };
29
+ /** Validate `tokenBudget()` options. */
30
+ export declare function resolveTokenBudgetOptions(options: TokenBudgetOptions, label: string): {
31
+ readonly thresholdTokens: number;
32
+ readonly keepRecentTurns: number;
33
+ };
@@ -0,0 +1,87 @@
1
+ /**
2
+ * window/options — validate a window strategy's options ONCE, at build time.
3
+ *
4
+ * Pattern: Guard functions returning a resolved config.
5
+ * Role: core/ layer. Every message here takes a `label`, because the same
6
+ * option bag arrives through two doors — `.compaction({...})` and
7
+ * `summarizeOldest({...})` — and an error should name the door the
8
+ * caller actually used. One implementation, so the two doors can
9
+ * never drift into validating differently.
10
+ * Emits: N/A.
11
+ *
12
+ * Everything fails at `.build()`, never mid-run. A window policy that turns
13
+ * out to be nonsense on iteration 40 of a paid run is a policy that cost you
14
+ * money to discover.
15
+ */
16
+ /** Default depth of the "never touch this" recent window. */
17
+ export const DEFAULT_KEEP_RECENT_TURNS = 6;
18
+ function requireObject(options, label, shape) {
19
+ if (options === null || typeof options !== 'object') {
20
+ throw new Error(`${label}: expected an options object (${shape}), got ${typeof options}.`);
21
+ }
22
+ }
23
+ function requireThreshold(thresholdTokens, label) {
24
+ if (typeof thresholdTokens !== 'number' ||
25
+ !Number.isFinite(thresholdTokens) ||
26
+ thresholdTokens <= 0) {
27
+ throw new Error(`${label}: thresholdTokens must be a positive number of tokens, got ` +
28
+ `${String(thresholdTokens)}. There is no default: the right budget depends on your ` +
29
+ `model and your bill, and a number this library invented would be inherited silently ` +
30
+ `by every run.`);
31
+ }
32
+ }
33
+ function requireKeepRecentTurns(keepRecentTurns, label) {
34
+ if (!Number.isInteger(keepRecentTurns) || keepRecentTurns < 1) {
35
+ throw new Error(`${label}: keepRecentTurns must be an integer >= 1, got ${String(keepRecentTurns)}. ` +
36
+ `Keeping zero recent turns would remove the turn the model is reasoning over right now.`);
37
+ }
38
+ }
39
+ /**
40
+ * Validate `.compaction()` / `summarizeOldest()` options.
41
+ *
42
+ * @param label the door being used, so the error names it
43
+ */
44
+ export function resolveCompactionOptions(options, label) {
45
+ requireObject(options, label, '{ thresholdTokens, summarizer, ... }');
46
+ const { thresholdTokens, summarizer, keepRecentTurns, model } = options;
47
+ requireThreshold(thresholdTokens, label);
48
+ if (summarizer === null ||
49
+ typeof summarizer !== 'object' ||
50
+ typeof summarizer.complete !== 'function') {
51
+ throw new Error(`${label}: summarizer must be an LLMProvider (an object with a ` +
52
+ 'complete() method). It is explicit on purpose — the library will not quietly bill ' +
53
+ 'your main model for compaction. Pass a cheap provider/model here.');
54
+ }
55
+ if (keepRecentTurns !== undefined)
56
+ requireKeepRecentTurns(keepRecentTurns, label);
57
+ if (model !== undefined && (typeof model !== 'string' || model.length === 0)) {
58
+ throw new Error(`${label}: model must be a non-empty model id, got ${String(model)}.`);
59
+ }
60
+ return {
61
+ thresholdTokens,
62
+ keepRecentTurns: keepRecentTurns ?? DEFAULT_KEEP_RECENT_TURNS,
63
+ summarizer,
64
+ model,
65
+ };
66
+ }
67
+ /** Validate `slidingWindow()` options. */
68
+ export function resolveSlidingWindowOptions(options, label) {
69
+ requireObject(options, label, '{ keepRecentTurns }');
70
+ const { keepRecentTurns } = options;
71
+ if (keepRecentTurns === undefined) {
72
+ throw new Error(`${label}: keepRecentTurns is required and has no default. It IS the policy — how much ` +
73
+ `past your agent needs is a fact about your agent, not about this library.`);
74
+ }
75
+ requireKeepRecentTurns(keepRecentTurns, label);
76
+ return { keepRecentTurns };
77
+ }
78
+ /** Validate `tokenBudget()` options. */
79
+ export function resolveTokenBudgetOptions(options, label) {
80
+ requireObject(options, label, '{ thresholdTokens, keepRecentTurns? }');
81
+ const { thresholdTokens, keepRecentTurns } = options;
82
+ requireThreshold(thresholdTokens, label);
83
+ if (keepRecentTurns !== undefined)
84
+ requireKeepRecentTurns(keepRecentTurns, label);
85
+ return { thresholdTokens, keepRecentTurns: keepRecentTurns ?? DEFAULT_KEEP_RECENT_TURNS };
86
+ }
87
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../../../../../src/core/agent/window/options.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * window/removal — "these messages left" → the facts the ledger needs.
3
+ *
4
+ * Pattern: Pure function over the meter's provenance.
5
+ * Role: core/ layer. The stage binds this into every
6
+ * `WindowStrategyInput.removalFacts`, so a strategy can name what it
7
+ * removed without ever touching the meter — and cannot file a
8
+ * removal it is unable to name.
9
+ * Emits: N/A.
10
+ */
11
+ import type { MessageOrigin } from '../../../recorders/core/CompactionMeter.js';
12
+ import type { RemovalFacts } from './strategy.js';
13
+ /**
14
+ * Resolve which stages wrote the removed messages, and how long each lived.
15
+ *
16
+ * `survivalMs` is measured (`atMs - bornAtMs`), and is 0 — not a guess — when
17
+ * a message's birth is unknown, which happens only for a window seeded from
18
+ * outside this run.
19
+ */
20
+ export declare function removalFacts(origins: readonly MessageOrigin[], indices: readonly number[], atMs: number): RemovalFacts;
21
+ /** `[from, toExclusive)` as a list of indices. */
22
+ export declare function indexRange(from: number, toExclusive: number): number[];
@@ -0,0 +1,40 @@
1
+ /**
2
+ * window/removal — "these messages left" → the facts the ledger needs.
3
+ *
4
+ * Pattern: Pure function over the meter's provenance.
5
+ * Role: core/ layer. The stage binds this into every
6
+ * `WindowStrategyInput.removalFacts`, so a strategy can name what it
7
+ * removed without ever touching the meter — and cannot file a
8
+ * removal it is unable to name.
9
+ * Emits: N/A.
10
+ */
11
+ /**
12
+ * Resolve which stages wrote the removed messages, and how long each lived.
13
+ *
14
+ * `survivalMs` is measured (`atMs - bornAtMs`), and is 0 — not a guess — when
15
+ * a message's birth is unknown, which happens only for a window seeded from
16
+ * outside this run.
17
+ */
18
+ export function removalFacts(origins, indices, atMs) {
19
+ const removedStageIds = [];
20
+ const evictions = [];
21
+ for (const index of indices) {
22
+ const origin = origins[index];
23
+ if (origin !== undefined && !removedStageIds.includes(origin.stageId)) {
24
+ removedStageIds.push(origin.stageId);
25
+ }
26
+ evictions.push({
27
+ index,
28
+ survivalMs: origin === undefined ? 0 : Math.max(0, atMs - origin.bornAtMs),
29
+ });
30
+ }
31
+ return { removedStageIds, evictions };
32
+ }
33
+ /** `[from, toExclusive)` as a list of indices. */
34
+ export function indexRange(from, toExclusive) {
35
+ const out = [];
36
+ for (let i = from; i < toExclusive; i++)
37
+ out.push(i);
38
+ return out;
39
+ }
40
+ //# sourceMappingURL=removal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"removal.js","sourceRoot":"","sources":["../../../../../src/core/agent/window/removal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAC1B,OAAiC,EACjC,OAA0B,EAC1B,IAAY;IAEZ,MAAM,eAAe,GAAa,EAAE,CAAC;IACrC,MAAM,SAAS,GAAqB,EAAE,CAAC;IACvC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YACtE,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACvC,CAAC;QACD,SAAS,CAAC,IAAI,CAAC;YACb,KAAK;YACL,UAAU,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC;SAC3E,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,CAAC;AACxC,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,WAAmB;IAC1D,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE;QAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACrD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * window/strategies/drop — the mechanic both drop strategies share.
3
+ *
4
+ * Pattern: One pure function over a `WindowStrategyInput`.
5
+ * Role: core/ layer. `slidingWindow` and `tokenBudget` differ ONLY in when
6
+ * they engage and what they file; what actually leaves the window is
7
+ * decided here, once, so a refusal reason means the same thing under
8
+ * both — and under compaction, which asks the same `planRemoval`.
9
+ * Emits: N/A.
10
+ *
11
+ * A drop makes no LLM call and writes no summary. What it must still do is
12
+ * keep the request valid and keep the record honest:
13
+ *
14
+ * • the span comes from the shared refusal engine, so an unanswered tool
15
+ * call, the paused tool, a pending check-in and the recent turns never
16
+ * leave, and each refusal is named;
17
+ * • when the span reaches the window's HEAD, an authored notice takes that
18
+ * position — the window must open on a user turn (see notice.ts), and a
19
+ * message we are forced to author should tell the truth;
20
+ * • when it does not, nothing is inserted: the original opening turn is
21
+ * still there, so there is no wire problem to solve.
22
+ */
23
+ import type { LLMMessage } from '../../../../adapters/types.js';
24
+ import type { WindowEviction, WindowStrategyInput } from '../strategy.js';
25
+ import type { WindowRefusal } from '../types.js';
26
+ /** What a drop attempt did, in the terms a record needs. */
27
+ export interface DropOutcome {
28
+ /** Every turn that refused to leave, named. */
29
+ readonly refusals: readonly WindowRefusal[];
30
+ /** The new window; absent when nothing was dropped. */
31
+ readonly window?: readonly LLMMessage[];
32
+ readonly rebase?: {
33
+ readonly headCount: number;
34
+ readonly keptTailCount: number;
35
+ readonly insertedAtMs?: number;
36
+ };
37
+ readonly removedStageIds: readonly string[];
38
+ readonly removedMessageCount: number;
39
+ readonly evictions: readonly WindowEviction[];
40
+ readonly windowCharsBefore: number;
41
+ readonly windowCharsAfter: number;
42
+ /** Turns in the window before / after. Counted from the segmentation. */
43
+ readonly turnsBefore: number;
44
+ readonly turnsAfter: number;
45
+ }
46
+ /**
47
+ * Drop the oldest contiguous removable span, if there is one.
48
+ *
49
+ * @param input the strategy's input, with the bound refusal engine
50
+ * @param keepRecentTurns how many trailing turns are off-limits
51
+ * @param strategyName named in the authored notice, so a reader of the
52
+ * window itself can tell which policy removed things
53
+ */
54
+ export declare function dropOldestSpan(input: WindowStrategyInput, keepRecentTurns: number, strategyName: string): DropOutcome;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * window/strategies/drop — the mechanic both drop strategies share.
3
+ *
4
+ * Pattern: One pure function over a `WindowStrategyInput`.
5
+ * Role: core/ layer. `slidingWindow` and `tokenBudget` differ ONLY in when
6
+ * they engage and what they file; what actually leaves the window is
7
+ * decided here, once, so a refusal reason means the same thing under
8
+ * both — and under compaction, which asks the same `planRemoval`.
9
+ * Emits: N/A.
10
+ *
11
+ * A drop makes no LLM call and writes no summary. What it must still do is
12
+ * keep the request valid and keep the record honest:
13
+ *
14
+ * • the span comes from the shared refusal engine, so an unanswered tool
15
+ * call, the paused tool, a pending check-in and the recent turns never
16
+ * leave, and each refusal is named;
17
+ * • when the span reaches the window's HEAD, an authored notice takes that
18
+ * position — the window must open on a user turn (see notice.ts), and a
19
+ * message we are forced to author should tell the truth;
20
+ * • when it does not, nothing is inserted: the original opening turn is
21
+ * still there, so there is no wire problem to solve.
22
+ */
23
+ import { buildDropNotice } from '../notice.js';
24
+ import { indexRange } from '../removal.js';
25
+ import { segmentTurns, windowChars } from '../turns.js';
26
+ /**
27
+ * Drop the oldest contiguous removable span, if there is one.
28
+ *
29
+ * @param input the strategy's input, with the bound refusal engine
30
+ * @param keepRecentTurns how many trailing turns are off-limits
31
+ * @param strategyName named in the authored notice, so a reader of the
32
+ * window itself can tell which policy removed things
33
+ */
34
+ export function dropOldestSpan(input, keepRecentTurns, strategyName) {
35
+ const { history, turns, iteration } = input;
36
+ const charsBefore = windowChars(history);
37
+ const nothing = (refusals) => ({
38
+ refusals,
39
+ removedStageIds: [],
40
+ removedMessageCount: 0,
41
+ evictions: [],
42
+ windowCharsBefore: charsBefore,
43
+ windowCharsAfter: charsBefore,
44
+ turnsBefore: turns.length,
45
+ turnsAfter: turns.length,
46
+ });
47
+ // No `isExistingSummary` predicate: that refusal exists to stop a strategy
48
+ // spending an LLM call to re-summarize a summary. A drop spends nothing, so
49
+ // there is nothing to protect against — a stale summary is as droppable as
50
+ // any other old turn.
51
+ const plan = input.planRemoval(keepRecentTurns);
52
+ if (plan.from === -1)
53
+ return nothing(plan.refusals);
54
+ const spanStart = turns[plan.from].start;
55
+ const spanEnd = turns[plan.to].start + turns[plan.to].length;
56
+ const head = history.slice(0, spanStart);
57
+ const span = history.slice(spanStart, spanEnd);
58
+ const tail = history.slice(spanEnd);
59
+ let window;
60
+ let insertedAtMs;
61
+ const droppedAtMs = input.now();
62
+ if (spanStart === 0) {
63
+ // The head is leaving: something must open the window, and it must be a
64
+ // user turn. Author the notice — and refuse the whole drop if that notice
65
+ // would not be smaller than what it replaces, because dropping two tiny
66
+ // turns to insert a longer notice is pure loss.
67
+ const notice = buildDropNotice({
68
+ droppedMessageCount: span.length,
69
+ iteration,
70
+ strategy: strategyName,
71
+ });
72
+ if (notice.content.length >= windowChars(span)) {
73
+ return nothing([
74
+ { reason: 'summary-not-smaller', turnIndex: plan.from, messageIndex: spanStart },
75
+ ...plan.refusals,
76
+ ]);
77
+ }
78
+ window = [notice, ...tail];
79
+ insertedAtMs = droppedAtMs;
80
+ }
81
+ else {
82
+ window = [...head, ...tail];
83
+ }
84
+ const facts = input.removalFacts(indexRange(spanStart, spanEnd), droppedAtMs);
85
+ return {
86
+ refusals: plan.refusals,
87
+ window,
88
+ rebase: {
89
+ headCount: head.length,
90
+ keptTailCount: tail.length,
91
+ ...(insertedAtMs !== undefined && { insertedAtMs }),
92
+ },
93
+ removedStageIds: facts.removedStageIds,
94
+ removedMessageCount: span.length,
95
+ evictions: facts.evictions,
96
+ windowCharsBefore: charsBefore,
97
+ windowCharsAfter: windowChars(window),
98
+ turnsBefore: turns.length,
99
+ turnsAfter: segmentTurns(window).length,
100
+ };
101
+ }
102
+ //# sourceMappingURL=drop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drop.js","sourceRoot":"","sources":["../../../../../../src/core/agent/window/strategies/drop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * slidingWindow — keep the last N turns, drop what is older.
3
+ *
4
+ * Pattern: WindowStrategy implementation. Its own module, registering
5
+ * nothing at import.
6
+ * Role: core/ layer.
7
+ * Emits: N/A — the stage emits, records and costs; this file decides.
8
+ *
9
+ * The market's simplest window policy (LangChain's `trim_messages`, the
10
+ * "last N messages" every framework ships). Two things are different here:
11
+ *
12
+ * 1. **It refuses by name.** It goes through the SAME turn segmentation and
13
+ * the SAME refusal engine as compaction, so it will not drop half of a
14
+ * `tool_use` / `tool_result` pair, will not drop an unanswered tool call
15
+ * whose answer may still arrive, will not drop the tool a paused run is
16
+ * waiting on or a pending check-in, and cannot touch the system
17
+ * envelope. Message-counting trimmers split those pairs and the vendor
18
+ * rejects the request; this one steps over them and says so in the
19
+ * record.
20
+ * 2. **It says what it dropped.** Every dropped message is an eviction
21
+ * event with its measured lifetime, and the record names the stage ids
22
+ * the messages came from. The turns themselves stay in the commit log,
23
+ * byte for byte.
24
+ *
25
+ * It triggers on turn COUNT, not tokens — so it needs no summarizer, makes no
26
+ * LLM call, and runs on ANY provider, including the ones that report no usage
27
+ * at all. Nothing here is unmeasurable, so nothing here throws.
28
+ */
29
+ import type { WindowStrategy } from '../strategy.js';
30
+ import type { SlidingWindowOptions } from '../types.js';
31
+ /** `WindowRecord.strategy` written by every record this strategy files. */
32
+ export declare const SLIDING_WINDOW = "sliding-window";
33
+ /**
34
+ * Keep the most recent `keepRecentTurns` turns in the live window and drop
35
+ * the older ones — except anything that refuses.
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * import { Agent, slidingWindow } from 'agentfootprint';
40
+ * import { ollama } from 'agentfootprint/llm-providers';
41
+ *
42
+ * // Works on a provider that reports no usage at all: the trigger is turns.
43
+ * const agent = Agent.create({ provider: ollama(), model: 'llama3' })
44
+ * .window(slidingWindow({ keepRecentTurns: 12 }))
45
+ * .build();
46
+ * ```
47
+ */
48
+ export declare function slidingWindow(options: SlidingWindowOptions): WindowStrategy;
@@ -0,0 +1,83 @@
1
+ /**
2
+ * slidingWindow — keep the last N turns, drop what is older.
3
+ *
4
+ * Pattern: WindowStrategy implementation. Its own module, registering
5
+ * nothing at import.
6
+ * Role: core/ layer.
7
+ * Emits: N/A — the stage emits, records and costs; this file decides.
8
+ *
9
+ * The market's simplest window policy (LangChain's `trim_messages`, the
10
+ * "last N messages" every framework ships). Two things are different here:
11
+ *
12
+ * 1. **It refuses by name.** It goes through the SAME turn segmentation and
13
+ * the SAME refusal engine as compaction, so it will not drop half of a
14
+ * `tool_use` / `tool_result` pair, will not drop an unanswered tool call
15
+ * whose answer may still arrive, will not drop the tool a paused run is
16
+ * waiting on or a pending check-in, and cannot touch the system
17
+ * envelope. Message-counting trimmers split those pairs and the vendor
18
+ * rejects the request; this one steps over them and says so in the
19
+ * record.
20
+ * 2. **It says what it dropped.** Every dropped message is an eviction
21
+ * event with its measured lifetime, and the record names the stage ids
22
+ * the messages came from. The turns themselves stay in the commit log,
23
+ * byte for byte.
24
+ *
25
+ * It triggers on turn COUNT, not tokens — so it needs no summarizer, makes no
26
+ * LLM call, and runs on ANY provider, including the ones that report no usage
27
+ * at all. Nothing here is unmeasurable, so nothing here throws.
28
+ */
29
+ import { dropOldestSpan } from './drop.js';
30
+ import { resolveSlidingWindowOptions } from '../options.js';
31
+ /** `WindowRecord.strategy` written by every record this strategy files. */
32
+ export const SLIDING_WINDOW = 'sliding-window';
33
+ /**
34
+ * Keep the most recent `keepRecentTurns` turns in the live window and drop
35
+ * the older ones — except anything that refuses.
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * import { Agent, slidingWindow } from 'agentfootprint';
40
+ * import { ollama } from 'agentfootprint/llm-providers';
41
+ *
42
+ * // Works on a provider that reports no usage at all: the trigger is turns.
43
+ * const agent = Agent.create({ provider: ollama(), model: 'llama3' })
44
+ * .window(slidingWindow({ keepRecentTurns: 12 }))
45
+ * .build();
46
+ * ```
47
+ */
48
+ export function slidingWindow(options) {
49
+ const config = resolveSlidingWindowOptions(options, 'slidingWindow');
50
+ return {
51
+ name: SLIDING_WINDOW,
52
+ async plan(input) {
53
+ // Did not engage: the window is already within its target depth. No
54
+ // record — the ledger tracks what this strategy DID, not every time it
55
+ // looked and there was nothing to do.
56
+ if (input.turns.length <= config.keepRecentTurns)
57
+ return undefined;
58
+ const outcome = dropOldestSpan(input, config.keepRecentTurns, SLIDING_WINDOW);
59
+ const record = {
60
+ strategy: SLIDING_WINDOW,
61
+ iteration: input.iteration,
62
+ keepRecentTurns: config.keepRecentTurns,
63
+ removedStageIds: outcome.removedStageIds,
64
+ removedMessageCount: outcome.removedMessageCount,
65
+ windowCharsBefore: outcome.windowCharsBefore,
66
+ windowCharsAfter: outcome.windowCharsAfter,
67
+ turnsBefore: outcome.turnsBefore,
68
+ turnsAfter: outcome.turnsAfter,
69
+ refusals: outcome.refusals,
70
+ };
71
+ // No `budgetPressure`: this strategy has no token budget. Reporting a
72
+ // cap nobody configured would be the invented number the family exists
73
+ // to refuse — so it reports evictions and stays quiet about budgets.
74
+ return {
75
+ ...(outcome.window !== undefined && { window: outcome.window }),
76
+ ...(outcome.rebase !== undefined && { rebase: outcome.rebase }),
77
+ record,
78
+ evictions: outcome.evictions,
79
+ };
80
+ },
81
+ };
82
+ }
83
+ //# sourceMappingURL=slidingWindow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slidingWindow.js","sourceRoot":"","sources":["../../../../../../src/core/agent/window/strategies/slidingWindow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAC;AAI5D,2EAA2E;AAC3E,MAAM,CAAC,MAAM,cAAc,GAAG,gBAAgB,CAAC;AAE/C;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,aAAa,CAAC,OAA6B;IACzD,MAAM,MAAM,GAAG,2BAA2B,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;IAErE,OAAO;QACL,IAAI,EAAE,cAAc;QAEpB,KAAK,CAAC,IAAI,CAAC,KAA0B;YACnC,oEAAoE;YACpE,uEAAuE;YACvE,sCAAsC;YACtC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,eAAe;gBAAE,OAAO,SAAS,CAAC;YAEnE,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,eAAe,EAAE,cAAc,CAAC,CAAC;YAC9E,MAAM,MAAM,GAAwB;gBAClC,QAAQ,EAAE,cAAc;gBACxB,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,eAAe,EAAE,MAAM,CAAC,eAAe;gBACvC,eAAe,EAAE,OAAO,CAAC,eAAe;gBACxC,mBAAmB,EAAE,OAAO,CAAC,mBAAmB;gBAChD,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;gBAC5C,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;gBAC1C,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,QAAQ,EAAE,OAAO,CAAC,QAAQ;aAC3B,CAAC;YAEF,sEAAsE;YACtE,uEAAuE;YACvE,qEAAqE;YACrE,OAAO;gBACL,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;gBAC/D,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;gBAC/D,MAAM;gBACN,SAAS,EAAE,OAAO,CAAC,SAAS;aAC7B,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * summarizeOldest — fold the oldest foldable turns into one summary message.
3
+ *
4
+ * Pattern: WindowStrategy implementation. Its own module, importing nothing
5
+ * that registers anything, so a bundle that never mentions it never
6
+ * carries it (and never carries the summarizer machinery either).
7
+ * Role: core/ layer.
8
+ * Emits: N/A — the stage emits, records and costs; this file decides.
9
+ *
10
+ * This is what `.compaction({...})` configures, and the market's familiar
11
+ * move (Claude Code / the Claude Agent SDK call it compaction). What is
12
+ * different here is not the fold — it is that the fold is filed as a claim:
13
+ * a summary is a claim ABOUT the past, and the past itself stays in the
14
+ * commit log, byte for byte.
15
+ *
16
+ * Everything it decides, it explains. Every engaged path returns a record —
17
+ * including the paths that change nothing, which are the ones a person
18
+ * debugging an over-budget window actually needs.
19
+ */
20
+ import type { WindowStrategy } from '../strategy.js';
21
+ import type { CompactionOptions } from '../types.js';
22
+ /** `WindowRecord.strategy` written by every record this strategy files. */
23
+ export declare const SUMMARIZE_OLDEST = "summarize-oldest";
24
+ /**
25
+ * Fold the oldest contiguous run of foldable turns into one summary message,
26
+ * keeping the recent turns and stepping over anything unresolved.
27
+ *
28
+ * Triggered by COUNTED tokens: the last call's adapter-reported input tokens
29
+ * against `thresholdTokens`. A provider that reports no usage gets
30
+ * `CompactionUnmeasurableError` rather than an invented number.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * import { Agent, summarizeOldest } from 'agentfootprint';
35
+ *
36
+ * const agent = Agent.create({ provider: anthropic(), model: 'claude-sonnet-4-5' })
37
+ * .window(summarizeOldest({ thresholdTokens: 120_000, summarizer: anthropic() }))
38
+ * .build();
39
+ * // `.compaction({ ... })` is this exact line, spelled shorter.
40
+ * ```
41
+ */
42
+ export declare function summarizeOldest(options: CompactionOptions): WindowStrategy;