@seanhogg/builderforce-memory 2026.10.24 → 2026.10.25
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/working/consolidationMarker.d.ts +75 -0
- package/dist/working/consolidationMarker.d.ts.map +1 -0
- package/dist/working/consolidationMarker.js +104 -0
- package/dist/working/consolidationMarker.js.map +1 -0
- package/dist/working/index.d.ts +11 -0
- package/dist/working/index.d.ts.map +1 -1
- package/dist/working/index.js +8 -0
- package/dist/working/index.js.map +1 -1
- package/dist/working/summary.d.ts +31 -0
- package/dist/working/summary.d.ts.map +1 -0
- package/dist/working/summary.js +48 -0
- package/dist/working/summary.js.map +1 -0
- package/dist/working/transcript.d.ts +137 -0
- package/dist/working/transcript.d.ts.map +1 -0
- package/dist/working/transcript.js +243 -0
- package/dist/working/transcript.js.map +1 -0
- package/package.json +3 -3
- package/src/working/consolidationMarker.test.ts +95 -0
- package/src/working/consolidationMarker.ts +115 -0
- package/src/working/index.ts +35 -0
- package/src/working/summary.test.ts +51 -0
- package/src/working/summary.ts +68 -0
- package/src/working/transcript.test.ts +125 -0
- package/src/working/transcript.ts +291 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat consolidation markers — the durable form of a compressed conversation.
|
|
3
|
+
*
|
|
4
|
+
* A long conversation is compressed into ONE summary that becomes the new base context:
|
|
5
|
+
* everything before the marker stays visible in the transcript, but the next run's model
|
|
6
|
+
* seed starts from the marker instead of the full history. The marker is a normal
|
|
7
|
+
* assistant message (so a reader SEES the summary) tagged in its metadata.
|
|
8
|
+
*
|
|
9
|
+
* Two writers:
|
|
10
|
+
* - MANUAL — "Consolidate" / "Fork": the summary covers everything before the marker.
|
|
11
|
+
* - AUTO — a run's working-transcript fold (`transcript.ts`), persisted so the next run
|
|
12
|
+
* (after a reload, on another surface) starts from the note instead of summarizing the
|
|
13
|
+
* whole history again. A fold covers only the OLDER part of the transcript; its
|
|
14
|
+
* `keepFromId` names the first persisted message it did NOT cover, and every message
|
|
15
|
+
* from there on stays verbatim in the seed. Nothing is in neither.
|
|
16
|
+
*/
|
|
17
|
+
/** The marker's metadata. `auto` / `keepFromId` / `folded` are set only by a run's fold. */
|
|
18
|
+
export interface ConsolidationMarkerMeta {
|
|
19
|
+
consolidation: true;
|
|
20
|
+
/** Written by auto-compaction, not by a person. */
|
|
21
|
+
auto?: true;
|
|
22
|
+
/** The first persisted message the note does NOT cover (kept verbatim after the marker). */
|
|
23
|
+
keepFromId?: number;
|
|
24
|
+
/** How many transcript messages the fold newly compressed. */
|
|
25
|
+
folded?: number;
|
|
26
|
+
}
|
|
27
|
+
/** The metadata of a manual marker. */
|
|
28
|
+
export declare const CONSOLIDATION_META: ConsolidationMarkerMeta;
|
|
29
|
+
/** Serialized metadata for a marker message (ready to persist). Manual when no `auto`. */
|
|
30
|
+
export declare function consolidationMetadata(auto?: {
|
|
31
|
+
keepFromId?: number;
|
|
32
|
+
folded?: number;
|
|
33
|
+
}): string;
|
|
34
|
+
/** The marker metadata a persisted message carries, or null when it is not a marker. */
|
|
35
|
+
export declare function consolidationMarkerOf(msg: {
|
|
36
|
+
metadata?: string | null;
|
|
37
|
+
}): ConsolidationMarkerMeta | null;
|
|
38
|
+
/** True when a persisted message is a consolidation marker (by its metadata flag). */
|
|
39
|
+
export declare function isConsolidationMarker(msg: {
|
|
40
|
+
metadata?: string | null;
|
|
41
|
+
}): boolean;
|
|
42
|
+
/** The index of the LAST consolidation marker in a message list, or -1 if none. */
|
|
43
|
+
export declare function lastConsolidationIndex(messages: ReadonlyArray<{
|
|
44
|
+
metadata?: string | null;
|
|
45
|
+
}>): number;
|
|
46
|
+
/**
|
|
47
|
+
* Trim a message list to the compressed context: the last marker, then what it does not
|
|
48
|
+
* cover. A manual marker covers everything before it (the list from the marker on). An
|
|
49
|
+
* auto marker covers up to its `keepFromId`: the marker, then every message from that id
|
|
50
|
+
* on (other markers dropped — an older note is superseded by the newer one). Unchanged
|
|
51
|
+
* when there is no marker; a `keepFromId` no longer in the list reads as manual.
|
|
52
|
+
*/
|
|
53
|
+
export declare function scopeToConsolidation<T extends {
|
|
54
|
+
id?: number;
|
|
55
|
+
metadata?: string | null;
|
|
56
|
+
}>(messages: T[]): T[];
|
|
57
|
+
/**
|
|
58
|
+
* Where an auto marker's verbatim part begins: given the persisted messages the run's
|
|
59
|
+
* transcript holds (`anchors`: transcript index → message id) and the index the fold now
|
|
60
|
+
* covers up to (exclusive), the id of the persisted message the next seed must keep from.
|
|
61
|
+
*
|
|
62
|
+
* Exact when a persisted message sits AT `coveredEnd` (the fold ended inside seeded
|
|
63
|
+
* history). Otherwise the nearest persisted message BEFORE it — conservative: the span
|
|
64
|
+
* between it and `coveredEnd` is then both in the note and verbatim, never in neither.
|
|
65
|
+
* Undefined when no persisted message precedes the cut (nothing to keep from).
|
|
66
|
+
*/
|
|
67
|
+
export declare function foldKeepFromId(anchors: ReadonlyArray<{
|
|
68
|
+
index: number;
|
|
69
|
+
id: number;
|
|
70
|
+
}>, coveredEnd: number): number | undefined;
|
|
71
|
+
/** The visible header prefixed onto a consolidation summary so a reader recognizes it. */
|
|
72
|
+
export declare const CONSOLIDATION_MARKER_PREFIX = "\uD83D\uDCCC **Consolidated summary** \u2014 context continues from here.\n\n";
|
|
73
|
+
/** Wrap a raw summary as the marker's visible content (prefix + summary). */
|
|
74
|
+
export declare function consolidationMarkerContent(summary: string): string;
|
|
75
|
+
//# sourceMappingURL=consolidationMarker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consolidationMarker.d.ts","sourceRoot":"","sources":["../../src/working/consolidationMarker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,4FAA4F;AAC5F,MAAM,WAAW,uBAAuB;IACtC,aAAa,EAAE,IAAI,CAAC;IACpB,mDAAmD;IACnD,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,4FAA4F;IAC5F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,uCAAuC;AACvC,eAAO,MAAM,kBAAkB,EAAE,uBAAiD,CAAC;AAEnF,0FAA0F;AAC1F,wBAAgB,qBAAqB,CAAC,IAAI,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAS7F;AAED,wFAAwF;AACxF,wBAAgB,qBAAqB,CAAC,GAAG,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,uBAAuB,GAAG,IAAI,CAcvG;AAED,sFAAsF;AACtF,wBAAgB,qBAAqB,CAAC,GAAG,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,OAAO,CAEhF;AAED,mFAAmF;AACnF,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,aAAa,CAAC;IAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,GAAG,MAAM,CAKpG;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAQ5G;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAM5H;AAED,0FAA0F;AAC1F,eAAO,MAAM,2BAA2B,kFAAmE,CAAC;AAE5G,6EAA6E;AAC7E,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAElE"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat consolidation markers — the durable form of a compressed conversation.
|
|
3
|
+
*
|
|
4
|
+
* A long conversation is compressed into ONE summary that becomes the new base context:
|
|
5
|
+
* everything before the marker stays visible in the transcript, but the next run's model
|
|
6
|
+
* seed starts from the marker instead of the full history. The marker is a normal
|
|
7
|
+
* assistant message (so a reader SEES the summary) tagged in its metadata.
|
|
8
|
+
*
|
|
9
|
+
* Two writers:
|
|
10
|
+
* - MANUAL — "Consolidate" / "Fork": the summary covers everything before the marker.
|
|
11
|
+
* - AUTO — a run's working-transcript fold (`transcript.ts`), persisted so the next run
|
|
12
|
+
* (after a reload, on another surface) starts from the note instead of summarizing the
|
|
13
|
+
* whole history again. A fold covers only the OLDER part of the transcript; its
|
|
14
|
+
* `keepFromId` names the first persisted message it did NOT cover, and every message
|
|
15
|
+
* from there on stays verbatim in the seed. Nothing is in neither.
|
|
16
|
+
*/
|
|
17
|
+
/** The metadata of a manual marker. */
|
|
18
|
+
export const CONSOLIDATION_META = { consolidation: true };
|
|
19
|
+
/** Serialized metadata for a marker message (ready to persist). Manual when no `auto`. */
|
|
20
|
+
export function consolidationMetadata(auto) {
|
|
21
|
+
if (!auto)
|
|
22
|
+
return JSON.stringify(CONSOLIDATION_META);
|
|
23
|
+
const meta = {
|
|
24
|
+
consolidation: true,
|
|
25
|
+
auto: true,
|
|
26
|
+
...(auto.keepFromId !== undefined ? { keepFromId: auto.keepFromId } : {}),
|
|
27
|
+
...(auto.folded !== undefined ? { folded: auto.folded } : {}),
|
|
28
|
+
};
|
|
29
|
+
return JSON.stringify(meta);
|
|
30
|
+
}
|
|
31
|
+
/** The marker metadata a persisted message carries, or null when it is not a marker. */
|
|
32
|
+
export function consolidationMarkerOf(msg) {
|
|
33
|
+
if (!msg.metadata)
|
|
34
|
+
return null;
|
|
35
|
+
try {
|
|
36
|
+
const m = JSON.parse(msg.metadata);
|
|
37
|
+
if (m?.consolidation !== true)
|
|
38
|
+
return null;
|
|
39
|
+
return {
|
|
40
|
+
consolidation: true,
|
|
41
|
+
...(m.auto === true ? { auto: true } : {}),
|
|
42
|
+
...(typeof m.keepFromId === 'number' && Number.isFinite(m.keepFromId) ? { keepFromId: m.keepFromId } : {}),
|
|
43
|
+
...(typeof m.folded === 'number' && Number.isFinite(m.folded) && m.folded > 0 ? { folded: m.folded } : {}),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** True when a persisted message is a consolidation marker (by its metadata flag). */
|
|
51
|
+
export function isConsolidationMarker(msg) {
|
|
52
|
+
return consolidationMarkerOf(msg) !== null;
|
|
53
|
+
}
|
|
54
|
+
/** The index of the LAST consolidation marker in a message list, or -1 if none. */
|
|
55
|
+
export function lastConsolidationIndex(messages) {
|
|
56
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
57
|
+
if (isConsolidationMarker(messages[i]))
|
|
58
|
+
return i;
|
|
59
|
+
}
|
|
60
|
+
return -1;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Trim a message list to the compressed context: the last marker, then what it does not
|
|
64
|
+
* cover. A manual marker covers everything before it (the list from the marker on). An
|
|
65
|
+
* auto marker covers up to its `keepFromId`: the marker, then every message from that id
|
|
66
|
+
* on (other markers dropped — an older note is superseded by the newer one). Unchanged
|
|
67
|
+
* when there is no marker; a `keepFromId` no longer in the list reads as manual.
|
|
68
|
+
*/
|
|
69
|
+
export function scopeToConsolidation(messages) {
|
|
70
|
+
const idx = lastConsolidationIndex(messages);
|
|
71
|
+
if (idx < 0)
|
|
72
|
+
return messages;
|
|
73
|
+
const marker = messages[idx];
|
|
74
|
+
const keepFromId = consolidationMarkerOf(marker)?.keepFromId;
|
|
75
|
+
const from = keepFromId === undefined ? -1 : messages.findIndex((m) => m.id === keepFromId);
|
|
76
|
+
if (from < 0 || from > idx)
|
|
77
|
+
return messages.slice(idx);
|
|
78
|
+
return [marker, ...messages.slice(from).filter((m) => m !== marker && !isConsolidationMarker(m))];
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Where an auto marker's verbatim part begins: given the persisted messages the run's
|
|
82
|
+
* transcript holds (`anchors`: transcript index → message id) and the index the fold now
|
|
83
|
+
* covers up to (exclusive), the id of the persisted message the next seed must keep from.
|
|
84
|
+
*
|
|
85
|
+
* Exact when a persisted message sits AT `coveredEnd` (the fold ended inside seeded
|
|
86
|
+
* history). Otherwise the nearest persisted message BEFORE it — conservative: the span
|
|
87
|
+
* between it and `coveredEnd` is then both in the note and verbatim, never in neither.
|
|
88
|
+
* Undefined when no persisted message precedes the cut (nothing to keep from).
|
|
89
|
+
*/
|
|
90
|
+
export function foldKeepFromId(anchors, coveredEnd) {
|
|
91
|
+
let best;
|
|
92
|
+
for (const a of anchors) {
|
|
93
|
+
if (a.index <= coveredEnd && (!best || a.index > best.index))
|
|
94
|
+
best = a;
|
|
95
|
+
}
|
|
96
|
+
return best?.id;
|
|
97
|
+
}
|
|
98
|
+
/** The visible header prefixed onto a consolidation summary so a reader recognizes it. */
|
|
99
|
+
export const CONSOLIDATION_MARKER_PREFIX = '📌 **Consolidated summary** — context continues from here.\n\n';
|
|
100
|
+
/** Wrap a raw summary as the marker's visible content (prefix + summary). */
|
|
101
|
+
export function consolidationMarkerContent(summary) {
|
|
102
|
+
return `${CONSOLIDATION_MARKER_PREFIX}${summary.trim()}`;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=consolidationMarker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consolidationMarker.js","sourceRoot":"","sources":["../../src/working/consolidationMarker.ts"],"names":[],"mappings":"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"}
|
package/dist/working/index.d.ts
CHANGED
|
@@ -15,10 +15,21 @@
|
|
|
15
15
|
* The memory knows observations ("read this file", "changed that one"), never tool
|
|
16
16
|
* names — a host maps its own tool vocabulary onto {@link Observation}.
|
|
17
17
|
*
|
|
18
|
+
* And what the MODEL sees of a run each turn — the working TRANSCRIPT (`transcript.ts`):
|
|
19
|
+
* the drop-oldest window and the incremental auto-compaction fold, the ONE summarizer
|
|
20
|
+
* prompt it folds with (`summary.ts`), and the consolidation markers a fold or a
|
|
21
|
+
* "Consolidate" persists so the next run starts from the note (`consolidationMarker.ts`).
|
|
22
|
+
*
|
|
18
23
|
* Pure: no `node:*`, no I/O — runs in a Cloudflare Worker, a browser page and Node alike.
|
|
19
24
|
*/
|
|
20
25
|
export { WorkingMemory } from './workingMemory.js';
|
|
21
26
|
export { consolidate, mergeConsolidated, consolidatedKey, CONSOLIDATED_KEY_PREFIX, MIN_CONSOLIDATED_FILES } from './consolidate.js';
|
|
22
27
|
export { rankByCount } from './rank.js';
|
|
23
28
|
export type { Observation, WorkingMemorySnapshot, ConsolidatedMemory, ConsolidateOptions } from './types.js';
|
|
29
|
+
export { HISTORY_WINDOW, HISTORY_TOKEN_BUDGET, COMPACT_TAIL_TOKEN_BUDGET, COMPACT_TAIL_TURNS, windowed, stillInWorkingContext, verbatimStart, compactTailStart, compactTailStartForBudget, pinnedDirectiveIndex, assembleCompacted, transcriptTokens, buildWorkingTranscript, } from './transcript.js';
|
|
30
|
+
export type { TranscriptMessage, CompactMemo, WorkingTranscriptState, TranscriptFold } from './transcript.js';
|
|
31
|
+
export { TRANSCRIPT_SUMMARY_PROMPT, TRANSCRIPT_SUMMARY_MAX_TOKENS, renderForSummary, summarizeTranscript } from './summary.js';
|
|
32
|
+
export type { SummaryCompletion } from './summary.js';
|
|
33
|
+
export { CONSOLIDATION_META, CONSOLIDATION_MARKER_PREFIX, consolidationMetadata, consolidationMarkerOf, consolidationMarkerContent, isConsolidationMarker, lastConsolidationIndex, scopeToConsolidation, foldKeepFromId, } from './consolidationMarker.js';
|
|
34
|
+
export type { ConsolidationMarkerMeta } from './consolidationMarker.js';
|
|
24
35
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/working/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/working/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,eAAe,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AACpI,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,YAAY,EAAE,WAAW,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC7G,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,yBAAyB,EACzB,kBAAkB,EAClB,QAAQ,EACR,qBAAqB,EACrB,aAAa,EACb,gBAAgB,EAChB,yBAAyB,EACzB,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,iBAAiB,EAAE,WAAW,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC9G,OAAO,EAAE,yBAAyB,EAAE,6BAA6B,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAC/H,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EACL,kBAAkB,EAClB,2BAA2B,EAC3B,qBAAqB,EACrB,qBAAqB,EACrB,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,GACf,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC"}
|
package/dist/working/index.js
CHANGED
|
@@ -15,9 +15,17 @@
|
|
|
15
15
|
* The memory knows observations ("read this file", "changed that one"), never tool
|
|
16
16
|
* names — a host maps its own tool vocabulary onto {@link Observation}.
|
|
17
17
|
*
|
|
18
|
+
* And what the MODEL sees of a run each turn — the working TRANSCRIPT (`transcript.ts`):
|
|
19
|
+
* the drop-oldest window and the incremental auto-compaction fold, the ONE summarizer
|
|
20
|
+
* prompt it folds with (`summary.ts`), and the consolidation markers a fold or a
|
|
21
|
+
* "Consolidate" persists so the next run starts from the note (`consolidationMarker.ts`).
|
|
22
|
+
*
|
|
18
23
|
* Pure: no `node:*`, no I/O — runs in a Cloudflare Worker, a browser page and Node alike.
|
|
19
24
|
*/
|
|
20
25
|
export { WorkingMemory } from './workingMemory.js';
|
|
21
26
|
export { consolidate, mergeConsolidated, consolidatedKey, CONSOLIDATED_KEY_PREFIX, MIN_CONSOLIDATED_FILES } from './consolidate.js';
|
|
22
27
|
export { rankByCount } from './rank.js';
|
|
28
|
+
export { HISTORY_WINDOW, HISTORY_TOKEN_BUDGET, COMPACT_TAIL_TOKEN_BUDGET, COMPACT_TAIL_TURNS, windowed, stillInWorkingContext, verbatimStart, compactTailStart, compactTailStartForBudget, pinnedDirectiveIndex, assembleCompacted, transcriptTokens, buildWorkingTranscript, } from './transcript.js';
|
|
29
|
+
export { TRANSCRIPT_SUMMARY_PROMPT, TRANSCRIPT_SUMMARY_MAX_TOKENS, renderForSummary, summarizeTranscript } from './summary.js';
|
|
30
|
+
export { CONSOLIDATION_META, CONSOLIDATION_MARKER_PREFIX, consolidationMetadata, consolidationMarkerOf, consolidationMarkerContent, isConsolidationMarker, lastConsolidationIndex, scopeToConsolidation, foldKeepFromId, } from './consolidationMarker.js';
|
|
23
31
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/working/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/working/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,eAAe,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AACpI,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,yBAAyB,EACzB,kBAAkB,EAClB,QAAQ,EACR,qBAAqB,EACrB,aAAa,EACb,gBAAgB,EAChB,yBAAyB,EACzB,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,yBAAyB,EAAE,6BAA6B,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAE/H,OAAO,EACL,kBAAkB,EAClB,2BAA2B,EAC3B,qBAAqB,EACrB,qBAAqB,EACrB,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,GACf,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The transcript summarizer — the ONE prompt every fold, hand-off note and chat
|
|
3
|
+
* consolidation compresses a conversation with. The model call is the host's
|
|
4
|
+
* ({@link SummaryCompletion}); this module owns what is asked and how the transcript is
|
|
5
|
+
* rendered for it, so a compaction note, a hand-off note and a "Consolidate" summary read
|
|
6
|
+
* the same everywhere.
|
|
7
|
+
*/
|
|
8
|
+
import type { TranscriptMessage } from './transcript.js';
|
|
9
|
+
/** The summarizer's instructions. */
|
|
10
|
+
export declare const TRANSCRIPT_SUMMARY_PROMPT = "You compress an in-progress AI agent transcript into a concise MEMORY the agent keeps working from. Capture: the CURRENT outstanding instruction from the user (the most recent user message is authoritative \u2014 earlier requests it supersedes are history, not the active task), concrete facts/answers discovered, tool results that matter (ids, paths, values), decisions made, and what still remains to do. Keep every file path, symbol name and line number the remaining work depends on, with the specific facts learned from each file \u2014 the agent no longer sees those results, so what you leave out it must read again. Be information-dense; drop pleasantries. No preamble.";
|
|
11
|
+
/**
|
|
12
|
+
* The note's output ceiling. A compaction note is a UTILITY completion: a bounded answer
|
|
13
|
+
* with no thinking. ~2.5k tokens: the note is the agent's only record of every file it
|
|
14
|
+
* folds (paths, symbols, line numbers), and 1.2k was too little to carry them.
|
|
15
|
+
*/
|
|
16
|
+
export declare const TRANSCRIPT_SUMMARY_MAX_TOKENS = 2500;
|
|
17
|
+
/** One no-tools, no-reasoning completion — the host's model call. Resolves the text. */
|
|
18
|
+
export type SummaryCompletion = (request: {
|
|
19
|
+
system: string;
|
|
20
|
+
user: string;
|
|
21
|
+
maxTokens: number;
|
|
22
|
+
signal?: AbortSignal;
|
|
23
|
+
}) => Promise<string | null | undefined>;
|
|
24
|
+
/** Render a slice of the transcript to the compact text the summarizer compresses. */
|
|
25
|
+
export declare function renderForSummary(msgs: readonly TranscriptMessage[]): string;
|
|
26
|
+
/**
|
|
27
|
+
* Compress `msgs` into a memory note. Null on any failure or an empty answer, so the
|
|
28
|
+
* caller falls back (drop-oldest, elision, "nothing to consolidate").
|
|
29
|
+
*/
|
|
30
|
+
export declare function summarizeTranscript(complete: SummaryCompletion, msgs: readonly TranscriptMessage[], signal?: AbortSignal): Promise<string | null>;
|
|
31
|
+
//# sourceMappingURL=summary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary.d.ts","sourceRoot":"","sources":["../../src/working/summary.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,qCAAqC;AACrC,eAAO,MAAM,yBAAyB,0qBACynB,CAAC;AAEhqB;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,OAAQ,CAAC;AAEnD,wFAAwF;AACxF,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAMzC,sFAAsF;AACtF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,iBAAiB,EAAE,GAAG,MAAM,CAU3E;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,SAAS,iBAAiB,EAAE,EAClC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAaxB"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The transcript summarizer — the ONE prompt every fold, hand-off note and chat
|
|
3
|
+
* consolidation compresses a conversation with. The model call is the host's
|
|
4
|
+
* ({@link SummaryCompletion}); this module owns what is asked and how the transcript is
|
|
5
|
+
* rendered for it, so a compaction note, a hand-off note and a "Consolidate" summary read
|
|
6
|
+
* the same everywhere.
|
|
7
|
+
*/
|
|
8
|
+
/** The summarizer's instructions. */
|
|
9
|
+
export const TRANSCRIPT_SUMMARY_PROMPT = 'You compress an in-progress AI agent transcript into a concise MEMORY the agent keeps working from. Capture: the CURRENT outstanding instruction from the user (the most recent user message is authoritative — earlier requests it supersedes are history, not the active task), concrete facts/answers discovered, tool results that matter (ids, paths, values), decisions made, and what still remains to do. Keep every file path, symbol name and line number the remaining work depends on, with the specific facts learned from each file — the agent no longer sees those results, so what you leave out it must read again. Be information-dense; drop pleasantries. No preamble.';
|
|
10
|
+
/**
|
|
11
|
+
* The note's output ceiling. A compaction note is a UTILITY completion: a bounded answer
|
|
12
|
+
* with no thinking. ~2.5k tokens: the note is the agent's only record of every file it
|
|
13
|
+
* folds (paths, symbols, line numbers), and 1.2k was too little to carry them.
|
|
14
|
+
*/
|
|
15
|
+
export const TRANSCRIPT_SUMMARY_MAX_TOKENS = 2_500;
|
|
16
|
+
/** Render a slice of the transcript to the compact text the summarizer compresses. */
|
|
17
|
+
export function renderForSummary(msgs) {
|
|
18
|
+
return msgs
|
|
19
|
+
.map((m) => {
|
|
20
|
+
const content = typeof m.content === 'string' ? m.content : JSON.stringify(m.content ?? '');
|
|
21
|
+
const calls = Array.isArray(m.tool_calls) && m.tool_calls.length > 0
|
|
22
|
+
? ` [called: ${m.tool_calls.map((t) => t.function?.name).filter((n) => typeof n === 'string' && n.length > 0).join(', ')}]`
|
|
23
|
+
: '';
|
|
24
|
+
return `${m.role}${calls}: ${content}`;
|
|
25
|
+
})
|
|
26
|
+
.join('\n\n');
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Compress `msgs` into a memory note. Null on any failure or an empty answer, so the
|
|
30
|
+
* caller falls back (drop-oldest, elision, "nothing to consolidate").
|
|
31
|
+
*/
|
|
32
|
+
export async function summarizeTranscript(complete, msgs, signal) {
|
|
33
|
+
if (msgs.length === 0)
|
|
34
|
+
return null;
|
|
35
|
+
try {
|
|
36
|
+
const out = ((await complete({
|
|
37
|
+
system: TRANSCRIPT_SUMMARY_PROMPT,
|
|
38
|
+
user: renderForSummary(msgs),
|
|
39
|
+
maxTokens: TRANSCRIPT_SUMMARY_MAX_TOKENS,
|
|
40
|
+
...(signal ? { signal } : {}),
|
|
41
|
+
})) ?? '').trim();
|
|
42
|
+
return out.length > 0 ? out : null;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=summary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary.js","sourceRoot":"","sources":["../../src/working/summary.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,qCAAqC;AACrC,MAAM,CAAC,MAAM,yBAAyB,GACpC,6pBAA6pB,CAAC;AAEhqB;;;;GAIG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,KAAK,CAAC;AAcnD,sFAAsF;AACtF,MAAM,UAAU,gBAAgB,CAAC,IAAkC;IACjE,OAAO,IAAI;SACR,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;QAC5F,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC;YAClE,CAAC,CAAC,aAAc,CAAC,CAAC,UAA8B,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;YAC7J,CAAC,CAAC,EAAE,CAAC;QACP,OAAO,GAAG,CAAC,CAAC,IAAI,GAAG,KAAK,KAAK,OAAO,EAAE,CAAC;IACzC,CAAC,CAAC;SACD,IAAI,CAAC,MAAM,CAAC,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,QAA2B,EAC3B,IAAkC,EAClC,MAAoB;IAEpB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,CAAC,CAAC,MAAM,QAAQ,CAAC;YAC3B,MAAM,EAAE,yBAAyB;YACjC,IAAI,EAAE,gBAAgB,CAAC,IAAI,CAAC;YAC5B,SAAS,EAAE,6BAA6B;YACxC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9B,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAClB,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The working transcript — what of a run the model is SHOWN each turn.
|
|
3
|
+
*
|
|
4
|
+
* A run's full transcript grows without bound; the request sent to the model must not.
|
|
5
|
+
* This module owns the two ways it is kept in bounds: the message-count + token WINDOW
|
|
6
|
+
* (drop-oldest, anchored on a user turn) and AUTO-COMPACTION (fold the older part into a
|
|
7
|
+
* memory note, keep every later message verbatim). A host supplies only what is
|
|
8
|
+
* run-specific: the summarizer it calls (see `summary.ts`) and what a fold records.
|
|
9
|
+
*
|
|
10
|
+
* Pure except for {@link buildWorkingTranscript}, which awaits the injected summarizer and
|
|
11
|
+
* updates the caller's `compactMemo`. Generic over the host's message shape: anything with
|
|
12
|
+
* the OpenAI-style `role` / `content` / `tool_calls` fields (`HandoffMessage`).
|
|
13
|
+
*/
|
|
14
|
+
import type { HandoffMessage } from '../handoff/types.js';
|
|
15
|
+
/** A transcript message — the hand-off packer's shape, so both count a message alike. */
|
|
16
|
+
export type TranscriptMessage = HandoffMessage;
|
|
17
|
+
/** How much history we send to the model (message-count ceiling). */
|
|
18
|
+
export declare const HISTORY_WINDOW = 80;
|
|
19
|
+
/**
|
|
20
|
+
* Token budget for the working transcript sent to the model each turn — how much of this
|
|
21
|
+
* run the model can SEE: the file windows it read, the searches it ran, what it decided.
|
|
22
|
+
*
|
|
23
|
+
* It bounds context, which message-count windowing (HISTORY_WINDOW) alone does not: one
|
|
24
|
+
* `tasks.list` result can be tens of thousands of tokens. It is NOT what keeps a request
|
|
25
|
+
* inside the serving model's window — the gateway fits every request to a model that can
|
|
26
|
+
* hold it.
|
|
27
|
+
*
|
|
28
|
+
* It was 24k, "sized under the smallest pool model's window". That protected nothing — the
|
|
29
|
+
* ~16k-token system prompt + tool catalog in front of it already put a full turn at ~40k,
|
|
30
|
+
* past any 32k window — and it cost every coding run its memory. Six 4k-token file windows
|
|
31
|
+
* filled it, compaction folded them into a 1.2k-token note, and the model went back for the
|
|
32
|
+
* files it had just read (chat #105: 55 turns, 63% of calls revisiting, zero edits, prompt
|
|
33
|
+
* peak 40,351). 64k holds a coding task's working set — a dozen-plus file windows —
|
|
34
|
+
* verbatim, while a full turn stays well inside the 128k window every coding-capable pool
|
|
35
|
+
* model has. See {@link windowed} and {@link buildWorkingTranscript}.
|
|
36
|
+
*/
|
|
37
|
+
export declare const HISTORY_TOKEN_BUDGET = 64000;
|
|
38
|
+
/**
|
|
39
|
+
* How much of {@link HISTORY_TOKEN_BUDGET} the verbatim TAIL may keep when the older part
|
|
40
|
+
* of the transcript is compacted. The rest is headroom new work fills before the next
|
|
41
|
+
* fold, so the memo is re-folded once per ~20k tokens of progress, not on every turn.
|
|
42
|
+
*/
|
|
43
|
+
export declare const COMPACT_TAIL_TOKEN_BUDGET = 40000;
|
|
44
|
+
/**
|
|
45
|
+
* The compressed memory of a transcript's older part: `note` summarizes
|
|
46
|
+
* `transcript[0, coveredEnd)`; every message from `coveredEnd` on is sent verbatim.
|
|
47
|
+
*/
|
|
48
|
+
export interface CompactMemo {
|
|
49
|
+
note: string;
|
|
50
|
+
coveredEnd: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* What the working transcript is built from — the run's full transcript and its cached
|
|
54
|
+
* compressed memory. {@link buildWorkingTranscript} updates `compactMemo` in place.
|
|
55
|
+
*/
|
|
56
|
+
export interface WorkingTranscriptState<M extends TranscriptMessage = TranscriptMessage> {
|
|
57
|
+
transcript: M[];
|
|
58
|
+
compactMemo: CompactMemo | null;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Trim the in-memory transcript to the history window before sending it to the
|
|
62
|
+
* model. Slicing the last N can leave the window starting on an `assistant`
|
|
63
|
+
* tool-call turn or an orphaned `tool` result (whose owning call fell off the
|
|
64
|
+
* front). That payload is invalid for strict vendors: Gemini rejects a request
|
|
65
|
+
* whose conversation does not begin with a user turn — surfaced as the cascade
|
|
66
|
+
* `[googleai] 400 INVALID_ARGUMENT` after a long tool-loop crossed the window
|
|
67
|
+
* boundary. So anchor the window at a user turn.
|
|
68
|
+
*
|
|
69
|
+
* If the last-N slice contains no user turn (a tool loop longer than the
|
|
70
|
+
* window), fall back to the most recent user turn in the FULL transcript and
|
|
71
|
+
* keep everything after it — correctness over the size cap.
|
|
72
|
+
*/
|
|
73
|
+
export declare function windowed<M extends TranscriptMessage>(convo: M[]): M[];
|
|
74
|
+
/**
|
|
75
|
+
* Is a transcript message still part of what the model sees? False once auto-compaction
|
|
76
|
+
* has folded it into the memory note, or the drop-oldest window has let it fall off the
|
|
77
|
+
* front. Deliberately conservative: it judges against the CURRENT transcript (which has
|
|
78
|
+
* grown since the last working set was built), so it can only ever call a message gone a
|
|
79
|
+
* turn early — a needless replay costs tokens, a stub for a vanished result costs the run.
|
|
80
|
+
*/
|
|
81
|
+
export declare function stillInWorkingContext<M extends TranscriptMessage>(state: WorkingTranscriptState<M>, anchor: unknown): boolean;
|
|
82
|
+
/** The fewest recent messages the verbatim tail keeps, however large they are. */
|
|
83
|
+
export declare const COMPACT_TAIL_TURNS = 8;
|
|
84
|
+
/** The first index at or after `from` that is not a `tool` result: a tool row whose
|
|
85
|
+
* assistant call was folded into the memo would be orphaned (strict vendors 400 on it). */
|
|
86
|
+
export declare function verbatimStart(convo: readonly TranscriptMessage[], from: number): number;
|
|
87
|
+
/** Start index of the last `tailTurns` messages, walked off a leading `tool` result. */
|
|
88
|
+
export declare function compactTailStart(convo: readonly TranscriptMessage[], tailTurns: number): number;
|
|
89
|
+
/**
|
|
90
|
+
* Where the verbatim tail begins when the transcript is compacted: as many of the most
|
|
91
|
+
* recent messages as fit in `budgetTokens` — never fewer than {@link COMPACT_TAIL_TURNS},
|
|
92
|
+
* never more than {@link COMPACT_TAIL_MAX_MESSAGES} — walked off a leading tool result.
|
|
93
|
+
*
|
|
94
|
+
* Sized by TOKENS, not a message count: a fixed eight messages is four tool results, so
|
|
95
|
+
* after a fold the fifth file the model needed was always the one it had to read again.
|
|
96
|
+
*/
|
|
97
|
+
export declare function compactTailStartForBudget(convo: readonly TranscriptMessage[], budgetTokens: number): number;
|
|
98
|
+
/**
|
|
99
|
+
* Index of the MOST RECENT user turn to re-inject verbatim ahead of the tail — the
|
|
100
|
+
* ACTIVE directive — or -1 when the latest user turn is already inside the tail. Pinning
|
|
101
|
+
* the FIRST user turn (the old behaviour) made a multi-instruction chat revert to its
|
|
102
|
+
* opening request (chat #55); the current instruction is always the LATEST user turn.
|
|
103
|
+
*/
|
|
104
|
+
export declare function pinnedDirectiveIndex(convo: readonly TranscriptMessage[], tailStart: number): number;
|
|
105
|
+
/**
|
|
106
|
+
* Assemble the working transcript from a compressed-memory `note` that covers
|
|
107
|
+
* `convo[0, coveredEnd)`: system + the memory note + the ACTIVE user directive re-injected
|
|
108
|
+
* verbatim (when the note covers it) + EVERY message from `coveredEnd` on (tool-pairing
|
|
109
|
+
* safe via {@link verbatimStart}). Nothing is in neither the note nor the tail. The
|
|
110
|
+
* directive sits immediately before the tail so the model reads it as the CURRENT
|
|
111
|
+
* instruction.
|
|
112
|
+
*/
|
|
113
|
+
export declare function assembleCompacted<M extends TranscriptMessage>(systemPrompt: string, convo: M[], note: string, coveredEnd: number): M[];
|
|
114
|
+
/** Estimated tokens of a message list — the ONE count compaction budgets by, exported so
|
|
115
|
+
* a run measures what compaction kept out of a call with the same rule. */
|
|
116
|
+
export declare function transcriptTokens(msgs: readonly TranscriptMessage[]): number;
|
|
117
|
+
/** What a fold reports: how many messages it newly compressed, the note it wrote, and
|
|
118
|
+
* the transcript index the note now covers up to (exclusive). */
|
|
119
|
+
export interface TranscriptFold {
|
|
120
|
+
droppedMessages: number;
|
|
121
|
+
note: string;
|
|
122
|
+
coveredEnd: number;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Build the working transcript for a turn. When the whole transcript fits → all of it,
|
|
126
|
+
* verbatim. Otherwise → a memory note of the older part plus EVERY message after it,
|
|
127
|
+
* verbatim; `onFolded` is told about each NEW fold, so the caller can record it (a
|
|
128
|
+
* visible step, a durable consolidation marker).
|
|
129
|
+
*
|
|
130
|
+
* The note is folded INCREMENTALLY, and only when it has to be. A note whose verbatim
|
|
131
|
+
* remainder still fits is reused as-is; once it no longer fits, the previous note plus
|
|
132
|
+
* exactly the messages since it are folded into a new one, up to a token-sized tail
|
|
133
|
+
* ({@link compactTailStartForBudget}). Falls back to drop-oldest when the summarizer is
|
|
134
|
+
* unavailable (`summarize` resolves null). A message-count overflow folds too.
|
|
135
|
+
*/
|
|
136
|
+
export declare function buildWorkingTranscript<M extends TranscriptMessage>(state: WorkingTranscriptState<M>, systemPrompt: string, summarize: (msgs: M[]) => Promise<string | null>, onFolded: (fold: TranscriptFold) => void): Promise<M[]>;
|
|
137
|
+
//# sourceMappingURL=transcript.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript.d.ts","sourceRoot":"","sources":["../../src/working/transcript.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC;AAE/C,qEAAqE;AACrE,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,oBAAoB,QAAS,CAAC;AAC3C;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAShD;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB,CAAC,CAAC,SAAS,iBAAiB,GAAG,iBAAiB;IACrF,UAAU,EAAE,CAAC,EAAE,CAAC;IAChB,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;CACjC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,iBAAiB,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAQrE;AAuBD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,iBAAiB,EAAE,KAAK,EAAE,sBAAsB,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAM7H;AAmBD,kFAAkF;AAClF,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAMpC;4FAC4F;AAC5F,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,iBAAiB,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAIvF;AAED,wFAAwF;AACxF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,iBAAiB,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE/F;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,SAAS,iBAAiB,EAAE,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAY3G;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,iBAAiB,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAGnG;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,iBAAiB,EAC3D,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,CAAC,EAAE,EACV,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,GACjB,CAAC,EAAE,CAQL;AAED;4EAC4E;AAC5E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,iBAAiB,EAAE,GAAG,MAAM,CAE3E;AAQD;kEACkE;AAClE,MAAM,WAAW,cAAc;IAC7B,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,sBAAsB,CAAC,CAAC,SAAS,iBAAiB,EACtE,KAAK,EAAE,sBAAsB,CAAC,CAAC,CAAC,EAChC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,EAChD,QAAQ,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,GACvC,OAAO,CAAC,CAAC,EAAE,CAAC,CA2Bd"}
|