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.
- package/dist/conventions.js +8 -6
- package/dist/conventions.js.map +1 -1
- package/dist/core/Agent.js +31 -26
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/agent/AgentBuilder.js +93 -43
- package/dist/core/agent/AgentBuilder.js.map +1 -1
- package/dist/core/agent/buildAgentChart.js +8 -8
- package/dist/core/agent/buildAgentChart.js.map +1 -1
- package/dist/core/agent/buildDynamicAgentChart.js +6 -6
- package/dist/core/agent/buildDynamicAgentChart.js.map +1 -1
- package/dist/core/agent/stages/window.js +141 -0
- package/dist/core/agent/stages/window.js.map +1 -0
- package/dist/core/agent/window/errors.js.map +1 -0
- package/dist/core/agent/window/index.js +36 -0
- package/dist/core/agent/window/index.js.map +1 -0
- package/dist/core/agent/window/notice.js +57 -0
- package/dist/core/agent/window/notice.js.map +1 -0
- package/dist/core/agent/window/options.js +93 -0
- package/dist/core/agent/window/options.js.map +1 -0
- package/dist/core/agent/window/removal.js +45 -0
- package/dist/core/agent/window/removal.js.map +1 -0
- package/dist/core/agent/window/strategies/drop.js +106 -0
- package/dist/core/agent/window/strategies/drop.js.map +1 -0
- package/dist/core/agent/window/strategies/slidingWindow.js +87 -0
- package/dist/core/agent/window/strategies/slidingWindow.js.map +1 -0
- package/dist/core/agent/window/strategies/summarizeOldest.js +178 -0
- package/dist/core/agent/window/strategies/summarizeOldest.js.map +1 -0
- package/dist/core/agent/window/strategies/tokenBudget.js +95 -0
- package/dist/core/agent/window/strategies/tokenBudget.js.map +1 -0
- package/dist/core/agent/window/strategy.js +42 -0
- package/dist/core/agent/window/strategy.js.map +1 -0
- package/dist/core/agent/window/summarize.js.map +1 -0
- package/dist/core/agent/{compaction → window}/turns.js +50 -28
- package/dist/core/agent/window/turns.js.map +1 -0
- package/dist/core/agent/window/types.js +23 -0
- package/dist/core/agent/window/types.js.map +1 -0
- package/dist/esm/conventions.d.ts +8 -6
- package/dist/esm/conventions.js +8 -6
- package/dist/esm/conventions.js.map +1 -1
- package/dist/esm/core/Agent.d.ts +6 -6
- package/dist/esm/core/Agent.js +31 -26
- package/dist/esm/core/Agent.js.map +1 -1
- package/dist/esm/core/agent/AgentBuilder.d.ts +60 -4
- package/dist/esm/core/agent/AgentBuilder.js +93 -43
- package/dist/esm/core/agent/AgentBuilder.js.map +1 -1
- package/dist/esm/core/agent/buildAgentChart.d.ts +14 -7
- package/dist/esm/core/agent/buildAgentChart.js +8 -8
- package/dist/esm/core/agent/buildAgentChart.js.map +1 -1
- package/dist/esm/core/agent/buildDynamicAgentChart.js +6 -6
- package/dist/esm/core/agent/buildDynamicAgentChart.js.map +1 -1
- package/dist/esm/core/agent/stages/window.d.ts +63 -0
- package/dist/esm/core/agent/stages/window.js +137 -0
- package/dist/esm/core/agent/stages/window.js.map +1 -0
- package/dist/esm/core/agent/types.d.ts +15 -8
- package/dist/esm/core/agent/window/errors.js.map +1 -0
- package/dist/esm/core/agent/window/index.d.ts +27 -0
- package/dist/esm/core/agent/window/index.js +25 -0
- package/dist/esm/core/agent/window/index.js.map +1 -0
- package/dist/esm/core/agent/window/notice.d.ts +46 -0
- package/dist/esm/core/agent/window/notice.js +52 -0
- package/dist/esm/core/agent/window/notice.js.map +1 -0
- package/dist/esm/core/agent/window/options.d.ts +33 -0
- package/dist/esm/core/agent/window/options.js +87 -0
- package/dist/esm/core/agent/window/options.js.map +1 -0
- package/dist/esm/core/agent/window/removal.d.ts +22 -0
- package/dist/esm/core/agent/window/removal.js +40 -0
- package/dist/esm/core/agent/window/removal.js.map +1 -0
- package/dist/esm/core/agent/window/strategies/drop.d.ts +54 -0
- package/dist/esm/core/agent/window/strategies/drop.js +102 -0
- package/dist/esm/core/agent/window/strategies/drop.js.map +1 -0
- package/dist/esm/core/agent/window/strategies/slidingWindow.d.ts +48 -0
- package/dist/esm/core/agent/window/strategies/slidingWindow.js +83 -0
- package/dist/esm/core/agent/window/strategies/slidingWindow.js.map +1 -0
- package/dist/esm/core/agent/window/strategies/summarizeOldest.d.ts +42 -0
- package/dist/esm/core/agent/window/strategies/summarizeOldest.js +174 -0
- package/dist/esm/core/agent/window/strategies/summarizeOldest.js.map +1 -0
- package/dist/esm/core/agent/window/strategies/tokenBudget.d.ts +47 -0
- package/dist/esm/core/agent/window/strategies/tokenBudget.js +91 -0
- package/dist/esm/core/agent/window/strategies/tokenBudget.js.map +1 -0
- package/dist/esm/core/agent/window/strategy.d.ts +181 -0
- package/dist/esm/core/agent/window/strategy.js +41 -0
- package/dist/esm/core/agent/window/strategy.js.map +1 -0
- package/dist/esm/core/agent/window/summarize.js.map +1 -0
- package/dist/esm/core/agent/window/turns.d.ts +108 -0
- package/dist/esm/core/agent/{compaction → window}/turns.js +48 -26
- package/dist/esm/core/agent/window/turns.js.map +1 -0
- package/dist/esm/core/agent/window/types.d.ts +263 -0
- package/dist/esm/core/agent/window/types.js +22 -0
- package/dist/esm/core/agent/window/types.js.map +1 -0
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +10 -5
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/recorders/core/CompactionMeter.d.ts +25 -19
- package/dist/esm/recorders/core/CompactionMeter.js +27 -19
- package/dist/esm/recorders/core/CompactionMeter.js.map +1 -1
- package/dist/index.js +17 -7
- package/dist/index.js.map +1 -1
- package/dist/recorders/core/CompactionMeter.js +27 -19
- package/dist/recorders/core/CompactionMeter.js.map +1 -1
- package/dist/types/conventions.d.ts +8 -6
- package/dist/types/conventions.d.ts.map +1 -1
- package/dist/types/core/Agent.d.ts +6 -6
- package/dist/types/core/Agent.d.ts.map +1 -1
- package/dist/types/core/agent/AgentBuilder.d.ts +60 -4
- package/dist/types/core/agent/AgentBuilder.d.ts.map +1 -1
- package/dist/types/core/agent/buildAgentChart.d.ts +14 -7
- package/dist/types/core/agent/buildAgentChart.d.ts.map +1 -1
- package/dist/types/core/agent/stages/window.d.ts +64 -0
- package/dist/types/core/agent/stages/window.d.ts.map +1 -0
- package/dist/types/core/agent/types.d.ts +15 -8
- package/dist/types/core/agent/types.d.ts.map +1 -1
- package/dist/types/core/agent/window/errors.d.ts.map +1 -0
- package/dist/types/core/agent/window/index.d.ts +28 -0
- package/dist/types/core/agent/window/index.d.ts.map +1 -0
- package/dist/types/core/agent/window/notice.d.ts +47 -0
- package/dist/types/core/agent/window/notice.d.ts.map +1 -0
- package/dist/types/core/agent/window/options.d.ts +34 -0
- package/dist/types/core/agent/window/options.d.ts.map +1 -0
- package/dist/types/core/agent/window/removal.d.ts +23 -0
- package/dist/types/core/agent/window/removal.d.ts.map +1 -0
- package/dist/types/core/agent/window/strategies/drop.d.ts +55 -0
- package/dist/types/core/agent/window/strategies/drop.d.ts.map +1 -0
- package/dist/types/core/agent/window/strategies/slidingWindow.d.ts +49 -0
- package/dist/types/core/agent/window/strategies/slidingWindow.d.ts.map +1 -0
- package/dist/types/core/agent/window/strategies/summarizeOldest.d.ts +43 -0
- package/dist/types/core/agent/window/strategies/summarizeOldest.d.ts.map +1 -0
- package/dist/types/core/agent/window/strategies/tokenBudget.d.ts +48 -0
- package/dist/types/core/agent/window/strategies/tokenBudget.d.ts.map +1 -0
- package/dist/types/core/agent/window/strategy.d.ts +182 -0
- package/dist/types/core/agent/window/strategy.d.ts.map +1 -0
- package/dist/types/core/agent/window/summarize.d.ts.map +1 -0
- package/dist/types/core/agent/window/turns.d.ts +109 -0
- package/dist/types/core/agent/window/turns.d.ts.map +1 -0
- package/dist/types/core/agent/window/types.d.ts +264 -0
- package/dist/types/core/agent/window/types.d.ts.map +1 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/recorders/core/CompactionMeter.d.ts +25 -19
- package/dist/types/recorders/core/CompactionMeter.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/core/agent/compaction/errors.js.map +0 -1
- package/dist/core/agent/compaction/index.js +0 -24
- package/dist/core/agent/compaction/index.js.map +0 -1
- package/dist/core/agent/compaction/strategy.js +0 -152
- package/dist/core/agent/compaction/strategy.js.map +0 -1
- package/dist/core/agent/compaction/summarize.js.map +0 -1
- package/dist/core/agent/compaction/turns.js.map +0 -1
- package/dist/core/agent/compaction/types.js +0 -21
- package/dist/core/agent/compaction/types.js.map +0 -1
- package/dist/core/agent/stages/compact.js +0 -130
- package/dist/core/agent/stages/compact.js.map +0 -1
- package/dist/esm/core/agent/compaction/errors.js.map +0 -1
- package/dist/esm/core/agent/compaction/index.d.ts +0 -18
- package/dist/esm/core/agent/compaction/index.js +0 -18
- package/dist/esm/core/agent/compaction/index.js.map +0 -1
- package/dist/esm/core/agent/compaction/strategy.d.ts +0 -103
- package/dist/esm/core/agent/compaction/strategy.js +0 -148
- package/dist/esm/core/agent/compaction/strategy.js.map +0 -1
- package/dist/esm/core/agent/compaction/summarize.js.map +0 -1
- package/dist/esm/core/agent/compaction/turns.d.ts +0 -85
- package/dist/esm/core/agent/compaction/turns.js.map +0 -1
- package/dist/esm/core/agent/compaction/types.d.ts +0 -148
- package/dist/esm/core/agent/compaction/types.js +0 -20
- package/dist/esm/core/agent/compaction/types.js.map +0 -1
- package/dist/esm/core/agent/stages/compact.d.ts +0 -59
- package/dist/esm/core/agent/stages/compact.js +0 -126
- package/dist/esm/core/agent/stages/compact.js.map +0 -1
- package/dist/types/core/agent/compaction/errors.d.ts.map +0 -1
- package/dist/types/core/agent/compaction/index.d.ts +0 -19
- package/dist/types/core/agent/compaction/index.d.ts.map +0 -1
- package/dist/types/core/agent/compaction/strategy.d.ts +0 -104
- package/dist/types/core/agent/compaction/strategy.d.ts.map +0 -1
- package/dist/types/core/agent/compaction/summarize.d.ts.map +0 -1
- package/dist/types/core/agent/compaction/turns.d.ts +0 -86
- package/dist/types/core/agent/compaction/turns.d.ts.map +0 -1
- package/dist/types/core/agent/compaction/types.d.ts +0 -149
- package/dist/types/core/agent/compaction/types.d.ts.map +0 -1
- package/dist/types/core/agent/stages/compact.d.ts +0 -60
- package/dist/types/core/agent/stages/compact.d.ts.map +0 -1
- /package/dist/core/agent/{compaction → window}/errors.js +0 -0
- /package/dist/core/agent/{compaction → window}/summarize.js +0 -0
- /package/dist/esm/core/agent/{compaction → window}/errors.d.ts +0 -0
- /package/dist/esm/core/agent/{compaction → window}/errors.js +0 -0
- /package/dist/esm/core/agent/{compaction → window}/summarize.d.ts +0 -0
- /package/dist/esm/core/agent/{compaction → window}/summarize.js +0 -0
- /package/dist/types/core/agent/{compaction → window}/errors.d.ts +0 -0
- /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;
|