@skillstate/opencode 2.2.2 → 3.0.1
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 +194 -142
- package/dist/feedback.d.ts +178 -0
- package/dist/feedback.d.ts.map +1 -0
- package/dist/feedback.js +235 -0
- package/dist/feedback.js.map +1 -0
- package/dist/index.d.ts +58 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +48 -3
- package/dist/index.js.map +1 -1
- package/dist/mode.d.ts +97 -0
- package/dist/mode.d.ts.map +1 -0
- package/dist/mode.js +111 -0
- package/dist/mode.js.map +1 -0
- package/dist/opencode-adapter.d.ts +10 -46
- package/dist/opencode-adapter.d.ts.map +1 -1
- package/dist/opencode-adapter.js +1 -64
- package/dist/opencode-adapter.js.map +1 -1
- package/dist/paper-mode.d.ts +421 -0
- package/dist/paper-mode.d.ts.map +1 -0
- package/dist/paper-mode.js +445 -0
- package/dist/paper-mode.js.map +1 -0
- package/dist/plugin.d.ts +218 -76
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +867 -232
- package/dist/plugin.js.map +1 -1
- package/dist/response-sink.d.ts +208 -0
- package/dist/response-sink.d.ts.map +1 -0
- package/dist/response-sink.js +243 -0
- package/dist/response-sink.js.map +1 -0
- package/dist/runtime.d.ts +203 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +332 -0
- package/dist/runtime.js.map +1 -0
- package/dist/session-registry.d.ts +148 -0
- package/dist/session-registry.d.ts.map +1 -0
- package/dist/session-registry.js +236 -0
- package/dist/session-registry.js.map +1 -0
- package/dist/spec-loader.d.ts +81 -0
- package/dist/spec-loader.d.ts.map +1 -0
- package/dist/spec-loader.js +163 -0
- package/dist/spec-loader.js.map +1 -0
- package/dist/state-store.d.ts +126 -0
- package/dist/state-store.d.ts.map +1 -0
- package/dist/state-store.js +186 -0
- package/dist/state-store.js.map +1 -0
- package/dist/step-boundary.d.ts +91 -0
- package/dist/step-boundary.d.ts.map +1 -0
- package/dist/step-boundary.js +109 -0
- package/dist/step-boundary.js.map +1 -0
- package/dist/system-hint.d.ts +129 -0
- package/dist/system-hint.d.ts.map +1 -0
- package/dist/system-hint.js +166 -0
- package/dist/system-hint.js.map +1 -0
- package/dist/tools.d.ts +148 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +350 -0
- package/dist/tools.js.map +1 -0
- package/package.json +3 -2
- package/dist/plugin-types.d.ts +0 -71
- package/dist/plugin-types.d.ts.map +0 -1
- package/dist/plugin-types.js +0 -8
- package/dist/plugin-types.js.map +0 -1
package/dist/plugin.d.ts
CHANGED
|
@@ -1,95 +1,237 @@
|
|
|
1
|
-
import { mergePatch, resolveHostStateForCwd } from '@skillstate/core';
|
|
2
|
-
import type { OpenCodeMessage, SkillStatePlugin } from './plugin-types.js';
|
|
3
|
-
export * from './plugin-types.js';
|
|
4
1
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* `
|
|
2
|
+
* `@skillstate/opencode` — the OpenCode **v2** plugin.
|
|
3
|
+
*
|
|
4
|
+
* ── What this replaces ───────────────────────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* The v1 integration rewrote the conversation on every model request. It
|
|
7
|
+
* kept the system messages and the last three non-system messages, dropped
|
|
8
|
+
* everything else from `output.messages`, and appended a synthetic
|
|
9
|
+
* `role: "user"` message containing the raw state JSON. The reported
|
|
10
|
+
* failure was that the agent stopped doing the user's task and started
|
|
11
|
+
* emitting state JSON instead.
|
|
12
|
+
*
|
|
13
|
+
* Both halves of that were destructive, and neither was a model quirk:
|
|
14
|
+
*
|
|
15
|
+
* 1. The injected message landed LAST, so for the model it was the current
|
|
16
|
+
* instruction — it displaced the user's actual request.
|
|
17
|
+
* 2. `slice(-3)` deleted the task statement, the tool results and the
|
|
18
|
+
* errors the agent had just been handed. It was reasoning about work it
|
|
19
|
+
* could no longer see.
|
|
20
|
+
*
|
|
21
|
+
* The MCP server made it worse: `spec.get` returned a procedural spec whose
|
|
22
|
+
* default was `INTERCODE_CTF_SPEC`, whose instructions read "You are an
|
|
23
|
+
* autonomous CTF agent ... hidden flag somewhere on its filesystem". A
|
|
24
|
+
* model told to look for a flag looks for a flag. (Fixed: the default is now
|
|
25
|
+
* the neutral `GENERIC_PROCEDURE_SPEC`, and its instructions describe the
|
|
26
|
+
* storage format instead of prescribing a way of working.)
|
|
27
|
+
*
|
|
28
|
+
* ── The v2 design ────────────────────────────────────────────────────────
|
|
29
|
+
*
|
|
30
|
+
* Two modes, each enforced by a test. They are different contracts with the
|
|
31
|
+
* model, not variants of one behaviour:
|
|
32
|
+
*
|
|
33
|
+
* - **`notes` (default).** Contribute one additive, bounded fragment to
|
|
34
|
+
* `event.system` and leave the transcript alone. The agent sees its own
|
|
35
|
+
* history the way the host intends, and the saved notes ride alongside it.
|
|
36
|
+
* This is the mode that fixed the v1 failure, and it is the default for
|
|
37
|
+
* exactly that reason.
|
|
38
|
+
* - **`paper` (opt-in).** Replace the model-facing context with
|
|
39
|
+
* Aₜ = (P, Σₜ, Oₜ) — the paper's Appendix A.4 prompt, byte-verbatim — and
|
|
40
|
+
* apply the `state_patch` the model emits in response. This is the paper's
|
|
41
|
+
* specification, and it is a real behavioural change: the model stops
|
|
42
|
+
* seeing its transcript, because §3.2 discards the reasoning trace by
|
|
43
|
+
* construction. Select it with `mode: "paper"` in the project's
|
|
44
|
+
* `skillstate.json` or `SKILLSTATE_MODE=paper`; see `mode.ts`.
|
|
45
|
+
*
|
|
46
|
+
* The default is `notes` and must stay that way: a default that discards the
|
|
47
|
+
* user's task is the v1 bug under a new name.
|
|
48
|
+
*
|
|
49
|
+
* Three rules, each enforced by a test:
|
|
50
|
+
*
|
|
51
|
+
* - **Notes mode never mutates `event.messages`.** The plugin contributes
|
|
52
|
+
* one additive fragment to `event.system` and leaves the transcript alone.
|
|
53
|
+
* See `tests/opencode/context-integrity.test.ts`.
|
|
54
|
+
* - **Never inject behavioural instructions in notes mode.** The system
|
|
55
|
+
* fragment describes what the notes are and when to use them; it contains
|
|
56
|
+
* no "you must", no "always", and no output format. See
|
|
57
|
+
* `system-hint.ts`.
|
|
58
|
+
* - **Inert until used.** A project with no state file gets no system
|
|
59
|
+
* fragment at all and behaves exactly like vanilla OpenCode. No files are
|
|
60
|
+
* created by loading the plugin.
|
|
61
|
+
*
|
|
62
|
+
* ── Native tools AND the MCP server, on purpose ──────────────────────────
|
|
63
|
+
*
|
|
64
|
+
* This package does not replace `@skillstate/mcp`; it sits beside it.
|
|
65
|
+
*
|
|
66
|
+
* - The native tools ({@link registerTools}) are the fast path inside
|
|
67
|
+
* opencode: a typed schema, structured output, no JSON-RPC round-trip and
|
|
68
|
+
* no untyped text result.
|
|
69
|
+
* - The MCP server is the portable path. It is what every other
|
|
70
|
+
* MCP-capable host reads, and the only way to reach this state from a
|
|
71
|
+
* client that is not opencode.
|
|
72
|
+
*
|
|
73
|
+
* Both address the same `<project>/.skillstate/skillstate.json`, so they
|
|
74
|
+
* cannot disagree about what is saved. `skillstate init` registers both.
|
|
75
|
+
*
|
|
76
|
+
* The reason v1 needed the MCP server is gone: an opencode v1 plugin could
|
|
77
|
+
* not contribute first-class tools at all.
|
|
78
|
+
*
|
|
79
|
+
* Load it from `opencode.json(c)`:
|
|
80
|
+
*
|
|
81
|
+
* ```json
|
|
82
|
+
* { "plugins": ["@skillstate/opencode"] }
|
|
83
|
+
* ```
|
|
12
84
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
export interface SkillStatePluginOptions {
|
|
17
|
-
/** Non-system messages kept in the prompt (default 3). */
|
|
18
|
-
maxHistoryMessages?: number;
|
|
19
|
-
}
|
|
85
|
+
import { Plugin } from '@opencode/plugin';
|
|
86
|
+
/** Stable plugin id — scopes plugin storage and identifies it in `/api/plugin`. */
|
|
87
|
+
export declare const PLUGIN_ID = "skillstate";
|
|
20
88
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
89
|
+
* The action carried forward when a turn produced no usable patch.
|
|
90
|
+
*
|
|
91
|
+
* NOT `__invalid_patch__`, though the paper names that sentinel at §5.1 line
|
|
92
|
+
* 9. It is a return value there — what the step function hands back to signal
|
|
93
|
+
* that Σ is unchanged — and forwarding it into the prompt as the next action
|
|
94
|
+
* is meaningless to a model: it is a name, not a request. The retry instruction
|
|
95
|
+
* the model actually needs already rides in Oₜ through the feedback queue, so
|
|
96
|
+
* this only has to say "keep going", and the queue says why.
|
|
25
97
|
*/
|
|
26
|
-
export declare
|
|
98
|
+
export declare const CONTINUE_ACTION = "continue";
|
|
27
99
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* processes never interleave state writes).
|
|
100
|
+
* Record every event type the plugin actually receives.
|
|
101
|
+
*
|
|
102
|
+
* @non-paper diagnostics, same file. The advance is triggered by one event
|
|
103
|
+
* type and one, and a trigger that never fires is indistinguishable from one
|
|
104
|
+
* that is wired wrong — so the arrival counts have to be visible. Cheap, and
|
|
105
|
+
* it would have saved guessing.
|
|
35
106
|
*/
|
|
36
|
-
export declare function
|
|
107
|
+
export declare function recordEvent(path: string | undefined, type: string): void;
|
|
37
108
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
109
|
+
* Whether the host has just executed a tool for this request.
|
|
110
|
+
*
|
|
111
|
+
* A tool result in the transcript is the observable edge of "an action ran".
|
|
112
|
+
* There is no event that says so in a shape this plugin can trust — and an
|
|
113
|
+
* event the host does not wait for is what caused the read-after-write race
|
|
114
|
+
* fixed in `response-sink.ts`, so the transcript is the more reliable of the
|
|
115
|
+
* two here as well as the more available one.
|
|
43
116
|
*/
|
|
44
|
-
|
|
117
|
+
/** Tool-result parts in the newest tool message, counted. */
|
|
118
|
+
export declare function countToolResults(messages: ReadonlyArray<{
|
|
119
|
+
role: string;
|
|
120
|
+
content: unknown;
|
|
121
|
+
}>): number;
|
|
45
122
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
123
|
+
* Append what the host actually handed us to a file, for diagnosis.
|
|
124
|
+
*
|
|
125
|
+
* @non-paper diagnostics. Enabled by `SKILLSTATE_DEBUG_PROMPT=<path>`.
|
|
126
|
+
*
|
|
127
|
+
* This exists because of a bug that was invisible from the inside for a
|
|
128
|
+
* long time. The model would run a tool, get the answer, and never record
|
|
129
|
+
* it — which looks exactly like a model refusing to cooperate, and sent the
|
|
130
|
+
* search through prompt slots, model choice and spec wording. The cause was
|
|
131
|
+
* the SHAPE: OpenCode v2 delivers a tool result as
|
|
132
|
+
* `{ type: 'tool-result', result: { value } }`, so a reader that only knew
|
|
133
|
+
* `{ type: 'text', text }` made Oₜ permanently empty without ever throwing.
|
|
134
|
+
*
|
|
135
|
+
* The dump records the part types alongside the extracted text, so that
|
|
136
|
+
* class of failure is visible on sight: a `tool-result` in the list next to
|
|
137
|
+
* an empty `observation` says the reader, not the model, is at fault.
|
|
138
|
+
*
|
|
139
|
+
* Append-only so a session's turns accumulate in order, and every failure
|
|
140
|
+
* is swallowed — diagnostics must never break the agent loop.
|
|
51
141
|
*/
|
|
52
|
-
export declare function
|
|
142
|
+
export declare function dumpPromptShape(path: string | undefined, messages: ReadonlyArray<{
|
|
143
|
+
role: string;
|
|
144
|
+
content: unknown;
|
|
145
|
+
}>, state?: Record<string, unknown>): void;
|
|
53
146
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
147
|
+
* One line per turn of the anti-drift diagnostic.
|
|
148
|
+
*
|
|
149
|
+
* The drift notice has a claim attached to it — "the model drifts, the notice
|
|
150
|
+
* brings it back" — and neither half can be checked from inside the process.
|
|
151
|
+
* A notice in a prompt is not an observation of a model: the fragment may be
|
|
152
|
+
* built correctly and the model may ignore it, and the two look identical
|
|
153
|
+
* from the code's side. Worse, both look identical from the *outside* too,
|
|
154
|
+
* which is what made the earlier `tool-result` bug so expensive to find.
|
|
155
|
+
*
|
|
156
|
+
* So each line carries the evidence that distinguishes them:
|
|
157
|
+
*
|
|
158
|
+
* - `notice` — was the drift sentence in the fragment that went out this turn;
|
|
159
|
+
* - `writes` — how many times the state file had changed when it went out, so
|
|
160
|
+
* a notice that repeats forever is visible as a flat counter;
|
|
161
|
+
* - `fragments` — how many turns had passed without a change.
|
|
162
|
+
*
|
|
163
|
+
* That is enough to say "the notice fired and the state moved afterwards" or
|
|
164
|
+
* "the notice fired and nothing happened", which is the only claim worth
|
|
165
|
+
* making about it.
|
|
166
|
+
*
|
|
167
|
+
* @non-paper diagnostics. Enabled by `SKILLSTATE_DEBUG_DRIFT=<path>`.
|
|
168
|
+
* Separate from {@link dumpPromptShape} because it answers a different
|
|
169
|
+
* question: that one asks what the host sent, this one asks what the model
|
|
170
|
+
* did about what we sent.
|
|
64
171
|
*/
|
|
65
|
-
export declare function
|
|
66
|
-
|
|
67
|
-
|
|
172
|
+
export declare function dumpDrift(path: string | undefined, record: {
|
|
173
|
+
readonly scope: string;
|
|
174
|
+
readonly turns: number;
|
|
175
|
+
readonly notice: boolean;
|
|
176
|
+
readonly writes: number;
|
|
177
|
+
}): void;
|
|
68
178
|
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
179
|
+
* One line per step, for the run that answered correctly while its state
|
|
180
|
+
* under-reported the work. Enabled by `SKILLSTATE_DEBUG_STEPS=<path>`.
|
|
181
|
+
*
|
|
182
|
+
* The 30-file measurement produced the most confusing result in this project's
|
|
183
|
+
* history: paper mode answered correctly, its state ended 25/30, and the
|
|
184
|
+
* control's ended 30/30 complete. Twenty-five patches were emitted and all
|
|
185
|
+
* twenty-five landed, so no patch was lost — the model read every file and
|
|
186
|
+
* declined to patch the last five, while the loop kept driving it. From the
|
|
187
|
+
* outside that is indistinguishable from the loop stopping, from the model
|
|
188
|
+
* silently abandoning the protocol, and from the ceiling being hit.
|
|
189
|
+
*
|
|
190
|
+
* So this prints the thing that tells those apart: at every step, whether a
|
|
191
|
+
* patch was applied, what the state looked like, and whether the driver asked
|
|
192
|
+
* again. Every previous wrong guess in this file came from reasoning about the
|
|
193
|
+
* loop instead of watching it.
|
|
72
194
|
*/
|
|
73
|
-
export declare function
|
|
195
|
+
export declare function dumpStepTrace(path: string | undefined, record: {
|
|
196
|
+
readonly sessionID: string;
|
|
197
|
+
readonly step: number;
|
|
198
|
+
readonly attempt: number;
|
|
199
|
+
readonly applied: boolean;
|
|
200
|
+
readonly done: number;
|
|
201
|
+
readonly total: number | null;
|
|
202
|
+
readonly drove: boolean;
|
|
203
|
+
readonly note: string;
|
|
204
|
+
}): void;
|
|
74
205
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
206
|
+
* The step ceiling, or `undefined` to keep the default.
|
|
207
|
+
*
|
|
208
|
+
* A malformed value is ignored rather than thrown on or silently clamped: a
|
|
209
|
+
* typo in an environment variable should leave the ceiling where the code says
|
|
210
|
+
* it is, not quietly become some other number that then gets measured.
|
|
79
211
|
*/
|
|
80
|
-
export declare function
|
|
81
|
-
/** Test-only: forget every registered session→parent edge. */
|
|
82
|
-
export declare function resetSessionParents(): void;
|
|
212
|
+
export declare function maxStepsFromEnv(): number | undefined;
|
|
83
213
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
214
|
+
* The plugin definition.
|
|
215
|
+
*
|
|
216
|
+
* `setup` wires the session registry, the project state store, the native
|
|
217
|
+
* tools, the mode resolver and the one `context` hook, then returns a cleanup
|
|
218
|
+
* function.
|
|
219
|
+
*
|
|
220
|
+
* - a {@link SessionRegistry}, fed by the server event stream, so a
|
|
221
|
+
* sub-agent session is recognised and given its own state file;
|
|
222
|
+
* - a {@link ProjectStateStore} rooted at the plugin's own project
|
|
223
|
+
* location, so two checkouts served by one OpenCode server never share
|
|
224
|
+
* state;
|
|
225
|
+
* - a {@link SpecResolver} for paper mode's P, so a project that ships its
|
|
226
|
+
* own `skill-spec.json` gets its own procedure;
|
|
227
|
+
* - a {@link PaperStateSink}, in paper mode only, which applies the
|
|
228
|
+
* `state_patch` the model emits;
|
|
229
|
+
* - native tools plus a single `context` hook whose body depends on the mode.
|
|
230
|
+
*
|
|
231
|
+
* The event subscription is the only resource the plugin owns, so the
|
|
232
|
+
* returned cleanup aborts it. Hook and tool registrations are disposed by
|
|
233
|
+
* OpenCode when the plugin unloads.
|
|
93
234
|
*/
|
|
94
|
-
export declare
|
|
235
|
+
export declare const SkillStatePlugin: Plugin.Plugin;
|
|
236
|
+
export default SkillStatePlugin;
|
|
95
237
|
//# sourceMappingURL=plugin.d.ts.map
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmFG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAkB1C,mFAAmF;AACnF,eAAO,MAAM,SAAS,eAAe,CAAC;AAEtC;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,aAAa,CAAC;AAqJ1C;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAOxE;AAED;;;;;;;;GAQG;AACH,6DAA6D;AAC7D,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,GAAG,MAAM,CAUpG;AAUD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,QAAQ,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,EAC3D,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,IAAI,CA4BN;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,MAAM,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC5G,IAAI,CAON;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,MAAM,EAAE;IACN,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,GACA,IAAI,CAON;AAED;;;;;;GAMG;AAEH,wBAAgB,eAAe,IAAI,MAAM,GAAG,SAAS,CAQpD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,gBAAgB,eAgf3B,CAAC;eAEY,gBAAgB"}
|