@actor0/harness 0.0.0-stage → 0.5.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.
@@ -0,0 +1,36 @@
1
+ import { type RejectedBlock } from "./tool-call-block.js";
2
+ import type { PlanPrefix, ToolCall } from "./types.js";
3
+ /**
4
+ * Status text kept from one line.
5
+ *
6
+ * A line that opens with the marker is a status line, and its text runs to
7
+ * the end of that line. Past this many characters it stops being a status and
8
+ * becomes the answer again, which is what keeps a runaway line from taking the
9
+ * rest of the response with it.
10
+ */
11
+ export declare const MAX_STATUS_CHARS = 80;
12
+ export type FilteredChunk = {
13
+ text: string;
14
+ statuses: string[];
15
+ plan: PlanPrefix | null;
16
+ /** Tool calls the model emitted as protocol blocks (see tool-call-block.ts). */
17
+ toolCalls: ToolCall[];
18
+ /**
19
+ * Blocks the protocol refused — a call for an unoffered tool, or arguments
20
+ * that were not valid JSON. Each becomes a synthetic call plus an error
21
+ * tool result, so the model is told next round that the call was wrong and
22
+ * why; nothing here is ever released into the answer text.
23
+ */
24
+ rejected: RejectedBlock[];
25
+ };
26
+ export type AnswerFilter = {
27
+ push(delta: string): FilteredChunk;
28
+ flush(): FilteredChunk;
29
+ reset(): void;
30
+ };
31
+ /**
32
+ * @param toolNames names from the request's tool list. A block naming
33
+ * anything else is not a call and is shown as ordinary text.
34
+ */
35
+ export declare function createAnswerFilter(toolNames?: string[]): AnswerFilter;
36
+ //# sourceMappingURL=answer-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"answer-filter.d.ts","sourceRoot":"","sources":["../src/answer-filter.ts"],"names":[],"mappings":"AACA,OAAO,EAWL,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAMvD;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,KAAK,CAAC;AA+EnC,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IACxB,gFAAgF;IAChF,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB;;;;;OAKG;IACH,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAAC;IACnC,KAAK,IAAI,aAAa,CAAC;IACvB,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AAEF;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,GAAE,MAAM,EAAO,GAAG,YAAY,CAkPzE"}
@@ -0,0 +1,308 @@
1
+ import { extractPlanPrefix, needsMorePlanData } from "./plan-prefix.js";
2
+ import { MAX_TOOL_BLOCK, matchCloseEnvelope, matchCloseFence, matchOpenEnvelope, matchOpenFence, mayBeOpenEnvelope, mayBeOpenFence, parseToolCallBlock, parseToolCallEnvelope, } from "./tool-call-block.js";
3
+ /** Incremental answer filtering for streamed model output. */
4
+ const MAX_STATUS_HOLD = 48;
5
+ const MAYBE_STATUS_PREFIX = /^(?:[ \t]*|[ \t]*\[[^\x5d\n]{0,40})$/;
6
+ /**
7
+ * Status text kept from one line.
8
+ *
9
+ * A line that opens with the marker is a status line, and its text runs to
10
+ * the end of that line. Past this many characters it stops being a status and
11
+ * becomes the answer again, which is what keeps a runaway line from taking the
12
+ * rest of the response with it.
13
+ */
14
+ export const MAX_STATUS_CHARS = 80;
15
+ /**
16
+ * Status markers.
17
+ *
18
+ * Only the bullet form is recognised: `[·] Searching the web`. That is the
19
+ * shape the system prompt specifies, and it is the only one that can be told
20
+ * apart from content by inspection rather than by guesswork.
21
+ *
22
+ * The bare bracket form, `[Searching the web]`, used to be accepted too. It is
23
+ * not any more, because it is genuinely indistinguishable from the link text a
24
+ * model writes: asked for a landing page, it emitted `[Get Started for Free]`
25
+ * and `[**Start Building**]`, and both were silently deleted from the answer
26
+ * along with the call to action. Nothing else in prose looks like `[·]`.
27
+ *
28
+ * The asymmetry is deliberate. Missing a status leaves a visible `[·] thing`
29
+ * line in the answer, which is ugly. Eating a link label is silent content
30
+ * loss. The ugly failure is the better one.
31
+ *
32
+ * One known blind spot, left alone on purpose: a marker-shaped line inside an
33
+ * ordinary (non-protocol) fenced code block is still eaten as a status. The
34
+ * filter has no notion of a code fence — only a ```json fence is a block it
35
+ * tracks, because buffering a bare fence would stall any code-heavy answer
36
+ * waiting for a close that may never come. Adding fence state to get this
37
+ * right means implementing CommonMark's opening and closing rules for a line
38
+ * shape that essentially never occurs, and getting them subtly wrong stalls
39
+ * the stream. Recorded rather than fixed.
40
+ */
41
+ /**
42
+ * A marker owns its line when a newline follows it immediately, which is what
43
+ * lets the trailing newline be swallowed so markers do not each prepend a
44
+ * blank line to the answer.
45
+ */
46
+ const ownsLine = (text, length) => text.indexOf("\n", length) === length;
47
+ /** The marker itself, with the indentation and the space that follow it. */
48
+ const STATUS_MARKER = /^[ \t]*\[\u00b7\][ \t]*/;
49
+ function matchStatusLine(text, atEnd) {
50
+ const marker = STATUS_MARKER.exec(text);
51
+ if (!marker)
52
+ return null;
53
+ const rest = text.slice(marker[0].length);
54
+ const text_ = (value) => value.replace(/^\u00b7+\s*/, "").trim().slice(0, MAX_STATUS_CHARS);
55
+ const newline = rest.indexOf("\n");
56
+ if (newline !== -1) {
57
+ // A line past the cap stops being a status halfway: the rest of it is
58
+ // content, and the newline that ends it is content's, not the status's.
59
+ const capped = newline > MAX_STATUS_CHARS;
60
+ return {
61
+ text: text_(rest.slice(0, newline)),
62
+ end: marker[0].length + (capped ? MAX_STATUS_CHARS : newline + 1),
63
+ complete: true,
64
+ };
65
+ }
66
+ if (!atEnd && rest.length <= MAX_STATUS_CHARS)
67
+ return { text: "", end: 0, complete: false };
68
+ return { text: text_(rest), end: marker[0].length + MAX_STATUS_CHARS, complete: true };
69
+ }
70
+ /**
71
+ * @param toolNames names from the request's tool list. A block naming
72
+ * anything else is not a call and is shown as ordinary text.
73
+ */
74
+ export function createAnswerFilter(toolNames = []) {
75
+ let held = "";
76
+ let atLineStart = true;
77
+ let eatNewline = false;
78
+ let planDone = false;
79
+ /**
80
+ * Non-null while inside a protocol block — a ```json fence or a
81
+ * `<tool_calls>` envelope. Everything from the opening delimiter is
82
+ * accumulated here and released in one go, so a block split across arbitrary
83
+ * deltas is never half-shown, and the block's own text is only released if it
84
+ * turns out not to be a tool call.
85
+ */
86
+ let block = null;
87
+ let blockKind = "fence";
88
+ /**
89
+ * Where the block's closer is.
90
+ *
91
+ * `pending` is the whole block so far, not just the newest delta: a closing
92
+ * delimiter can straddle a push boundary, and one that lands half in the
93
+ * buffer and half in the delta is the normal case for a small-chunked SSE
94
+ * stream. An envelope resumes its scan where the last one stopped, because
95
+ * the accumulated half has already been searched and had no close in it.
96
+ */
97
+ const findClose = (pending, atEnd) => blockKind === "envelope"
98
+ ? matchCloseEnvelope(pending, block?.length ?? 0)
99
+ : matchCloseFence(pending, atEnd);
100
+ /** The calls and rejections a closed block carries. */
101
+ const parseClosed = (finished) => {
102
+ if (blockKind === "envelope")
103
+ return parseToolCallEnvelope(finished, toolNames);
104
+ const result = parseToolCallBlock(finished, toolNames);
105
+ if (result === null)
106
+ return { calls: [], rejected: [] };
107
+ if ("reason" in result)
108
+ return { calls: [], rejected: [result] };
109
+ return { calls: [result], rejected: [] };
110
+ };
111
+ /**
112
+ * The one place text is turned into visible answer, statuses and tool calls.
113
+ *
114
+ * push() and flush() both go through here rather than keeping parallel
115
+ * copies of the rules: a fence parked in `held` by the plan-prefix logic is
116
+ * still in the buffer when the stream ends, and a flush path that did not
117
+ * re-run the loop would show it as text instead of a tool call.
118
+ *
119
+ * @param atEnd end of stream — a closing fence is final even without its
120
+ * trailing newline, and a block that never closed is released as text.
121
+ */
122
+ const run = (delta, atEnd) => {
123
+ let output = "";
124
+ const statuses = [];
125
+ const toolCalls = [];
126
+ const rejected = [];
127
+ let plan = null;
128
+ let buffer = held + delta;
129
+ held = "";
130
+ if (!planDone) {
131
+ const extracted = extractPlanPrefix(buffer);
132
+ if (extracted.plan) {
133
+ planDone = true;
134
+ plan = extracted.plan;
135
+ buffer = extracted.rest;
136
+ }
137
+ }
138
+ while (buffer.length > 0) {
139
+ if (eatNewline) {
140
+ eatNewline = false;
141
+ if (buffer.startsWith("\r\n"))
142
+ buffer = buffer.slice(2);
143
+ else if (buffer.startsWith("\n"))
144
+ buffer = buffer.slice(1);
145
+ continue;
146
+ }
147
+ // Inside a candidate block: nothing may be shown until it closes.
148
+ if (block !== null) {
149
+ const pending = block + buffer;
150
+ const close = findClose(pending, atEnd);
151
+ if (!close) {
152
+ block = pending;
153
+ buffer = "";
154
+ if (block.length > MAX_TOOL_BLOCK) {
155
+ // Unterminated: give up rather than swallow the rest of the answer.
156
+ output += block;
157
+ block = null;
158
+ atLineStart = false;
159
+ }
160
+ continue;
161
+ }
162
+ const finished = pending.slice(0, close.index + close.length);
163
+ buffer = pending.slice(close.index + close.length);
164
+ block = null;
165
+ const parsed = parseClosed(finished);
166
+ if (parsed.rejected.length > 0) {
167
+ rejected.push(...parsed.rejected);
168
+ // The block owned its line, so drop the newline that ends it.
169
+ eatNewline = true;
170
+ atLineStart = true;
171
+ }
172
+ else if (parsed.calls.length > 0) {
173
+ toolCalls.push(...parsed.calls);
174
+ // The block owned its line, so drop the newline that ends it.
175
+ //
176
+ // Unconditionally, and that is the point: the newline ending a
177
+ // block is very often the next delta to arrive, and deciding from
178
+ // a buffer that does not hold it yet is how a blank line appeared in
179
+ // the answer only when the stream happened to be finely chunked. The
180
+ // flag is cleared by the next character whatever it is, so setting
181
+ // it for a block followed by anything else costs nothing.
182
+ eatNewline = true;
183
+ atLineStart = true;
184
+ }
185
+ else {
186
+ output += finished;
187
+ atLineStart = ownsLine(finished, finished.length);
188
+ }
189
+ continue;
190
+ }
191
+ if (atLineStart) {
192
+ const fence = matchOpenFence(buffer);
193
+ const envelope = fence ? null : matchOpenEnvelope(buffer);
194
+ const open = fence ?? envelope;
195
+ if (open) {
196
+ blockKind = fence ? "fence" : "envelope";
197
+ block = buffer.slice(0, open.length);
198
+ buffer = buffer.slice(open.length);
199
+ continue;
200
+ }
201
+ // A delimiter split across deltas ("``" then "`json\n", or "<tool_"
202
+ // then "calls>"): hold the half rather than releasing it as text. A
203
+ // handful of characters, released on the next push once this is
204
+ // clearly not a tool block.
205
+ if (mayBeOpenFence(buffer) || mayBeOpenEnvelope(buffer)) {
206
+ held = buffer;
207
+ buffer = "";
208
+ continue;
209
+ }
210
+ }
211
+ if (!atLineStart) {
212
+ const newline = buffer.indexOf("\n");
213
+ if (newline === -1) {
214
+ output += buffer;
215
+ buffer = "";
216
+ }
217
+ else {
218
+ output += buffer.slice(0, newline + 1);
219
+ buffer = buffer.slice(newline + 1);
220
+ atLineStart = true;
221
+ }
222
+ continue;
223
+ }
224
+ const status = matchStatusLine(buffer, atEnd);
225
+ if (status) {
226
+ if (!status.complete) {
227
+ // More of the line may still be coming. Held for exactly as long as
228
+ // it takes to find out, and no longer: a fragment that is already
229
+ // over the cap stops being a status and falls through to the answer.
230
+ held = buffer;
231
+ buffer = "";
232
+ continue;
233
+ }
234
+ if (status.text)
235
+ statuses.push(status.text);
236
+ buffer = buffer.slice(status.end);
237
+ continue;
238
+ }
239
+ // The two "wait and see" holds are only legitimate mid-stream. At the
240
+ // end there is no more input coming, so holding would strand the text
241
+ // forever — a plan-shaped prefix must not swallow a tool-call block that
242
+ // follows it.
243
+ if (!atEnd) {
244
+ if (buffer.length < MAX_STATUS_HOLD && MAYBE_STATUS_PREFIX.test(buffer)) {
245
+ held = buffer;
246
+ buffer = "";
247
+ continue;
248
+ }
249
+ if (!planDone && output.length === 0 && !plan && needsMorePlanData(buffer)) {
250
+ held = buffer;
251
+ buffer = "";
252
+ continue;
253
+ }
254
+ }
255
+ atLineStart = false;
256
+ }
257
+ // A block still open when the buffer runs dry is only decidable at the end
258
+ // of the stream: its closing fence may be the last thing that arrives, or
259
+ // it may never come. Left alone it would be dropped — neither shown nor
260
+ // run — so it is resolved here.
261
+ if (atEnd && block !== null) {
262
+ const close = findClose(block, true);
263
+ const finished = close ? block.slice(0, close.index + close.length) : block;
264
+ const rest = close ? block.slice(close.index + close.length) : "";
265
+ block = null;
266
+ // Never execute a block that never closed: whatever it parses as, it is
267
+ // the model showing the user JSON, not a protocol message.
268
+ const parsed = close ? parseClosed(finished) : { calls: [], rejected: [] };
269
+ if (parsed.rejected.length > 0) {
270
+ rejected.push(...parsed.rejected);
271
+ // The block is the protocol, not content: it must not also be shown.
272
+ output += rest;
273
+ }
274
+ else if (parsed.calls.length > 0) {
275
+ toolCalls.push(...parsed.calls);
276
+ output += rest;
277
+ }
278
+ else {
279
+ output += finished + rest;
280
+ }
281
+ }
282
+ return { text: output, statuses, plan, toolCalls, rejected };
283
+ };
284
+ return {
285
+ push(delta) {
286
+ return run(delta, false);
287
+ },
288
+ flush() {
289
+ atLineStart = true;
290
+ eatNewline = false;
291
+ const result = run("", true);
292
+ // `run` may have parked a final fragment (a half fence, a status
293
+ // prefix). Nothing more is coming, so release it as answer text.
294
+ if (held) {
295
+ result.text += held;
296
+ held = "";
297
+ }
298
+ return result;
299
+ },
300
+ reset() {
301
+ held = "";
302
+ atLineStart = true;
303
+ eatNewline = false;
304
+ planDone = false;
305
+ block = null;
306
+ },
307
+ };
308
+ }
@@ -0,0 +1,154 @@
1
+ import type { ChatMessage, HarnessObserver, ModelClient, RoundResult, RunResult, ToolHost, ToolRoundPolicy } from "./types.js";
2
+ export type HarnessConfig = {
3
+ maxRetries: number;
4
+ initialBackoffMs: number;
5
+ maxBackoffMs: number;
6
+ idleTimeoutMs: number;
7
+ /**
8
+ * Hard cap on tool rounds. Defaults to unbounded — a turn that needs to
9
+ * research for hours should be allowed to. A caller that pays per round
10
+ * should set it: reaching it ends the turn with `needs_user` and everything
11
+ * the rounds already produced, never a thrown error.
12
+ */
13
+ maxToolRounds: number;
14
+ /**
15
+ * Consecutive rounds in which *every* tool call failed. Reaching this stops
16
+ * the turn and reports `needs_user` rather than throwing, so the caller can
17
+ * ask whether to continue. A single bad call is normal and never counts.
18
+ */
19
+ maxConsecutiveToolErrors: number;
20
+ recoverEmptyAnswer: boolean;
21
+ /**
22
+ * Whether one continuation is attempted when the endpoint cuts an answer
23
+ * off at its output cap. Once, not repeatedly: a model that is too long
24
+ * for the cap is too long for it twice, and the second attempt costs a
25
+ * full request to learn the same thing.
26
+ */
27
+ recoverTruncatedAnswer: boolean;
28
+ };
29
+ export declare const DEFAULT_HARNESS_CONFIG: HarnessConfig;
30
+ /** Marker appended to an answer a cancel cut short. */
31
+ export declare const INTERRUPT_MARKER = "[interrupted]";
32
+ /**
33
+ * `text` with the interrupt marker appended, or the marker alone when the
34
+ * cancel landed before anything streamed. The marker is what tells a resumed
35
+ * session that the text above it is a fragment the user stopped, not a
36
+ * completed answer the model should stand behind.
37
+ */
38
+ export declare function withInterruptMarker(text: string): string;
39
+ /** Marker appended to an answer a failed turn cut short. */
40
+ export declare const FAILED_MARKER = "[failed]";
41
+ /**
42
+ * `text` with the failure marker appended, or the marker alone when
43
+ * the failure landed before anything streamed. The marker is what
44
+ * tells a resumed session that the text above it is a fragment of a
45
+ * failed attempt, not a completed answer the model stands behind.
46
+ */
47
+ export declare function withFailedMarker(text: string): string;
48
+ /** Marker appended to an answer the endpoint cut off at its output cap. */
49
+ export declare const TRUNCATED_MARKER = "[cut off at the output limit]";
50
+ /**
51
+ * `text` with the truncation marker appended, or the marker alone when
52
+ * the answer never started.
53
+ *
54
+ * The third of the three markers a terminal state leaves in the
55
+ * transcript, and the only one whose host flag was optional: a cancel and a
56
+ * failure each had to hand the transcript back with the marker on it, while
57
+ * a cut-off answer was saved as the plain words the model had written so
58
+ * far. It is not a completed reply and the transcript is what the next turn
59
+ * reads first, so the marker goes here rather than in whichever host
60
+ * remembered to render `RunResult.truncated`.
61
+ */
62
+ export declare function withTruncationMarker(text: string): string;
63
+ /**
64
+ * What a tool call that a cancel cut short is answered with.
65
+ *
66
+ * It is the model's half of the record: the call happened, the command may
67
+ * have done anything, and nobody knows what it produced. Exported because
68
+ * the same sentence is now also the event the host shows the user, and a
69
+ * host that renders its own copy must not be able to word it differently
70
+ * from the one the next turn's model reads.
71
+ */
72
+ export declare const CANCELLED_TOOL_RESULT = "[cancelled] The turn was interrupted around this call, so its result is unknown. Run it again if you still need it.";
73
+ /**
74
+ * Thrown when the caller aborted a turn. Carries the transcript as the turn
75
+ * left it — the input, every finished round, answers for calls the cancel cut
76
+ * short, and the partial answer marked interrupted — so a host can persist an
77
+ * exchange the user watched happen. Without it, a cancel rewinds the
78
+ * conversation to before the turn: the work vanishes from the saved session
79
+ * and a resume replays it from zero.
80
+ *
81
+ * The transcript is API-valid by construction (see `abortTurn`), so a host can
82
+ * save it as-is and the next request will be accepted.
83
+ */
84
+ export declare class AbortedTurnError extends Error {
85
+ readonly messages: ChatMessage[];
86
+ /** What the cancelled attempt had streamed, without the marker. */
87
+ readonly partialText: string;
88
+ constructor(messages: ChatMessage[],
89
+ /** What the cancelled attempt had streamed, without the marker. */
90
+ partialText: string);
91
+ }
92
+ /**
93
+ * Thrown when a turn failed for any reason other than a cancel: the
94
+ * endpoint refused the request, the stream died mid-answer, a retry
95
+ * budget ran out. Carries the transcript as the turn left it — the
96
+ * input, every finished round, and the partial answer marked failed
97
+ * — so a host can persist an exchange whose later rounds really
98
+ * happened. Without it, a mid-turn failure rewinds the conversation
99
+ * to before the turn: tool calls that ran and files that changed
100
+ * vanish from the saved session, and a resume replays them from zero
101
+ * as though they never happened. Claude Code yields the error into
102
+ * the conversation instead of rewinding, and so does this.
103
+ *
104
+ * The transcript is API-valid by construction (see `failedTurn`), so
105
+ * a host can save it as-is and the next request will be accepted.
106
+ */
107
+ export declare class FailedTurnError extends Error {
108
+ readonly messages: ChatMessage[];
109
+ /** What the failed attempt had streamed, without the marker. */
110
+ readonly partialText: string;
111
+ constructor(messages: ChatMessage[],
112
+ /** What the failed attempt had streamed, without the marker. */
113
+ partialText: string, cause?: unknown);
114
+ }
115
+ export declare function runModelRound(model: ModelClient, messages: ChatMessage[], tools: Parameters<ModelClient["stream"]>[1], signal: AbortSignal, observer?: HarnessObserver, config?: HarnessConfig): Promise<RoundResult>;
116
+ export declare function runAgentTurn(options: {
117
+ model: ModelClient;
118
+ messages: ChatMessage[];
119
+ input: string;
120
+ toolHost?: ToolHost;
121
+ signal: AbortSignal;
122
+ observer?: HarnessObserver;
123
+ config?: HarnessConfig;
124
+ policy?: ToolRoundPolicy;
125
+ }): Promise<RunResult>;
126
+ /**
127
+ * Jitter added to every wait, as a fraction of the delay itself.
128
+ *
129
+ * Without it, every client that hit a rate limit in the same second comes back
130
+ * in the same second, and the limit that just rejected them rejects them
131
+ * again. It is added and never subtracted: a wait shorter than the backoff it
132
+ * is jittering is not a backoff.
133
+ */
134
+ export declare const RETRY_JITTER_RATIO = 0.25;
135
+ /**
136
+ * How long to wait before the next attempt.
137
+ *
138
+ * Two inputs, and the second can only make the wait longer:
139
+ *
140
+ * - exponential backoff, capped, which is what a dropped connection or a
141
+ * 5xx wants;
142
+ * - `Retry-After`, which is the server stating a rate limit window. A 429
143
+ * that says "come back in 30" and is retried after one, two and four
144
+ * seconds has honoured nothing: every attempt lands inside the window and
145
+ * the turn fails having spent seven seconds confirming what it was told.
146
+ *
147
+ * A wait beyond `MAX_HONOURED_RETRY_AFTER_MS` never reaches here — the
148
+ * transport has already made that failure non-retriable rather than have the
149
+ * turn vanish for a number of minutes a user is waiting on.
150
+ *
151
+ * Exported so the arithmetic can be tested without sleeping through it.
152
+ */
153
+ export declare function retryDelay(attempt: number, config: HarnessConfig, error?: unknown): number;
154
+ //# sourceMappingURL=harness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../src/harness.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,WAAW,EACX,eAAe,EACf,WAAW,EAGX,WAAW,EACX,SAAS,EAET,QAAQ,EACR,eAAe,EAGhB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,wBAAwB,EAAE,MAAM,CAAC;IACjC,kBAAkB,EAAE,OAAO,CAAC;IAC5B;;;;;OAKG;IACH,sBAAsB,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,aASpC,CAAC;AAEF,uDAAuD;AACvD,eAAO,MAAM,gBAAgB,kBAAkB,CAAC;AAEhD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,4DAA4D;AAC5D,eAAO,MAAM,aAAa,aAAa,CAAC;AAExC;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED,2EAA2E;AAC3E,eAAO,MAAM,gBAAgB,kCAAkC,CAAC;AAEhE;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,wHACqF,CAAC;AAExH;;;;;;;;;;GAUG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IAEvC,QAAQ,CAAC,QAAQ,EAAE,WAAW,EAAE;IAChC,mEAAmE;IACnE,QAAQ,CAAC,WAAW,EAAE,MAAM;gBAFnB,QAAQ,EAAE,WAAW,EAAE;IAChC,mEAAmE;IAC1D,WAAW,EAAE,MAAM;CAK/B;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,eAAgB,SAAQ,KAAK;IAEtC,QAAQ,CAAC,QAAQ,EAAE,WAAW,EAAE;IAChC,gEAAgE;IAChE,QAAQ,CAAC,WAAW,EAAE,MAAM;gBAFnB,QAAQ,EAAE,WAAW,EAAE;IAChC,gEAAgE;IACvD,WAAW,EAAE,MAAM,EAC5B,KAAK,CAAC,EAAE,OAAO;CAKlB;AAED,wBAAsB,aAAa,CACjC,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,WAAW,EAAE,EACvB,KAAK,EAAE,UAAU,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAC3C,MAAM,EAAE,WAAW,EACnB,QAAQ,CAAC,EAAE,eAAe,EAC1B,MAAM,GAAE,aAAsC,GAC7C,OAAO,CAAC,WAAW,CAAC,CAwBtB;AA0KD,wBAAsB,YAAY,CAAC,OAAO,EAAE;IAC1C,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B,GAAG,OAAO,CAAC,SAAS,CAAC,CAqRrB;AAqHD;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAI1F"}