@melite/estate-drafts 0.0.0-stage → 0.1.4-alpha

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/lib/remote.js ADDED
@@ -0,0 +1,1116 @@
1
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
2
+ var useValue = arguments.length > 2;
3
+ for (var i = 0; i < initializers.length; i++) {
4
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
5
+ }
6
+ return useValue ? value : void 0;
7
+ };
8
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
9
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
10
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
11
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
12
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
13
+ var _, done = false;
14
+ for (var i = decorators.length - 1; i >= 0; i--) {
15
+ var context = {};
16
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
17
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
18
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
19
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
20
+ if (kind === "accessor") {
21
+ if (result === void 0) continue;
22
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
23
+ if (_ = accept(result.get)) descriptor.get = _;
24
+ if (_ = accept(result.set)) descriptor.set = _;
25
+ if (_ = accept(result.init)) initializers.unshift(_);
26
+ }
27
+ else if (_ = accept(result)) {
28
+ if (kind === "field") initializers.unshift(_);
29
+ else descriptor[key] = _;
30
+ }
31
+ }
32
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
33
+ done = true;
34
+ };
35
+ import { Remote, RemoteError, TypertRemoteService, } from "@deepseek-ai/dsh-typert-protocol";
36
+ import { admitWiring, benchBlockOf, strippedForAdmission } from "./bench.js";
37
+ import { STAGE_PATCH_KEYS, RESTORED_PROVENANCE_SPELLING } from "@melite/contracts/benchStage";
38
+ export { STAGE_PATCH_KEYS };
39
+ import { unwrapItemArrays } from "@melite/comfyui-card-kit";
40
+ import { openDraftsDomain } from "./store.js";
41
+ import { DRAFT_ID_RE } from "./spec.js";
42
+ import { addStageBench, ensureBench, notifyBenchChange, notifyBenchDocWrite, onBenchChange, setBenchChangeSeam, getBench, groupBench, patchBench, clearBench, removeStageBench, reorderBench, rewireBench, undoBench, ungroupBench, } from "./bench.js";
43
+ import { benchDraftIdOf } from "@melite/contracts/benchStage";
44
+
45
+
46
+
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+
59
+
60
+
61
+
62
+ export function installBenchChangeSeam(ctx) {
63
+ let detach = null;
64
+ ctx.effect(() => {
65
+ setBenchChangeSeam({
66
+ emit: (session) => { ctx.emit("bench.changed", session); },
67
+ on: (fn) => {
68
+ const stop = ctx.on("bench.changed", fn);
69
+ return () => { stop(); };
70
+ },
71
+ });
72
+ detach = () => { setBenchChangeSeam(null); };
73
+ return detach;
74
+ });
75
+ return () => { detach?.(); };
76
+ }
77
+ /** The wrap voice for a thrown store error: Error.message when the
78
+ * value carries one, else the string form. Every store/domain throw
79
+ * site constructs `new Error` (or an Error subclass), so the else
80
+ * arm only guards degenerate non-Error throws. */
81
+ function thrownMessage(error) {
82
+ /* v8 ignore next -- a non-Error throw requires a degenerate throw
83
+ * site; openDraftsDomain and the domain layer construct Errors. */
84
+ return error instanceof Error ? error.message : String(error);
85
+ }
86
+ /** ── THE OUTPUT HISTORY HELPERS (stroke 40) ──────────────────────
87
+ * A result's OUTPUT urls, one spelling: the artifacts name→url map
88
+ * first, the flat assetUrls list second, the lone url third (the
89
+ * same harvest order the client's preview reads — the history and
90
+ * the hero can never disagree about what a run produced). */
91
+ export function resultOutputUrls(result) {
92
+ if (typeof result !== "object" || result === null || Array.isArray(result))
93
+ return [];
94
+ const row = result;
95
+ const urls = [];
96
+ if (row["artifacts"] !== null && typeof row["artifacts"] === "object" && !Array.isArray(row["artifacts"])) {
97
+ for (const v of Object.values(row["artifacts"])) {
98
+ if (typeof v === "string" && v !== "")
99
+ urls.push(v);
100
+ }
101
+ }
102
+ if (urls.length === 0 && Array.isArray(row["assetUrls"])) {
103
+ for (const v of row["assetUrls"]) {
104
+ if (typeof v === "string" && v !== "")
105
+ urls.push(v);
106
+ }
107
+ }
108
+ if (urls.length === 0 && typeof row["url"] === "string" && row["url"] !== "")
109
+ urls.push(row["url"]);
110
+ return urls;
111
+ }
112
+ /** Same OUTPUTS (the uniqueness key): two results match when their
113
+ * url sets are equal — a re-run landing the same urls is not a new
114
+ * history entry (the operator's "when it is unique"). */
115
+ export function sameResultOutputs(a, b) {
116
+ return sameUrlSet(resultOutputUrls(a), resultOutputUrls(b));
117
+ }
118
+ function sameUrlSet(a, b) {
119
+ if (a.length !== b.length)
120
+ return false;
121
+ const sa = new Set(a);
122
+ return b.every((u) => sa.has(u));
123
+ }
124
+ function resolverOf(ctx) {
125
+ let seat;
126
+ try {
127
+ seat = ctx.get("cardRegistry", false);
128
+ }
129
+ catch {
130
+ return null;
131
+ }
132
+ if (seat === null || seat === undefined || typeof seat !== "object")
133
+ return null;
134
+ const fn = seat.resolveStageParams;
135
+ if (typeof fn !== "function")
136
+ return null;
137
+ return seat;
138
+ }
139
+
140
+
141
+
142
+
143
+
144
+
145
+
146
+
147
+
148
+
149
+
150
+
151
+
152
+
153
+
154
+
155
+
156
+
157
+
158
+
159
+
160
+ export function admissionOf(ctx) {
161
+ const reg = resolverOf(ctx);
162
+ if (reg === null || reg.admitStageModel === undefined)
163
+ return undefined;
164
+ return (card, params, provenance) => {
165
+ const admitOnce = () => {
166
+ const verdict = reg.admitStageModel(card, params["model"]);
167
+ if (!verdict.ok) {
168
+ throw new RemoteError("bench/bad-model", verdict.error, { card, offered: verdict.offered });
169
+ }
170
+ };
171
+ try {
172
+ admitOnce();
173
+ return;
174
+ }
175
+ catch (original) {
176
+ if (provenance !== RESTORED_PROVENANCE
177
+ && provenance !== RESTORED_WIRE_SPELLING
178
+ || typeof params["model"] !== "string"
179
+ || params["model"] === ""
180
+ || reg.repairStageModel === undefined)
181
+ throw original;
182
+ const fix = reg.repairStageModel(card, params["model"]);
183
+ if (fix === null)
184
+ throw original;
185
+ params["model"] = fix.model;
186
+ if (fix.variant !== undefined
187
+ && (params["variant"] === undefined || params["variant"] === "")) {
188
+ params["variant"] = fix.variant;
189
+ }
190
+ try {
191
+ admitOnce();
192
+ }
193
+ catch {
194
+ // the repair did not clear admission — the ORIGINAL refusal
195
+ // is the honest voice, never the repair's shadow error
196
+ throw original;
197
+ }
198
+ }
199
+ };
200
+ }
201
+
202
+
203
+
204
+
205
+
206
+ export const RESTORED_PROVENANCE = Symbol("melite.restored-provenance");
207
+
208
+
209
+
210
+
211
+
212
+
213
+
214
+
215
+
216
+
217
+
218
+ export const RESTORED_WIRE_SPELLING = RESTORED_PROVENANCE_SPELLING;
219
+ /** The bench documents' id prefix (the bench family, not a flow export). */
220
+ const BENCH_ID_PREFIX = "bench-";
221
+ /** A plain record (the module's own shape guard). */
222
+ const isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
223
+
224
+
225
+
226
+
227
+
228
+
229
+
230
+
231
+
232
+
233
+
234
+
235
+
236
+ export function laneAdmissionOf(ctx) {
237
+ const reg = resolverOf(ctx);
238
+ if (reg === null || reg.admitStageRelations === undefined)
239
+ return undefined;
240
+ return (card, params, provenance) => {
241
+
242
+ // door audit's HIGH-2): `provenance` arrives in the request body,
243
+ // and melite_construct's load verb forwards it verbatim — so a
244
+ // caller could exempt any state by claiming "restored", and the
245
+ // one receipt for the carve in the suite WAS that bypass. The
246
+ // carve now rides a marker the SERVER sets, never a field a
247
+ // caller writes: a domain-internal restore (bench-load) passes
248
+ // the symbol below, and a client claim is just a string. The run
249
+
250
+ // still teaches at Run — the carve only spares a live composer
251
+
252
+ if (provenance === RESTORED_PROVENANCE || provenance === RESTORED_WIRE_SPELLING) {
253
+ return; // the archive loads; the run teaches
254
+ }
255
+ const verdict = reg.admitStageRelations(card, params);
256
+ if (!verdict.ok) {
257
+ throw new RemoteError("bench/lane-refusal", verdict.error, { card });
258
+ }
259
+ };
260
+ }
261
+
262
+
263
+
264
+
265
+
266
+ export function benchAdmissionOf(ctx) {
267
+ const modelAdmit = admissionOf(ctx);
268
+ const laneAdmit = laneAdmissionOf(ctx);
269
+ if (modelAdmit === undefined)
270
+ return laneAdmit ?? undefined;
271
+ if (laneAdmit === undefined)
272
+ return modelAdmit;
273
+ return (card, params, provenance) => {
274
+ modelAdmit(card, params, provenance);
275
+ laneAdmit(card, params, provenance);
276
+ };
277
+ }
278
+ /** Attach the served `resolved` map per stage (prepareCardParams'
279
+ * own stamped + lifted body — the exact input the next run
280
+ * composes). Copies rows, never mutates the domain's cached doc:
281
+ * the stored record keeps params/result/runStatus only, the serve
282
+ * derives `resolved` fresh on every read. A stage whose params name
283
+ * no resolvable unit rides un-resolved (no key at all — the client
284
+ * treats absence as the local-derive fallback). */
285
+ function withResolvedStages(ctx, stages) {
286
+ if (!Array.isArray(stages))
287
+ return stages;
288
+ const reg = resolverOf(ctx);
289
+ if (reg === null)
290
+ return stages;
291
+ return stages.map((s) => {
292
+ if (typeof s !== "object" || s === null || Array.isArray(s))
293
+ return s;
294
+ const row = s;
295
+ const params = row["params"];
296
+ if (params === null || typeof params !== "object" || Array.isArray(params))
297
+ return row;
298
+ const resolved = reg.resolveStageParams(params);
299
+ if (resolved === null)
300
+ return row;
301
+ return { ...row, resolved };
302
+ });
303
+ }
304
+ /** First-seen display strings walk the doc depth-first (the index
305
+ * law: model/service cap 80, name/intent/title cap 160). */
306
+ function docIndexFields(doc) {
307
+ const first = {};
308
+ const walk = (v) => {
309
+ if (typeof v === "string")
310
+ return;
311
+ if (Array.isArray(v)) {
312
+ for (const e of v)
313
+ walk(e);
314
+ return;
315
+ }
316
+ if (typeof v !== "object" || v === null)
317
+ return;
318
+ for (const [k, e] of Object.entries(v)) {
319
+ if (first[k] === undefined &&
320
+ (k === "model" ||
321
+ k === "name" ||
322
+ k === "intent" ||
323
+ k === "title" ||
324
+ k === "service") &&
325
+ typeof e === "string") {
326
+ first[k] = e;
327
+ }
328
+ walk(e);
329
+ }
330
+ };
331
+ walk(doc);
332
+ const cap = (s, n) => s === undefined ? "" : s.length + 1 < n ? s : s.slice(0, n - 1);
333
+ let model = cap(first["model"], 80);
334
+ let disp = cap(first["name"], 160);
335
+ if (disp === "")
336
+ disp = cap(first["intent"], 160);
337
+ if (disp === "")
338
+ disp = cap(first["title"], 160);
339
+ if (model === "")
340
+ model = cap(first["service"], 80);
341
+ return { model, name: disp };
342
+ }
343
+ /** The drafts Remote owner: list/get over the storage domain. */
344
+ let DraftsRemoteService = (() => {
345
+ let _classSuper = TypertRemoteService;
346
+ let _instanceExtraInitializers = [];
347
+ let _remoteExportList_decorators;
348
+ let _remoteExportGet_decorators;
349
+ let _remoteExportPatch_decorators;
350
+ return class DraftsRemoteService extends _classSuper {
351
+ static {
352
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
353
+ _remoteExportList_decorators = [Remote("list")];
354
+ _remoteExportGet_decorators = [Remote("get")];
355
+ _remoteExportPatch_decorators = [Remote("patch")];
356
+ __esDecorate(this, null, _remoteExportList_decorators, { kind: "method", name: "remoteExportList", static: false, private: false, access: { has: obj => "remoteExportList" in obj, get: obj => obj.remoteExportList }, metadata: _metadata }, null, _instanceExtraInitializers);
357
+ __esDecorate(this, null, _remoteExportGet_decorators, { kind: "method", name: "remoteExportGet", static: false, private: false, access: { has: obj => "remoteExportGet" in obj, get: obj => obj.remoteExportGet }, metadata: _metadata }, null, _instanceExtraInitializers);
358
+ __esDecorate(this, null, _remoteExportPatch_decorators, { kind: "method", name: "remoteExportPatch", static: false, private: false, access: { has: obj => "remoteExportPatch" in obj, get: obj => obj.remoteExportPatch }, metadata: _metadata }, null, _instanceExtraInitializers);
359
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
360
+ }
361
+
362
+
363
+
364
+
365
+
366
+
367
+
368
+
369
+
370
+
371
+
372
+
373
+
374
+ benchAdmit() {
375
+ return benchAdmissionOf(this.ctx);
376
+ }
377
+ constructor(ctx) {
378
+ super(ctx, "draftsRemote", { namespace: "drafts" });
379
+ __runInitializers(this, _instanceExtraInitializers);
380
+ }
381
+ /** Endpoint `drafts/list` — the domain index, recency DESC. */
382
+ async remoteExportList(signal) {
383
+ return await this.list(signal);
384
+ }
385
+ /** Endpoint `drafts/get` — one doc by id. */
386
+ async remoteExportGet(draftId, signal) {
387
+ return (await this.get(draftId, signal));
388
+ }
389
+ /** The in-process API the adapters above delegate to. */
390
+ async list(signal) {
391
+ signal.throwIfAborted();
392
+ let drafts;
393
+ try {
394
+ drafts = await openDraftsDomain(this.ctx);
395
+ }
396
+ catch (error) {
397
+ throw new RemoteError("draft/store-failed", thrownMessage(error), {}, { cause: error });
398
+ }
399
+ const ents = [];
400
+ for (const [did, rec] of drafts.table("docs").entries()) {
401
+ ents.push({ mt: rec.updated_at, row: { draft_id: did, ...docIndexFields(rec.doc) } });
402
+ }
403
+ ents.sort((a, b) => b.mt - a.mt);
404
+ return {
405
+ count: ents.length,
406
+ drafts: ents.slice(0, 600).map((e) => e.row),
407
+ };
408
+ }
409
+ /** The in-process API the adapter above delegates to. */
410
+ async get(draftId, signal) {
411
+ signal.throwIfAborted();
412
+ if (!DRAFT_ID_RE.test(draftId)) {
413
+ throw new RemoteError("draft/bad-id", `bad draft id ${JSON.stringify(draftId)}`, {
414
+ draftId,
415
+ });
416
+ }
417
+ let drafts;
418
+ try {
419
+ drafts = await openDraftsDomain(this.ctx);
420
+ }
421
+ catch (error) {
422
+ throw new RemoteError("draft/store-failed", thrownMessage(error), {}, { cause: error });
423
+ }
424
+ const rec = drafts.table("docs").get(draftId);
425
+ if (rec === undefined) {
426
+ throw new RemoteError("draft/not-found", `draft not found: ${draftId}`, {
427
+ draftId,
428
+ });
429
+ }
430
+ const doc = rec.doc;
431
+ return { ...doc, stages: withResolvedStages(this.ctx, doc["stages"]) };
432
+ }
433
+
434
+
435
+
436
+
437
+
438
+
439
+
440
+
441
+
442
+
443
+
444
+
445
+
446
+
447
+
448
+
449
+
450
+
451
+
452
+
453
+
454
+
455
+ async remoteExportPatch(draftId, stageId, patch, expectRev, signal) {
456
+ return await this.patch(draftId, stageId, patch, expectRev, signal);
457
+ }
458
+ /** The patch arm the adapter above delegates to. */
459
+ async patch(draftId, stageId, patch, expectRev, signal) {
460
+ signal.throwIfAborted();
461
+ if (!DRAFT_ID_RE.test(draftId)) {
462
+ throw new RemoteError("draft/bad-id", `bad draft id ${JSON.stringify(draftId)}`, {
463
+ draftId,
464
+ });
465
+ }
466
+ if (typeof stageId !== "string" || stageId === "") {
467
+ throw new RemoteError("draft/bad-stage-id", `bad stage id ${JSON.stringify(stageId)}`, {
468
+ stageId,
469
+ });
470
+ }
471
+ if (patch === null || typeof patch !== "object" || Array.isArray(patch)) {
472
+ throw new RemoteError("draft/bad-patch", "patch must be a plain object", {});
473
+ }
474
+ // Admission: the door's fixed key set, unknown keys refused with
475
+ // the old voice (STAGE_PATCH_KEYS order).
476
+ const unknown = Object.keys(patch).filter((k) => !STAGE_PATCH_KEYS.includes(k));
477
+ if (unknown.length > 0) {
478
+ throw new RemoteError("draft/patch-unknown-keys", `unknown stage patch key(s): ${unknown.join(", ")} (want ${STAGE_PATCH_KEYS.join(", ")})`, { unknown });
479
+ }
480
+
481
+ // vacuously "telemetry" (every() on no keys) — without this arm
482
+ // it would persist an unchanged doc and wake the stream for
483
+ // nothing. An empty patch is a caller bug: refuse with the
484
+ // door's own voice, like the bench verb refuses its own.
485
+ if (Object.keys(patch).length === 0) {
486
+ throw new RemoteError("draft/bad-patch", "patch is empty (nothing to patch)", {});
487
+ }
488
+ let drafts;
489
+ try {
490
+ drafts = await openDraftsDomain(this.ctx);
491
+ }
492
+ catch (error) {
493
+ throw new RemoteError("draft/store-failed", thrownMessage(error), {}, { cause: error });
494
+ }
495
+ const rec = drafts.table("docs").get(draftId);
496
+ if (rec === undefined) {
497
+ // The old door's delivered:false envelope — a missing doc is a
498
+ // DOMAIN outcome, not an error.
499
+ return { draftId, stageId, delivered: false, reason: "draft not found" };
500
+ }
501
+ const doc = rec.doc;
502
+ // The domain schema guarantees stages is an array (schema-failing
503
+ // records are backed up and skipped at read) — no re-defense here.
504
+ const stages = doc["stages"];
505
+ const stage = stages.find((s) => typeof s === "object" && s !== null && s["id"] === stageId) ?? null;
506
+ if (stage === null) {
507
+ return { draftId, stageId, delivered: false, reason: "stage not found" };
508
+ }
509
+
510
+ // even when I am just generating a song"): a patch body whose
511
+ // keys are ALL run telemetry (result and/or runStatus — never
512
+ // params/inputs/inputOutputs) is a RUN FACE, not an author move.
513
+ // The outcome observer writes one such body per poll state —
514
+ // run start, EVERY progress frame, terminal — and under the
515
+ // every-write-bumps law the bench's CAS rev ticked for the whole
516
+ // generation (held expect_revs went stale mid-run; the undo log
517
+ // filled with no-op 'patch' entries that consumed undo targets
518
+ // restoring params_before:{} — telemetry was never authorship).
519
+ // Telemetry now writes the stage row (result/runStatus + the
520
+ // derived history archive) and fires the doc-write notify, but
521
+ // does NOT fence, does NOT bump bench.rev, does NOT append to
522
+ // the bench log — last-write-wins is the poll's own semantics,
523
+ // and the rebase dance the client kept performing was this
524
+ // fence's disease, not its cure. Any body carrying an author key
525
+ // (params | inputs | inputOutputs) walks the full law unchanged.
526
+ const bench = benchBlockOf(doc);
527
+ // `health` walks the telemetry arm with result/runStatus (LOW-23,
528
+
529
+ // writes it, the recovery clears it, neither is authorship.
530
+ const telemetry = Object.keys(patch).every((k) => k === "result" || k === "runStatus" || k === "health");
531
+ // THE CAS WALK (the layered-params law): an AUTHOR patch is a
532
+ // bench author and joins the revision law every other author
533
+ // walks — fence on expectRev BEFORE any mutation (the record is
534
+ // the domain's cached object; a refusal must leave no trace),
535
+ // then bump and log below. A bench that moved under the caller
536
+ // refuses loud, never merges under it.
537
+ if (!telemetry && expectRev !== null && bench.rev !== expectRev) {
538
+ throw new RemoteError("bench/cas-conflict", `bench moved under the patch (expect_rev ${expectRev}, current ${bench.rev}) — re-read and re-apply`, { current_rev: bench.rev, expect_rev: expectRev });
539
+ }
540
+ // THE TELEMETRY WRITE IS DEFERRED (the bench audit's gap 1,
541
+
542
+ // assigned into the CACHED record here, ABOVE the model and
543
+ // wiring gates — so a patch carrying a result AND a bad model
544
+ // wrote the result into the live doc and then threw, leaving a
545
+ // result with no rev and no log (the copy-on-judge law's exact
546
+ // shape: a refused write must leave NOTHING behind). The arms
547
+ // now run as one closure AFTER both gates pass.
548
+ const writeTelemetry = () => {
549
+ if ("result" in patch) {
550
+ // THE OUTPUT HISTORY (stroke 40 — the operator's QOL law: the
551
+ // preview's mini row is a CARD OUTPUT HISTORY): a stage's
552
+ // result is the CURRENT run's face, and every poll state
553
+ // overwrites it — the live patch ({run_id, progress}) would
554
+ // destroy the previous run's outputs. Before any result
555
+ // overwrite, a result CARRYING outputs is archived onto the
556
+ // stage's history, newest first, capped (12), deduped by its
557
+ // url set: only UNIQUE outputs ride (the operator's word — a
558
+ // re-run landing the same urls never double-lists). The
559
+ // history is DERIVED here, never patched in — the estate owns
560
+ // the trail; the client reads it.
561
+ //
562
+ // THE PARAMS SNAPSHOT (the output-component stroke — "old
563
+ // generations that had different parameters"): the params that
564
+ // MADE a result are captured at ITS LANDING (a parallel
565
+ // server-derived key), never at overwrite time — params
566
+ // edited between runs must not retroactively re-label the old
567
+ // generation. The archive entry carries the snapshot; the
568
+ // client diffs it against the live params for the chip.
569
+ const prev = stage["result"];
570
+ const next = patch["result"];
571
+
572
+ // the lost-gen disease: "when I make a new gen it doesn't
573
+ // appear in history — clicking a history item overwrites the
574
+ // preview with no way to recover the most recent gen"). Two
575
+ // archives ride the SAME write, oldest-first so the order in
576
+ // the list is append-stable:
577
+ // 1. SUPERSEDE — a cleared/overwritten result that carried
578
+ // outputs is archived (the stroke-40 law, unchanged).
579
+ // 2. LANDING — the incoming result itself, when it CARRIES
580
+ // outputs, enters the trail at its own landing. The old
581
+ // supersede-only law kept the CURRENT generation outside
582
+ // history (it lived only in the result slot, one
583
+ // view-pick away from unreachable). With the landing
584
+ // archived, history[0] is ALWAYS the newest gen — no
585
+ // pick, overwrite, or crash can orphan it. Live mid-run
586
+ // patches ({run_id, progress} — no urls) archive
587
+ // nothing, and the url-set dedup makes re-landings of
588
+ // the same outputs idempotent.
589
+ const archiveInto = (urls, entry) => {
590
+ const hist = Array.isArray(stage["history"]) ? [...stage["history"]] : [];
591
+ const already = hist.some((e) => sameUrlSet(resultOutputUrls(e), urls));
592
+ if (!already) {
593
+ hist.unshift(entry);
594
+ stage["history"] = hist.slice(0, 12);
595
+ }
596
+ };
597
+ // the params arm rides only when the snapshot NAMES something —
598
+ // an undefined or empty {} snapshot carries no key (the chip
599
+ // diffs against live params; an empty diff is no chip at all)
600
+ const paramsArm = (snapshot) => snapshot !== undefined && typeof snapshot === "object" && !Array.isArray(snapshot)
601
+ && Object.keys(snapshot).length > 0
602
+ ? { params: snapshot }
603
+ : {};
604
+ if (resultOutputUrls(prev).length > 0 && !sameResultOutputs(prev, next)) {
605
+ archiveInto(resultOutputUrls(prev), {
606
+ ...prev,
607
+ ...paramsArm(stage["resultParams"]),
608
+ });
609
+ }
610
+ stage["result"] = patch["result"];
611
+ stage["resultParams"] = stage["params"];
612
+ const landingUrls = resultOutputUrls(next);
613
+ if (landingUrls.length > 0) {
614
+ archiveInto(landingUrls, {
615
+ ...next,
616
+ ...paramsArm(stage["resultParams"]),
617
+ });
618
+ }
619
+ }
620
+ if ("runStatus" in patch)
621
+ stage["runStatus"] = patch["runStatus"];
622
+ // THE HEALTH ARM (LOW-23): the transport word's own channel — a
623
+ // null value DELETES the field (the recovery clear; a stage that
624
+ // never escalated never carries it), the merge-patch semantics
625
+ // the params arm already owns.
626
+ if ("health" in patch) {
627
+ if (patch["health"] === null)
628
+ delete stage["health"];
629
+ else
630
+ stage["health"] = patch["health"];
631
+ }
632
+ };
633
+ // the params the row will HOLD once this write lands (undefined
634
+ // when the body carries no params arm) — judged as half of the
635
+ // (params, inputs) PAIR before either assignment rides.
636
+ let pendingParams;
637
+ if ("params" in patch && patch["params"] !== null && typeof patch["params"] === "object" && !Array.isArray(patch["params"])) {
638
+ // PER-KEY merge (never replace) — the old door's law, now with
639
+ // JSON Merge Patch null semantics: a null value DELETES the
640
+ // key (the layered-params unset path — presence IS the override
641
+ // mark, so removal is a first-class write, never a value trick).
642
+ const merged = stage["params"] !== null && typeof stage["params"] === "object" && !Array.isArray(stage["params"])
643
+ ? { ...stage["params"] }
644
+ : {};
645
+
646
+ // every other params-bearing door (add, patch, load) unwraps the
647
+ // wire's {item:[…]} array shape before it stores, so storage
648
+ // holds clean arrays; this door merged the raw body, so the SAME
649
+ // patch through bench/patch and through drafts/patch produced
650
+ // two different stored rows — and the wrapped shape is poison
651
+ // for every reader downstream.
652
+ const incoming = unwrapItemArrays(patch["params"]);
653
+ for (const k of Object.keys(incoming)) {
654
+ if (incoming[k] === null)
655
+ delete merged[k];
656
+ else
657
+ merged[k] = incoming[k];
658
+ }
659
+
660
+ // audit's CRITICAL): bench docs live in THIS table (a
661
+ // "bench-<session>" id satisfies DRAFT_ID_RE), and the GUI's
662
+ // param editor rides exactly this call (card-view's
663
+ // patchStageParams) — so every keystroke edited a live stage
664
+ // with the model board AND the lane relations unjudged, while
665
+ // the agent's bench/patch refused the identical body. The
666
+ // merged view (never the patch arm alone — a per-key merge
667
+ // composes with what the row already carried) walks the same
668
+ // composed admission, BEFORE the assignment: the domain's
669
+ // cached record is mutated in place below, so a refusal after
670
+ // the assignment would leak the refused merge.
671
+ const stageCard = typeof stage["card"] === "string" ? stage["card"] : "";
672
+ const admit = this.benchAdmit();
673
+ if (stageCard !== "" && admit !== undefined) {
674
+ // NO PROVENANCE (the audit's GAP 3): the restored carve is
675
+ // load-time grace; a patch is a live compose act judged by
676
+ // the DECL the next run composes from. Forwarding the row's
677
+ // provenance here made the grace sticky forever.
678
+
679
+ // audit's row 7): the same self-naming strip every bench
680
+ // door judges through — a model key naming the row's own
681
+ // card never survives to compose, so this door judges the
682
+ // stripped view or refuses what the run admits.
683
+ admit(stageCard, strippedForAdmission(stageCard, merged), undefined);
684
+ }
685
+ // DEFERRED (the write-seat audit's row 3, second half): the
686
+ // assignment waits for the wiring pair-gate below — the pair
687
+ // (params, inputs) is judged as ONE write, and a refused
688
+ // orphan must leak NEITHER half into the cached record.
689
+ pendingParams = merged;
690
+ }
691
+
692
+ // CRITICAL-2: the params arm walked the composed admission and
693
+ // this one did not — so a keystroke through the GUI's param
694
+ // editor could write a phantom wire or a $spell that the agent's
695
+ // bench/patch refuses on the identical body. The wiring gate is
696
+ // the bench domain's own (one implementation): the MERGED edges,
697
+ // judged against the live set BEFORE the assignment (the cached
698
+ // record mutates in place below).
699
+
700
+ // this door also patches FLOW-export drafts, whose inputs wire
701
+ // stages of their own document — a different law, judged where
702
+ // that document is compiled. The wiring gate belongs to the bench.
703
+ // A FLOW-EXPORT DOC'S INPUTS ARE ITS OWN DOCUMENT'S LAW
704
+
705
+ // below is the BENCH's wire law (a bench row is patched in place,
706
+ // so `null` must remove an edge). A flow-export draft replaces its
707
+ // inputs wholesale, which is what this door did before the wiring
708
+ // gate was added and what a later cut silently changed. The gate
709
+ // itself is bench-only — a flow doc's stages wire within their own
710
+ // document, judged where that document is compiled.
711
+ const isBenchDoc = draftId.startsWith(BENCH_ID_PREFIX);
712
+ if (!isBenchDoc) {
713
+ if ("inputs" in patch)
714
+ stage["inputs"] = patch["inputs"];
715
+ }
716
+ else {
717
+ const priorInputs = isObj(stage["inputs"]) ? stage["inputs"] : {};
718
+ const mergedInputs = { ...priorInputs };
719
+ if (isObj(patch["inputs"])) {
720
+ for (const [chan, ref] of Object.entries(patch["inputs"])) {
721
+ if (ref === null)
722
+ delete mergedInputs[chan];
723
+ else
724
+ mergedInputs[chan] = ref;
725
+ }
726
+ }
727
+
728
+
729
+ // `inputs`, so a params-only keystroke that dropped the
730
+ // "$image" spell orphaned the wire SILENTLY — the merged pair
731
+ // nobody judged was exactly the dead wire bench/patch refuses
732
+ // on the identical body. The gate now runs whenever EITHER
733
+ // half of the pair changes, over the PENDING params (the row's
734
+ // next view) and the merged inputs, BEFORE either lands.
735
+ if (pendingParams !== undefined || "inputs" in patch) {
736
+ // THE LIVE SET READS THE STAGES THE LOOKUP ABOVE ALREADY
737
+
738
+ // same value `stages.find` answered one screen up — a value
739
+ // that answers `find` is the array it walked — so the
740
+ // non-array arm and this row's own `?? ""` were dead legs
741
+ // (the row is the very one the finder selected BECAUSE its
742
+ // `id` equals the stage id asked for).
743
+ const liveIds = doc["stages"]
744
+ .map((r) => (isObj(r) ? String(r["id"] ?? "") : ""))
745
+ .filter((id) => id !== stageId);
746
+ admitWiring(stage, pendingParams !== undefined
747
+ ? pendingParams
748
+ : (isObj(stage["params"]) ? stage["params"] : {}), mergedInputs, liveIds, stageId, draftId.replace(/^bench-/, ""));
749
+ }
750
+ stage["inputs"] = mergedInputs;
751
+ }
752
+ if (pendingParams !== undefined) {
753
+ // THE STORED ROW IS THE JUDGED ROW (the bench audit's gap 3,
754
+
755
+ // the raw one — so a doc written here held a self-naming model
756
+ // key that add/load never store, and the next reader judged a
757
+ // different object than the one on disk. Strip once, judge that,
758
+ // store that.
759
+ stage["params"] = strippedForAdmission(typeof stage["card"] === "string" ? stage["card"] : "", pendingParams);
760
+ }
761
+ if ("inputOutputs" in patch)
762
+ stage["inputOutputs"] = patch["inputOutputs"];
763
+ // EVERY GATE PASSED — the telemetry write lands now (deferred
764
+ // above so a refusal left no result behind; the run seat's own
765
+ // polls ride the same door and are judged by nothing, which is
766
+ // correct: a poll is not a composition)
767
+ writeTelemetry();
768
+ // THE BUMP (author patches only — the telemetry law above): the
769
+ // rev advances and the log carries the entry; a telemetry write
770
+ // persists the row WITHOUT touching the bench block (no bump, no
771
+ // log — the answer serves the CURRENT rev so a caller that treats
772
+ // it as the next CAS handle stays honest).
773
+ let rev = bench.rev;
774
+
775
+ // from cut to carried" — the toggle left NO trace: every params
776
+ // patch logged the summary "params", so four revs of edits could
777
+ // not be told apart from four model stamps. A params patch now
778
+ // names the keys it merged — the log alone answers "did the seam
779
+ // write ever land".) The spell: `params` alone when the patch
780
+ // carried an empty/non-object params arm (a door refusal would
781
+ // never let us get here), else `params:<inner keys sorted>`.
782
+ const paramsSummary = (p) => {
783
+ if (p === null || typeof p !== "object" || Array.isArray(p))
784
+ return "params";
785
+ const inner = Object.keys(p).sort();
786
+ return inner.length === 0 ? "params" : "params:" + inner.join(",");
787
+ };
788
+ const summary = Object.keys(patch).sort()
789
+ .map((k) => (k === "params" ? paramsSummary(patch["params"]) : k))
790
+ .join(",");
791
+ if (!telemetry) {
792
+ rev = bench.rev + 1;
793
+ const log = [...bench.log, {
794
+ rev,
795
+ at: Math.floor(Date.now() / 1000),
796
+ author: "user",
797
+ kind: "patch",
798
+ stage: stageId,
799
+ summary,
800
+ }];
801
+ doc["bench"] = { rev, log };
802
+ }
803
+ // THE AUDIT VOICE (the same night): the patch's arrival and its
804
+ // named keys also land in the service log — a UI write that dies
805
+ // client-side (never sent) leaves NO such line, so the silence is
806
+ // distinguishable from a refusal.
807
+ this.ctx.logger.info("drafts/patch %s %s rev=%d %s", draftId, stageId, rev, summary);
808
+ try {
809
+ await drafts.table("docs").put(draftId, {
810
+ updated_at: Math.floor(Date.now() / 1000),
811
+ doc: doc,
812
+ });
813
+ }
814
+ catch (error) {
815
+ /* v8 ignore next 7 -- a domain put failure requires a storage IO
816
+ * fault (disk/permission); the record is schema-valid by
817
+ * construction, so no in-process input can reach this arm. */
818
+ throw new RemoteError("draft/cannot-persist", thrownMessage(error), {}, { cause: error });
819
+ }
820
+ notifyBenchDocWrite(draftId);
821
+ return {
822
+ draftId,
823
+ stageId,
824
+ delivered: true,
825
+ patched: Object.keys(patch).sort(),
826
+ rev,
827
+ };
828
+ }
829
+ };
830
+ })();
831
+ export { DraftsRemoteService };
832
+ /** The bench Remote owner: ensure/get over the same drafts domain
833
+ * (the bench doc IS a draft doc, id "bench-<session>"). */
834
+ let BenchRemoteService = (() => {
835
+ let _classSuper = TypertRemoteService;
836
+ let _instanceExtraInitializers = [];
837
+ let _remoteExportGet_decorators;
838
+ let _subscribe_decorators;
839
+ let _remoteExportEnsure_decorators;
840
+ let _remoteExportPatch_decorators;
841
+ let _remoteExportAddStage_decorators;
842
+ let _remoteExportRemoveStage_decorators;
843
+ let _remoteExportClear_decorators;
844
+ let _remoteExportRewire_decorators;
845
+ let _remoteExportReorder_decorators;
846
+ let _remoteExportGroup_decorators;
847
+ let _remoteExportUngroup_decorators;
848
+ let _remoteExportUndo_decorators;
849
+ return class BenchRemoteService extends _classSuper {
850
+ static {
851
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
852
+ _remoteExportGet_decorators = [Remote("get")];
853
+ _subscribe_decorators = [Remote({ mode: "stream" })];
854
+ _remoteExportEnsure_decorators = [Remote("ensure")];
855
+ _remoteExportPatch_decorators = [Remote("patch")];
856
+ _remoteExportAddStage_decorators = [Remote("add-stage")];
857
+ _remoteExportRemoveStage_decorators = [Remote("remove-stage")];
858
+ _remoteExportClear_decorators = [Remote("clear")];
859
+ _remoteExportRewire_decorators = [Remote("rewire")];
860
+ _remoteExportReorder_decorators = [Remote("reorder")];
861
+ _remoteExportGroup_decorators = [Remote("group")];
862
+ _remoteExportUngroup_decorators = [Remote("ungroup")];
863
+ _remoteExportUndo_decorators = [Remote("undo")];
864
+ __esDecorate(this, null, _remoteExportGet_decorators, { kind: "method", name: "remoteExportGet", static: false, private: false, access: { has: obj => "remoteExportGet" in obj, get: obj => obj.remoteExportGet }, metadata: _metadata }, null, _instanceExtraInitializers);
865
+ __esDecorate(this, null, _subscribe_decorators, { kind: "method", name: "subscribe", static: false, private: false, access: { has: obj => "subscribe" in obj, get: obj => obj.subscribe }, metadata: _metadata }, null, _instanceExtraInitializers);
866
+ __esDecorate(this, null, _remoteExportEnsure_decorators, { kind: "method", name: "remoteExportEnsure", static: false, private: false, access: { has: obj => "remoteExportEnsure" in obj, get: obj => obj.remoteExportEnsure }, metadata: _metadata }, null, _instanceExtraInitializers);
867
+ __esDecorate(this, null, _remoteExportPatch_decorators, { kind: "method", name: "remoteExportPatch", static: false, private: false, access: { has: obj => "remoteExportPatch" in obj, get: obj => obj.remoteExportPatch }, metadata: _metadata }, null, _instanceExtraInitializers);
868
+ __esDecorate(this, null, _remoteExportAddStage_decorators, { kind: "method", name: "remoteExportAddStage", static: false, private: false, access: { has: obj => "remoteExportAddStage" in obj, get: obj => obj.remoteExportAddStage }, metadata: _metadata }, null, _instanceExtraInitializers);
869
+ __esDecorate(this, null, _remoteExportRemoveStage_decorators, { kind: "method", name: "remoteExportRemoveStage", static: false, private: false, access: { has: obj => "remoteExportRemoveStage" in obj, get: obj => obj.remoteExportRemoveStage }, metadata: _metadata }, null, _instanceExtraInitializers);
870
+ __esDecorate(this, null, _remoteExportClear_decorators, { kind: "method", name: "remoteExportClear", static: false, private: false, access: { has: obj => "remoteExportClear" in obj, get: obj => obj.remoteExportClear }, metadata: _metadata }, null, _instanceExtraInitializers);
871
+ __esDecorate(this, null, _remoteExportRewire_decorators, { kind: "method", name: "remoteExportRewire", static: false, private: false, access: { has: obj => "remoteExportRewire" in obj, get: obj => obj.remoteExportRewire }, metadata: _metadata }, null, _instanceExtraInitializers);
872
+ __esDecorate(this, null, _remoteExportReorder_decorators, { kind: "method", name: "remoteExportReorder", static: false, private: false, access: { has: obj => "remoteExportReorder" in obj, get: obj => obj.remoteExportReorder }, metadata: _metadata }, null, _instanceExtraInitializers);
873
+ __esDecorate(this, null, _remoteExportGroup_decorators, { kind: "method", name: "remoteExportGroup", static: false, private: false, access: { has: obj => "remoteExportGroup" in obj, get: obj => obj.remoteExportGroup }, metadata: _metadata }, null, _instanceExtraInitializers);
874
+ __esDecorate(this, null, _remoteExportUngroup_decorators, { kind: "method", name: "remoteExportUngroup", static: false, private: false, access: { has: obj => "remoteExportUngroup" in obj, get: obj => obj.remoteExportUngroup }, metadata: _metadata }, null, _instanceExtraInitializers);
875
+ __esDecorate(this, null, _remoteExportUndo_decorators, { kind: "method", name: "remoteExportUndo", static: false, private: false, access: { has: obj => "remoteExportUndo" in obj, get: obj => obj.remoteExportUndo }, metadata: _metadata }, null, _instanceExtraInitializers);
876
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
877
+ }
878
+ constructor(ctx) {
879
+ super(ctx, "benchRemote", { namespace: "bench" });
880
+ __runInitializers(this, _instanceExtraInitializers);
881
+
882
+ // push spine is the typed `bench.changed` event, not a module Set.
883
+ installBenchChangeSeam(ctx);
884
+ }
885
+ /** Endpoint `bench/get` — one session's bench block as the row. */
886
+ async remoteExportGet(sessionKey, signal) {
887
+ return await this.get(sessionKey, signal);
888
+ }
889
+
890
+
891
+
892
+
893
+
894
+
895
+
896
+
897
+
898
+
899
+ async *subscribe(sessionKey, signal) {
900
+ signal.throwIfAborted();
901
+ for (;;) {
902
+ // THE ZERO-STATE LAW (the stream tail's birth watch): a fresh
903
+ // conversation's bench does not exist until its first write.
904
+ // The gateway's RemoteStream classifies a REJECTED open as
905
+ // TERMINAL (no retry — terminalStreamFailure), so a throwing
906
+ // first read here would kill the browser's whole bench stream
907
+ // the moment a conversation starts. The watch instead serves
908
+ // the ZERO STATE (rev 0, empty stages — the same row shape the
909
+ // client's emptyBench served in the poll era) and stays open:
910
+ // the birth (ensure's notifyBenchChange) wakes the park and the
911
+ // next read serves the born bench. `bench/get` still refuses
912
+ // not-found for direct unary callers — only the WATCH degrades.
913
+ let row;
914
+ try {
915
+ row = await this.get(sessionKey, signal);
916
+ }
917
+ catch (error) {
918
+ if (error instanceof RemoteError &&
919
+ error.code === "bench/not-found") {
920
+ row = {
921
+ draft_id: benchDraftIdOf(sessionKey),
922
+ session: sessionKey,
923
+ rev: 0,
924
+ intent: "",
925
+ stages: [],
926
+ log: [],
927
+ };
928
+ }
929
+ else {
930
+ throw error;
931
+ }
932
+ }
933
+ yield row;
934
+ // wake on the session's next write (or abort). An abort that
935
+ // landed while the generator was suspended AT A YIELD has no
936
+ // listener attached — the executor re-checks signal.aborted so
937
+ // the missed event still wakes the park. The stream then ends
938
+ // CLEAN on abort (done: true — the holder released; a throw
939
+ // would surface as an error on a normal close). One finish arm
940
+ // removes BOTH listeners: no stale abort listener per park.
941
+ await new Promise((resolve) => {
942
+ if (signal.aborted) {
943
+ resolve();
944
+ return;
945
+ }
946
+ // One finish arm, no re-entry guard needed: finish detaches
947
+ // BOTH listeners before resolving, so it can never run twice
948
+ // (the old settled-guard was unreachable by construction).
949
+ const offAbort = () => { signal.removeEventListener("abort", onAbort); };
950
+ const finish = () => {
951
+ offChange();
952
+ offAbort();
953
+ resolve();
954
+ };
955
+ const offChange = onBenchChange((session) => {
956
+ if (session !== sessionKey)
957
+ return;
958
+ finish();
959
+ });
960
+ const onAbort = () => { finish(); };
961
+ signal.addEventListener("abort", onAbort, { once: true });
962
+ });
963
+ if (signal.aborted)
964
+ return;
965
+ }
966
+ }
967
+ /** Endpoint `bench/ensure` — birth or load (the only creating arm). */
968
+ async remoteExportEnsure(doc, signal) {
969
+ return await this.ensure(doc, signal);
970
+ }
971
+ /** The in-process API the adapters above delegate to. Classified
972
+ * bench failures pass through untouched; only the open itself
973
+ * wraps as bench/store-failed. */
974
+ async get(session, signal) {
975
+ signal.throwIfAborted();
976
+ let drafts;
977
+ try {
978
+ drafts = await openDraftsDomain(this.ctx);
979
+ }
980
+ catch (error) {
981
+ throw new RemoteError("bench/store-failed", thrownMessage(error), {}, { cause: error });
982
+ }
983
+ const row = getBench(drafts, session);
984
+ return { ...row, stages: withResolvedStages(this.ctx, row.stages) };
985
+ }
986
+ /** The in-process API the adapter above delegates to. */
987
+ async ensure(doc, signal) {
988
+ signal.throwIfAborted();
989
+ if (typeof doc !== "object" || doc === null || Array.isArray(doc)) {
990
+ throw new RemoteError("bench/bad-body", "body must be a JSON object", {});
991
+ }
992
+ let drafts;
993
+ try {
994
+ drafts = await openDraftsDomain(this.ctx);
995
+ }
996
+ catch (error) {
997
+ throw new RemoteError("bench/store-failed", thrownMessage(error), {}, { cause: error });
998
+ }
999
+ const row = await ensureBench(drafts, doc, benchAdmissionOf(this.ctx));
1000
+ // ensureBench gates doc.session as a valid key before this line —
1001
+ // the row's own session can never differ here (no dead fallback).
1002
+ notifyBenchChange(String(doc["session"]));
1003
+ return row;
1004
+ }
1005
+ /** Endpoint `bench/patch` — params merge (+result/runStatus) with
1006
+ * CAS + log. */
1007
+ async remoteExportPatch(sessionKey, doc, signal) {
1008
+ return await this.patch(sessionKey, doc, signal);
1009
+ }
1010
+ /** Endpoint `bench/add-stage` — append a card stage. */
1011
+ async remoteExportAddStage(sessionKey, doc, signal) {
1012
+ return await this.addStage(sessionKey, doc, signal);
1013
+ }
1014
+ /** Endpoint `bench/remove-stage` — splice one stage out. */
1015
+ async remoteExportRemoveStage(sessionKey, doc, signal) {
1016
+ return await this.removeStage(sessionKey, doc, signal);
1017
+ }
1018
+
1019
+
1020
+
1021
+
1022
+ async remoteExportClear(sessionKey, doc, signal) {
1023
+ return await this.clear(sessionKey, doc, signal);
1024
+ }
1025
+ /** Endpoint `bench/rewire` — set or clear one input binding. */
1026
+ async remoteExportRewire(sessionKey, doc, signal) {
1027
+ return await this.rewire(sessionKey, doc, signal);
1028
+ }
1029
+
1030
+
1031
+ async remoteExportReorder(sessionKey, doc, signal) {
1032
+ signal.throwIfAborted();
1033
+ const drafts = await this.open(signal);
1034
+ const row = await reorderBench(drafts, sessionKey, doc);
1035
+ notifyBenchChange(sessionKey);
1036
+ return row;
1037
+ }
1038
+
1039
+
1040
+ async remoteExportGroup(sessionKey, doc, signal) {
1041
+ signal.throwIfAborted();
1042
+ const drafts = await this.open(signal);
1043
+ const row = await groupBench(drafts, sessionKey, doc, benchAdmissionOf(this.ctx));
1044
+ notifyBenchChange(sessionKey);
1045
+ return row;
1046
+ }
1047
+ /** Endpoint `bench/ungroup` — spill a compound's children back. */
1048
+ async remoteExportUngroup(sessionKey, doc, signal) {
1049
+ signal.throwIfAborted();
1050
+ const drafts = await this.open(signal);
1051
+ const row = await ungroupBench(drafts, sessionKey, doc, benchAdmissionOf(this.ctx));
1052
+ notifyBenchChange(sessionKey);
1053
+ return row;
1054
+ }
1055
+ /** Endpoint `bench/undo` — walk the log tail, apply inverses. */
1056
+ async remoteExportUndo(sessionKey, doc, signal) {
1057
+ return await this.undo(sessionKey, doc, signal);
1058
+ }
1059
+ /** The in-process APIs the adapters above delegate to. The
1060
+ * domain's classified failures pass through untouched; only the
1061
+ * open itself wraps as bench/store-failed. */
1062
+ async open(signal) {
1063
+ signal.throwIfAborted();
1064
+ try {
1065
+ return await openDraftsDomain(this.ctx);
1066
+ }
1067
+ catch (error) {
1068
+ throw new RemoteError("bench/store-failed", thrownMessage(error), {}, { cause: error });
1069
+ }
1070
+ }
1071
+ async patch(session, doc, signal) {
1072
+ signal.throwIfAborted();
1073
+ const drafts = await this.open(signal);
1074
+ const row = await patchBench(drafts, session, doc, benchAdmissionOf(this.ctx));
1075
+ notifyBenchChange(session);
1076
+ return row;
1077
+ }
1078
+ async addStage(session, doc, signal) {
1079
+ signal.throwIfAborted();
1080
+ const drafts = await this.open(signal);
1081
+ const row = await addStageBench(drafts, session, doc, benchAdmissionOf(this.ctx));
1082
+ notifyBenchChange(session);
1083
+ return row;
1084
+ }
1085
+ async removeStage(session, doc, signal) {
1086
+ signal.throwIfAborted();
1087
+ const drafts = await this.open(signal);
1088
+ const row = await removeStageBench(drafts, session, doc);
1089
+ notifyBenchChange(session);
1090
+ return row;
1091
+ }
1092
+ async clear(session, doc, signal) {
1093
+ signal.throwIfAborted();
1094
+ const drafts = await this.open(signal);
1095
+ const row = await clearBench(drafts, session, doc);
1096
+ notifyBenchChange(session);
1097
+ return row;
1098
+ }
1099
+ async rewire(session, doc, signal) {
1100
+ signal.throwIfAborted();
1101
+ const drafts = await this.open(signal);
1102
+ const row = await rewireBench(drafts, session, doc);
1103
+ notifyBenchChange(session);
1104
+ return row;
1105
+ }
1106
+ async undo(session, doc, signal) {
1107
+ signal.throwIfAborted();
1108
+ const drafts = await this.open(signal);
1109
+ const row = await undoBench(drafts, session, doc, benchAdmissionOf(this.ctx));
1110
+ notifyBenchChange(session);
1111
+ return row;
1112
+ }
1113
+ };
1114
+ })();
1115
+ export { BenchRemoteService };
1116
+ //# sourceMappingURL=remote.js.map