@te-river/opencode-team-mode 1.7.0 → 1.7.2
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/README.md +0 -3
- package/README.zh-CN.md +0 -3
- package/dist/host/v2-capabilities.d.ts +0 -17
- package/dist/host/v2-capabilities.d.ts.map +1 -1
- package/dist/host/v2-capabilities.js +0 -28
- package/dist/host/v2-capabilities.js.map +1 -1
- package/dist/host/v2-events.d.ts +0 -1
- package/dist/host/v2-events.d.ts.map +1 -1
- package/dist/host/v2-events.js +0 -13
- package/dist/host/v2-events.js.map +1 -1
- package/dist/host/v2-prune.js +2 -2
- package/dist/host/v2-prune.js.map +1 -1
- package/dist/host/v2-types.d.ts +7 -2
- package/dist/host/v2-types.d.ts.map +1 -1
- package/dist/host/v2-usage.d.ts +50 -0
- package/dist/host/v2-usage.d.ts.map +1 -0
- package/dist/host/v2-usage.js +82 -0
- package/dist/host/v2-usage.js.map +1 -0
- package/dist/host/v2.d.ts.map +1 -1
- package/dist/host/v2.js +2 -72
- package/dist/host/v2.js.map +1 -1
- package/dist/tm/config-layers.d.ts.map +1 -1
- package/dist/tm/config-layers.js +0 -3
- package/dist/tm/config-layers.js.map +1 -1
- package/docs/installation-v2.md +28 -2
- package/docs/v2-port-notes.md +78 -1
- package/package.json +1 -1
- package/scripts/gen-v2-config.mjs +40 -4
- package/scripts/lib/fake-ctx.mjs +6 -14
- package/dist/host/v2-compaction.d.ts +0 -129
- package/dist/host/v2-compaction.d.ts.map +0 -1
- package/dist/host/v2-compaction.js +0 -346
- package/dist/host/v2-compaction.js.map +0 -1
package/scripts/lib/fake-ctx.mjs
CHANGED
|
@@ -78,10 +78,8 @@ export function makeFakeCtx({
|
|
|
78
78
|
const syntheticCalls = []
|
|
79
79
|
const inboxListCalls = []
|
|
80
80
|
const inboxCancelCalls = []
|
|
81
|
-
/**
|
|
82
|
-
* catalog
|
|
83
|
-
* "we asked the host to compact" from "we decided to and told nobody". */
|
|
84
|
-
const compactCalls = []
|
|
81
|
+
/** How many times the catalog was listed — the prune layer's denominator read, so a
|
|
82
|
+
* per-request catalog re-read is a number and not a shrug. */
|
|
85
83
|
const modelListCalls = []
|
|
86
84
|
|
|
87
85
|
/** Turn a seeded verdict into a host-shaped method: a function is called with the args
|
|
@@ -152,11 +150,6 @@ export function makeFakeCtx({
|
|
|
152
150
|
// published operation ids imply, and flat `session["inbox.list"]`), and
|
|
153
151
|
// `inboxFlat` lets a test drive the fallback and pin which one answered.
|
|
154
152
|
...(has("prompt") ? { prompt: verdictMethod(sessionData.prompt, promptCalls) } : {}),
|
|
155
|
-
// #39 — `compact` is seeded ONLY when a test asks for it, for the same reason
|
|
156
|
-
// as `interrupt`: the 2.0.23 probe proved the seam exists (`compact({})` →
|
|
157
|
-
// Missing key at ["sessionID"]), but a fake that always had it would let the
|
|
158
|
-
// layer claim a host that has none.
|
|
159
|
-
...(has("compact") ? { compact: verdictMethod(sessionData.compact, compactCalls) } : {}),
|
|
160
153
|
...(has("synthetic") ? { synthetic: verdictMethod(sessionData.synthetic, syntheticCalls) } : {}),
|
|
161
154
|
...(has("inbox") && sessionData.inbox && !sessionData.inboxFlat
|
|
162
155
|
? {
|
|
@@ -180,9 +173,9 @@ export function makeFakeCtx({
|
|
|
180
173
|
: { hook: mkHook("session") },
|
|
181
174
|
shell: { hook: mkHook("shell") },
|
|
182
175
|
// `model.list()` measured on 2.0.23: `{location, data:[{id, modelID, providerID,
|
|
183
|
-
// …, limit:{context, output}}]}`. The shape below is that shape, because the
|
|
184
|
-
//
|
|
185
|
-
//
|
|
176
|
+
// …, limit:{context, output}}]}`. The shape below is that shape, because the prune
|
|
177
|
+
// layer reads `limit.context` as the denominator and a fake with a friendlier
|
|
178
|
+
// shape would test the reading, not the contract.
|
|
186
179
|
model: {
|
|
187
180
|
async list(opts = {}) {
|
|
188
181
|
modelListCalls.push({ ...(opts ?? {}) })
|
|
@@ -222,8 +215,7 @@ export function makeFakeCtx({
|
|
|
222
215
|
syntheticCalls,
|
|
223
216
|
inboxListCalls,
|
|
224
217
|
inboxCancelCalls,
|
|
225
|
-
/**
|
|
226
|
-
compactCalls,
|
|
218
|
+
/** The catalog reads, with their args. */
|
|
227
219
|
modelListCalls,
|
|
228
220
|
}
|
|
229
221
|
}
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Early compaction — Team's own trigger at a fraction of the context window.
|
|
3
|
-
*
|
|
4
|
-
* WHY THIS EXISTS AS PLUGIN LOGIC
|
|
5
|
-
* The host's `compaction` config block (authoritative: `https://opencode.ai/config.json`
|
|
6
|
-
* `$defs.Config.properties.compaction`, read 2026-10-05) exposes exactly five keys —
|
|
7
|
-
* `auto`, `prune`, `tail_turns`, `preserve_recent_tokens`, `reserved` — and NONE of them
|
|
8
|
-
* is a percentage. The trigger is "when context is full", with `reserved`/`buffer` as an
|
|
9
|
-
* ABSOLUTE token window. So "start compacting at 75%" is not expressible in config, and
|
|
10
|
-
* a plugin cannot write config at runtime either: the 2.x `ctx` has no `config` domain
|
|
11
|
-
* (measured on 2.0.23 with a zero-token probe). What 2.x DOES give is
|
|
12
|
-
* `ctx.session.compact({sessionID})` — `compact({})` fails with `Missing key at
|
|
13
|
-
* ["sessionID"]`, a bogus id with `Session.NotFoundError` — so the threshold lives here,
|
|
14
|
-
* in the package, and ships to every user instead of one machine's file.
|
|
15
|
-
*
|
|
16
|
-
* WHERE THE NUMBER COMES FROM — MEASURED, NOT ASSUMED
|
|
17
|
-
* The first version read usage off `session.hook("context")`'s `messages`, on the theory
|
|
18
|
-
* that the host's own meter does `o.tokens.input + … + o.model.providerID` off message
|
|
19
|
-
* objects. A live 2.0.23 turn disproved it: on the SECOND request of a session, with
|
|
20
|
-
* three messages including an assistant one, the payload still carried no usage numbers
|
|
21
|
-
* (`measured source=no_usage messages=3`, run r-20261005-235343-114738). The numbers are
|
|
22
|
-
* on the EVENT FEED instead — read out of the binary:
|
|
23
|
-
*
|
|
24
|
-
* jq = rt({ type: "session.usage.updated", schema: { ...e, cost: Ps, tokens: Yp } })
|
|
25
|
-
* case "session.usage.updated": m("session","info", D.data.sessionID, {cost, tokens})
|
|
26
|
-
*
|
|
27
|
-
* and our own trajectory shows that event firing 440 times in one desktop session. So the
|
|
28
|
-
* event is the primary source; the message read stays as a secondary (a host that moves
|
|
29
|
-
* the numbers back onto messages must not strand the feature), and a session where neither
|
|
30
|
-
* answers is counted `no_usage` rather than guessed at.
|
|
31
|
-
*
|
|
32
|
-
* THE DENOMINATOR
|
|
33
|
-
* `limit.context` comes from `ctx.model.list()` keyed by providerID+modelID — the same
|
|
34
|
-
* lookup the desktop's usage meter does (`contextLimit:(s)=>providers.find(…).models[…].limit.context`).
|
|
35
|
-
* The model itself comes from the session record (`ctx.session.get({sessionID})`, which is
|
|
36
|
-
* where the reducer just wrote cost/tokens) or, failing that, from the usage-bearing
|
|
37
|
-
* message. No model → no percentage → counted `no_limit`, never a guessed one.
|
|
38
|
-
*
|
|
39
|
-
* THE TEAM GATE
|
|
40
|
-
* An event carries a sessionID and no agent, so it cannot be scope-decided on its own.
|
|
41
|
-
* The layer only ever acts on sessions the request layer has already LEARNED as ours (the
|
|
42
|
-
* context hook passes the same `scope.count(decide(...)) === "ours"` gate every other
|
|
43
|
-
* writer uses and records the id). A usage event for a stranger session is counted and
|
|
44
|
-
* dropped.
|
|
45
|
-
*
|
|
46
|
-
* WHY IT IS SAFE TO ASK MID-TURN
|
|
47
|
-
* Compaction on 2.x is an INBOX INPUT, not a side effect (`SessionInbox.admitCompaction`,
|
|
48
|
-
* handler `a.compact({sessionID, id, delivery})`, duplicate id → `Session.CompactionConflictError`).
|
|
49
|
-
* We admit one per usage number per session with a floor interval, so a ratio that does
|
|
50
|
-
* not drop cannot become a compaction loop, and a failed admission is forgotten so the
|
|
51
|
-
* next signal may retry.
|
|
52
|
-
*
|
|
53
|
-
* WHAT IS DELIBERATELY NOT HERE
|
|
54
|
-
* - No config rewriting, no reading of `compaction.auto`: we cannot see it, so we do not
|
|
55
|
-
* claim to respect it. `TM_COMPACT_TRIGGER=off` restores host-only timing on our side.
|
|
56
|
-
* - No throwing into a hook or an event loop, ever. Every failure is a counted verdict and
|
|
57
|
-
* its own `v2-compact` trajectory line, because the counters ride rows a CLI run never
|
|
58
|
-
* produces (`v2-shutdown` needs dispose, `v2-surface` is throttled).
|
|
59
|
-
*/
|
|
60
|
-
import type { TeamScope } from "./v2-scope.js";
|
|
61
|
-
import type { V2Context, V2Registration } from "./v2-types.js";
|
|
62
|
-
export interface CompactConfig {
|
|
63
|
-
enabled: boolean;
|
|
64
|
-
/** Percent of the model's context window at which we admit a compaction. */
|
|
65
|
-
percent: number;
|
|
66
|
-
/** Floor between two admissions for the same session, ms. */
|
|
67
|
-
minIntervalMs: number;
|
|
68
|
-
}
|
|
69
|
-
/** `off` is the only opt-out; a garbage percent falls back to the documented default. */
|
|
70
|
-
export declare function resolveCompactConfig(env?: Record<string, string | undefined>): CompactConfig;
|
|
71
|
-
/**
|
|
72
|
-
* The host's usage block, summed the way the host sums it:
|
|
73
|
-
* `input + output + reasoning + cache.read + cache.write`. Returns null when the object
|
|
74
|
-
* carries no numbers at all — "we did not find them" is not "usage is zero".
|
|
75
|
-
*/
|
|
76
|
-
export declare function readUsedTokens(message: unknown): number | null;
|
|
77
|
-
/** The model that produced that usage — the key we need to find its context limit. */
|
|
78
|
-
export declare function readModelKey(message: unknown): {
|
|
79
|
-
providerID: string;
|
|
80
|
-
id: string;
|
|
81
|
-
} | null;
|
|
82
|
-
/** Newest message that carries usage wins — that is what the host's meter calls `usage.last`. */
|
|
83
|
-
export declare function lastUsageOf(messages: unknown): {
|
|
84
|
-
used: number;
|
|
85
|
-
model: {
|
|
86
|
-
providerID: string;
|
|
87
|
-
id: string;
|
|
88
|
-
} | null;
|
|
89
|
-
} | null;
|
|
90
|
-
export declare function percentOf(used: number, limit: number): number;
|
|
91
|
-
export interface CompactReport {
|
|
92
|
-
enabled: boolean;
|
|
93
|
-
percent: number;
|
|
94
|
-
checked: number;
|
|
95
|
-
/** `session.usage.updated` lines tapped at all. */
|
|
96
|
-
usageSeen: number;
|
|
97
|
-
/** `session.usage.updated` lines seen for sessions we know. */
|
|
98
|
-
usageEvents: number;
|
|
99
|
-
/** Decisions taken from an event rather than a request assembly. */
|
|
100
|
-
eventDecided: number;
|
|
101
|
-
fired: number;
|
|
102
|
-
confirmed: number;
|
|
103
|
-
below: number;
|
|
104
|
-
deduped: number;
|
|
105
|
-
noUsage: number;
|
|
106
|
-
noLimit: number;
|
|
107
|
-
conflicts: number;
|
|
108
|
-
threw: number;
|
|
109
|
-
foreignSkipped: number;
|
|
110
|
-
lastSource: string;
|
|
111
|
-
lastPercent: number;
|
|
112
|
-
lastError: string;
|
|
113
|
-
}
|
|
114
|
-
export interface CompactInput {
|
|
115
|
-
config?: CompactConfig;
|
|
116
|
-
scope?: TeamScope;
|
|
117
|
-
/** Injectable clock, so the floor interval is testable without waiting. */
|
|
118
|
-
now?: () => number;
|
|
119
|
-
/** Per-event trajectory line (see the header: counters alone are invisible in a CLI run). */
|
|
120
|
-
onEvent?: (row: Record<string, unknown>) => void;
|
|
121
|
-
}
|
|
122
|
-
export interface CompactLayer {
|
|
123
|
-
registrations: V2Registration[];
|
|
124
|
-
report: CompactReport;
|
|
125
|
-
/** Fed by the event layer for `session.usage.updated`. */
|
|
126
|
-
observeUsage: (data: unknown) => void;
|
|
127
|
-
}
|
|
128
|
-
export declare function applyV2EarlyCompaction(ctx: V2Context, input?: CompactInput): Promise<CompactLayer>;
|
|
129
|
-
//# sourceMappingURL=v2-compaction.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"v2-compaction.d.ts","sourceRoot":"","sources":["../../src/host/v2-compaction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAoB,MAAM,eAAe,CAAA;AAEhF,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAA;IACf,6DAA6D;IAC7D,aAAa,EAAE,MAAM,CAAA;CACtB;AAKD,yFAAyF;AACzF,wBAAgB,oBAAoB,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,aAAa,CAQzG;AAID;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAW9D;AAED,sFAAsF;AACtF,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAUxF;AAED,iGAAiG;AACjG,wBAAgB,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,CAOxH;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAA;IACjB,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAA;IACnB,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAA;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,aAAa,CAAA;IACtB,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,2EAA2E;IAC3E,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,6FAA6F;IAC7F,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;CACjD;AAED,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,cAAc,EAAE,CAAA;IAC/B,MAAM,EAAE,aAAa,CAAA;IACrB,0DAA0D;IAC1D,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;CACtC;AAED,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,SAAS,EACd,KAAK,GAAE,YAAiB,GACvB,OAAO,CAAC,YAAY,CAAC,CAyNvB"}
|
|
@@ -1,346 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Early compaction — Team's own trigger at a fraction of the context window.
|
|
3
|
-
*
|
|
4
|
-
* WHY THIS EXISTS AS PLUGIN LOGIC
|
|
5
|
-
* The host's `compaction` config block (authoritative: `https://opencode.ai/config.json`
|
|
6
|
-
* `$defs.Config.properties.compaction`, read 2026-10-05) exposes exactly five keys —
|
|
7
|
-
* `auto`, `prune`, `tail_turns`, `preserve_recent_tokens`, `reserved` — and NONE of them
|
|
8
|
-
* is a percentage. The trigger is "when context is full", with `reserved`/`buffer` as an
|
|
9
|
-
* ABSOLUTE token window. So "start compacting at 75%" is not expressible in config, and
|
|
10
|
-
* a plugin cannot write config at runtime either: the 2.x `ctx` has no `config` domain
|
|
11
|
-
* (measured on 2.0.23 with a zero-token probe). What 2.x DOES give is
|
|
12
|
-
* `ctx.session.compact({sessionID})` — `compact({})` fails with `Missing key at
|
|
13
|
-
* ["sessionID"]`, a bogus id with `Session.NotFoundError` — so the threshold lives here,
|
|
14
|
-
* in the package, and ships to every user instead of one machine's file.
|
|
15
|
-
*
|
|
16
|
-
* WHERE THE NUMBER COMES FROM — MEASURED, NOT ASSUMED
|
|
17
|
-
* The first version read usage off `session.hook("context")`'s `messages`, on the theory
|
|
18
|
-
* that the host's own meter does `o.tokens.input + … + o.model.providerID` off message
|
|
19
|
-
* objects. A live 2.0.23 turn disproved it: on the SECOND request of a session, with
|
|
20
|
-
* three messages including an assistant one, the payload still carried no usage numbers
|
|
21
|
-
* (`measured source=no_usage messages=3`, run r-20261005-235343-114738). The numbers are
|
|
22
|
-
* on the EVENT FEED instead — read out of the binary:
|
|
23
|
-
*
|
|
24
|
-
* jq = rt({ type: "session.usage.updated", schema: { ...e, cost: Ps, tokens: Yp } })
|
|
25
|
-
* case "session.usage.updated": m("session","info", D.data.sessionID, {cost, tokens})
|
|
26
|
-
*
|
|
27
|
-
* and our own trajectory shows that event firing 440 times in one desktop session. So the
|
|
28
|
-
* event is the primary source; the message read stays as a secondary (a host that moves
|
|
29
|
-
* the numbers back onto messages must not strand the feature), and a session where neither
|
|
30
|
-
* answers is counted `no_usage` rather than guessed at.
|
|
31
|
-
*
|
|
32
|
-
* THE DENOMINATOR
|
|
33
|
-
* `limit.context` comes from `ctx.model.list()` keyed by providerID+modelID — the same
|
|
34
|
-
* lookup the desktop's usage meter does (`contextLimit:(s)=>providers.find(…).models[…].limit.context`).
|
|
35
|
-
* The model itself comes from the session record (`ctx.session.get({sessionID})`, which is
|
|
36
|
-
* where the reducer just wrote cost/tokens) or, failing that, from the usage-bearing
|
|
37
|
-
* message. No model → no percentage → counted `no_limit`, never a guessed one.
|
|
38
|
-
*
|
|
39
|
-
* THE TEAM GATE
|
|
40
|
-
* An event carries a sessionID and no agent, so it cannot be scope-decided on its own.
|
|
41
|
-
* The layer only ever acts on sessions the request layer has already LEARNED as ours (the
|
|
42
|
-
* context hook passes the same `scope.count(decide(...)) === "ours"` gate every other
|
|
43
|
-
* writer uses and records the id). A usage event for a stranger session is counted and
|
|
44
|
-
* dropped.
|
|
45
|
-
*
|
|
46
|
-
* WHY IT IS SAFE TO ASK MID-TURN
|
|
47
|
-
* Compaction on 2.x is an INBOX INPUT, not a side effect (`SessionInbox.admitCompaction`,
|
|
48
|
-
* handler `a.compact({sessionID, id, delivery})`, duplicate id → `Session.CompactionConflictError`).
|
|
49
|
-
* We admit one per usage number per session with a floor interval, so a ratio that does
|
|
50
|
-
* not drop cannot become a compaction loop, and a failed admission is forgotten so the
|
|
51
|
-
* next signal may retry.
|
|
52
|
-
*
|
|
53
|
-
* WHAT IS DELIBERATELY NOT HERE
|
|
54
|
-
* - No config rewriting, no reading of `compaction.auto`: we cannot see it, so we do not
|
|
55
|
-
* claim to respect it. `TM_COMPACT_TRIGGER=off` restores host-only timing on our side.
|
|
56
|
-
* - No throwing into a hook or an event loop, ever. Every failure is a counted verdict and
|
|
57
|
-
* its own `v2-compact` trajectory line, because the counters ride rows a CLI run never
|
|
58
|
-
* produces (`v2-shutdown` needs dispose, `v2-surface` is throttled).
|
|
59
|
-
*/
|
|
60
|
-
const DEFAULT_PERCENT = 75;
|
|
61
|
-
const DEFAULT_MIN_INTERVAL_MS = 60_000;
|
|
62
|
-
/** `off` is the only opt-out; a garbage percent falls back to the documented default. */
|
|
63
|
-
export function resolveCompactConfig(env = process.env) {
|
|
64
|
-
const raw = String(env.TM_COMPACT_TRIGGER ?? "on").trim().toLowerCase();
|
|
65
|
-
const enabled = raw !== "off" && raw !== "false" && raw !== "0" && raw !== "no";
|
|
66
|
-
const p = Number(env.TM_COMPACT_AT_PERCENT);
|
|
67
|
-
const percent = Number.isFinite(p) && p >= 5 && p <= 95 ? p : DEFAULT_PERCENT;
|
|
68
|
-
const ms = Number(env.TM_COMPACT_MIN_MS);
|
|
69
|
-
const minIntervalMs = Number.isFinite(ms) && ms >= 0 ? Math.min(ms, 600_000) : DEFAULT_MIN_INTERVAL_MS;
|
|
70
|
-
return { enabled, percent, minIntervalMs };
|
|
71
|
-
}
|
|
72
|
-
const num = (v) => (typeof v === "number" && Number.isFinite(v) ? v : 0);
|
|
73
|
-
/**
|
|
74
|
-
* The host's usage block, summed the way the host sums it:
|
|
75
|
-
* `input + output + reasoning + cache.read + cache.write`. Returns null when the object
|
|
76
|
-
* carries no numbers at all — "we did not find them" is not "usage is zero".
|
|
77
|
-
*/
|
|
78
|
-
export function readUsedTokens(message) {
|
|
79
|
-
const m = message;
|
|
80
|
-
if (!m || typeof m !== "object")
|
|
81
|
-
return null;
|
|
82
|
-
const candidates = [m.tokens, m.metadata?.tokens, m.info?.tokens, m.usage?.tokens, m.usage];
|
|
83
|
-
for (const t of candidates) {
|
|
84
|
-
if (!t || typeof t !== "object")
|
|
85
|
-
continue;
|
|
86
|
-
const cache = t.cache && typeof t.cache === "object" ? t.cache : {};
|
|
87
|
-
const sum = num(t.input) + num(t.output) + num(t.reasoning) + num(cache.read) + num(cache.write);
|
|
88
|
-
if (sum > 0)
|
|
89
|
-
return sum;
|
|
90
|
-
}
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
/** The model that produced that usage — the key we need to find its context limit. */
|
|
94
|
-
export function readModelKey(message) {
|
|
95
|
-
const m = message;
|
|
96
|
-
if (!m || typeof m !== "object")
|
|
97
|
-
return null;
|
|
98
|
-
for (const c of [m.model, m.metadata?.model, m.info?.model]) {
|
|
99
|
-
if (!c || typeof c !== "object")
|
|
100
|
-
continue;
|
|
101
|
-
const providerID = typeof c.providerID === "string" ? c.providerID : typeof c.provider === "string" ? c.provider : "";
|
|
102
|
-
const id = typeof c.id === "string" ? c.id : typeof c.modelID === "string" ? c.modelID : "";
|
|
103
|
-
if (providerID && id)
|
|
104
|
-
return { providerID, id };
|
|
105
|
-
}
|
|
106
|
-
return null;
|
|
107
|
-
}
|
|
108
|
-
/** Newest message that carries usage wins — that is what the host's meter calls `usage.last`. */
|
|
109
|
-
export function lastUsageOf(messages) {
|
|
110
|
-
if (!Array.isArray(messages))
|
|
111
|
-
return null;
|
|
112
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
113
|
-
const used = readUsedTokens(messages[i]);
|
|
114
|
-
if (used !== null)
|
|
115
|
-
return { used, model: readModelKey(messages[i]) };
|
|
116
|
-
}
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
export function percentOf(used, limit) {
|
|
120
|
-
if (!Number.isFinite(used) || !Number.isFinite(limit) || limit <= 0)
|
|
121
|
-
return 0;
|
|
122
|
-
return Math.round((used / limit) * 100);
|
|
123
|
-
}
|
|
124
|
-
export async function applyV2EarlyCompaction(ctx, input = {}) {
|
|
125
|
-
const config = input.config ?? resolveCompactConfig();
|
|
126
|
-
const now = input.now ?? (() => Date.now());
|
|
127
|
-
const report = {
|
|
128
|
-
enabled: config.enabled,
|
|
129
|
-
percent: config.percent,
|
|
130
|
-
checked: 0,
|
|
131
|
-
usageSeen: 0,
|
|
132
|
-
usageEvents: 0,
|
|
133
|
-
eventDecided: 0,
|
|
134
|
-
fired: 0,
|
|
135
|
-
confirmed: 0,
|
|
136
|
-
below: 0,
|
|
137
|
-
deduped: 0,
|
|
138
|
-
noUsage: 0,
|
|
139
|
-
noLimit: 0,
|
|
140
|
-
conflicts: 0,
|
|
141
|
-
threw: 0,
|
|
142
|
-
foreignSkipped: 0,
|
|
143
|
-
lastSource: "(none)",
|
|
144
|
-
lastPercent: 0,
|
|
145
|
-
lastError: "",
|
|
146
|
-
};
|
|
147
|
-
const registrations = [];
|
|
148
|
-
const session = ctx.session;
|
|
149
|
-
const hasCompact = !!session && typeof session.hook === "function" && typeof session.compact === "function";
|
|
150
|
-
if (!config.enabled || !session || !hasCompact) {
|
|
151
|
-
return { registrations, report, observeUsage: () => { } };
|
|
152
|
-
}
|
|
153
|
-
/** Sessions the request layer has already proven are ours. An event alone cannot prove it. */
|
|
154
|
-
const teamSessions = new Set();
|
|
155
|
-
const usageBySession = new Map();
|
|
156
|
-
const modelBySession = new Map();
|
|
157
|
-
const measured = new Set();
|
|
158
|
-
const lastBySession = new Map();
|
|
159
|
-
const emit = (kind, fields) => {
|
|
160
|
-
try {
|
|
161
|
-
input.onEvent?.({ kind, ...fields });
|
|
162
|
-
}
|
|
163
|
-
catch {
|
|
164
|
-
/* a diagnostic that cannot be written never breaks the thing it describes */
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
// Catalog cache: one `model.list()` per process, refreshed once per unknown key.
|
|
168
|
-
let limits = null;
|
|
169
|
-
const askedUnknown = new Set();
|
|
170
|
-
async function loadLimits(force = false) {
|
|
171
|
-
if (!limits || force) {
|
|
172
|
-
const next = new Map();
|
|
173
|
-
try {
|
|
174
|
-
// Property access, never a captured reference (§10 in dispatch.ts).
|
|
175
|
-
const res = (await ctx.model?.list?.({}));
|
|
176
|
-
const data = Array.isArray(res?.data) ? res.data : [];
|
|
177
|
-
for (const m of data) {
|
|
178
|
-
const any = m;
|
|
179
|
-
const providerID = typeof any?.providerID === "string" ? any.providerID : typeof any?.id === "string" ? any.id : "";
|
|
180
|
-
const id = typeof any?.id === "string" ? any.id : typeof any?.modelID === "string" ? any.modelID : "";
|
|
181
|
-
const limit = any?.limit?.context;
|
|
182
|
-
if (providerID && id && typeof limit === "number" && limit > 0)
|
|
183
|
-
next.set(`${providerID}/${id}`, limit);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
catch {
|
|
187
|
-
/* no catalog is a counted no_limit, never a guess */
|
|
188
|
-
}
|
|
189
|
-
limits = next;
|
|
190
|
-
}
|
|
191
|
-
return limits;
|
|
192
|
-
}
|
|
193
|
-
/** The session record is where the host's own reducer writes cost/tokens, and where the
|
|
194
|
-
* model of the session lives. Read once per session, refreshed when we have no model. */
|
|
195
|
-
async function sessionModel(sessionID) {
|
|
196
|
-
const known = modelBySession.get(sessionID);
|
|
197
|
-
if (known)
|
|
198
|
-
return known;
|
|
199
|
-
try {
|
|
200
|
-
// Typed through the index signature, and called with the domain as receiver — a
|
|
201
|
-
// detached `get` loses it and the host's client throws (§10 in dispatch.ts).
|
|
202
|
-
const get = session.get;
|
|
203
|
-
const rec = get ? (await get.call(session, { sessionID })) : undefined;
|
|
204
|
-
const found = readModelKey(rec);
|
|
205
|
-
if (found)
|
|
206
|
-
modelBySession.set(sessionID, found);
|
|
207
|
-
return found;
|
|
208
|
-
}
|
|
209
|
-
catch {
|
|
210
|
-
return null;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
async function decide(sessionID, from, fallback) {
|
|
214
|
-
let used = usageBySession.get(sessionID);
|
|
215
|
-
let source = "event";
|
|
216
|
-
let model = modelBySession.get(sessionID) ?? null;
|
|
217
|
-
if (used === undefined) {
|
|
218
|
-
if (!fallback) {
|
|
219
|
-
report.noUsage++;
|
|
220
|
-
if (!measured.has(sessionID)) {
|
|
221
|
-
measured.add(sessionID);
|
|
222
|
-
emit("measured", { sessionID, source: "no_usage", from });
|
|
223
|
-
}
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
used = fallback.used;
|
|
227
|
-
source = "message";
|
|
228
|
-
model = fallback.model ?? null;
|
|
229
|
-
}
|
|
230
|
-
if (used <= 0) {
|
|
231
|
-
report.noUsage++;
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
if (!model)
|
|
235
|
-
model = (await sessionModel(sessionID)) ?? fallback?.model ?? null;
|
|
236
|
-
if (!model) {
|
|
237
|
-
report.noLimit++;
|
|
238
|
-
emit("no_denominator", { sessionID, used, from });
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
modelBySession.set(sessionID, model);
|
|
242
|
-
const key = `${model.providerID}/${model.id}`;
|
|
243
|
-
const table = await loadLimits();
|
|
244
|
-
let limit = table.get(key);
|
|
245
|
-
if (!limit && !askedUnknown.has(key)) {
|
|
246
|
-
askedUnknown.add(key);
|
|
247
|
-
limit = (await loadLimits(true)).get(key);
|
|
248
|
-
}
|
|
249
|
-
if (!limit) {
|
|
250
|
-
report.noLimit++;
|
|
251
|
-
emit("no_denominator", { sessionID, used, model: key, from });
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
const pct = percentOf(used, limit);
|
|
255
|
-
report.lastSource = source;
|
|
256
|
-
report.lastPercent = pct;
|
|
257
|
-
if (!measured.has(sessionID)) {
|
|
258
|
-
measured.add(sessionID);
|
|
259
|
-
emit("measured", { sessionID, source, from, used, limit, percent: pct, threshold: config.percent, model: key });
|
|
260
|
-
}
|
|
261
|
-
if (pct < config.percent) {
|
|
262
|
-
report.below++;
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
const prev = lastBySession.get(sessionID);
|
|
266
|
-
const t = now();
|
|
267
|
-
if (prev && (t - prev.at < config.minIntervalMs || prev.used >= used)) {
|
|
268
|
-
report.deduped++;
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
lastBySession.set(sessionID, { at: t, used });
|
|
272
|
-
report.fired++;
|
|
273
|
-
emit("admit", { sessionID, percent: pct, threshold: config.percent, used, limit, source, from });
|
|
274
|
-
try {
|
|
275
|
-
// Property access at the call site, never a captured reference — a detached
|
|
276
|
-
// `compact` loses the receiver and the host's client throws (§10 in dispatch.ts).
|
|
277
|
-
await session.compact({ sessionID });
|
|
278
|
-
report.confirmed++;
|
|
279
|
-
emit("confirmed", { sessionID, percent: pct, from });
|
|
280
|
-
}
|
|
281
|
-
catch (err) {
|
|
282
|
-
const msg = String(err?.message ?? err ?? "").replace(/\s+/g, " ").slice(0, 120);
|
|
283
|
-
// The host's refusal shapes stay distinct: a duplicate input id is a conflict,
|
|
284
|
-
// anything else is a failure we know nothing about.
|
|
285
|
-
if (/CompactionConflict/i.test(msg)) {
|
|
286
|
-
report.conflicts++;
|
|
287
|
-
emit("conflict", { sessionID, percent: pct, error: msg });
|
|
288
|
-
}
|
|
289
|
-
else {
|
|
290
|
-
report.threw++;
|
|
291
|
-
emit("threw", { sessionID, percent: pct, error: msg });
|
|
292
|
-
}
|
|
293
|
-
report.lastError = msg;
|
|
294
|
-
// A failed admission is not a success: let the next signal retry.
|
|
295
|
-
lastBySession.delete(sessionID);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
registrations.push(await session.hook("context", (event) => {
|
|
299
|
-
try {
|
|
300
|
-
const sessionID = typeof event?.sessionID === "string" ? String(event.sessionID) : "";
|
|
301
|
-
if (!sessionID)
|
|
302
|
-
return;
|
|
303
|
-
if (input.scope && input.scope.count(input.scope.decide(event)) !== "ours") {
|
|
304
|
-
report.foreignSkipped++;
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
teamSessions.add(sessionID);
|
|
308
|
-
report.checked++;
|
|
309
|
-
// Secondary source only: the live 2.0.23 payload carries no usage on messages.
|
|
310
|
-
const fallback = lastUsageOf(event.messages) ?? undefined;
|
|
311
|
-
void decide(sessionID, "request", fallback).catch(() => {
|
|
312
|
-
report.threw++;
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
catch {
|
|
316
|
-
/* the request must not be held hostage by a threshold check */
|
|
317
|
-
}
|
|
318
|
-
}));
|
|
319
|
-
return {
|
|
320
|
-
registrations,
|
|
321
|
-
report,
|
|
322
|
-
observeUsage: (data) => {
|
|
323
|
-
const d = data;
|
|
324
|
-
const sessionID = typeof d?.sessionID === "string" ? d.sessionID : "";
|
|
325
|
-
if (!sessionID)
|
|
326
|
-
return;
|
|
327
|
-
report.usageSeen++;
|
|
328
|
-
if (!teamSessions.has(sessionID)) {
|
|
329
|
-
// A usage event carries no agent, so it cannot be scope-decided on its own: an
|
|
330
|
-
// id the request layer never proved is ours is counted and dropped, never acted on.
|
|
331
|
-
report.foreignSkipped++;
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
const used = readUsedTokens(d);
|
|
335
|
-
if (used === null)
|
|
336
|
-
return;
|
|
337
|
-
report.usageEvents++;
|
|
338
|
-
usageBySession.set(sessionID, used);
|
|
339
|
-
report.eventDecided++;
|
|
340
|
-
void decide(sessionID, "event").catch(() => {
|
|
341
|
-
report.threw++;
|
|
342
|
-
});
|
|
343
|
-
},
|
|
344
|
-
};
|
|
345
|
-
}
|
|
346
|
-
//# sourceMappingURL=v2-compaction.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"v2-compaction.js","sourceRoot":"","sources":["../../src/host/v2-compaction.ts"],"names":[],"mappings":"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"}
|