@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.
Files changed (62) hide show
  1. package/README.md +194 -142
  2. package/dist/feedback.d.ts +178 -0
  3. package/dist/feedback.d.ts.map +1 -0
  4. package/dist/feedback.js +235 -0
  5. package/dist/feedback.js.map +1 -0
  6. package/dist/index.d.ts +58 -2
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +48 -3
  9. package/dist/index.js.map +1 -1
  10. package/dist/mode.d.ts +97 -0
  11. package/dist/mode.d.ts.map +1 -0
  12. package/dist/mode.js +111 -0
  13. package/dist/mode.js.map +1 -0
  14. package/dist/opencode-adapter.d.ts +10 -46
  15. package/dist/opencode-adapter.d.ts.map +1 -1
  16. package/dist/opencode-adapter.js +1 -64
  17. package/dist/opencode-adapter.js.map +1 -1
  18. package/dist/paper-mode.d.ts +421 -0
  19. package/dist/paper-mode.d.ts.map +1 -0
  20. package/dist/paper-mode.js +445 -0
  21. package/dist/paper-mode.js.map +1 -0
  22. package/dist/plugin.d.ts +218 -76
  23. package/dist/plugin.d.ts.map +1 -1
  24. package/dist/plugin.js +867 -232
  25. package/dist/plugin.js.map +1 -1
  26. package/dist/response-sink.d.ts +208 -0
  27. package/dist/response-sink.d.ts.map +1 -0
  28. package/dist/response-sink.js +243 -0
  29. package/dist/response-sink.js.map +1 -0
  30. package/dist/runtime.d.ts +203 -0
  31. package/dist/runtime.d.ts.map +1 -0
  32. package/dist/runtime.js +332 -0
  33. package/dist/runtime.js.map +1 -0
  34. package/dist/session-registry.d.ts +148 -0
  35. package/dist/session-registry.d.ts.map +1 -0
  36. package/dist/session-registry.js +236 -0
  37. package/dist/session-registry.js.map +1 -0
  38. package/dist/spec-loader.d.ts +81 -0
  39. package/dist/spec-loader.d.ts.map +1 -0
  40. package/dist/spec-loader.js +163 -0
  41. package/dist/spec-loader.js.map +1 -0
  42. package/dist/state-store.d.ts +126 -0
  43. package/dist/state-store.d.ts.map +1 -0
  44. package/dist/state-store.js +186 -0
  45. package/dist/state-store.js.map +1 -0
  46. package/dist/step-boundary.d.ts +91 -0
  47. package/dist/step-boundary.d.ts.map +1 -0
  48. package/dist/step-boundary.js +109 -0
  49. package/dist/step-boundary.js.map +1 -0
  50. package/dist/system-hint.d.ts +129 -0
  51. package/dist/system-hint.d.ts.map +1 -0
  52. package/dist/system-hint.js +166 -0
  53. package/dist/system-hint.js.map +1 -0
  54. package/dist/tools.d.ts +148 -0
  55. package/dist/tools.d.ts.map +1 -0
  56. package/dist/tools.js +350 -0
  57. package/dist/tools.js.map +1 -0
  58. package/package.json +3 -2
  59. package/dist/plugin-types.d.ts +0 -71
  60. package/dist/plugin-types.d.ts.map +0 -1
  61. package/dist/plugin-types.js +0 -8
  62. package/dist/plugin-types.js.map +0 -1
@@ -0,0 +1,203 @@
1
+ /**
2
+ * The runtime that owns the loop — the half of Algorithm 1 a plugin otherwise
3
+ * has to ask the model to do for it.
4
+ *
5
+ * ── What was missing, and why it was not a model problem ──────────────────
6
+ *
7
+ * A.4 tells the model to answer with `{state_patch, action}` and says nothing
8
+ * about who runs `action`, because in the paper a runtime does: Algorithm 1
9
+ * runs aₜ and feeds Oₜ₊₁ back. Everything here existed to compensate for that
10
+ * gap with prompts — a note in the system slot, a rule in the spec, a notice
11
+ * about drift. Measured on two models, in three placements: the run still
12
+ * ended after the first or second file.
13
+ *
14
+ * And the model was right to stop. It was asked for a patch, it produced a
15
+ * correct patch, and the instruction had been satisfied completely. Every
16
+ * prompt added to compensate made the ask larger without ever supplying the
17
+ * missing executor. The defect was that the step loop belonged to the model at
18
+ * all; it belongs here.
19
+ *
20
+ * ── The mechanism ────────────────────────────────────────────────────────
21
+ *
22
+ * When a patch is applied and its `action` is not terminal, this calls
23
+ * `session.prompt` to request the next step. The context hook then rebuilds
24
+ * (P, Σₜ, Oₜ) from the new state, the model reads the observation, and the
25
+ * cycle continues without the model having volunteered anything.
26
+ *
27
+ * The plugin still cannot invoke a tool on the model's behalf — the host owns
28
+ * that, and that is a real limit. What it can own is step advancement, and
29
+ * that is exactly the part the loop was missing. Aₜ is a request, and the
30
+ * model performs it; who decides that step t+1 happens at all is the
31
+ * runtime's job, and it is this.
32
+ */
33
+ /**
34
+ * Default ceiling on runtime-driven steps before it stops asking.
35
+ *
36
+ * 100, not a number chosen here. §10.1's reference `Run` signature reads
37
+ * `Run(P, Σ0, O0, llm, execute, isDone, maxSteps = 100)`, and this value was 64
38
+ * until the paper was re-read for that line — with a comment claiming the paper
39
+ * did not give a number. It does.
40
+ *
41
+ * The cost of getting this wrong was measured rather than argued: at 64 a
42
+ * thirty-file task stopped at 25/30 having answered correctly, and the ceiling
43
+ * looked like the binding constraint when the real cause was that narration
44
+ * turns were each spending a step. Fixing the retry loop changed the pace;
45
+ * matching the paper's ceiling is what makes the two agree.
46
+ */
47
+ export declare const DEFAULT_MAX_STEPS = 100;
48
+ /**
49
+ * Why {@link RuntimeDriver.advance} declined to spend a step.
50
+ *
51
+ * `'terminal'` — the model emitted a terminal action: a real ending.
52
+ * `'max_steps'` — §10.1's ceiling arrived. NOT completion.
53
+ * `'no_action'` — the turn carried no action to advance on.
54
+ * `'host_refused'` — the host would not start a turn.
55
+ */
56
+ export type StopReason = 'terminal' | 'max_steps' | 'no_action' | 'host_refused';
57
+ /** What the loop driver needs from its host, injected so it is testable. */
58
+ export interface RuntimeDriverOptions {
59
+ /**
60
+ * Ask the host for another step in `sessionID`. Returns true when the
61
+ * request was accepted, false when the host refused — a refusal is a fact
62
+ * to count, not an error to throw, because it usually means the session has
63
+ * already ended.
64
+ */
65
+ readonly prompt: (sessionID: string, text: string) => Promise<boolean>;
66
+ /** Hard ceiling on runtime-driven steps per session. */
67
+ readonly maxSteps?: number;
68
+ /** §5.1's `k`. Attempts per step are `k + 1`. */
69
+ readonly retries?: number;
70
+ }
71
+ /** One advancement the driver decided to make. */
72
+ export interface RuntimeStep {
73
+ readonly sessionID: string;
74
+ /** The action the model asked for, carried forward verbatim. */
75
+ readonly action: string;
76
+ /** The step number, 1-based, for this session. */
77
+ readonly step: number;
78
+ /** True when this prompt was a §5.1 retry inside the current step. */
79
+ readonly retry: boolean;
80
+ }
81
+ /** §5.1 line 2: `k` retries, so `k + 1` attempts per step. */
82
+ export declare const DEFAULT_VALIDATION_RETRIES = 2;
83
+ /** §5.1 line 10: the sentinel a step returns when every attempt failed. */
84
+ export declare const INVALID_PATCH = "__invalid_patch__";
85
+ export declare class RuntimeDriver {
86
+ #private;
87
+ /** Every advancement made, for diagnostics and tests. */
88
+ readonly advanced: RuntimeStep[];
89
+ /** Every step that exhausted its attempts, for diagnostics and tests. */
90
+ readonly invalidated: string[];
91
+ constructor(options: RuntimeDriverOptions);
92
+ /**
93
+ * How many attempts the current step has spent, and what it should do next.
94
+ *
95
+ * `retry` means the step continues: the loop is asked again, the corrective
96
+ * feedback rides in Oₜ, and the step number does not move. `advance` means
97
+ * the attempts are spent and the next prompt opens step t+1. `done` means
98
+ * the step succeeded and needs no further prompting at all.
99
+ */
100
+ record(sessionID: string, applied: boolean): {
101
+ readonly action: 'retry' | 'advance';
102
+ readonly attempt: number;
103
+ readonly step: number;
104
+ readonly result: typeof INVALID_PATCH | null;
105
+ };
106
+ /**
107
+ * Whether an action ends the procedure.
108
+ *
109
+ * Exported as a function because the test for it is worth more than the
110
+ * call site: getting this wrong in the permissive direction costs tokens
111
+ * forever, and getting it wrong in the strict direction ends a run early,
112
+ * so both directions are pinned.
113
+ */
114
+ static isTerminal(action: string): boolean;
115
+ /**
116
+ * Why {@link RuntimeDriver.advance} last declined to spend a step.
117
+ *
118
+ * Four ways `advance` returns `null` and the caller cannot tell them from the
119
+ * return value: the model emitted a terminal action (a real ending), the host
120
+ * would not start a turn, the turn had no action at all, or the ceiling
121
+ * arrived. The last is the one that lies — a loop stopped at `maxSteps` is
122
+ * indistinguishable from a loop that ran its course.
123
+ *
124
+ * A 90-file run was read as "the model lost track of its running sum at file
125
+ * 78" when the ceiling had arrived at step 100, mid-file-79, with the work
126
+ * still going. Nothing in this driver said so, and the caller discards
127
+ * `advance`'s return value entirely. The honest fix is to make the reason
128
+ * readable, not to guess it back out of a transcript.
129
+ */
130
+ get lastStop(): {
131
+ reason: StopReason;
132
+ sessionID: string;
133
+ steps: number;
134
+ } | undefined;
135
+ /**
136
+ * Advance the loop for one applied patch.
137
+ *
138
+ * Returns the step it took, or `null` when it deliberately did not — a
139
+ * terminal action, an already-ended turn, or the ceiling. The ceiling is the
140
+ * important one: a runtime that asks forever against a model that keeps
141
+ * answering "continue" is a runaway, and the cost of that is paid at the
142
+ * provider.
143
+ */
144
+ advance(sessionID: string, action: string | undefined): Promise<RuntimeStep | null>;
145
+ /**
146
+ * What the runtime did at the end of the last step, for Oₜ.
147
+ *
148
+ * A REPORT, not an order, and the distinction is the whole of this method.
149
+ *
150
+ * The model's own action used to ride here. §2 forbids it — "the agent
151
+ * receives only Oₜ — never prior observations or actions" — and with it in
152
+ * place the model obeyed a stored order rather than choosing: 54 `read` calls
153
+ * for thirty files, and one grep used three times as a side errand, where a
154
+ * control with no step driver read one file, ran ONE grep and finished in six
155
+ * calls. The order was its own past action, so it never looked for a better
156
+ * way than the one already written down.
157
+ *
158
+ * Simply removing it was also wrong, and measurably so. The runtime re-prompts
159
+ * when a turn produced a patch but no tool call, and with nothing in Oₜ
160
+ * saying why, the model looped instead of converging: 98 text blocks against
161
+ * a baseline of 43, and 7.9M prompt tokens against 1.6M. It read 41 files
162
+ * rather than 53, so the re-reads did halve — and the run never finished.
163
+ *
164
+ * So the environment says what it did. Factual, not imperative, and the model
165
+ * still chooses its own next action, which is the division the paper draws:
166
+ * `execute(aₜ, Σ_{t+1})` is the runtime's, and choosing aₜ is the model's.
167
+ */
168
+ stepReport(sessionID: string): string | undefined;
169
+ /**
170
+ * Note what the environment executed on this session's behalf, and return how
171
+ * much of it is new.
172
+ *
173
+ * The context hook is the only place the host's own tool results are visible,
174
+ * and it runs on EVERY model request. A tool message that is still the newest
175
+ * one across two requests — which happens whenever the model is re-asked
176
+ * before it has produced a message of its own — would therefore be counted
177
+ * twice, and a report of "2 actions ran" for one `read` is a lie of exactly
178
+ * the kind this whole method exists to stop.
179
+ *
180
+ * So the driver keeps a watermark per session: the id of the last message it
181
+ * counted, and only results after it are new. Idempotent by construction —
182
+ * calling it with the same messages twice is a no-op, which is also what makes
183
+ * it testable without a host.
184
+ */
185
+ noteExecutions(sessionID: string, messages: ReadonlyArray<{
186
+ id: string;
187
+ role: string;
188
+ content: unknown;
189
+ }>): number;
190
+ /**
191
+ * Take the action this session was last asked to perform, if any.
192
+ *
193
+ * Taken exactly once per step, so a prompt built twice for one turn cannot
194
+ * show the model the same continuation twice — the same discipline the
195
+ * feedback queue uses, and for the same reason.
196
+ */
197
+ takeContinuation(sessionID: string): string | undefined;
198
+ /** Forget a session's pending action, on reset or teardown. */
199
+ forget(sessionID: string): void;
200
+ /** Steps taken for a session, for diagnostics. */
201
+ stepsFor(sessionID: string): number;
202
+ }
203
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAKH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,WAAW,GAAG,WAAW,GAAG,cAAc,CAAC;AAEjF,4EAA4E;AAC5E,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACvE,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,iDAAiD;IACjD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,kDAAkD;AAClD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,gEAAgE;IAChE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,8DAA8D;AAC9D,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C,2EAA2E;AAC3E,eAAO,MAAM,aAAa,sBAAsB,CAAC;AAEjD,qBAAa,aAAa;;IA2CxB,yDAAyD;IACzD,QAAQ,CAAC,QAAQ,EAAE,WAAW,EAAE,CAAM;IAGtC,yEAAyE;IACzE,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,CAAM;IAEpC,YAAY,OAAO,EAAE,oBAAoB,EAIxC;IAED;;;;;;;OAOG;IACH,MAAM,CACJ,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,GACf;QACD,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC;QACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,MAAM,EAAE,OAAO,aAAa,GAAG,IAAI,CAAC;KAC9C,CA8BA;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEzC;IAED;;;;;;;;;;;;;;OAcG;IACH,IAAI,QAAQ,IAAI;QAAE,MAAM,EAAE,UAAU,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAEnF;IAaD;;;;;;;;OAQG;IACG,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAgCxF;IAED;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAmBhD;IAED;;;;;;;;;;;;;;;OAeG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,GAAG,MAAM,CAwBjH;IAED;;;;;;OAMG;IACH,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKtD;IAED,+DAA+D;IAC/D,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAE9B;IAED,kDAAkD;IAClD,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAElC;CACF"}
@@ -0,0 +1,332 @@
1
+ /**
2
+ * The runtime that owns the loop — the half of Algorithm 1 a plugin otherwise
3
+ * has to ask the model to do for it.
4
+ *
5
+ * ── What was missing, and why it was not a model problem ──────────────────
6
+ *
7
+ * A.4 tells the model to answer with `{state_patch, action}` and says nothing
8
+ * about who runs `action`, because in the paper a runtime does: Algorithm 1
9
+ * runs aₜ and feeds Oₜ₊₁ back. Everything here existed to compensate for that
10
+ * gap with prompts — a note in the system slot, a rule in the spec, a notice
11
+ * about drift. Measured on two models, in three placements: the run still
12
+ * ended after the first or second file.
13
+ *
14
+ * And the model was right to stop. It was asked for a patch, it produced a
15
+ * correct patch, and the instruction had been satisfied completely. Every
16
+ * prompt added to compensate made the ask larger without ever supplying the
17
+ * missing executor. The defect was that the step loop belonged to the model at
18
+ * all; it belongs here.
19
+ *
20
+ * ── The mechanism ────────────────────────────────────────────────────────
21
+ *
22
+ * When a patch is applied and its `action` is not terminal, this calls
23
+ * `session.prompt` to request the next step. The context hook then rebuilds
24
+ * (P, Σₜ, Oₜ) from the new state, the model reads the observation, and the
25
+ * cycle continues without the model having volunteered anything.
26
+ *
27
+ * The plugin still cannot invoke a tool on the model's behalf — the host owns
28
+ * that, and that is a real limit. What it can own is step advancement, and
29
+ * that is exactly the part the loop was missing. Aₜ is a request, and the
30
+ * model performs it; who decides that step t+1 happens at all is the
31
+ * runtime's job, and it is this.
32
+ */
33
+ /** Actions that mean the model considers the procedure finished. */
34
+ const TERMINAL_ACTIONS = new Set(['done', 'complete', 'completed', 'finished', 'stop', 'end', '']);
35
+ /**
36
+ * Default ceiling on runtime-driven steps before it stops asking.
37
+ *
38
+ * 100, not a number chosen here. §10.1's reference `Run` signature reads
39
+ * `Run(P, Σ0, O0, llm, execute, isDone, maxSteps = 100)`, and this value was 64
40
+ * until the paper was re-read for that line — with a comment claiming the paper
41
+ * did not give a number. It does.
42
+ *
43
+ * The cost of getting this wrong was measured rather than argued: at 64 a
44
+ * thirty-file task stopped at 25/30 having answered correctly, and the ceiling
45
+ * looked like the binding constraint when the real cause was that narration
46
+ * turns were each spending a step. Fixing the retry loop changed the pace;
47
+ * matching the paper's ceiling is what makes the two agree.
48
+ */
49
+ export const DEFAULT_MAX_STEPS = 100;
50
+ /** §5.1 line 2: `k` retries, so `k + 1` attempts per step. */
51
+ export const DEFAULT_VALIDATION_RETRIES = 2;
52
+ /** §5.1 line 10: the sentinel a step returns when every attempt failed. */
53
+ export const INVALID_PATCH = '__invalid_patch__';
54
+ export class RuntimeDriver {
55
+ #prompt;
56
+ #maxSteps;
57
+ #retries;
58
+ #steps = new Map();
59
+ /** The action each session is mid-way through, read by the context hook. */
60
+ #pending = new Map();
61
+ /**
62
+ * Attempts spent on the CURRENT step, per session.
63
+ *
64
+ * §5.1 lines 2–8: a step is up to `k + 1` attempts at the *same* Aₜ, each
65
+ * after the first carrying the reason the last one failed. Only when all of
66
+ * them fail does the step return `__invalid_patch__` and the loop move on.
67
+ *
68
+ * This was missing, and it was not a small omission. Without it every failed
69
+ * attempt became its own step, so the corrective feedback arrived on a
70
+ * *different* Aₜ than the one it was correcting — which is precisely what
71
+ * §7's rollback-retry forbids. Measured consequence: the model narrated
72
+ * instead of patching on about 63% of turns, and because the state advances
73
+ * only on patching turns, the state grew at 37% of the step rate. Thirty
74
+ * files cost about 88 steps against a ceiling of 64, and the run stopped at
75
+ * 25/30 having answered correctly anyway.
76
+ *
77
+ * A narration turn is now an attempt, not a step.
78
+ */
79
+ #attempts = new Map();
80
+ /**
81
+ * Tool results seen since the last report, per session. The environment's own
82
+ * account of what it did — the only account that is not the model's claim.
83
+ */
84
+ #executed = new Map();
85
+ /**
86
+ * The id of the last message counted, per session.
87
+ *
88
+ * The context hook runs on every model request, so without this a tool
89
+ * message that stays newest across two requests is counted twice. See
90
+ * {@link noteExecutions}.
91
+ */
92
+ #counted = new Map();
93
+ /** What {@link record} decided, so `advance` does not re-decide it. */
94
+ #decisions = new Map();
95
+ /** Every advancement made, for diagnostics and tests. */
96
+ advanced = [];
97
+ #stops = new Map();
98
+ #lastStoppedSession = '';
99
+ /** Every step that exhausted its attempts, for diagnostics and tests. */
100
+ invalidated = [];
101
+ constructor(options) {
102
+ this.#prompt = options.prompt;
103
+ this.#maxSteps = options.maxSteps ?? DEFAULT_MAX_STEPS;
104
+ this.#retries = options.retries ?? DEFAULT_VALIDATION_RETRIES;
105
+ }
106
+ /**
107
+ * How many attempts the current step has spent, and what it should do next.
108
+ *
109
+ * `retry` means the step continues: the loop is asked again, the corrective
110
+ * feedback rides in Oₜ, and the step number does not move. `advance` means
111
+ * the attempts are spent and the next prompt opens step t+1. `done` means
112
+ * the step succeeded and needs no further prompting at all.
113
+ */
114
+ record(sessionID, applied) {
115
+ if (applied) {
116
+ // Lines 11-13: the patch is applied and the step still opens the next
117
+ // one, chaining Oₜ into Oₜ₊₁. A success is not the end of the loop —
118
+ // only a terminal action or the ceiling is, and both live in `advance`.
119
+ this.#attempts.delete(sessionID);
120
+ this.#decisions.set(sessionID, 'advance');
121
+ return {
122
+ action: 'advance',
123
+ attempt: 0,
124
+ step: (this.#steps.get(sessionID) ?? 0) + 1,
125
+ result: null,
126
+ };
127
+ }
128
+ const attempt = (this.#attempts.get(sessionID) ?? 0) + 1;
129
+ this.#attempts.set(sessionID, attempt);
130
+ if (attempt > this.#retries) {
131
+ // Line 10: Σ_t is UNCHANGED and never written. The step is spent.
132
+ this.#attempts.delete(sessionID);
133
+ this.invalidated.push(sessionID);
134
+ this.#decisions.set(sessionID, 'advance');
135
+ return {
136
+ action: 'advance',
137
+ attempt,
138
+ step: (this.#steps.get(sessionID) ?? 0) + 1,
139
+ result: INVALID_PATCH,
140
+ };
141
+ }
142
+ this.#decisions.set(sessionID, 'retry');
143
+ return { action: 'retry', attempt, step: this.#steps.get(sessionID) ?? 0, result: null };
144
+ }
145
+ /**
146
+ * Whether an action ends the procedure.
147
+ *
148
+ * Exported as a function because the test for it is worth more than the
149
+ * call site: getting this wrong in the permissive direction costs tokens
150
+ * forever, and getting it wrong in the strict direction ends a run early,
151
+ * so both directions are pinned.
152
+ */
153
+ static isTerminal(action) {
154
+ return TERMINAL_ACTIONS.has(action.trim().toLowerCase());
155
+ }
156
+ /**
157
+ * Why {@link RuntimeDriver.advance} last declined to spend a step.
158
+ *
159
+ * Four ways `advance` returns `null` and the caller cannot tell them from the
160
+ * return value: the model emitted a terminal action (a real ending), the host
161
+ * would not start a turn, the turn had no action at all, or the ceiling
162
+ * arrived. The last is the one that lies — a loop stopped at `maxSteps` is
163
+ * indistinguishable from a loop that ran its course.
164
+ *
165
+ * A 90-file run was read as "the model lost track of its running sum at file
166
+ * 78" when the ceiling had arrived at step 100, mid-file-79, with the work
167
+ * still going. Nothing in this driver said so, and the caller discards
168
+ * `advance`'s return value entirely. The honest fix is to make the reason
169
+ * readable, not to guess it back out of a transcript.
170
+ */
171
+ get lastStop() {
172
+ return this.#stops.get(this.#lastStoppedSession);
173
+ }
174
+ #stop(reason, sessionID, steps) {
175
+ const entry = {
176
+ reason,
177
+ sessionID,
178
+ steps: steps ?? this.#steps.get(sessionID) ?? 0,
179
+ };
180
+ this.#stops.set(sessionID, entry);
181
+ this.#lastStoppedSession = sessionID;
182
+ return null;
183
+ }
184
+ /**
185
+ * Advance the loop for one applied patch.
186
+ *
187
+ * Returns the step it took, or `null` when it deliberately did not — a
188
+ * terminal action, an already-ended turn, or the ceiling. The ceiling is the
189
+ * important one: a runtime that asks forever against a model that keeps
190
+ * answering "continue" is a runaway, and the cost of that is paid at the
191
+ * provider.
192
+ */
193
+ async advance(sessionID, action) {
194
+ if (action === undefined)
195
+ return this.#stop('no_action', sessionID);
196
+ if (RuntimeDriver.isTerminal(action))
197
+ return this.#stop('terminal', sessionID);
198
+ // A retry re-asks within the CURRENT step and must not spend another one.
199
+ // §5.1 counts attempts inside a step, not steps: the whole point of the
200
+ // bounded retry is that the model gets its corrections without the loop
201
+ // moving on, and a retry that consumed a step would be indistinguishable
202
+ // from the behaviour this replaced.
203
+ const decision = this.#decisions.get(sessionID);
204
+ this.#decisions.delete(sessionID);
205
+ const isRetry = decision === 'retry';
206
+ const step = isRetry ? (this.#steps.get(sessionID) ?? 0) : (this.#steps.get(sessionID) ?? 0) + 1;
207
+ if (step > this.#maxSteps)
208
+ return this.#stop('max_steps', sessionID, step - 1);
209
+ // The action is remembered BEFORE the host is asked, because the context
210
+ // hook that follows reads it, and a host that starts the turn quickly must
211
+ // not find an empty slot. The text handed to `session.prompt` is only a
212
+ // wake-up: `applyPaperContext` clears the messages, so the model reads the
213
+ // action from Oₜ instead. See the `continuation` option.
214
+ this.#pending.set(sessionID, action);
215
+ const asked = await this.#prompt(sessionID, action);
216
+ if (!asked) {
217
+ this.#pending.delete(sessionID);
218
+ return this.#stop('host_refused', sessionID);
219
+ }
220
+ this.#steps.set(sessionID, step);
221
+ const record = { sessionID, action, step, retry: isRetry };
222
+ this.advanced.push(record);
223
+ return record;
224
+ }
225
+ /**
226
+ * What the runtime did at the end of the last step, for Oₜ.
227
+ *
228
+ * A REPORT, not an order, and the distinction is the whole of this method.
229
+ *
230
+ * The model's own action used to ride here. §2 forbids it — "the agent
231
+ * receives only Oₜ — never prior observations or actions" — and with it in
232
+ * place the model obeyed a stored order rather than choosing: 54 `read` calls
233
+ * for thirty files, and one grep used three times as a side errand, where a
234
+ * control with no step driver read one file, ran ONE grep and finished in six
235
+ * calls. The order was its own past action, so it never looked for a better
236
+ * way than the one already written down.
237
+ *
238
+ * Simply removing it was also wrong, and measurably so. The runtime re-prompts
239
+ * when a turn produced a patch but no tool call, and with nothing in Oₜ
240
+ * saying why, the model looped instead of converging: 98 text blocks against
241
+ * a baseline of 43, and 7.9M prompt tokens against 1.6M. It read 41 files
242
+ * rather than 53, so the re-reads did halve — and the run never finished.
243
+ *
244
+ * So the environment says what it did. Factual, not imperative, and the model
245
+ * still chooses its own next action, which is the division the paper draws:
246
+ * `execute(aₜ, Σ_{t+1})` is the runtime's, and choosing aₜ is the model's.
247
+ */
248
+ stepReport(sessionID) {
249
+ const step = this.#steps.get(sessionID);
250
+ if (step === undefined)
251
+ return undefined;
252
+ const executed = this.#executed.get(sessionID) ?? 0;
253
+ this.#executed.delete(sessionID);
254
+ // The count is the environment's, and it is read from tool RESULTS the host
255
+ // handed back — not from the action the model proposed. Those are different
256
+ // facts: a model can name an action and never cause it, which is precisely
257
+ // what happened for a week in this project.
258
+ //
259
+ // The sentence used to be unconditional, and it was wrong whenever a model
260
+ // both patched and acted in one turn. On the n=3 run it happened to be
261
+ // right — 45 patches, 58 tool calls, and not one turn containing both — but
262
+ // nothing enforced that, it was a property of the model that day, and a
263
+ // false report in O_t is worse than no report: O_t is the environment's
264
+ // channel and the whole contract is that what arrives there happened.
265
+ return executed === 0
266
+ ? `step ${step} ended; your state patch was applied; nothing was executed.`
267
+ : `step ${step} ended; your state patch was applied; ${executed} action${executed === 1 ? '' : 's'} ran.`;
268
+ }
269
+ /**
270
+ * Note what the environment executed on this session's behalf, and return how
271
+ * much of it is new.
272
+ *
273
+ * The context hook is the only place the host's own tool results are visible,
274
+ * and it runs on EVERY model request. A tool message that is still the newest
275
+ * one across two requests — which happens whenever the model is re-asked
276
+ * before it has produced a message of its own — would therefore be counted
277
+ * twice, and a report of "2 actions ran" for one `read` is a lie of exactly
278
+ * the kind this whole method exists to stop.
279
+ *
280
+ * So the driver keeps a watermark per session: the id of the last message it
281
+ * counted, and only results after it are new. Idempotent by construction —
282
+ * calling it with the same messages twice is a no-op, which is also what makes
283
+ * it testable without a host.
284
+ */
285
+ noteExecutions(sessionID, messages) {
286
+ const seen = this.#counted.get(sessionID);
287
+ // Everything up to and INCLUDING the watermark was already counted. Skipping
288
+ // only the matching id is not enough: a request that carries three new
289
+ // messages would advance the watermark three times, and the next request
290
+ // would re-count the first two.
291
+ const start = seen === undefined ? 0 : messages.findIndex((m) => m.id === seen) + 1;
292
+ let fresh = 0;
293
+ for (const message of messages.slice(start)) {
294
+ if (message.role === 'tool' && Array.isArray(message.content)) {
295
+ fresh += message.content.filter((part) => typeof part === 'object' &&
296
+ part !== null &&
297
+ String(part.type).startsWith('tool-result')).length;
298
+ }
299
+ }
300
+ const last = messages[messages.length - 1];
301
+ // A watermark that is not in this list means the host trimmed the history.
302
+ // Everything present is then new, and `start` is 0, so nothing is lost.
303
+ if (last !== undefined)
304
+ this.#counted.set(sessionID, last.id);
305
+ if (fresh > 0)
306
+ this.#executed.set(sessionID, (this.#executed.get(sessionID) ?? 0) + fresh);
307
+ return fresh;
308
+ }
309
+ /**
310
+ * Take the action this session was last asked to perform, if any.
311
+ *
312
+ * Taken exactly once per step, so a prompt built twice for one turn cannot
313
+ * show the model the same continuation twice — the same discipline the
314
+ * feedback queue uses, and for the same reason.
315
+ */
316
+ takeContinuation(sessionID) {
317
+ const action = this.#pending.get(sessionID);
318
+ if (action === undefined)
319
+ return undefined;
320
+ this.#pending.delete(sessionID);
321
+ return action;
322
+ }
323
+ /** Forget a session's pending action, on reset or teardown. */
324
+ forget(sessionID) {
325
+ this.#pending.delete(sessionID);
326
+ }
327
+ /** Steps taken for a session, for diagnostics. */
328
+ stepsFor(sessionID) {
329
+ return this.#steps.get(sessionID) ?? 0;
330
+ }
331
+ }
332
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,oEAAoE;AACpE,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;AAEnG;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAsCrC,8DAA8D;AAC9D,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC;AAE5C,2EAA2E;AAC3E,MAAM,CAAC,MAAM,aAAa,GAAG,mBAAmB,CAAC;AAEjD,MAAM,OAAO,aAAa;IACf,OAAO,CAAwD;IAC/D,SAAS,CAAS;IAClB,QAAQ,CAAS;IACjB,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC5C,4EAA4E;IACnE,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC9C;;;;;;;;;;;;;;;;;OAiBG;IACM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC/C;;;OAGG;IACM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE/C;;;;;;OAMG;IACM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE9C,uEAAuE;IAC9D,UAAU,GAAG,IAAI,GAAG,EAA+B,CAAC;IAC7D,yDAAyD;IAChD,QAAQ,GAAkB,EAAE,CAAC;IAC7B,MAAM,GAAG,IAAI,GAAG,EAAoE,CAAC;IAC9F,mBAAmB,GAAG,EAAE,CAAC;IACzB,yEAAyE;IAChE,WAAW,GAAa,EAAE,CAAC;IAEpC,YAAY,OAA6B;QACvC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;QAC9B,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,QAAQ,IAAI,iBAAiB,CAAC;QACvD,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,IAAI,0BAA0B,CAAC;IAChE,CAAC;IAED;;;;;;;OAOG;IACH,MAAM,CACJ,SAAiB,EACjB,OAAgB;QAOhB,IAAI,OAAO,EAAE,CAAC;YACZ,sEAAsE;YACtE,qEAAqE;YACrE,wEAAwE;YACxE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACjC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YAC1C,OAAO;gBACL,MAAM,EAAE,SAAS;gBACjB,OAAO,EAAE,CAAC;gBACV,IAAI,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC;gBAC3C,MAAM,EAAE,IAAI;aACb,CAAC;QACJ,CAAC;QACD,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACzD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACvC,IAAI,OAAO,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC5B,kEAAkE;YAClE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACjC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACjC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YAC1C,OAAO;gBACL,MAAM,EAAE,SAAS;gBACjB,OAAO;gBACP,IAAI,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC;gBAC3C,MAAM,EAAE,aAAa;aACtB,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACxC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC3F,CAAC;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,UAAU,CAAC,MAAc;QAC9B,OAAO,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACH,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IACnD,CAAC;IAED,KAAK,CAAC,MAAkB,EAAE,SAAiB,EAAE,KAAc;QACzD,MAAM,KAAK,GAAG;YACZ,MAAM;YACN,SAAS;YACT,KAAK,EAAE,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC;SAChD,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QAClC,IAAI,CAAC,mBAAmB,GAAG,SAAS,CAAC;QACrC,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,OAAO,CAAC,SAAiB,EAAE,MAA0B;QACzD,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;QACpE,IAAI,aAAa,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QAE/E,0EAA0E;QAC1E,wEAAwE;QACxE,wEAAwE;QACxE,yEAAyE;QACzE,oCAAoC;QACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAChD,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAClC,MAAM,OAAO,GAAG,QAAQ,KAAK,OAAO,CAAC;QACrC,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACjG,IAAI,IAAI,GAAG,IAAI,CAAC,SAAS;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,SAAS,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC;QAE/E,yEAAyE;QACzE,2EAA2E;QAC3E,wEAAwE;QACxE,2EAA2E;QAC3E,yDAAyD;QACzD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAErC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACpD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAChC,OAAO,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;QAC/C,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACjC,MAAM,MAAM,GAAgB,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;QACxE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3B,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,UAAU,CAAC,SAAiB;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACxC,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACpD,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACjC,4EAA4E;QAC5E,4EAA4E;QAC5E,2EAA2E;QAC3E,4CAA4C;QAC5C,EAAE;QACF,2EAA2E;QAC3E,uEAAuE;QACvE,4EAA4E;QAC5E,wEAAwE;QACxE,wEAAwE;QACxE,sEAAsE;QACtE,OAAO,QAAQ,KAAK,CAAC;YACnB,CAAC,CAAC,QAAQ,IAAI,6DAA6D;YAC3E,CAAC,CAAC,QAAQ,IAAI,yCAAyC,QAAQ,UAAU,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;IAC9G,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACH,cAAc,CAAC,SAAiB,EAAE,QAAuE;QACvG,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC1C,6EAA6E;QAC7E,uEAAuE;QACvE,yEAAyE;QACzE,gCAAgC;QAChC,MAAM,KAAK,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,GAAG,CAAC,CAAC;QACpF,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5C,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC9D,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,CAC7B,CAAC,IAAI,EAAE,EAAE,CACP,OAAO,IAAI,KAAK,QAAQ;oBACxB,IAAI,KAAK,IAAI;oBACb,MAAM,CAAE,IAA2B,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC,CACtE,CAAC,MAAM,CAAC;YACX,CAAC;QACH,CAAC;QACD,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC3C,2EAA2E;QAC3E,wEAAwE;QACxE,IAAI,IAAI,KAAK,SAAS;YAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9D,IAAI,KAAK,GAAG,CAAC;YAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;QAC3F,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;;OAMG;IACH,gBAAgB,CAAC,SAAiB;QAChC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC5C,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QAC3C,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAChC,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,+DAA+D;IAC/D,MAAM,CAAC,SAAiB;QACtB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;IAED,kDAAkD;IAClD,QAAQ,CAAC,SAAiB;QACxB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;CACF"}