@r3b1s/pi-repair-layer 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +159 -293
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/{index.ts → dist/index.js} +2 -1
- package/dist/index.js.map +1 -0
- package/dist/src/core.d.ts +8 -0
- package/dist/src/core.d.ts.map +1 -0
- package/dist/src/core.js +7 -0
- package/dist/src/core.js.map +1 -0
- package/dist/src/envelope.d.ts +21 -0
- package/dist/src/envelope.d.ts.map +1 -0
- package/dist/src/envelope.js +158 -0
- package/dist/src/envelope.js.map +1 -0
- package/dist/src/grammar-recovery.d.ts +105 -0
- package/dist/src/grammar-recovery.d.ts.map +1 -0
- package/dist/src/grammar-recovery.js +1052 -0
- package/dist/src/grammar-recovery.js.map +1 -0
- package/dist/src/grammar.d.ts +2 -0
- package/dist/src/grammar.d.ts.map +1 -0
- package/dist/src/grammar.js +2 -0
- package/dist/src/grammar.js.map +1 -0
- package/dist/src/index.d.ts +36 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +558 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lifecycle.d.ts +34 -0
- package/dist/src/lifecycle.d.ts.map +1 -0
- package/dist/src/lifecycle.js +133 -0
- package/dist/src/lifecycle.js.map +1 -0
- package/dist/src/pi.d.ts +19 -0
- package/dist/src/pi.d.ts.map +1 -0
- package/dist/src/pi.js +38 -0
- package/dist/src/pi.js.map +1 -0
- package/dist/src/pipeline.d.ts +10 -0
- package/dist/src/pipeline.d.ts.map +1 -0
- package/dist/src/pipeline.js +166 -0
- package/dist/src/pipeline.js.map +1 -0
- package/dist/src/policy.d.ts +16 -0
- package/dist/src/policy.d.ts.map +1 -0
- package/dist/src/policy.js +31 -0
- package/dist/src/policy.js.map +1 -0
- package/dist/src/preprocess.d.ts +37 -0
- package/dist/src/preprocess.d.ts.map +1 -0
- package/dist/src/preprocess.js +216 -0
- package/dist/src/preprocess.js.map +1 -0
- package/dist/src/repair-engine.d.ts +91 -0
- package/dist/src/repair-engine.d.ts.map +1 -0
- package/dist/src/repair-engine.js +457 -0
- package/dist/src/repair-engine.js.map +1 -0
- package/dist/src/settings.d.ts +38 -0
- package/dist/src/settings.d.ts.map +1 -0
- package/dist/src/settings.js +87 -0
- package/dist/src/settings.js.map +1 -0
- package/dist/src/tables.d.ts +16 -0
- package/dist/src/tables.d.ts.map +1 -0
- package/dist/src/tables.js +302 -0
- package/dist/src/tables.js.map +1 -0
- package/dist/src/types.d.ts +52 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/value-strips.d.ts +52 -0
- package/dist/src/value-strips.d.ts.map +1 -0
- package/dist/src/value-strips.js +191 -0
- package/dist/src/value-strips.js.map +1 -0
- package/docs/how-it-works.md +227 -0
- package/docs/operations.md +141 -0
- package/docs/research.md +247 -0
- package/docs/tool-owner-integration.md +327 -0
- package/package.json +31 -6
- package/src/grammar-recovery.ts +0 -1269
- package/src/index.ts +0 -621
- package/src/repair-engine.ts +0 -498
- package/src/settings.ts +0 -95
- package/src/tables.ts +0 -184
- package/src/value-strips.ts +0 -218
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { unwrapMarkdownAutoLinks } from "./repair-engine.js";
|
|
2
|
+
function decodePointerSegment(segment) {
|
|
3
|
+
return segment.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
4
|
+
}
|
|
5
|
+
function locationsAt(root, selector) {
|
|
6
|
+
if (selector === "" || selector === "/") {
|
|
7
|
+
return [{ parent: undefined, key: undefined, value: root }];
|
|
8
|
+
}
|
|
9
|
+
const segments = selector.split("/").slice(1).map(decodePointerSegment);
|
|
10
|
+
let locations = [
|
|
11
|
+
{ parent: undefined, key: undefined, value: root },
|
|
12
|
+
];
|
|
13
|
+
for (const segment of segments) {
|
|
14
|
+
const next = [];
|
|
15
|
+
for (const location of locations) {
|
|
16
|
+
if (segment === "*") {
|
|
17
|
+
if (Array.isArray(location.value)) {
|
|
18
|
+
location.value.forEach((value, key) => {
|
|
19
|
+
next.push({ parent: location.value, key, value });
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (location.value !== null &&
|
|
25
|
+
typeof location.value === "object" &&
|
|
26
|
+
!Array.isArray(location.value)) {
|
|
27
|
+
const parent = location.value;
|
|
28
|
+
next.push({ parent, key: segment, value: parent[segment] });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
locations = next;
|
|
32
|
+
}
|
|
33
|
+
return locations;
|
|
34
|
+
}
|
|
35
|
+
function accepts(value, expected) {
|
|
36
|
+
if (expected === undefined)
|
|
37
|
+
return value !== null && value !== undefined;
|
|
38
|
+
if (expected === "array")
|
|
39
|
+
return Array.isArray(value);
|
|
40
|
+
if (expected === "object") {
|
|
41
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
42
|
+
}
|
|
43
|
+
return typeof value === expected;
|
|
44
|
+
}
|
|
45
|
+
function setLocation(location, value) {
|
|
46
|
+
if (location.parent === undefined || location.key === undefined)
|
|
47
|
+
return false;
|
|
48
|
+
if (Array.isArray(location.parent) && typeof location.key === "number") {
|
|
49
|
+
location.parent[location.key] = value;
|
|
50
|
+
}
|
|
51
|
+
else if (!Array.isArray(location.parent) &&
|
|
52
|
+
typeof location.key === "string") {
|
|
53
|
+
location.parent[location.key] = value;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
location.value = value;
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
function stripAnchors(value) {
|
|
62
|
+
return value.replace(/^\^+/, "").replace(/\$+$/, "");
|
|
63
|
+
}
|
|
64
|
+
const GRAMMAR_TOKENS = /^(?:<arg_key>|<arg_value>)|(?:<\/arg_key>|<\/arg_value>)$/g;
|
|
65
|
+
function stripGrammarTokens(value) {
|
|
66
|
+
return value.replace(GRAMMAR_TOKENS, "").trim();
|
|
67
|
+
}
|
|
68
|
+
function modelMatches(modelId, families) {
|
|
69
|
+
return Boolean(modelId && families?.some((family) => family.test(modelId)));
|
|
70
|
+
}
|
|
71
|
+
function note(ruleId, text) {
|
|
72
|
+
return { ruleId, stage: "preprocess", note: text };
|
|
73
|
+
}
|
|
74
|
+
export function preprocessInput(options) {
|
|
75
|
+
const { toolName, preprocessors, modelId, allowValidValueTransforms } = options;
|
|
76
|
+
let value = structuredClone(options.input);
|
|
77
|
+
const changes = [];
|
|
78
|
+
const observations = [];
|
|
79
|
+
for (const preprocessor of preprocessors) {
|
|
80
|
+
if (preprocessor.kind === "alias") {
|
|
81
|
+
const parts = preprocessor.selector.split("/");
|
|
82
|
+
const canonical = decodePointerSegment(parts.pop() ?? "");
|
|
83
|
+
const parentSelector = (parts.join("/") || "/");
|
|
84
|
+
for (const parentLocation of locationsAt(value, parentSelector)) {
|
|
85
|
+
const parent = parentLocation.value;
|
|
86
|
+
if (parent === null ||
|
|
87
|
+
typeof parent !== "object" ||
|
|
88
|
+
Array.isArray(parent)) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const record = parent;
|
|
92
|
+
const canonicalValue = record[canonical];
|
|
93
|
+
const replaceEmpty = preprocessor.emptyEquivalentToMissing === true &&
|
|
94
|
+
canonicalValue === "";
|
|
95
|
+
if (canonical in record &&
|
|
96
|
+
canonicalValue !== undefined &&
|
|
97
|
+
canonicalValue !== null &&
|
|
98
|
+
!replaceEmpty) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
for (const alias of preprocessor.aliases) {
|
|
102
|
+
if (!(alias in record) ||
|
|
103
|
+
!accepts(record[alias], preprocessor.accepts)) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (record[alias] === "")
|
|
107
|
+
continue;
|
|
108
|
+
record[canonical] = record[alias];
|
|
109
|
+
delete record[alias];
|
|
110
|
+
changes.push(note("preprocess.exact-alias", `Renamed \`${alias}\` to \`${canonical}\` for tool "${toolName}". Use \`${canonical}\` next time.`));
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (preprocessor.kind === "structural") {
|
|
117
|
+
for (const location of locationsAt(value, preprocessor.selector)) {
|
|
118
|
+
const result = preprocessor.apply(location.value);
|
|
119
|
+
if (!result)
|
|
120
|
+
continue;
|
|
121
|
+
if (location.parent === undefined)
|
|
122
|
+
value = result.value;
|
|
123
|
+
else
|
|
124
|
+
setLocation(location, result.value);
|
|
125
|
+
changes.push(note(preprocessor.ruleId, result.note));
|
|
126
|
+
}
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const heuristic = preprocessor.kind === "anchor-bleed" ||
|
|
130
|
+
preprocessor.kind === "grammar-tokens";
|
|
131
|
+
if (heuristic && !modelMatches(modelId, preprocessor.modelFamilies))
|
|
132
|
+
continue;
|
|
133
|
+
for (const location of locationsAt(value, preprocessor.selector)) {
|
|
134
|
+
const current = location.value;
|
|
135
|
+
let next = current;
|
|
136
|
+
if (preprocessor.kind === "filesystem-path" &&
|
|
137
|
+
typeof current === "string") {
|
|
138
|
+
next = unwrapMarkdownAutoLinks(current);
|
|
139
|
+
}
|
|
140
|
+
else if (preprocessor.kind === "filesystem-path-array" &&
|
|
141
|
+
Array.isArray(current)) {
|
|
142
|
+
next = current.map((item) => typeof item === "string" ? unwrapMarkdownAutoLinks(item) : item);
|
|
143
|
+
}
|
|
144
|
+
else if (preprocessor.kind === "string-or-array" &&
|
|
145
|
+
typeof current === "string") {
|
|
146
|
+
next = [current];
|
|
147
|
+
}
|
|
148
|
+
else if (preprocessor.kind === "scalar") {
|
|
149
|
+
if (preprocessor.scalarType === "string" &&
|
|
150
|
+
["number", "boolean"].includes(typeof current)) {
|
|
151
|
+
next = String(current);
|
|
152
|
+
}
|
|
153
|
+
else if (preprocessor.scalarType === "number" &&
|
|
154
|
+
typeof current === "string" &&
|
|
155
|
+
current.trim() !== "") {
|
|
156
|
+
const parsed = Number(current);
|
|
157
|
+
if (Number.isFinite(parsed))
|
|
158
|
+
next = parsed;
|
|
159
|
+
}
|
|
160
|
+
else if (preprocessor.scalarType === "boolean" &&
|
|
161
|
+
typeof current === "string" &&
|
|
162
|
+
/^(?:true|false)$/i.test(current)) {
|
|
163
|
+
next = current.toLowerCase() === "true";
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
else if (preprocessor.kind === "anchor-bleed" &&
|
|
167
|
+
typeof current === "string") {
|
|
168
|
+
next = stripAnchors(current);
|
|
169
|
+
}
|
|
170
|
+
else if (preprocessor.kind === "grammar-tokens") {
|
|
171
|
+
if (typeof current === "string") {
|
|
172
|
+
next = stripGrammarTokens(current);
|
|
173
|
+
}
|
|
174
|
+
else if (current !== null &&
|
|
175
|
+
typeof current === "object" &&
|
|
176
|
+
!Array.isArray(current)) {
|
|
177
|
+
const record = Object.create(null);
|
|
178
|
+
for (const [key, item] of Object.entries(current)) {
|
|
179
|
+
const cleanKey = stripGrammarTokens(key);
|
|
180
|
+
record[cleanKey] =
|
|
181
|
+
typeof item === "string" ? stripGrammarTokens(item) : item;
|
|
182
|
+
}
|
|
183
|
+
next = record;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (Object.is(next, current) ||
|
|
187
|
+
JSON.stringify(next) === JSON.stringify(current)) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
const ruleId = preprocessor.kind === "anchor-bleed"
|
|
191
|
+
? "stripAnchorBleed"
|
|
192
|
+
: preprocessor.kind === "grammar-tokens"
|
|
193
|
+
? "stripGrammarTokenLeak"
|
|
194
|
+
: `preprocess.${preprocessor.kind}`;
|
|
195
|
+
if (heuristic && !allowValidValueTransforms) {
|
|
196
|
+
if (!observations.includes(ruleId))
|
|
197
|
+
observations.push(ruleId);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (location.parent === undefined)
|
|
201
|
+
value = next;
|
|
202
|
+
else
|
|
203
|
+
setLocation(location, next);
|
|
204
|
+
changes.push(note(ruleId, preprocessor.kind === "anchor-bleed"
|
|
205
|
+
? `Stripped leaked regex anchors (\`^\` / \`$\`) from a configured value for tool "${toolName}".`
|
|
206
|
+
: preprocessor.kind === "grammar-tokens"
|
|
207
|
+
? `Removed leaked grammar tokens (\`<arg_key>\`/\`<arg_value>\`) from configured keys or values for tool "${toolName}".`
|
|
208
|
+
: preprocessor.kind === "filesystem-path" ||
|
|
209
|
+
preprocessor.kind === "filesystem-path-array"
|
|
210
|
+
? `Unwrapped a markdown auto-link at ${preprocessor.selector || "/"} for tool "${toolName}". Send plain filesystem paths, not markdown links.`
|
|
211
|
+
: `Applied configured ${preprocessor.kind} preprocessing at ${preprocessor.selector || "/"} for tool "${toolName}".`));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return { value, changes, observations };
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=preprocess.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preprocess.js","sourceRoot":"","sources":["../../src/preprocess.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate-then-repair engine for LLM tool-call inputs.
|
|
3
|
+
*
|
|
4
|
+
* Design (matching the publicly described behavior of commandcode's repair layer):
|
|
5
|
+
*
|
|
6
|
+
* - Strictly valid inputs are never touched: the fast path is a plain
|
|
7
|
+
* `Value.Check` and returns the original input by reference.
|
|
8
|
+
* - On failure, the validator's own issue list localizes the damage. Repairs are
|
|
9
|
+
* attempted only at the exact paths the schema disagreed with, in a fixed
|
|
10
|
+
* order (JSON-array parsing must run before bare-string wrapping, or
|
|
11
|
+
* `'["a","b"]'` becomes `['["a","b"]']`).
|
|
12
|
+
* - Every mutation produces a model-facing note so the model can learn the real
|
|
13
|
+
* contract on the next turn. Transparency over silent magic.
|
|
14
|
+
*
|
|
15
|
+
* The strict check deliberately runs BEFORE TypeBox's `Value.Convert` (which pi
|
|
16
|
+
* applies during validation), because Convert silently corrupts exactly the
|
|
17
|
+
* inputs this layer exists to fix: `'["a","b"]'` for an array field becomes
|
|
18
|
+
* `['["a","b"]']`, `null` for an optional string becomes the string `"null"`,
|
|
19
|
+
* and `null` for an optional number becomes `0` — all of which then pass
|
|
20
|
+
* validation and execute with garbage. Repairing at the strict-error sites
|
|
21
|
+
* first means those inputs are fixed properly (with a note) instead. Benign
|
|
22
|
+
* coercions ("5" -> 5) are left to Convert: if no repair rule fires and Convert
|
|
23
|
+
* alone makes the input valid, the input is reported as valid and returned
|
|
24
|
+
* untouched, so pi's native behavior is preserved.
|
|
25
|
+
*
|
|
26
|
+
* The one deliberate exception to validate-then-repair is markdown auto-link
|
|
27
|
+
* unwrapping on path fields: `[notes.md](http://notes.md)` is a perfectly valid
|
|
28
|
+
* string, so validation can never flag it. It is unwrapped unconditionally, but
|
|
29
|
+
* only in the degenerate case where the link text equals the url without its
|
|
30
|
+
* protocol — real markdown links pass through untouched.
|
|
31
|
+
*/
|
|
32
|
+
import type { TSchema } from "typebox";
|
|
33
|
+
export interface StructuralRepair {
|
|
34
|
+
/** Rule name recorded in telemetry when the repair fires. */
|
|
35
|
+
name: string;
|
|
36
|
+
/** Mutate `args` in place. Return a model-facing note when a repair was applied, false otherwise. */
|
|
37
|
+
apply(args: Record<string, unknown>, toolName: string): string | false;
|
|
38
|
+
}
|
|
39
|
+
export interface ToolRepairConfig {
|
|
40
|
+
/** canonical field name -> wrong names models emit for it, matched at any depth by key. */
|
|
41
|
+
fieldAliases?: Record<string, readonly string[]>;
|
|
42
|
+
/** When the whole input is a bare string, wrap it as `{ [field]: value }`. */
|
|
43
|
+
rootString?: {
|
|
44
|
+
field: string;
|
|
45
|
+
wrapInArray?: boolean;
|
|
46
|
+
};
|
|
47
|
+
/** Top-level string fields holding filesystem paths (markdown auto-link unwrapping). */
|
|
48
|
+
pathFields?: readonly string[];
|
|
49
|
+
/** Tool-specific shape folds that single-field rules cannot express. */
|
|
50
|
+
structural?: readonly StructuralRepair[];
|
|
51
|
+
}
|
|
52
|
+
export interface RepairResult {
|
|
53
|
+
outcome: "valid" | "repaired" | "unrepairable";
|
|
54
|
+
/** What prepareArguments should return: the untouched input, the repaired input, or (unrepairable) the untouched input. */
|
|
55
|
+
args: unknown;
|
|
56
|
+
rulesFired: string[];
|
|
57
|
+
notes: string[];
|
|
58
|
+
/** One entry per mutation, including repeated use of the same stable rule. */
|
|
59
|
+
changes: Array<{
|
|
60
|
+
ruleId: string;
|
|
61
|
+
note: string;
|
|
62
|
+
}>;
|
|
63
|
+
/** Compact description of the original validation failure, for telemetry. */
|
|
64
|
+
issueSummary: string | undefined;
|
|
65
|
+
/** Stable hash of (tool, failure shape), for spotting per-model regressions. */
|
|
66
|
+
fingerprint: string | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* Model-readable error for unrepairable input. Throwing this from
|
|
69
|
+
* prepareArguments matters: handing the raw input back to pi instead would
|
|
70
|
+
* let Value.Convert corrupt it (null -> "null") and execute the call anyway.
|
|
71
|
+
*/
|
|
72
|
+
retryMessage: string | undefined;
|
|
73
|
+
}
|
|
74
|
+
export declare function unwrapMarkdownAutoLinks(value: string): string;
|
|
75
|
+
export declare function repairSchemaInput(options: {
|
|
76
|
+
toolName: string;
|
|
77
|
+
schema: TSchema;
|
|
78
|
+
input: unknown;
|
|
79
|
+
config?: ToolRepairConfig;
|
|
80
|
+
}): RepairResult;
|
|
81
|
+
/**
|
|
82
|
+
* Current-major compatibility facade. New consumers should prefer
|
|
83
|
+
* `runRepairPipeline`, but this shape and its legacy rule names remain stable.
|
|
84
|
+
*/
|
|
85
|
+
export declare function repairToolInput(options: {
|
|
86
|
+
toolName: string;
|
|
87
|
+
schema: TSchema;
|
|
88
|
+
input: unknown;
|
|
89
|
+
config?: ToolRepairConfig;
|
|
90
|
+
}): RepairResult;
|
|
91
|
+
//# sourceMappingURL=repair-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repair-engine.d.ts","sourceRoot":"","sources":["../../src/repair-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAIvC,MAAM,WAAW,gBAAgB;IAC/B,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,qGAAqG;IACrG,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;CACxE;AAED,MAAM,WAAW,gBAAgB;IAC/B,2FAA2F;IAC3F,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;IACjD,8EAA8E;IAC9E,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACtD,wFAAwF;IACxF,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,wEAAwE;IACxE,UAAU,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,GAAG,UAAU,GAAG,cAAc,CAAC;IAC/C,2HAA2H;IAC3H,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,8EAA8E;IAC9E,OAAO,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,6EAA6E;IAC7E,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,gFAAgF;IAChF,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC;;;;OAIG;IACH,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAWD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI7D;AAoRD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC3B,GAAG,YAAY,CA+Jf;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC3B,GAAG,YAAY,CA6Bf"}
|