orizu 0.5.26 → 0.5.28
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 +19 -0
- package/dist/artifacts-git-runtime.d.ts +34 -0
- package/dist/artifacts-git-runtime.d.ts.map +1 -0
- package/dist/artifacts-git-runtime.js +312 -0
- package/dist/cloudflare-artifacts-git-remote.d.ts +5 -0
- package/dist/cloudflare-artifacts-git-remote.d.ts.map +1 -0
- package/dist/cloudflare-artifacts-git-remote.js +21 -0
- package/dist/dataset-transfer-download.d.ts +2 -0
- package/dist/dataset-transfer-download.d.ts.map +1 -0
- package/dist/dataset-transfer-download.js +66 -0
- package/dist/dataset-transfer-policy.d.ts +2 -0
- package/dist/dataset-transfer-policy.d.ts.map +1 -0
- package/dist/dataset-transfer-policy.js +8 -0
- package/dist/dataset-transfer-upload.d.ts +42 -0
- package/dist/dataset-transfer-upload.d.ts.map +1 -0
- package/dist/dataset-transfer-upload.js +110 -0
- package/dist/git-credential.d.ts.map +1 -1
- package/dist/git-credential.js +1 -1
- package/dist/git-recovery-environment.d.ts +1 -0
- package/dist/git-recovery-environment.d.ts.map +1 -1
- package/dist/git-recovery-environment.js +1 -1
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +44 -11
- package/dist/hosted-boot.d.ts +75 -2
- package/dist/hosted-boot.d.ts.map +1 -1
- package/dist/hosted-boot.js +585 -102
- package/dist/hosted-bootstrap.d.ts +2 -0
- package/dist/hosted-bootstrap.d.ts.map +1 -1
- package/dist/hosted-bootstrap.js +12 -3
- package/dist/hosted-harness-opencode.d.ts +8 -9
- package/dist/hosted-harness-opencode.d.ts.map +1 -1
- package/dist/hosted-harness-opencode.js +126 -24
- package/dist/hosted-harness.d.ts +3 -0
- package/dist/hosted-harness.d.ts.map +1 -1
- package/dist/hosted-harness.js +4 -1
- package/dist/hosted-harvest.d.ts +30 -9
- package/dist/hosted-harvest.d.ts.map +1 -1
- package/dist/hosted-harvest.js +222 -28
- package/dist/hosted-loop-lifecycle.d.ts +15 -3
- package/dist/hosted-loop-lifecycle.d.ts.map +1 -1
- package/dist/hosted-loop-lifecycle.js +6 -4
- package/dist/hosted-loop.d.ts +47 -7
- package/dist/hosted-loop.d.ts.map +1 -1
- package/dist/hosted-loop.js +310 -64
- package/dist/hosted-question-limits.d.ts +7 -0
- package/dist/hosted-question-limits.d.ts.map +1 -0
- package/dist/hosted-question-limits.js +6 -0
- package/dist/hosted-question.d.ts +27 -0
- package/dist/hosted-question.d.ts.map +1 -0
- package/dist/hosted-question.js +118 -0
- package/dist/hosted-run-event-sink.d.ts +10 -5
- package/dist/hosted-run-event-sink.d.ts.map +1 -1
- package/dist/hosted-run-event-sink.js +12 -8
- package/dist/hosted-runtime-assets.d.ts +16 -7
- package/dist/hosted-runtime-assets.d.ts.map +1 -1
- package/dist/hosted-runtime-assets.js +40 -12
- package/dist/hosted-session-cli.d.ts.map +1 -1
- package/dist/hosted-session-cli.js +10 -19
- package/dist/http.d.ts +1 -1
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +10 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +113 -92
- package/dist/instruction-set-gepa-launch.d.ts +1 -1
- package/dist/instruction-set-gepa-launch.d.ts.map +1 -1
- package/dist/instruction-set-gepa-launch.js +5 -3
- package/dist/instruction-set-loader.d.ts.map +1 -1
- package/dist/instruction-set-loader.js +63 -2
- package/dist/instruction-set-lock/index.d.ts +51 -0
- package/dist/instruction-set-lock/index.d.ts.map +1 -0
- package/dist/instruction-set-lock/index.js +459 -0
- package/dist/instruction-set-lock/schema.v1.d.ts +136 -0
- package/dist/instruction-set-lock/schema.v1.d.ts.map +1 -0
- package/dist/instruction-set-lock/schema.v1.js +155 -0
- package/dist/instruction-set-provenance.d.ts +26 -0
- package/dist/instruction-set-provenance.d.ts.map +1 -0
- package/dist/instruction-set-provenance.js +68 -0
- package/dist/instruction-set-sync/helpers.d.ts +22 -0
- package/dist/instruction-set-sync/helpers.d.ts.map +1 -0
- package/dist/instruction-set-sync/helpers.js +677 -0
- package/dist/instruction-set-sync/index.d.ts +60 -0
- package/dist/instruction-set-sync/index.d.ts.map +1 -0
- package/dist/instruction-set-sync/index.js +689 -0
- package/dist/instruction-set-update/index.d.ts +42 -0
- package/dist/instruction-set-update/index.d.ts.map +1 -0
- package/dist/instruction-set-update/index.js +445 -0
- package/dist/instruction-set-verify/index.d.ts +27 -0
- package/dist/instruction-set-verify/index.d.ts.map +1 -0
- package/dist/instruction-set-verify/index.js +865 -0
- package/dist/instruction-sets-cli.d.ts +1 -4
- package/dist/instruction-sets-cli.d.ts.map +1 -1
- package/dist/instruction-sets-cli.js +220 -109
- package/dist/model-config-fields.d.ts +35 -0
- package/dist/model-config-fields.d.ts.map +1 -0
- package/dist/model-config-fields.js +28 -0
- package/dist/optimizations-list-cli.d.ts.map +1 -1
- package/dist/optimizations-list-cli.js +7 -4
- package/dist/score-format.d.ts +2 -0
- package/dist/score-format.d.ts.map +1 -0
- package/dist/score-format.js +18 -0
- package/dist/workbench-repo-contract.d.ts +65 -0
- package/dist/workbench-repo-contract.d.ts.map +1 -0
- package/dist/workbench-repo-contract.js +250 -0
- package/dist/workspace-bootstrap-job.d.ts +45 -0
- package/dist/workspace-bootstrap-job.d.ts.map +1 -0
- package/dist/workspace-bootstrap-job.js +431 -0
- package/dist/workspace-bootstrap-seed.d.ts +74 -0
- package/dist/workspace-bootstrap-seed.d.ts.map +1 -0
- package/dist/workspace-bootstrap-seed.js +209 -0
- package/package.json +4 -3
- package/src/artifacts-git-runtime.ts +360 -0
- package/src/cloudflare-artifacts-git-remote.ts +24 -0
- package/src/dataset-transfer-download.ts +61 -0
- package/src/dataset-transfer-policy.ts +8 -0
- package/src/dataset-transfer-upload.ts +145 -0
- package/src/git-credential.ts +1 -2
- package/src/git-recovery-environment.ts +1 -1
- package/src/help.ts +44 -11
- package/src/hosted-boot.ts +644 -109
- package/src/hosted-bootstrap.ts +14 -3
- package/src/hosted-harness-opencode.ts +130 -28
- package/src/hosted-harness.ts +4 -1
- package/src/hosted-harvest.ts +258 -33
- package/src/hosted-loop-lifecycle.ts +19 -5
- package/src/hosted-loop.ts +340 -68
- package/src/hosted-question-limits.ts +6 -0
- package/src/hosted-question.ts +145 -0
- package/src/hosted-run-event-sink.ts +24 -12
- package/src/hosted-runtime-assets.ts +51 -11
- package/src/hosted-session-cli.ts +10 -24
- package/src/http.ts +10 -4
- package/src/index.ts +108 -134
- package/src/instruction-set-gepa-launch.ts +4 -4
- package/src/instruction-set-loader.ts +53 -2
- package/src/instruction-set-lock/index.ts +561 -0
- package/src/instruction-set-lock/schema.v1.json +141 -0
- package/src/instruction-set-lock/schema.v1.ts +155 -0
- package/src/instruction-set-provenance.ts +89 -0
- package/src/instruction-set-sync/helpers.ts +707 -0
- package/src/instruction-set-sync/index.ts +791 -0
- package/src/instruction-set-update/index.ts +488 -0
- package/src/instruction-set-verify/index.ts +1067 -0
- package/src/instruction-sets-cli.ts +226 -114
- package/src/model-config-fields.ts +31 -0
- package/src/optimizations-list-cli.ts +7 -4
- package/src/score-format.ts +16 -0
- package/src/workbench-repo-contract.ts +314 -0
- package/src/workspace-bootstrap-job.ts +611 -0
- package/src/workspace-bootstrap-seed.ts +350 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/instruction_set_loader.py +46 -2
- package/vendor/skills/orizu/SKILL.md +4 -2
- package/vendor/skills/orizu/flows/promote.md +4 -0
- package/vendor/skills/orizu/references/assess-and-plan.md +1 -1
- package/vendor/skills/orizu/references/authority-map.md +1 -1
- package/vendor/skills/orizu/references/cli-reference.md +151 -35
- package/vendor/skills/orizu/references/instructions-after-prompts.md +3 -3
- package/vendor/skills/orizu/references/migrate-existing-gepa-setup.md +5 -6
- package/vendor/skills/orizu/references/migrate-hand-rolled-instruction-layout.md +252 -0
- package/vendor/skills/orizu/references/optimization-reports.md +5 -4
- package/vendor/skills/orizu/references/optimization-with-gepa.md +1 -1
- package/vendor/skills/orizu/references/orizu-in-your-codebase.md +195 -0
- package/vendor/skills/orizu/references/primer.md +4 -2
- package/vendor/skills/orizu/references/prompt-control-plane.md +33 -29
- package/vendor/skills/orizu/references/vocabulary.md +1 -1
package/dist/hosted-loop.js
CHANGED
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
* driver; 'claude-agent-sdk': construct the in-process Claude-Agent-SDK
|
|
17
17
|
* driver — no server to install or spawn), then drive the SINGLE task prompt
|
|
18
18
|
* (v0: no queue) through `drainHarnessToSink`;
|
|
19
|
-
* 5.
|
|
19
|
+
* 5. checkpoint dirty or local-only Git work without replacing the agent's
|
|
20
|
+
* terminal result, then deliver that original result through the sink.
|
|
20
21
|
*
|
|
21
22
|
* SWAPPABILITY (ALI-929 / P3.6): harness SELECTION is the ONLY thing that differs
|
|
22
23
|
* between the two drivers — `start()` → `runPrompt()` → `drainHarnessToSink()` →
|
|
@@ -29,19 +30,22 @@
|
|
|
29
30
|
* per-exec fallback — with firewall brokering the real key never reaches here).
|
|
30
31
|
*
|
|
31
32
|
* TESTABILITY: `runHostedLoop` takes injected `fetchImpl`, `spawnOpenCode`,
|
|
32
|
-
* `installOpenCode`,
|
|
33
|
-
* against
|
|
33
|
+
* `installOpenCode`, `createHarness`, and Git execution, so the whole loop can
|
|
34
|
+
* run in-process against fake boundaries; separate tests exercise the real
|
|
35
|
+
* packaged CLI, HTTPS smart-Git transport, and OpenCode process boundary.
|
|
34
36
|
*/
|
|
35
37
|
import { spawnSync, spawn as nodeSpawn } from 'child_process';
|
|
36
38
|
import { closeSync, existsSync, openSync, readFileSync } from 'fs';
|
|
37
39
|
import { tmpdir } from 'os';
|
|
38
40
|
import { join } from 'path';
|
|
39
|
-
import {
|
|
41
|
+
import { isHarnessTerminalKind } from './hosted-harness.js';
|
|
42
|
+
import { SETUP_HOOK_RELATIVE_PATH, composeHostedTaskPrompt, parsePrebakedMarker, resolveHostedTaskPreamble, resolvePrebakedMarkerPath, } from './hosted-runtime-assets.js';
|
|
40
43
|
import { harvestWorkspace } from './hosted-harvest.js';
|
|
41
44
|
import { annotateHeadlessQuestions, withIdleWatchdog } from './hosted-headless.js';
|
|
42
|
-
import { DEFAULT_PROMPT_MAX_DURATION_MS, OPENCODE_PINNED_VERSION, awaitOpenCodeModelResolvable, createOpenCodeHarness, spawnOpenCode, } from './hosted-harness-opencode.js';
|
|
45
|
+
import { DEFAULT_PROMPT_MAX_DURATION_MS, OPENCODE_PINNED_VERSION, awaitOpenCodeModelResolvable, createOpenCodeHarness, permissionForOrigin, spawnOpenCode, } from './hosted-harness-opencode.js';
|
|
43
46
|
import { createClaudeAgentHarness } from './hosted-harness-claude.js';
|
|
44
47
|
import { INJECTED_ENV_VARS_ENV } from './hosted-environment.js';
|
|
48
|
+
import { interceptHostedQuestions } from './hosted-question.js';
|
|
45
49
|
import { drainHarnessToSink, resumeRunEventSink, RunTerminalError, TerminalDeliveryError, } from './hosted-run-event-sink.js';
|
|
46
50
|
import { buildOpenCodeSpawnEnv, defaultProbeEgress, egressCanaryAllowedHost, runEgressCanary, } from './hosted-loop-lifecycle.js';
|
|
47
51
|
// The lifecycle/orchestration seam moved to `hosted-loop-lifecycle.ts`
|
|
@@ -136,16 +140,17 @@ function redactionListFromEnv() {
|
|
|
136
140
|
* falls back to a belt read of the `/opt/orizu/prebaked.json` marker (validated
|
|
137
141
|
* via `parsePrebakedMarker`, so a stray file cannot trigger a skip). Either → true.
|
|
138
142
|
*/
|
|
139
|
-
function
|
|
140
|
-
if (flag)
|
|
141
|
-
return true;
|
|
143
|
+
function hasValidPrebakedMarker(markerPath) {
|
|
142
144
|
try {
|
|
143
|
-
return parsePrebakedMarker(readFileSync(
|
|
145
|
+
return parsePrebakedMarker(readFileSync(markerPath, 'utf8')) !== null;
|
|
144
146
|
}
|
|
145
147
|
catch {
|
|
146
148
|
return false;
|
|
147
149
|
}
|
|
148
150
|
}
|
|
151
|
+
function detectLoopPrebaked(flag, markerPath) {
|
|
152
|
+
return flag === true || hasValidPrebakedMarker(markerPath);
|
|
153
|
+
}
|
|
149
154
|
/** Best-effort global install of the pinned opencode. Non-fatal: a failure is
|
|
150
155
|
* recorded and the run finishes cleanly when the subsequent connect fails. */
|
|
151
156
|
function installOpenCodePinned(version) {
|
|
@@ -193,6 +198,12 @@ export async function runHostedLoop(opts) {
|
|
|
193
198
|
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
194
199
|
const signal = opts.signal ?? new AbortController().signal;
|
|
195
200
|
const pinnedVersion = context.opencodePinnedVersion ?? OPENCODE_PINNED_VERSION;
|
|
201
|
+
const diagnose = (message) => {
|
|
202
|
+
try {
|
|
203
|
+
opts.onDiagnostic?.(message);
|
|
204
|
+
}
|
|
205
|
+
catch { /* diagnostics are best-effort */ }
|
|
206
|
+
};
|
|
196
207
|
// Resolve the bearer per request. Precedence: an explicit provider, then a
|
|
197
208
|
// fixed string, else read the rotated 0600 bearer file every time (production
|
|
198
209
|
// + rotation path — a host-side rotation overwrites this file and the sink's
|
|
@@ -211,6 +222,7 @@ export async function runHostedLoop(opts) {
|
|
|
211
222
|
fetchImpl,
|
|
212
223
|
now: opts.now,
|
|
213
224
|
redactSecretsList: [...(opts.redactSecretsList ?? []), ...redactionListFromEnv()],
|
|
225
|
+
onDiagnostic: diagnose,
|
|
214
226
|
});
|
|
215
227
|
// G5 startup canary (fail-closed, POSITIVE CONTROL): when the host applied an
|
|
216
228
|
// enforced egress policy it sets `egressCanaryHost`; probe BOTH the denied host
|
|
@@ -220,7 +232,7 @@ export async function runHostedLoop(opts) {
|
|
|
220
232
|
// unreachable → network broken / indistinguishable from a block) emits
|
|
221
233
|
// `egress_allowed` and finishes the run FAILED rather than touch real customer
|
|
222
234
|
// data. A canary whose own event append fails is likewise fail-closed.
|
|
223
|
-
if (context.egressCanaryHost) {
|
|
235
|
+
if (context.egressCanaryHost && !opts.sharedRuntime?.startupComplete) {
|
|
224
236
|
const probe = opts.probeEgress ?? defaultProbeEgress;
|
|
225
237
|
const targets = {
|
|
226
238
|
allowedHost: egressCanaryAllowedHost(context.apiBaseUrl),
|
|
@@ -273,7 +285,7 @@ export async function runHostedLoop(opts) {
|
|
|
273
285
|
// firewall is live, BEFORE any harness/network work. bootstrap deferred it (for
|
|
274
286
|
// enforced-egress providers) precisely so its network access could not precede
|
|
275
287
|
// the canary. Non-fatal, single-writer (this loop sink owns the run now).
|
|
276
|
-
if (context.runSetupHook) {
|
|
288
|
+
if (context.runSetupHook && !opts.sharedRuntime?.startupComplete) {
|
|
277
289
|
try {
|
|
278
290
|
const runHook = opts.runSetupHook ?? defaultRunSetupHook;
|
|
279
291
|
const outcome = await runHook({ workspaceDir: context.workspaceDir });
|
|
@@ -303,27 +315,38 @@ export async function runHostedLoop(opts) {
|
|
|
303
315
|
}
|
|
304
316
|
}
|
|
305
317
|
}
|
|
318
|
+
if (opts.sharedRuntime)
|
|
319
|
+
opts.sharedRuntime.startupComplete = true;
|
|
306
320
|
// End-of-run auto-harvest (ALI-1036): commit + push anything the agent left
|
|
307
321
|
// uncommitted/unpushed so a run NEVER loses work — on BOTH the success and
|
|
308
|
-
// failure paths. Runs at most once (guarded), records exactly one
|
|
309
|
-
//
|
|
310
|
-
//
|
|
322
|
+
// failure paths. Runs at most once (guarded), records exactly one outcome with
|
|
323
|
+
// critical-intent delivery, and NEVER throws or changes the run's terminal
|
|
324
|
+
// status. If the event cannot land, safe outcome metadata rides the already-
|
|
325
|
+
// critical terminal PATCH instead.
|
|
326
|
+
const prebakedMarkerPath = resolvePrebakedMarkerPath();
|
|
311
327
|
let harvested = false;
|
|
328
|
+
let checkpointFinishPatch;
|
|
312
329
|
const runAutoHarvest = async () => {
|
|
313
330
|
if (harvested)
|
|
314
|
-
return;
|
|
331
|
+
return checkpointFinishPatch;
|
|
315
332
|
harvested = true;
|
|
316
333
|
let outcome;
|
|
317
334
|
try {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
335
|
+
const markerValid = hasValidPrebakedMarker(prebakedMarkerPath);
|
|
336
|
+
outcome = !opts.harvestExec && !markerValid
|
|
337
|
+
? { kind: 'work_persist_failed', error: 'prebaked_marker_invalid' }
|
|
338
|
+
: harvestWorkspace({
|
|
339
|
+
workspaceDir: context.workspaceDir,
|
|
340
|
+
runId: context.runId,
|
|
341
|
+
sessionBranch: context.sessionBranch,
|
|
342
|
+
repositoryRemote: context.repositoryRemote,
|
|
343
|
+
credentialHelper: context.repositoryCredentialHelper,
|
|
344
|
+
author: context.author,
|
|
345
|
+
exec: opts.harvestExec,
|
|
346
|
+
// Real harvest requires a parsed marker from the hosted image;
|
|
347
|
+
// mere path existence must never authorize default Git mutations.
|
|
348
|
+
enabled: markerValid,
|
|
349
|
+
});
|
|
327
350
|
}
|
|
328
351
|
catch (error) {
|
|
329
352
|
outcome = { kind: 'work_persist_failed', error: error instanceof Error ? error.message : String(error) };
|
|
@@ -332,29 +355,54 @@ export async function runHostedLoop(opts) {
|
|
|
332
355
|
return;
|
|
333
356
|
try {
|
|
334
357
|
if (outcome.kind === 'work_persisted') {
|
|
335
|
-
await sink.append({
|
|
358
|
+
await sink.append({
|
|
359
|
+
kind: 'work_persisted',
|
|
360
|
+
payload: { sha: outcome.sha, ...(outcome.files ? { files: outcome.files } : {}) },
|
|
361
|
+
critical: true,
|
|
362
|
+
});
|
|
336
363
|
}
|
|
337
364
|
else if (outcome.kind === 'work_none') {
|
|
338
|
-
await sink.append({ kind: 'work_none', payload: {} });
|
|
365
|
+
await sink.append({ kind: 'work_none', payload: {}, critical: true });
|
|
366
|
+
}
|
|
367
|
+
else if (outcome.kind === 'work_not_inspected') {
|
|
368
|
+
await sink.append({ kind: 'work_not_inspected', payload: {}, critical: true });
|
|
339
369
|
}
|
|
340
370
|
else {
|
|
341
|
-
await sink.append({ kind: 'work_persist_failed', payload: { error: outcome.error } });
|
|
371
|
+
await sink.append({ kind: 'work_persist_failed', payload: { error: outcome.error }, critical: true });
|
|
342
372
|
}
|
|
343
373
|
}
|
|
344
374
|
catch {
|
|
345
|
-
//
|
|
346
|
-
//
|
|
375
|
+
// Value-free by construction: raw Git/provider errors and credentials must
|
|
376
|
+
// not enter process diagnostics or the terminal summary.
|
|
377
|
+
diagnose('hosted checkpoint event delivery failed');
|
|
378
|
+
const checkpointDelivery = outcome.kind === 'work_persisted'
|
|
379
|
+
? {
|
|
380
|
+
outcome: 'saved', sha: outcome.sha,
|
|
381
|
+
...(outcome.files ? { fileCount: outcome.files.length } : {}),
|
|
382
|
+
}
|
|
383
|
+
: outcome.kind === 'work_none'
|
|
384
|
+
? { outcome: 'no_changes' }
|
|
385
|
+
: outcome.kind === 'work_not_inspected'
|
|
386
|
+
? { outcome: 'not_inspected' }
|
|
387
|
+
: { outcome: 'unsaved' };
|
|
388
|
+
checkpointFinishPatch = {
|
|
389
|
+
summary: { checkpoint_delivery_failed: checkpointDelivery },
|
|
390
|
+
};
|
|
347
391
|
}
|
|
392
|
+
return checkpointFinishPatch;
|
|
348
393
|
};
|
|
349
|
-
let spawned = null;
|
|
350
|
-
let agentSessionId = null;
|
|
351
|
-
let installOk = false;
|
|
394
|
+
let spawned = opts.sharedRuntime?.spawned ?? null;
|
|
395
|
+
let agentSessionId = opts.sharedRuntime?.agentSessionId ?? null;
|
|
396
|
+
let installOk = opts.sharedRuntime?.installOk ?? false;
|
|
352
397
|
const harnessKind = context.harness ?? 'opencode';
|
|
353
398
|
try {
|
|
354
|
-
//
|
|
355
|
-
//
|
|
399
|
+
// A session runtime provisions and starts its harness exactly once. Later
|
|
400
|
+
// turns reuse this object and therefore the same native conversation.
|
|
356
401
|
let harness;
|
|
357
|
-
if (
|
|
402
|
+
if (opts.sharedRuntime?.harness) {
|
|
403
|
+
harness = opts.sharedRuntime.harness;
|
|
404
|
+
}
|
|
405
|
+
else if (harnessKind === 'claude-agent-sdk') {
|
|
358
406
|
// In-process agent loop: no server to install or spawn. Record an
|
|
359
407
|
// analogous setup artifact so the event stream shape is unchanged.
|
|
360
408
|
installOk = true;
|
|
@@ -371,7 +419,7 @@ export async function runHostedLoop(opts) {
|
|
|
371
419
|
// PRE-BAKED (ALI-1017): the pinned `opencode` is already on PATH, so SKIP the
|
|
372
420
|
// install (npm is blocked under G5 default-deny egress) and record
|
|
373
421
|
// `opencode_prebaked`. FROM-SCRATCH (local-sim / fallback): install as before.
|
|
374
|
-
const prebaked = detectLoopPrebaked(context.prebaked);
|
|
422
|
+
const prebaked = detectLoopPrebaked(context.prebaked, prebakedMarkerPath);
|
|
375
423
|
if (prebaked) {
|
|
376
424
|
installOk = true;
|
|
377
425
|
await sink.append({
|
|
@@ -396,11 +444,12 @@ export async function runHostedLoop(opts) {
|
|
|
396
444
|
// connection-refused "fetch failed" (ALI-1034).
|
|
397
445
|
spawned = await spawnImpl({
|
|
398
446
|
model: context.model,
|
|
447
|
+
permission: permissionForOrigin(context.sessionOrigin),
|
|
399
448
|
cwd: context.workspaceDir,
|
|
400
449
|
port: context.opencodePort,
|
|
401
450
|
env: buildOpenCodeSpawnEnv(context),
|
|
402
451
|
spawn: nodeChildSpawner,
|
|
403
|
-
logPath: join(tmpdir(), `opencode-serve-${context.runId}.log`),
|
|
452
|
+
logPath: opts.sharedRuntime?.spawnLogPath ?? join(tmpdir(), `opencode-serve-${context.runId}.log`),
|
|
404
453
|
signal,
|
|
405
454
|
});
|
|
406
455
|
if (typeof spawned.readyAfterMs === 'number') {
|
|
@@ -451,23 +500,32 @@ export async function runHostedLoop(opts) {
|
|
|
451
500
|
harness = (opts.createHarness ??
|
|
452
501
|
((baseUrl, options) => createOpenCodeHarness({ baseUrl, promptMaxDurationMs: options?.promptMaxDurationMs })))(spawned.baseUrl, { promptMaxDurationMs });
|
|
453
502
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
503
|
+
if (!opts.sharedRuntime?.harness) {
|
|
504
|
+
const started = await harness.start({
|
|
505
|
+
workspaceDir: context.workspaceDir,
|
|
506
|
+
model: context.model,
|
|
507
|
+
reasoningEffort: context.reasoningEffort,
|
|
508
|
+
resumeAgentSessionId: context.resumeAgentSessionId,
|
|
509
|
+
});
|
|
510
|
+
agentSessionId = started.agentSessionId;
|
|
511
|
+
if (opts.sharedRuntime) {
|
|
512
|
+
opts.sharedRuntime.harness = harness;
|
|
513
|
+
opts.sharedRuntime.spawned = spawned;
|
|
514
|
+
opts.sharedRuntime.agentSessionId = agentSessionId;
|
|
515
|
+
opts.sharedRuntime.installOk = installOk;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
461
518
|
// Prompt scaffolding (ALI-1036): wrap the user task with the standing
|
|
462
519
|
// headless preamble, keeping the task verbatim beneath a delimiter.
|
|
463
520
|
const prompt = {
|
|
464
521
|
runId: context.runId,
|
|
465
522
|
messageId: context.messageId,
|
|
466
|
-
content: composeHostedTaskPrompt(taskPrompt, opts.taskPreamble ??
|
|
523
|
+
content: composeHostedTaskPrompt(taskPrompt, opts.taskPreamble ?? resolveHostedTaskPreamble(context.sessionOrigin)),
|
|
467
524
|
author: context.author,
|
|
468
525
|
};
|
|
469
|
-
// Drive the prompt through
|
|
470
|
-
//
|
|
526
|
+
// Drive the prompt through question-deny annotation unless hosted-web
|
|
527
|
+
// OpenCode interception owns the outcome, then through the idle watchdog
|
|
528
|
+
// (ALI-1037): no harness event for `idleTimeoutMs` aborts the
|
|
471
529
|
// prompt and throws `AgentStalledError` → the catch below harvests, then
|
|
472
530
|
// finishes the run `failed`. The prompt runs under a CHILD AbortController
|
|
473
531
|
// linked to the loop signal so the watchdog can abort the in-flight prompt
|
|
@@ -479,7 +537,95 @@ export async function runHostedLoop(opts) {
|
|
|
479
537
|
promptController.abort();
|
|
480
538
|
else
|
|
481
539
|
signal.addEventListener('abort', () => promptController.abort(), { once: true });
|
|
482
|
-
|
|
540
|
+
// A human interrupt is prompt-scoped: signal the production driver (whose
|
|
541
|
+
// abort path sends OpenCode POST /session/:id/abort) and invoke stop() as a
|
|
542
|
+
// best-effort belt. This callback is deliberately total. All callers share
|
|
543
|
+
// one promise so the question path can await confirmed quiescence. Once this
|
|
544
|
+
// prompt yields its terminal event, a late marker belongs to the terminal/
|
|
545
|
+
// harvest window and must not abort the next queued prompt.
|
|
546
|
+
let hasPromptTerminal = false;
|
|
547
|
+
let quiescencePromise = null;
|
|
548
|
+
const beginQuiescence = () => {
|
|
549
|
+
promptController.abort();
|
|
550
|
+
if (quiescencePromise)
|
|
551
|
+
return quiescencePromise;
|
|
552
|
+
try {
|
|
553
|
+
quiescencePromise = Promise.resolve(harness.stop()).catch(() => {
|
|
554
|
+
diagnose('hosted interrupt stop failed; prompt signal remains authoritative');
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
catch {
|
|
558
|
+
diagnose('hosted interrupt stop failed; prompt signal remains authoritative');
|
|
559
|
+
quiescencePromise = Promise.resolve();
|
|
560
|
+
}
|
|
561
|
+
return quiescencePromise;
|
|
562
|
+
};
|
|
563
|
+
const handleInterrupt = () => {
|
|
564
|
+
if (hasPromptTerminal)
|
|
565
|
+
return;
|
|
566
|
+
void beginQuiescence();
|
|
567
|
+
};
|
|
568
|
+
const interruptSignal = opts.interruptSignal;
|
|
569
|
+
if (interruptSignal?.aborted)
|
|
570
|
+
handleInterrupt();
|
|
571
|
+
else
|
|
572
|
+
interruptSignal?.addEventListener('abort', handleInterrupt, { once: true });
|
|
573
|
+
const terminalSummary = { agentSessionId };
|
|
574
|
+
let hasQuestionOutcome = false;
|
|
575
|
+
const promptEvents = harness.runPrompt(prompt, promptController.signal);
|
|
576
|
+
const hasHostedQuestionInterception = harnessKind === 'opencode' && context.sessionOrigin === 'hosted-web';
|
|
577
|
+
const intercepted = hasHostedQuestionInterception
|
|
578
|
+
? interceptHostedQuestions(promptEvents, {
|
|
579
|
+
onDetected: question => {
|
|
580
|
+
hasQuestionOutcome = true;
|
|
581
|
+
terminalSummary.outcome = 'question_pending';
|
|
582
|
+
terminalSummary.questionId = question.questionId;
|
|
583
|
+
},
|
|
584
|
+
onInvalid: reason => {
|
|
585
|
+
hasQuestionOutcome = true;
|
|
586
|
+
terminalSummary.outcome = 'question_invalid';
|
|
587
|
+
terminalSummary.reason = reason;
|
|
588
|
+
},
|
|
589
|
+
// Async-generator resumption occurs only after drain appended the
|
|
590
|
+
// yielded critical event. Reuse ALI-1760's single abort owner.
|
|
591
|
+
onPersisted: beginQuiescence,
|
|
592
|
+
onInvalidPersisted: beginQuiescence,
|
|
593
|
+
})
|
|
594
|
+
: promptEvents;
|
|
595
|
+
const questionAware = hasHostedQuestionInterception
|
|
596
|
+
? intercepted
|
|
597
|
+
: annotateHeadlessQuestions(intercepted);
|
|
598
|
+
const interruptAware = (async function* () {
|
|
599
|
+
for await (const event of questionAware) {
|
|
600
|
+
if (isHarnessTerminalKind(event.kind)) {
|
|
601
|
+
hasPromptTerminal = true;
|
|
602
|
+
if (hasQuestionOutcome || (interruptSignal?.aborted && event.kind !== 'error')) {
|
|
603
|
+
yield {
|
|
604
|
+
kind: 'execution_complete',
|
|
605
|
+
messageId: prompt.messageId,
|
|
606
|
+
critical: true,
|
|
607
|
+
payload: { success: false, aborted: true },
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
else {
|
|
611
|
+
yield event;
|
|
612
|
+
}
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
yield event;
|
|
616
|
+
}
|
|
617
|
+
// A signal already durable before prompt startup can make a driver end
|
|
618
|
+
// without yielding. It is still an intentional cancellation, not a run
|
|
619
|
+
// failure or a missing-terminal fault.
|
|
620
|
+
if (interruptSignal?.aborted || hasQuestionOutcome) {
|
|
621
|
+
yield {
|
|
622
|
+
kind: 'execution_complete',
|
|
623
|
+
messageId: prompt.messageId,
|
|
624
|
+
critical: true,
|
|
625
|
+
payload: { success: false, aborted: true },
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
})();
|
|
483
629
|
if (idleTimeoutMs <= 0) {
|
|
484
630
|
// <= 0 explicitly DISABLES the watchdog (e.g. ORIZU_AGENT_IDLE_TIMEOUT_MS=0):
|
|
485
631
|
// a stalled prompt will hang until the sandbox/prompt hard cap. Record it so
|
|
@@ -493,32 +639,42 @@ export async function runHostedLoop(opts) {
|
|
|
493
639
|
});
|
|
494
640
|
}
|
|
495
641
|
const drivenStream = idleTimeoutMs > 0
|
|
496
|
-
? withIdleWatchdog(
|
|
642
|
+
? withIdleWatchdog(interruptAware, {
|
|
497
643
|
timeoutMs: idleTimeoutMs,
|
|
498
644
|
onTimeout: async () => {
|
|
499
645
|
promptController.abort();
|
|
500
646
|
await harness.stop();
|
|
501
647
|
},
|
|
502
|
-
//
|
|
503
|
-
//
|
|
648
|
+
// Without hosted-web OpenCode interception, a model stuck retrying a
|
|
649
|
+
// denied question emits synthetic `question_auto_answered` events. Those are
|
|
504
650
|
// NOT progress — excluding them lets a deny/retry loop trip agent_stalled
|
|
505
651
|
// instead of resetting the idle timer forever (ALI-1069).
|
|
506
652
|
isProgress: event => event.kind !== 'question_auto_answered',
|
|
507
653
|
})
|
|
508
|
-
:
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
654
|
+
: interruptAware;
|
|
655
|
+
let status;
|
|
656
|
+
try {
|
|
657
|
+
status = await drainHarnessToSink(drivenStream, sink, {
|
|
658
|
+
summary: terminalSummary,
|
|
659
|
+
// Auto-harvest runs on EVERY terminal path, before the sink seals.
|
|
660
|
+
beforeFinish: runAutoHarvest,
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
finally {
|
|
664
|
+
interruptSignal?.removeEventListener('abort', handleInterrupt);
|
|
665
|
+
}
|
|
666
|
+
if (!opts.sharedRuntime)
|
|
667
|
+
await harness.shutdown();
|
|
668
|
+
if (opts.sharedRuntime)
|
|
669
|
+
opts.sharedRuntime.agentSessionId = agentSessionId;
|
|
515
670
|
return { status, agentSessionId, installOk, error: null };
|
|
516
671
|
}
|
|
517
672
|
catch (error) {
|
|
518
673
|
const message = error instanceof Error ? error.message : String(error);
|
|
519
674
|
// Harvest FIRST on every abnormal path (partial work is valuable, incl. an
|
|
520
675
|
// idle-watchdog abort) — before any terminal write seals the sink.
|
|
521
|
-
await runAutoHarvest();
|
|
676
|
+
const checkpointPatch = await runAutoHarvest();
|
|
677
|
+
const checkpointSummary = checkpointPatch?.summary ?? {};
|
|
522
678
|
// ALI-1065 finding 2: an undeliverable TERMINAL transition must NEVER be
|
|
523
679
|
// re-recorded as 'failed' — the run's outcome was already decided; only its
|
|
524
680
|
// DELIVERY failed. Retry the delivery with the ORIGINAL status (spaced past
|
|
@@ -569,7 +725,7 @@ export async function runHostedLoop(opts) {
|
|
|
569
725
|
// no-ops when the drain already sealed the sink.
|
|
570
726
|
if (!sink.sealed) {
|
|
571
727
|
try {
|
|
572
|
-
await sink.finish('failed', { summary: { error: message } });
|
|
728
|
+
await sink.finish('failed', { summary: { error: message, ...checkpointSummary } });
|
|
573
729
|
}
|
|
574
730
|
catch {
|
|
575
731
|
// best-effort — the terminal write itself may be impossible (bearer gone)
|
|
@@ -578,7 +734,7 @@ export async function runHostedLoop(opts) {
|
|
|
578
734
|
return { status: 'failed', agentSessionId, installOk, error: message };
|
|
579
735
|
}
|
|
580
736
|
finally {
|
|
581
|
-
if (spawned) {
|
|
737
|
+
if (spawned && (!opts.sharedRuntime || opts.sharedRuntime.spawned !== spawned)) {
|
|
582
738
|
try {
|
|
583
739
|
spawned.stop();
|
|
584
740
|
}
|
|
@@ -588,6 +744,90 @@ export async function runHostedLoop(opts) {
|
|
|
588
744
|
}
|
|
589
745
|
}
|
|
590
746
|
}
|
|
747
|
+
export async function startHostedLoopSession(context) {
|
|
748
|
+
return {
|
|
749
|
+
context,
|
|
750
|
+
runtime: {
|
|
751
|
+
harness: null,
|
|
752
|
+
spawned: null,
|
|
753
|
+
agentSessionId: null,
|
|
754
|
+
installOk: false,
|
|
755
|
+
startupComplete: false,
|
|
756
|
+
spawnLogPath: join(context.sessionDir, `opencode-serve-${context.sessionId}.log`),
|
|
757
|
+
closed: false,
|
|
758
|
+
},
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
export async function runHostedLoopTurn(session, turn, options) {
|
|
762
|
+
if (session.runtime.closed)
|
|
763
|
+
throw new Error('hosted loop session is closed');
|
|
764
|
+
return runHostedLoop({
|
|
765
|
+
...options,
|
|
766
|
+
context: {
|
|
767
|
+
...session.context,
|
|
768
|
+
runId: turn.runId,
|
|
769
|
+
taskFile: turn.taskFile,
|
|
770
|
+
messageId: turn.messageId,
|
|
771
|
+
bearerFile: turn.bearerFile,
|
|
772
|
+
resumeAgentSessionId: session.runtime.agentSessionId ?? session.context.resumeAgentSessionId,
|
|
773
|
+
},
|
|
774
|
+
taskPrompt: options.taskPrompt,
|
|
775
|
+
sharedRuntime: session.runtime,
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
export async function closeHostedLoopSession(session) {
|
|
779
|
+
if (session.runtime.closed)
|
|
780
|
+
return;
|
|
781
|
+
session.runtime.closed = true;
|
|
782
|
+
try {
|
|
783
|
+
await session.runtime.harness?.shutdown();
|
|
784
|
+
}
|
|
785
|
+
finally {
|
|
786
|
+
session.runtime.spawned?.stop();
|
|
787
|
+
session.runtime.harness = null;
|
|
788
|
+
session.runtime.spawned = null;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
const REQUIRED_CONTEXT_STRINGS = [
|
|
792
|
+
'apiBaseUrl', 'runId', 'bearerFile', 'taskFile', 'workspaceDir',
|
|
793
|
+
'sessionBranch', 'repositoryRemote', 'model', 'messageId',
|
|
794
|
+
];
|
|
795
|
+
const OPTIONAL_CONTEXT_STRINGS = [
|
|
796
|
+
'reasoningEffort', 'opencodePinnedVersion', 'anthropicDummyKey',
|
|
797
|
+
'resumeAgentSessionId', 'egressCanaryHost', 'repositoryCredentialHelper',
|
|
798
|
+
];
|
|
799
|
+
function isNonemptyString(value) {
|
|
800
|
+
return typeof value === 'string' && value.length > 0;
|
|
801
|
+
}
|
|
802
|
+
function validateHostedLoopContext(value) {
|
|
803
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
804
|
+
return { context: null, fields: ['root'] };
|
|
805
|
+
const record = value;
|
|
806
|
+
const fields = REQUIRED_CONTEXT_STRINGS.filter(name => !isNonemptyString(record[name]));
|
|
807
|
+
const branch = record.sessionBranch;
|
|
808
|
+
if (typeof branch === 'string' && (branch.length > 255 || !/^[A-Za-z0-9][A-Za-z0-9._/-]*$/u.test(branch) ||
|
|
809
|
+
branch.includes('..') || branch.includes('//') || branch.endsWith('/') || branch.includes('@{')))
|
|
810
|
+
fields.push('sessionBranch');
|
|
811
|
+
const author = record.author;
|
|
812
|
+
if (!author || typeof author !== 'object' || Array.isArray(author) ||
|
|
813
|
+
!isNonemptyString(author.name) || !isNonemptyString(author.email))
|
|
814
|
+
fields.push('author');
|
|
815
|
+
if (record.harness !== undefined && record.harness !== 'opencode' && record.harness !== 'claude-agent-sdk')
|
|
816
|
+
fields.push('harness');
|
|
817
|
+
if (record.sessionOrigin !== undefined && record.sessionOrigin !== 'hosted-web' && record.sessionOrigin !== 'cli')
|
|
818
|
+
fields.push('sessionOrigin');
|
|
819
|
+
if (record.sandboxBudgetMs !== undefined && (typeof record.sandboxBudgetMs !== 'number' || !Number.isFinite(record.sandboxBudgetMs) || record.sandboxBudgetMs <= 0))
|
|
820
|
+
fields.push('sandboxBudgetMs');
|
|
821
|
+
if (record.opencodePort !== undefined && (typeof record.opencodePort !== 'number' || !Number.isInteger(record.opencodePort) || record.opencodePort <= 0 || record.opencodePort > 65_535))
|
|
822
|
+
fields.push('opencodePort');
|
|
823
|
+
for (const name of OPTIONAL_CONTEXT_STRINGS)
|
|
824
|
+
if (record[name] !== undefined && typeof record[name] !== 'string')
|
|
825
|
+
fields.push(name);
|
|
826
|
+
for (const name of ['prebaked', 'runSetupHook'])
|
|
827
|
+
if (record[name] !== undefined && typeof record[name] !== 'boolean')
|
|
828
|
+
fields.push(name);
|
|
829
|
+
return { context: fields.length === 0 ? value : null, fields: [...new Set(fields)] };
|
|
830
|
+
}
|
|
591
831
|
function argValue(args, flag) {
|
|
592
832
|
const index = args.indexOf(flag);
|
|
593
833
|
if (index === -1 || index + 1 >= args.length)
|
|
@@ -606,19 +846,25 @@ export async function hostedLoopCommand(args, io) {
|
|
|
606
846
|
io.printErr?.('Usage: orizu internal hosted-loop --context <path>');
|
|
607
847
|
return 1;
|
|
608
848
|
}
|
|
609
|
-
let
|
|
849
|
+
let rawContext;
|
|
610
850
|
try {
|
|
611
|
-
|
|
851
|
+
rawContext = JSON.parse(readFileSync(contextPath, 'utf8'));
|
|
612
852
|
}
|
|
613
853
|
catch (error) {
|
|
614
854
|
io.printErr?.(`unreadable loop context: ${error instanceof Error ? error.message : String(error)}`);
|
|
615
855
|
return 1;
|
|
616
856
|
}
|
|
857
|
+
const parsedContext = validateHostedLoopContext(rawContext);
|
|
858
|
+
if (!parsedContext.context) {
|
|
859
|
+
io.printErr?.(`invalid loop context: ${parsedContext.fields.join(', ')}`);
|
|
860
|
+
return 1;
|
|
861
|
+
}
|
|
862
|
+
const context = parsedContext.context;
|
|
617
863
|
// Read the bearer per request (via the provider) so a host-side rotation that
|
|
618
864
|
// overwrites the 0600 file is picked up without restarting the loop.
|
|
619
865
|
const bearerProvider = () => readFileSync(context.bearerFile, 'utf8').trim();
|
|
620
866
|
const taskPrompt = readFileSync(context.taskFile, 'utf8');
|
|
621
|
-
const result = await runHostedLoop({ context, bearerProvider, taskPrompt });
|
|
867
|
+
const result = await runHostedLoop({ context, bearerProvider, taskPrompt, onDiagnostic: io.printErr });
|
|
622
868
|
io.print(io.json
|
|
623
869
|
? JSON.stringify({ status: result.status, agentSessionId: result.agentSessionId, error: result.error })
|
|
624
870
|
: `hosted-loop finished: ${result.status}${result.error ? ` (${result.error})` : ''}`);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hosted-question-limits.d.ts","sourceRoot":"","sources":["../src/hosted-question-limits.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB;;;;;EAKxB,CAAA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { HarnessEvent } from './hosted-harness.js';
|
|
2
|
+
export { HOSTED_QUESTION_LIMITS } from './hosted-question-limits.js';
|
|
3
|
+
export interface HostedPendingQuestionOption {
|
|
4
|
+
label: string;
|
|
5
|
+
description: string | null;
|
|
6
|
+
}
|
|
7
|
+
export interface HostedPendingQuestion {
|
|
8
|
+
questionId: string;
|
|
9
|
+
question: string;
|
|
10
|
+
options: HostedPendingQuestionOption[];
|
|
11
|
+
}
|
|
12
|
+
export declare const INVALID_HOSTED_QUESTION_REASON = "expected_exactly_one_bounded_question";
|
|
13
|
+
export declare class InvalidHostedQuestionError extends Error {
|
|
14
|
+
readonly reason = "expected_exactly_one_bounded_question";
|
|
15
|
+
constructor();
|
|
16
|
+
}
|
|
17
|
+
export declare function parseHostedQuestion(args: unknown, createQuestionId?: () => string): HostedPendingQuestion;
|
|
18
|
+
export interface InterceptHostedQuestionOptions {
|
|
19
|
+
createQuestionId?: () => string;
|
|
20
|
+
onDetected(question: HostedPendingQuestion): void;
|
|
21
|
+
onPersisted(question: HostedPendingQuestion): Promise<void>;
|
|
22
|
+
onInvalid?(reason: string): void;
|
|
23
|
+
onInvalidPersisted?(reason: string): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
export declare function interceptHostedQuestions(source: AsyncIterable<HarnessEvent>, options: InterceptHostedQuestionOptions): AsyncGenerator<HarnessEvent>;
|
|
26
|
+
export declare function composeHostedAnswerPrompt(question: Pick<HostedPendingQuestion, 'questionId' | 'question'>, answerText: string): string;
|
|
27
|
+
//# sourceMappingURL=hosted-question.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hosted-question.d.ts","sourceRoot":"","sources":["../src/hosted-question.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAIvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAA;AAQpE,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,2BAA2B,EAAE,CAAA;CACvC;AAED,eAAO,MAAM,8BAA8B,0CAA0C,CAAA;AAErF,qBAAa,0BAA2B,SAAQ,KAAK;IACnD,QAAQ,CAAC,MAAM,2CAAiC;;CAMjD;AAsBD,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,OAAO,EACb,gBAAgB,GAAE,MAAM,MAAmB,GAC1C,qBAAqB,CAgBvB;AAED,MAAM,WAAW,8BAA8B;IAC7C,gBAAgB,CAAC,EAAE,MAAM,MAAM,CAAA;IAC/B,UAAU,CAAC,QAAQ,EAAE,qBAAqB,GAAG,IAAI,CAAA;IACjD,WAAW,CAAC,QAAQ,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3D,SAAS,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,kBAAkB,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACnD;AAED,wBAAuB,wBAAwB,CAC7C,MAAM,EAAE,aAAa,CAAC,YAAY,CAAC,EACnC,OAAO,EAAE,8BAA8B,GACtC,cAAc,CAAC,YAAY,CAAC,CAwC9B;AAED,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,IAAI,CAAC,qBAAqB,EAAE,YAAY,GAAG,UAAU,CAAC,EAChE,UAAU,EAAE,MAAM,GACjB,MAAM,CAWR"}
|