@dsh-cc/context-crusher 0.7.1 → 0.8.0-rc.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.i18n.yaml +2 -2
- package/README.md +4 -0
- package/README.zh.md +4 -0
- package/lib/config.d.ts +9 -1
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +114 -1
- package/lib/config.js.map +1 -1
- package/lib/defer/counter.d.ts +44 -0
- package/lib/defer/counter.d.ts.map +1 -0
- package/lib/defer/counter.js +81 -0
- package/lib/defer/counter.js.map +1 -0
- package/lib/defer/gate.d.ts +41 -0
- package/lib/defer/gate.d.ts.map +1 -0
- package/lib/defer/gate.js +32 -0
- package/lib/defer/gate.js.map +1 -0
- package/lib/defer/pass.d.ts +70 -0
- package/lib/defer/pass.d.ts.map +1 -0
- package/lib/defer/pass.js +288 -0
- package/lib/defer/pass.js.map +1 -0
- package/lib/defer/residents.d.ts +126 -0
- package/lib/defer/residents.d.ts.map +1 -0
- package/lib/defer/residents.js +171 -0
- package/lib/defer/residents.js.map +1 -0
- package/lib/defer/swap.d.ts +57 -0
- package/lib/defer/swap.d.ts.map +1 -0
- package/lib/defer/swap.js +120 -0
- package/lib/defer/swap.js.map +1 -0
- package/lib/fold-counters.d.ts +29 -0
- package/lib/fold-counters.d.ts.map +1 -0
- package/lib/fold-counters.js +62 -0
- package/lib/fold-counters.js.map +1 -0
- package/lib/index.d.ts +20 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +86 -15
- package/lib/index.js.map +1 -1
- package/lib/receipt.d.ts +45 -0
- package/lib/receipt.d.ts.map +1 -0
- package/lib/receipt.js +73 -0
- package/lib/receipt.js.map +1 -0
- package/lib/reducer.d.ts +52 -0
- package/lib/reducer.d.ts.map +1 -0
- package/lib/reducer.js +202 -0
- package/lib/reducer.js.map +1 -0
- package/lib/settings.d.ts.map +1 -1
- package/lib/settings.js +6 -3
- package/lib/settings.js.map +1 -1
- package/lib/types.d.ts +52 -1
- package/lib/types.d.ts.map +1 -1
- package/lib/verifier.d.ts +29 -0
- package/lib/verifier.d.ts.map +1 -0
- package/lib/verifier.js +57 -0
- package/lib/verifier.js.map +1 -0
- package/package.json +26 -13
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm check:readme --write
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: d0ce4a85fa2ce5c6942cb4b14b7983ad19f88391
|
|
6
|
+
README.zh.md: edeca1f5c2009bdad6cef61b4b98baad4d2bbe58
|
package/README.md
CHANGED
|
@@ -32,6 +32,10 @@ Namespace `cc-context-compression` (settings overlay re-read on every use; confi
|
|
|
32
32
|
| `min-savings-ratio` | `0.4` | Minimum token saving fraction. |
|
|
33
33
|
| `protected-tools` | over-inclusive default list | Tools never crushed. |
|
|
34
34
|
|
|
35
|
+
## foldCounters convention
|
|
36
|
+
|
|
37
|
+
This package exports `foldCounters(materials)` for `@dsh-cc/token-efficiency`; any change to the marker/ledger shape MUST update the fold and its tests in the same PR.
|
|
38
|
+
|
|
35
39
|
## Caveats
|
|
36
40
|
|
|
37
41
|
- The transcript keeps the COMPRESSED form forever: TUI replay and `command-export` show the compressed blob (the original lives only in the store + ledger).
|
package/README.zh.md
CHANGED
|
@@ -32,6 +32,10 @@ CCR(Compress-Cache-Retrieve)可逆的工具输出压缩。一个 `tools/post
|
|
|
32
32
|
| `min-savings-ratio` | `0.4` | 最小 token 节省比例。 |
|
|
33
33
|
| `protected-tools` | 过宽的默认列表 | 永不压缩的工具。 |
|
|
34
34
|
|
|
35
|
+
## foldCounters 约定
|
|
36
|
+
|
|
37
|
+
本包为 `@dsh-cc/token-efficiency` 导出 `foldCounters(materials)`;标记/台账形态的任何变更必须在同一个 PR 中同步更新该 fold 及其测试。
|
|
38
|
+
|
|
35
39
|
## 注意事项
|
|
36
40
|
|
|
37
41
|
- 转录中永久保留的是压缩后的形态:TUI 回放与 `command-export` 展示压缩块(原文只在存储与台账中)。
|
package/lib/config.d.ts
CHANGED
|
@@ -10,6 +10,12 @@ import type { CrusherConfig, ResolvedConfig } from './types.ts';
|
|
|
10
10
|
* until ledger data proves a narrower list is safe).
|
|
11
11
|
*/
|
|
12
12
|
export declare const DEFAULT_PROTECTED_TOOLS: readonly string[];
|
|
13
|
+
/**
|
|
14
|
+
* Default reducer command patterns (plan §3.2): regex sources matched
|
|
15
|
+
* against the bash invocation command line. `make\b` also matches `cmake` —
|
|
16
|
+
* accepted as an eligibility-only over-trigger.
|
|
17
|
+
*/
|
|
18
|
+
export declare const DEFAULT_REDUCER_COMMANDS: readonly string[];
|
|
13
19
|
export declare const DEFAULTS: ResolvedConfig;
|
|
14
20
|
/** Row-level deployment-default schema (cordis `static Config`). */
|
|
15
21
|
export declare const Config: z<CrusherConfig>;
|
|
@@ -20,7 +26,9 @@ export declare const SettingsSchema: z<CrusherConfig>;
|
|
|
20
26
|
* @param config - raw row config.
|
|
21
27
|
* @returns a detached deeply immutable configuration.
|
|
22
28
|
*/
|
|
23
|
-
export declare function resolveConfig(config?: CrusherConfig
|
|
29
|
+
export declare function resolveConfig(config?: CrusherConfig, options?: {
|
|
30
|
+
log?: (message: string) => void;
|
|
31
|
+
}): ResolvedConfig;
|
|
24
32
|
/**
|
|
25
33
|
* Overlay the live settings scope onto the config defaults. `protected-tools`
|
|
26
34
|
* uses REPLACE semantics: an explicitly set settings list replaces the
|
package/lib/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAExC,OAAO,KAAK,EAAE,aAAa,EAAe,cAAc,EAAE,MAAM,YAAY,CAAA;AAE5E;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAIpD,CAAA;AAED,eAAO,MAAM,QAAQ,EAAE,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAExC,OAAO,KAAK,EAAE,aAAa,EAAe,cAAc,EAAE,MAAM,YAAY,CAAA;AAE5E;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAIpD,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,SAAS,MAAM,EAMrD,CAAA;AAED,eAAO,MAAM,QAAQ,EAAE,cAgBrB,CAAA;AAIF,oEAAoE;AACpE,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,aAAa,CAiBlC,CAAA;AAEF,kEAAkE;AAClE,eAAO,MAAM,cAAc,EAAE,CAAC,CAAC,aAAa,CAAU,CAAA;AAEtD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,GAAE,aAAkB,EAAE,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,GAAG,cAAc,CAyEvH;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,aAAa,GAAG,SAAS,GAAG,cAAc,CAgCtG"}
|
package/lib/config.js
CHANGED
|
@@ -14,12 +14,34 @@ export const DEFAULT_PROTECTED_TOOLS = [
|
|
|
14
14
|
'enter_worktree', 'exit_worktree', 'subagent_fork', 'send_message',
|
|
15
15
|
'task', 'interrupt_agent',
|
|
16
16
|
];
|
|
17
|
+
/**
|
|
18
|
+
* Default reducer command patterns (plan §3.2): regex sources matched
|
|
19
|
+
* against the bash invocation command line. `make\b` also matches `cmake` —
|
|
20
|
+
* accepted as an eligibility-only over-trigger.
|
|
21
|
+
*/
|
|
22
|
+
export const DEFAULT_REDUCER_COMMANDS = [
|
|
23
|
+
'pnpm .* (build|test|vitest|tsc|lint)',
|
|
24
|
+
'npm (run )?(build|test)',
|
|
25
|
+
'yarn (build|test)',
|
|
26
|
+
'npx (vitest|jest|tsc|eslint)',
|
|
27
|
+
'vitest|jest|mocha|pytest|go test|cargo test|make\\b',
|
|
28
|
+
];
|
|
17
29
|
export const DEFAULTS = deepFreeze({
|
|
18
30
|
enabled: false,
|
|
19
31
|
mode: 'dry-run',
|
|
20
32
|
minBytes: 8192,
|
|
21
33
|
minSavingsRatio: 0.4,
|
|
22
34
|
protectedTools: DEFAULT_PROTECTED_TOOLS,
|
|
35
|
+
reducerEnabled: false,
|
|
36
|
+
reducerCommands: DEFAULT_REDUCER_COMMANDS.map((source) => new RegExp(source)),
|
|
37
|
+
reducerMaxInputTokens: 30_000,
|
|
38
|
+
reducerMinSavingsRatio: 0.5,
|
|
39
|
+
reducerMaxTokens: 1024,
|
|
40
|
+
reducerTimeoutMs: 10_000,
|
|
41
|
+
reducerAlias: 'haiku',
|
|
42
|
+
deferRequests: 0,
|
|
43
|
+
deferMargin: 1.5,
|
|
44
|
+
deferMaxAgeMs: 1_800_000,
|
|
23
45
|
});
|
|
24
46
|
const MODES = ['dry-run', 'on'];
|
|
25
47
|
/** Row-level deployment-default schema (cordis `static Config`). */
|
|
@@ -29,6 +51,17 @@ export const Config = z.object({
|
|
|
29
51
|
'min-bytes': z.number().step(1).min(1),
|
|
30
52
|
'min-savings-ratio': z.number().min(0).max(1),
|
|
31
53
|
'protected-tools': z.array(z.string()),
|
|
54
|
+
'reducer-enabled': z.boolean(),
|
|
55
|
+
'reducer-commands': z.array(z.string()),
|
|
56
|
+
'reducer-max-input-tokens': z.number().step(1).min(1),
|
|
57
|
+
'reducer-min-savings-ratio': z.number().min(0).max(1),
|
|
58
|
+
'reducer-max-tokens': z.number().step(1).min(1),
|
|
59
|
+
'reducer-timeout-ms': z.number().step(1).min(1),
|
|
60
|
+
'reducer-alias': z.string(),
|
|
61
|
+
'defer-requests': z.number().step(1).min(0),
|
|
62
|
+
'defer-margin': z.number().min(0),
|
|
63
|
+
'defer-max-age-ms': z.number().step(1).min(1),
|
|
64
|
+
'defer-urgency-tokens': z.number().step(1).min(1),
|
|
32
65
|
});
|
|
33
66
|
/** Settings namespace schema (same shape as the config layer). */
|
|
34
67
|
export const SettingsSchema = Config;
|
|
@@ -37,7 +70,7 @@ export const SettingsSchema = Config;
|
|
|
37
70
|
* @param config - raw row config.
|
|
38
71
|
* @returns a detached deeply immutable configuration.
|
|
39
72
|
*/
|
|
40
|
-
export function resolveConfig(config = {}) {
|
|
73
|
+
export function resolveConfig(config = {}, options) {
|
|
41
74
|
if (config.mode !== undefined && !MODES.includes(config.mode)) {
|
|
42
75
|
throw new Error(`context-crusher: mode must be one of ${MODES.join(', ')}`);
|
|
43
76
|
}
|
|
@@ -48,12 +81,68 @@ export function resolveConfig(config = {}) {
|
|
|
48
81
|
&& (!Number.isFinite(config['min-savings-ratio']) || config['min-savings-ratio'] < 0 || config['min-savings-ratio'] > 1)) {
|
|
49
82
|
throw new Error('context-crusher: min-savings-ratio must be within [0, 1]');
|
|
50
83
|
}
|
|
84
|
+
if (config['reducer-min-savings-ratio'] !== undefined
|
|
85
|
+
&& (!Number.isFinite(config['reducer-min-savings-ratio']) || config['reducer-min-savings-ratio'] < 0 || config['reducer-min-savings-ratio'] > 1)) {
|
|
86
|
+
throw new Error('context-crusher: reducer-min-savings-ratio must be within [0, 1]');
|
|
87
|
+
}
|
|
88
|
+
for (const key of ['reducer-max-input-tokens', 'reducer-max-tokens', 'reducer-timeout-ms']) {
|
|
89
|
+
const value = config[key];
|
|
90
|
+
if (value !== undefined && (!Number.isFinite(value) || value < 1)) {
|
|
91
|
+
throw new Error(`context-crusher: ${key} must be a positive number`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Compile command patterns once at resolve time (§3.2): an invalid user
|
|
95
|
+
// regex is dropped with a debug log, never thrown and never recompiled on
|
|
96
|
+
// the hot path. An empty list falls back to the defaults (schemastery
|
|
97
|
+
// normalizes an ABSENT array to [] on the cordis config plane, so []
|
|
98
|
+
// cannot be distinguished from unset there).
|
|
99
|
+
const log = options?.log;
|
|
100
|
+
const rawCommands = config['reducer-commands'];
|
|
101
|
+
const commands = rawCommands !== undefined && rawCommands.length > 0 ? rawCommands : DEFAULT_REDUCER_COMMANDS;
|
|
102
|
+
const reducerCommands = commands.flatMap((source) => {
|
|
103
|
+
try {
|
|
104
|
+
return [new RegExp(source)];
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
log?.(`context-crusher: invalid reducer-commands pattern dropped: ${source}`);
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
if (config['defer-requests'] !== undefined
|
|
112
|
+
&& (!Number.isInteger(config['defer-requests']) || config['defer-requests'] < 0)) {
|
|
113
|
+
throw new Error('context-crusher: defer-requests must be a non-negative integer');
|
|
114
|
+
}
|
|
115
|
+
if (config['defer-margin'] !== undefined
|
|
116
|
+
&& (!Number.isFinite(config['defer-margin']) || config['defer-margin'] <= 0)) {
|
|
117
|
+
throw new Error('context-crusher: defer-margin must be a positive number');
|
|
118
|
+
}
|
|
119
|
+
if (config['defer-max-age-ms'] !== undefined
|
|
120
|
+
&& (!Number.isFinite(config['defer-max-age-ms']) || config['defer-max-age-ms'] <= 0)) {
|
|
121
|
+
throw new Error('context-crusher: defer-max-age-ms must be a positive number');
|
|
122
|
+
}
|
|
123
|
+
if (config['defer-urgency-tokens'] !== undefined
|
|
124
|
+
&& (!Number.isFinite(config['defer-urgency-tokens']) || config['defer-urgency-tokens'] <= 0)) {
|
|
125
|
+
throw new Error('context-crusher: defer-urgency-tokens must be a positive number');
|
|
126
|
+
}
|
|
51
127
|
return deepFreeze({
|
|
52
128
|
enabled: config.enabled ?? DEFAULTS.enabled,
|
|
53
129
|
mode: config.mode ?? DEFAULTS.mode,
|
|
54
130
|
minBytes: config['min-bytes'] ?? DEFAULTS.minBytes,
|
|
55
131
|
minSavingsRatio: config['min-savings-ratio'] ?? DEFAULTS.minSavingsRatio,
|
|
56
132
|
protectedTools: config['protected-tools'] ?? DEFAULTS.protectedTools,
|
|
133
|
+
reducerEnabled: config['reducer-enabled'] ?? DEFAULTS.reducerEnabled,
|
|
134
|
+
reducerCommands,
|
|
135
|
+
reducerMaxInputTokens: config['reducer-max-input-tokens'] ?? DEFAULTS.reducerMaxInputTokens,
|
|
136
|
+
reducerMinSavingsRatio: config['reducer-min-savings-ratio'] ?? DEFAULTS.reducerMinSavingsRatio,
|
|
137
|
+
reducerMaxTokens: config['reducer-max-tokens'] ?? DEFAULTS.reducerMaxTokens,
|
|
138
|
+
reducerTimeoutMs: config['reducer-timeout-ms'] ?? DEFAULTS.reducerTimeoutMs,
|
|
139
|
+
reducerAlias: config['reducer-alias'] ?? DEFAULTS.reducerAlias,
|
|
140
|
+
deferRequests: config['defer-requests'] ?? DEFAULTS.deferRequests,
|
|
141
|
+
deferMargin: config['defer-margin'] ?? DEFAULTS.deferMargin,
|
|
142
|
+
deferMaxAgeMs: config['defer-max-age-ms'] ?? DEFAULTS.deferMaxAgeMs,
|
|
143
|
+
...(config['defer-urgency-tokens'] !== undefined
|
|
144
|
+
? { deferUrgencyTokens: config['defer-urgency-tokens'] }
|
|
145
|
+
: {}),
|
|
57
146
|
});
|
|
58
147
|
}
|
|
59
148
|
/**
|
|
@@ -73,6 +162,30 @@ export function overlaySettings(base, scope) {
|
|
|
73
162
|
minBytes: scope['min-bytes'] ?? base.minBytes,
|
|
74
163
|
minSavingsRatio: scope['min-savings-ratio'] ?? base.minSavingsRatio,
|
|
75
164
|
protectedTools: scope['protected-tools'] ?? base.protectedTools,
|
|
165
|
+
// Every reducer key uses REPLACE semantics (§3.6): an explicitly set
|
|
166
|
+
// settings value replaces the config default wholesale (never a union).
|
|
167
|
+
reducerEnabled: scope['reducer-enabled'] ?? base.reducerEnabled,
|
|
168
|
+
reducerCommands: scope['reducer-commands'] !== undefined && scope['reducer-commands'].length > 0
|
|
169
|
+
? scope['reducer-commands'].flatMap((source) => {
|
|
170
|
+
try {
|
|
171
|
+
return [new RegExp(source)];
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
: base.reducerCommands,
|
|
178
|
+
reducerMaxInputTokens: scope['reducer-max-input-tokens'] ?? base.reducerMaxInputTokens,
|
|
179
|
+
reducerMinSavingsRatio: scope['reducer-min-savings-ratio'] ?? base.reducerMinSavingsRatio,
|
|
180
|
+
reducerMaxTokens: scope['reducer-max-tokens'] ?? base.reducerMaxTokens,
|
|
181
|
+
reducerTimeoutMs: scope['reducer-timeout-ms'] ?? base.reducerTimeoutMs,
|
|
182
|
+
reducerAlias: scope['reducer-alias'] ?? base.reducerAlias,
|
|
183
|
+
deferRequests: scope['defer-requests'] ?? base.deferRequests,
|
|
184
|
+
deferMargin: scope['defer-margin'] ?? base.deferMargin,
|
|
185
|
+
deferMaxAgeMs: scope['defer-max-age-ms'] ?? base.deferMaxAgeMs,
|
|
186
|
+
...(scope['defer-urgency-tokens'] !== undefined || base.deferUrgencyTokens !== undefined
|
|
187
|
+
? { deferUrgencyTokens: scope['defer-urgency-tokens'] ?? base.deferUrgencyTokens }
|
|
188
|
+
: {}),
|
|
76
189
|
};
|
|
77
190
|
}
|
|
78
191
|
//# sourceMappingURL=config.js.map
|
package/lib/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,CAAC,MAAM,0BAA0B,CAAA;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAA;AAGzD;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAsB;IACxD,MAAM,EAAE,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,mBAAmB;IACpE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc;IAClE,MAAM,EAAE,iBAAiB;CAC1B,CAAA;AAED,MAAM,CAAC,MAAM,QAAQ,GAAmB,UAAU,CAAC;IACjD,OAAO,EAAE,KAAK;IACd,IAAI,EAAE,SAAS;IACf,QAAQ,EAAE,IAAI;IACd,eAAe,EAAE,GAAG;IACpB,cAAc,EAAE,uBAAuB;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* First-chunk send counting for deferred residents (design §3.2).
|
|
3
|
+
*
|
|
4
|
+
* A resident entry counts as sent when a provider request produces its FIRST
|
|
5
|
+
* chunk and the request's `options.messages` contain a tool-result block
|
|
6
|
+
* whose text fingerprint (sha256 first 16 hex — the same shape as the store
|
|
7
|
+
* hash) matches the stored full text. Counting at first chunk — not request
|
|
8
|
+
* start — excludes attempts that fail before reaching the provider.
|
|
9
|
+
*
|
|
10
|
+
* Purpose filtering is an ALLOWLIST: only main-loop requests count
|
|
11
|
+
* (`purpose` undefined; the harness purpose union is closed to
|
|
12
|
+
* `'compaction' | 'session-title'`). Subagent fan-outs are excluded as a
|
|
13
|
+
* fingerprint side effect (fresh history never contains the resident); the
|
|
14
|
+
* fork-inheriting corner is closed by requiring `options.sessionId` to be an
|
|
15
|
+
* owning session id.
|
|
16
|
+
*
|
|
17
|
+
* @module @dsh-cc/context-crusher/defer/counter
|
|
18
|
+
*/
|
|
19
|
+
import type { ContentBlock, GenerateOptions, Message, StreamChunk } from '@deepseek-ai/dsh-llm';
|
|
20
|
+
/**
|
|
21
|
+
* Join text blocks; returns undefined when any block is non-text. Same rule
|
|
22
|
+
* as the crusher's insertion-time text extraction, so the fingerprint of a
|
|
23
|
+
* live tool-result block equals the store hash of the original.
|
|
24
|
+
*/
|
|
25
|
+
export declare function joinTextBlocks(blocks: readonly ContentBlock[]): string | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Fingerprints of every fully-textual tool-result block in one outbound
|
|
28
|
+
* prompt. A request that contains the same resident twice still counts once
|
|
29
|
+
* (Set semantics).
|
|
30
|
+
*/
|
|
31
|
+
export declare function toolResultFingerprints(messages: readonly Message[]): Set<string>;
|
|
32
|
+
/**
|
|
33
|
+
* The counting guard: main-loop purpose allowlist AND an owning session id.
|
|
34
|
+
* `owningSessionIds` is the in-memory set of sessions with defer state —
|
|
35
|
+
* never the ledger (§3.5 keeps the ledger session-id-free).
|
|
36
|
+
*/
|
|
37
|
+
export declare function shouldCountRequest(options: GenerateOptions, owningSessionIds: ReadonlySet<string>): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Wrap one provider stream so `onFirstChunk` fires exactly when the first
|
|
40
|
+
* chunk is observed. A stream that throws before its first yield counts ZERO
|
|
41
|
+
* (Phase-0 pin) and the error propagates untouched to the caller.
|
|
42
|
+
*/
|
|
43
|
+
export declare function wrapStreamWithCounting(options: GenerateOptions, stream: AsyncIterable<StreamChunk>, onFirstChunk: (options: GenerateOptions) => void): AsyncIterable<StreamChunk>;
|
|
44
|
+
//# sourceMappingURL=counter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"counter.d.ts","sourceRoot":"","sources":["../../src/defer/counter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAG/F;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,GAAG,SAAS,CAOlF;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAUhF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,eAAe,EACxB,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAAC,GACpC,OAAO,CAIT;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,aAAa,CAAC,WAAW,CAAC,EAClC,YAAY,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,GAC/C,aAAa,CAAC,WAAW,CAAC,CAW5B"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* First-chunk send counting for deferred residents (design §3.2).
|
|
3
|
+
*
|
|
4
|
+
* A resident entry counts as sent when a provider request produces its FIRST
|
|
5
|
+
* chunk and the request's `options.messages` contain a tool-result block
|
|
6
|
+
* whose text fingerprint (sha256 first 16 hex — the same shape as the store
|
|
7
|
+
* hash) matches the stored full text. Counting at first chunk — not request
|
|
8
|
+
* start — excludes attempts that fail before reaching the provider.
|
|
9
|
+
*
|
|
10
|
+
* Purpose filtering is an ALLOWLIST: only main-loop requests count
|
|
11
|
+
* (`purpose` undefined; the harness purpose union is closed to
|
|
12
|
+
* `'compaction' | 'session-title'`). Subagent fan-outs are excluded as a
|
|
13
|
+
* fingerprint side effect (fresh history never contains the resident); the
|
|
14
|
+
* fork-inheriting corner is closed by requiring `options.sessionId` to be an
|
|
15
|
+
* owning session id.
|
|
16
|
+
*
|
|
17
|
+
* @module @dsh-cc/context-crusher/defer/counter
|
|
18
|
+
*/
|
|
19
|
+
import { shortHash } from "../store.js";
|
|
20
|
+
/**
|
|
21
|
+
* Join text blocks; returns undefined when any block is non-text. Same rule
|
|
22
|
+
* as the crusher's insertion-time text extraction, so the fingerprint of a
|
|
23
|
+
* live tool-result block equals the store hash of the original.
|
|
24
|
+
*/
|
|
25
|
+
export function joinTextBlocks(blocks) {
|
|
26
|
+
const parts = [];
|
|
27
|
+
for (const block of blocks) {
|
|
28
|
+
if (block.type !== 'text')
|
|
29
|
+
return undefined;
|
|
30
|
+
parts.push(block.text);
|
|
31
|
+
}
|
|
32
|
+
return parts.join('\n');
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Fingerprints of every fully-textual tool-result block in one outbound
|
|
36
|
+
* prompt. A request that contains the same resident twice still counts once
|
|
37
|
+
* (Set semantics).
|
|
38
|
+
*/
|
|
39
|
+
export function toolResultFingerprints(messages) {
|
|
40
|
+
const out = new Set();
|
|
41
|
+
for (const message of messages) {
|
|
42
|
+
for (const block of message.content) {
|
|
43
|
+
if (block.type !== 'tool-result')
|
|
44
|
+
continue;
|
|
45
|
+
const text = joinTextBlocks(block.content);
|
|
46
|
+
if (text !== undefined)
|
|
47
|
+
out.add(shortHash(text));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The counting guard: main-loop purpose allowlist AND an owning session id.
|
|
54
|
+
* `owningSessionIds` is the in-memory set of sessions with defer state —
|
|
55
|
+
* never the ledger (§3.5 keeps the ledger session-id-free).
|
|
56
|
+
*/
|
|
57
|
+
export function shouldCountRequest(options, owningSessionIds) {
|
|
58
|
+
if (options.purpose !== undefined)
|
|
59
|
+
return false;
|
|
60
|
+
if (options.sessionId === undefined)
|
|
61
|
+
return false;
|
|
62
|
+
return owningSessionIds.has(String(options.sessionId));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Wrap one provider stream so `onFirstChunk` fires exactly when the first
|
|
66
|
+
* chunk is observed. A stream that throws before its first yield counts ZERO
|
|
67
|
+
* (Phase-0 pin) and the error propagates untouched to the caller.
|
|
68
|
+
*/
|
|
69
|
+
export function wrapStreamWithCounting(options, stream, onFirstChunk) {
|
|
70
|
+
return (async function* () {
|
|
71
|
+
let counted = false;
|
|
72
|
+
for await (const chunk of stream) {
|
|
73
|
+
if (!counted) {
|
|
74
|
+
counted = true;
|
|
75
|
+
onFirstChunk(options);
|
|
76
|
+
}
|
|
77
|
+
yield chunk;
|
|
78
|
+
}
|
|
79
|
+
})();
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=counter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"counter.js","sourceRoot":"","sources":["../../src/defer/counter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAEvC;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,MAA+B;IAC5D,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;YAAE,OAAO,SAAS,CAAA;QAC3C,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACxB,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,QAA4B;IACjE,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAA;IAC7B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpC,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa;gBAAE,SAAQ;YAC1C,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;YAC1C,IAAI,IAAI,KAAK,SAAS;gBAAE,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAA;QAClD,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAwB,EACxB,gBAAqC;IAErC,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS;QAAE,OAAO,KAAK,CAAA;IAC/C,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS;QAAE,OAAO,KAAK,CAAA;IACjD,OAAO,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAA;AACxD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CACpC,OAAwB,EACxB,MAAkC,EAClC,YAAgD;IAEhD,OAAO,CAAC,KAAK,SAAS,CAAC;QACrB,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YACjC,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,GAAG,IAAI,CAAA;gBACd,YAAY,CAAC,OAAO,CAAC,CAAA;YACvB,CAAC;YACD,MAAM,KAAK,CAAA;QACb,CAAC;IACH,CAAC,CAAC,EAAE,CAAA;AACN,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure suffix-cost gate for the deferred swap (design §3.4). No I/O, no
|
|
3
|
+
* clock, no services — a table-testable fold over measured inputs.
|
|
4
|
+
*
|
|
5
|
+
* Prompt cache is a prefix: replacing the message at index k busts cache from
|
|
6
|
+
* k to the surface end, so the swap's rewrite price is the suffix measured AT
|
|
7
|
+
* SWAP TIME, not the stored prefix. With no price table resolved the
|
|
8
|
+
* comparison stays in token units — conservative in the direction of NOT
|
|
9
|
+
* swapping, the safe side.
|
|
10
|
+
*
|
|
11
|
+
* @module @dsh-cc/context-crusher/defer/gate
|
|
12
|
+
*/
|
|
13
|
+
export interface DeferGateInput {
|
|
14
|
+
/** tokensBefore − tokensAfter, recorded when the resident was stored. */
|
|
15
|
+
readonly tokensSaved: number;
|
|
16
|
+
/** Observed requests-per-step × pending todo steps; floored at 1 here too. */
|
|
17
|
+
readonly remainingRequestsEstimate: number;
|
|
18
|
+
/** `defer-margin` multiplier over the suffix cost. */
|
|
19
|
+
readonly margin: number;
|
|
20
|
+
/** Σ estimateMessage over surface nodes after the entry, at swap time. */
|
|
21
|
+
readonly suffixTokens: number;
|
|
22
|
+
/** `defer-urgency-tokens` window-pressure distance; unset = inactive. */
|
|
23
|
+
readonly urgencyTokens?: number;
|
|
24
|
+
/** Current session token estimate, when a meter is available. */
|
|
25
|
+
readonly sessionTokens?: number;
|
|
26
|
+
/** Routed model context window, when a window source advertised one. */
|
|
27
|
+
readonly contextWindow?: number;
|
|
28
|
+
}
|
|
29
|
+
export interface DeferGateOutcome {
|
|
30
|
+
readonly pass: boolean;
|
|
31
|
+
readonly projectedSavings: number;
|
|
32
|
+
readonly suffixCost: number;
|
|
33
|
+
readonly urgencyOverride: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Evaluate the swap gate for one eligible resident entry.
|
|
37
|
+
* @param input - measured gate inputs (suffix measured at swap time).
|
|
38
|
+
* @returns the decision with every intermediate term for the ledger.
|
|
39
|
+
*/
|
|
40
|
+
export declare function evaluateDeferGate(input: DeferGateInput): DeferGateOutcome;
|
|
41
|
+
//# sourceMappingURL=gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../src/defer/gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,8EAA8E;IAC9E,QAAQ,CAAC,yBAAyB,EAAE,MAAM,CAAA;IAC1C,sDAAsD;IACtD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,0EAA0E;IAC1E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,yEAAyE;IACzE,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,iEAAiE;IACjE,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,wEAAwE;IACxE,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;CAClC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,gBAAgB,CAazE"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure suffix-cost gate for the deferred swap (design §3.4). No I/O, no
|
|
3
|
+
* clock, no services — a table-testable fold over measured inputs.
|
|
4
|
+
*
|
|
5
|
+
* Prompt cache is a prefix: replacing the message at index k busts cache from
|
|
6
|
+
* k to the surface end, so the swap's rewrite price is the suffix measured AT
|
|
7
|
+
* SWAP TIME, not the stored prefix. With no price table resolved the
|
|
8
|
+
* comparison stays in token units — conservative in the direction of NOT
|
|
9
|
+
* swapping, the safe side.
|
|
10
|
+
*
|
|
11
|
+
* @module @dsh-cc/context-crusher/defer/gate
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Evaluate the swap gate for one eligible resident entry.
|
|
15
|
+
* @param input - measured gate inputs (suffix measured at swap time).
|
|
16
|
+
* @returns the decision with every intermediate term for the ledger.
|
|
17
|
+
*/
|
|
18
|
+
export function evaluateDeferGate(input) {
|
|
19
|
+
const projectedSavings = input.tokensSaved * Math.max(1, input.remainingRequestsEstimate);
|
|
20
|
+
const suffixCost = input.margin * input.suffixTokens;
|
|
21
|
+
const urgencyOverride = input.urgencyTokens !== undefined
|
|
22
|
+
&& input.sessionTokens !== undefined
|
|
23
|
+
&& input.contextWindow !== undefined
|
|
24
|
+
&& input.sessionTokens >= input.contextWindow - input.urgencyTokens;
|
|
25
|
+
return {
|
|
26
|
+
pass: urgencyOverride || projectedSavings > suffixCost,
|
|
27
|
+
projectedSavings,
|
|
28
|
+
suffixCost,
|
|
29
|
+
urgencyOverride,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../../src/defer/gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA0BH;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAqB;IACrD,MAAM,gBAAgB,GAAG,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,yBAAyB,CAAC,CAAA;IACzF,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,YAAY,CAAA;IACpD,MAAM,eAAe,GAAG,KAAK,CAAC,aAAa,KAAK,SAAS;WACpD,KAAK,CAAC,aAAa,KAAK,SAAS;WACjC,KAAK,CAAC,aAAa,KAAK,SAAS;WACjC,KAAK,CAAC,aAAa,IAAI,KAAK,CAAC,aAAa,GAAG,KAAK,CAAC,aAAa,CAAA;IACrE,OAAO;QACL,IAAI,EAAE,eAAe,IAAI,gBAAgB,GAAG,UAAU;QACtD,gBAAgB;QAChB,UAAU;QACV,eAAe;KAChB,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deferred-mode orchestration (design §3): the `llm/stream` first-chunk
|
|
3
|
+
* counter, the `agent/pre-step` swap pass, resume-by-fingerprint, and the
|
|
4
|
+
* per-session ledger plumbing. Extracted from the crusher service to keep
|
|
5
|
+
* `index.ts` inside the file-size budget; the crusher wires one instance
|
|
6
|
+
* and forwards its post-execute hooks.
|
|
7
|
+
*
|
|
8
|
+
* @module @dsh-cc/context-crusher/defer/pass
|
|
9
|
+
*/
|
|
10
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
11
|
+
import type { ToolExecution } from '@dsh-cc/tools';
|
|
12
|
+
import type { CrusherStore } from '../store.ts';
|
|
13
|
+
import type { ResolvedConfig } from '../types.ts';
|
|
14
|
+
/** dsh-home path resolver (see the crusher's `dshHomePath` seam). */
|
|
15
|
+
type HomeFn = (...segments: string[]) => string;
|
|
16
|
+
export interface DeferralDeps {
|
|
17
|
+
readonly ctx: Context;
|
|
18
|
+
readonly store: () => CrusherStore | undefined;
|
|
19
|
+
readonly home: () => HomeFn | undefined;
|
|
20
|
+
readonly effectiveConfig: () => ResolvedConfig;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Owns all deferred-externalization state and listeners. Every failure
|
|
24
|
+
* degrades to a log line: observation must never break a request or a turn.
|
|
25
|
+
*/
|
|
26
|
+
export declare class Deferral {
|
|
27
|
+
private readonly deps;
|
|
28
|
+
private readonly table;
|
|
29
|
+
private readonly ledgers;
|
|
30
|
+
constructor(deps: DeferralDeps);
|
|
31
|
+
/**
|
|
32
|
+
* Deferred-mode seams (design §3): an `llm/stream` first-chunk counter
|
|
33
|
+
* (`{ global: true, prepend: true }` — the crusher runs in its own realm),
|
|
34
|
+
* an `agent/pre-step` swap pass, and state cleanup on session disposal.
|
|
35
|
+
*/
|
|
36
|
+
registerListeners(): void;
|
|
37
|
+
/**
|
|
38
|
+
* First-chunk send counter (§3.2): one send per resident whose stored full
|
|
39
|
+
* text fingerprint appears in the outbound prompt. Main-loop purpose
|
|
40
|
+
* allowlist + owning-session guard live in {@link shouldCountRequest}.
|
|
41
|
+
*/
|
|
42
|
+
private observeStreamFirstChunk;
|
|
43
|
+
/**
|
|
44
|
+
* One pre-step pass (§3.3): sweep aged residents, then evaluate every
|
|
45
|
+
* entry whose sent count crossed the residency threshold in one
|
|
46
|
+
* non-awaiting critical section per entry. No timers exist — eligibility
|
|
47
|
+
* is recomputed at this boundary. A failing entry never aborts the others
|
|
48
|
+
* (microcompact failure ordering).
|
|
49
|
+
*/
|
|
50
|
+
private preStepPass;
|
|
51
|
+
/** Record one resident entry (post-execute, deferral applies). */
|
|
52
|
+
recordResident(exec: ToolExecution, hash: string, tokensSaved: number): Promise<void>;
|
|
53
|
+
/** Latest `todo_write` snapshot → the remaining-requests estimate inputs. */
|
|
54
|
+
observeTodoSnapshot(exec: ToolExecution): void;
|
|
55
|
+
/**
|
|
56
|
+
* Resume rebuild (§3.5), once per session: replay the per-session ledger
|
|
57
|
+
* and re-adopt entries whose stored text fingerprints the live surface.
|
|
58
|
+
* Session-id keying alone would strand residents across resume/fork, so
|
|
59
|
+
* the fingerprint — not the row — is the identity.
|
|
60
|
+
*/
|
|
61
|
+
private ensureLoaded;
|
|
62
|
+
/** Per-session defer ledger (constructed lazily; all writes never throw). */
|
|
63
|
+
private ledgerFor;
|
|
64
|
+
/** Routed model context window via the token-meter pressure projection; absent = no window source. */
|
|
65
|
+
private contextWindowOf;
|
|
66
|
+
/** Token-based sizing, same estimator as the crusher's gates and markers. */
|
|
67
|
+
private estimate;
|
|
68
|
+
}
|
|
69
|
+
export {};
|
|
70
|
+
//# sourceMappingURL=pass.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pass.d.ts","sourceRoot":"","sources":["../../src/defer/pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAMlD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAKlD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAYjD,qEAAqE;AACrE,KAAK,MAAM,GAAG,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,KAAK,MAAM,CAAA;AAE/C,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,YAAY,GAAG,SAAS,CAAA;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,eAAe,EAAE,MAAM,cAAc,CAAA;CAC/C;AAED;;;GAGG;AACH,qBAAa,QAAQ;IAIP,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;gBAE5B,IAAI,EAAE,YAAY;IAE/C;;;;OAIG;IACH,iBAAiB,IAAI,IAAI;IAsBzB;;;;OAIG;IACH,OAAO,CAAC,uBAAuB;IAe/B;;;;;;OAMG;YACW,WAAW;IAsGzB,kEAAkE;IAC5D,cAAc,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB3F,6EAA6E;IAC7E,mBAAmB,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IAS9C;;;;;OAKG;YACW,YAAY;IAqB1B,6EAA6E;IAC7E,OAAO,CAAC,SAAS;IAWjB,sGAAsG;IACtG,OAAO,CAAC,eAAe;IAavB,6EAA6E;IAC7E,OAAO,CAAC,QAAQ;CAMjB"}
|