@tangle-network/agent-app 0.44.47 → 0.44.49
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 +4 -4
- package/dist/assistant/index.js +2 -2
- package/dist/assistant/index.js.map +1 -1
- package/dist/chat-routes/index.d.ts +2 -1
- package/dist/chat-routes/index.js +9 -7
- package/dist/chat-routes/index.js.map +1 -1
- package/dist/{chunk-2EJFIQUV.js → chunk-6VWA26BV.js} +57 -79
- package/dist/chunk-6VWA26BV.js.map +1 -0
- package/dist/{chunk-VGATER6G.js → chunk-6W5Y4J2X.js} +2 -2
- package/dist/chunk-6W5Y4J2X.js.map +1 -0
- package/dist/{chunk-T3TYG3VW.js → chunk-D3GK2IPA.js} +2 -2
- package/dist/chunk-JEZJ6HTF.js +77 -0
- package/dist/chunk-JEZJ6HTF.js.map +1 -0
- package/dist/{chunk-3E4MW5LR.js → chunk-YOSSGDIG.js} +25 -16
- package/dist/chunk-YOSSGDIG.js.map +1 -0
- package/dist/runtime/index.d.ts +4 -4
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/index.js.map +1 -1
- package/dist/sandbox/index.d.ts +2 -1
- package/dist/sandbox/index.js +1 -1
- package/dist/session-shell/index.d.ts +11 -2
- package/dist/session-shell/index.js +1 -1
- package/dist/stream/index.d.ts +1 -1
- package/dist/stream/index.js +11 -5
- package/dist/{turn-buffer-BcPfhr1_.d.ts → turn-buffer-CFKQlnxf.d.ts} +24 -6
- package/dist/turn-stream/index.d.ts +3 -1
- package/dist/turn-stream/index.js +8 -1
- package/dist/turn-stream/index.js.map +1 -1
- package/dist/web-react/index.js +2 -2
- package/package.json +17 -20
- package/dist/chunk-2EJFIQUV.js.map +0 -1
- package/dist/chunk-3E4MW5LR.js.map +0 -1
- package/dist/chunk-VGATER6G.js.map +0 -1
- /package/dist/{chunk-T3TYG3VW.js.map → chunk-D3GK2IPA.js.map} +0 -0
|
@@ -1,73 +1,6 @@
|
|
|
1
|
-
// src/stream/turn-identity.ts
|
|
2
|
-
function normalizeClientTurnId(value) {
|
|
3
|
-
if (value === void 0 || value === null) return void 0;
|
|
4
|
-
if (typeof value !== "string") throw new Error("turnId must be a string");
|
|
5
|
-
const trimmed = value.trim();
|
|
6
|
-
if (!trimmed) throw new Error("turnId must not be blank");
|
|
7
|
-
if (trimmed.length > 160) throw new Error("turnId is too long");
|
|
8
|
-
if (!/^[A-Za-z0-9:_-]+$/.test(trimmed)) {
|
|
9
|
-
throw new Error("turnId contains unsupported characters");
|
|
10
|
-
}
|
|
11
|
-
return trimmed;
|
|
12
|
-
}
|
|
13
|
-
function buildUserTextParts(text, turnId) {
|
|
14
|
-
const part = { type: "text", text };
|
|
15
|
-
if (turnId) part.turnId = turnId;
|
|
16
|
-
return [part];
|
|
17
|
-
}
|
|
18
|
-
function messageHasTurnId(message, turnId) {
|
|
19
|
-
for (const part of message.parts ?? []) {
|
|
20
|
-
if (part && typeof part === "object" && String(part.turnId ?? "") === turnId) {
|
|
21
|
-
return true;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
function resolveChatTurn(input) {
|
|
27
|
-
const { existingMessages, userContent, turnId } = input;
|
|
28
|
-
const reusableIndex = findReusableUserMessageIndex(
|
|
29
|
-
existingMessages,
|
|
30
|
-
userContent,
|
|
31
|
-
turnId,
|
|
32
|
-
input.hasRunningTurn === true
|
|
33
|
-
);
|
|
34
|
-
if (reusableIndex >= 0) {
|
|
35
|
-
const reusedId = existingMessages[reusableIndex]?.id;
|
|
36
|
-
return {
|
|
37
|
-
turnIndex: countUserMessages(existingMessages.slice(0, reusableIndex)),
|
|
38
|
-
shouldInsertUserMessage: false,
|
|
39
|
-
priorMessages: existingMessages.slice(0, reusableIndex),
|
|
40
|
-
userParts: buildUserTextParts(userContent, turnId),
|
|
41
|
-
...typeof reusedId === "string" && reusedId ? { reusedUserMessageId: reusedId } : {}
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
return {
|
|
45
|
-
turnIndex: countUserMessages(existingMessages),
|
|
46
|
-
shouldInsertUserMessage: true,
|
|
47
|
-
priorMessages: existingMessages,
|
|
48
|
-
userParts: buildUserTextParts(userContent, turnId)
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
function findReusableUserMessageIndex(messages, userContent, turnId, hasRunningTurn) {
|
|
52
|
-
if (turnId) {
|
|
53
|
-
for (let index2 = messages.length - 1; index2 >= 0; index2 -= 1) {
|
|
54
|
-
const message = messages[index2];
|
|
55
|
-
if (message?.role === "user" && messageHasTurnId(message, turnId)) return index2;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
let index = messages.length - 1;
|
|
59
|
-
if (hasRunningTurn) {
|
|
60
|
-
while (index >= 0 && messages[index]?.role === "assistant") index -= 1;
|
|
61
|
-
}
|
|
62
|
-
const latest = index >= 0 ? messages[index] : void 0;
|
|
63
|
-
if (latest?.role === "user" && latest.content === userContent) return index;
|
|
64
|
-
return -1;
|
|
65
|
-
}
|
|
66
|
-
function countUserMessages(messages) {
|
|
67
|
-
return messages.filter((message) => message.role === "user").length;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
1
|
// src/stream/turn-buffer.ts
|
|
2
|
+
var DEFAULT_RUNNING_TURN_LEASE_MS = 5 * 6e4;
|
|
3
|
+
var DEFAULT_RUNNING_TURN_RENEW_INTERVAL_MS = 3e4;
|
|
71
4
|
function deltaTypeOf(ev) {
|
|
72
5
|
const e = ev;
|
|
73
6
|
if (!e || typeof e !== "object") return null;
|
|
@@ -128,6 +61,29 @@ function createBufferedTurnTap(opts) {
|
|
|
128
61
|
let pending = [];
|
|
129
62
|
let lastFlush = Date.now();
|
|
130
63
|
let started = false;
|
|
64
|
+
let settled = false;
|
|
65
|
+
let renewalTimer;
|
|
66
|
+
let renewal = Promise.resolve();
|
|
67
|
+
function clearRenewalTimer() {
|
|
68
|
+
if (renewalTimer !== void 0) clearTimeout(renewalTimer);
|
|
69
|
+
renewalTimer = void 0;
|
|
70
|
+
}
|
|
71
|
+
function scheduleRenewal() {
|
|
72
|
+
if (settled || !opts.scopeId) return;
|
|
73
|
+
const intervalMs = Math.max(
|
|
74
|
+
1,
|
|
75
|
+
opts.runningTurnRenewIntervalMs ?? DEFAULT_RUNNING_TURN_RENEW_INTERVAL_MS
|
|
76
|
+
);
|
|
77
|
+
renewalTimer = setTimeout(() => {
|
|
78
|
+
renewalTimer = void 0;
|
|
79
|
+
if (settled) return;
|
|
80
|
+
renewal = opts.store.setStatus(opts.turnId, "running", opts.scopeId).catch(() => {
|
|
81
|
+
}).then(scheduleRenewal);
|
|
82
|
+
}, intervalMs);
|
|
83
|
+
if (typeof renewalTimer === "object" && "unref" in renewalTimer) {
|
|
84
|
+
renewalTimer.unref();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
131
87
|
async function flush() {
|
|
132
88
|
if (pending.length === 0) return;
|
|
133
89
|
const batch = coalesce(pending);
|
|
@@ -140,6 +96,7 @@ function createBufferedTurnTap(opts) {
|
|
|
140
96
|
if (started) return;
|
|
141
97
|
started = true;
|
|
142
98
|
await opts.store.setStatus(opts.turnId, "running", opts.scopeId);
|
|
99
|
+
scheduleRenewal();
|
|
143
100
|
}
|
|
144
101
|
return {
|
|
145
102
|
async onEvent(raw) {
|
|
@@ -157,6 +114,9 @@ function createBufferedTurnTap(opts) {
|
|
|
157
114
|
},
|
|
158
115
|
async done(status = "complete") {
|
|
159
116
|
await ensureStarted();
|
|
117
|
+
settled = true;
|
|
118
|
+
clearRenewalTimer();
|
|
119
|
+
await renewal;
|
|
160
120
|
if (status === "error") {
|
|
161
121
|
await flush().catch(() => {
|
|
162
122
|
});
|
|
@@ -225,7 +185,12 @@ CREATE TABLE IF NOT EXISTS turn_status (
|
|
|
225
185
|
CREATE INDEX IF NOT EXISTS idx_turn_status_scope ON turn_status (scopeId, status);
|
|
226
186
|
`;
|
|
227
187
|
var TURN_STATUS_SCOPE_MIGRATION_SQL = `ALTER TABLE turn_status ADD COLUMN scopeId TEXT;`;
|
|
228
|
-
function createD1TurnEventStore(db) {
|
|
188
|
+
function createD1TurnEventStore(db, options = {}) {
|
|
189
|
+
const now = options.now ?? Date.now;
|
|
190
|
+
const runningTurnLeaseMs = Math.max(
|
|
191
|
+
1,
|
|
192
|
+
options.runningTurnLeaseMs ?? DEFAULT_RUNNING_TURN_LEASE_MS
|
|
193
|
+
);
|
|
229
194
|
return {
|
|
230
195
|
async append(turnId, events) {
|
|
231
196
|
if (!events.length) return;
|
|
@@ -240,23 +205,31 @@ function createD1TurnEventStore(db) {
|
|
|
240
205
|
async setStatus(turnId, status, scopeId) {
|
|
241
206
|
await db.prepare(
|
|
242
207
|
"INSERT INTO turn_status (turnId, status, scopeId, updatedAt) VALUES (?, ?, ?, ?) ON CONFLICT(turnId) DO UPDATE SET status = excluded.status, scopeId = COALESCE(excluded.scopeId, turn_status.scopeId), updatedAt = excluded.updatedAt"
|
|
243
|
-
).bind(turnId, status, scopeId ?? null,
|
|
208
|
+
).bind(turnId, status, scopeId ?? null, new Date(now()).toISOString()).run();
|
|
244
209
|
},
|
|
245
210
|
async getStatus(turnId) {
|
|
246
211
|
const row = await db.prepare("SELECT status FROM turn_status WHERE turnId = ?").bind(turnId).first();
|
|
247
212
|
return row?.status ?? null;
|
|
248
213
|
},
|
|
249
214
|
async listRunning(scopeId) {
|
|
250
|
-
const { results } = await db.prepare(
|
|
215
|
+
const { results } = await db.prepare(
|
|
216
|
+
"SELECT turnId FROM turn_status WHERE scopeId = ? AND status = 'running' AND updatedAt >= ? ORDER BY updatedAt DESC, rowid DESC"
|
|
217
|
+
).bind(scopeId, new Date(now() - runningTurnLeaseMs).toISOString()).all();
|
|
251
218
|
return results.map((r) => r.turnId);
|
|
252
219
|
}
|
|
253
220
|
};
|
|
254
221
|
}
|
|
255
|
-
function createMemoryTurnEventStore() {
|
|
222
|
+
function createMemoryTurnEventStore(options = {}) {
|
|
256
223
|
const events = /* @__PURE__ */ new Map();
|
|
257
224
|
const status = /* @__PURE__ */ new Map();
|
|
258
225
|
const scopes = /* @__PURE__ */ new Map();
|
|
259
226
|
const order = [];
|
|
227
|
+
const updatedAt = /* @__PURE__ */ new Map();
|
|
228
|
+
const now = options.now ?? Date.now;
|
|
229
|
+
const runningTurnLeaseMs = Math.max(
|
|
230
|
+
1,
|
|
231
|
+
options.runningTurnLeaseMs ?? DEFAULT_RUNNING_TURN_LEASE_MS
|
|
232
|
+
);
|
|
260
233
|
return {
|
|
261
234
|
async append(turnId, rows) {
|
|
262
235
|
const list = events.get(turnId) ?? [];
|
|
@@ -270,21 +243,26 @@ function createMemoryTurnEventStore() {
|
|
|
270
243
|
status.set(turnId, s);
|
|
271
244
|
if (scopeId) scopes.set(turnId, scopeId);
|
|
272
245
|
if (!order.includes(turnId)) order.push(turnId);
|
|
246
|
+
updatedAt.set(turnId, now());
|
|
273
247
|
},
|
|
274
248
|
async getStatus(turnId) {
|
|
275
249
|
return status.get(turnId) ?? null;
|
|
276
250
|
},
|
|
277
251
|
async listRunning(scopeId) {
|
|
278
|
-
|
|
252
|
+
const cutoff = now() - runningTurnLeaseMs;
|
|
253
|
+
return order.filter(
|
|
254
|
+
(turnId) => status.get(turnId) === "running" && scopes.get(turnId) === scopeId && (updatedAt.get(turnId) ?? Number.NEGATIVE_INFINITY) >= cutoff
|
|
255
|
+
).sort((left, right) => {
|
|
256
|
+
const updatedDelta = (updatedAt.get(right) ?? 0) - (updatedAt.get(left) ?? 0);
|
|
257
|
+
return updatedDelta || order.indexOf(right) - order.indexOf(left);
|
|
258
|
+
});
|
|
279
259
|
}
|
|
280
260
|
};
|
|
281
261
|
}
|
|
282
262
|
|
|
283
263
|
export {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
messageHasTurnId,
|
|
287
|
-
resolveChatTurn,
|
|
264
|
+
DEFAULT_RUNNING_TURN_LEASE_MS,
|
|
265
|
+
DEFAULT_RUNNING_TURN_RENEW_INTERVAL_MS,
|
|
288
266
|
coalesceDeltas,
|
|
289
267
|
coalesceChatStreamEvents,
|
|
290
268
|
createBufferedTurnTap,
|
|
@@ -296,4 +274,4 @@ export {
|
|
|
296
274
|
createD1TurnEventStore,
|
|
297
275
|
createMemoryTurnEventStore
|
|
298
276
|
};
|
|
299
|
-
//# sourceMappingURL=chunk-
|
|
277
|
+
//# sourceMappingURL=chunk-6VWA26BV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/stream/turn-buffer.ts"],"sourcesContent":["/**\n * Resumable chat turns — the router-path answer to \"streams resume on\n * disconnect\" (issue #27). A turn's loop events are teed into a store as they\n * stream; the turn keeps running under `ctx.waitUntil` when the client drops;\n * a reconnecting client replays the buffered tail by sequence number and\n * keeps following until the turn completes.\n *\n * POST /chat/stream → pumpBufferedTurn(...) + live NDJSON\n * GET /chat/stream/:turnId → replayTurnEvents({ fromSeq }) → NDJSON\n *\n * Storage is a structural seam ({@link TurnEventStore}); a D1 implementation\n * ships here because that's what Cloudflare products have (KV is unsuitable:\n * eventually consistent cross-isolate). Per-token deltas would mean hundreds\n * of rows per turn, so consecutive text/reasoning deltas are coalesced within\n * a flush window before they are persisted — replay yields slightly chunkier\n * deltas with identical concatenation.\n */\n\nexport type TurnStatus = 'running' | 'complete' | 'error'\n\n/** A running row is a renewable lease, not permanent truth. If the process\n * driving a turn dies before writing a terminal status, reconnect discovery\n * must eventually stop returning that abandoned row. */\nexport const DEFAULT_RUNNING_TURN_LEASE_MS = 5 * 60_000\n\n/** Keep a healthy turn's lease comfortably ahead of expiry without turning\n * per-token streaming into status-write traffic. */\nexport const DEFAULT_RUNNING_TURN_RENEW_INTERVAL_MS = 30_000\n\n/** Represent a buffered turn event with a sequence number and serialized event data */\nexport interface BufferedTurnEvent {\n seq: number\n /** The serialized event line (JSON string, no trailing newline). */\n event: string\n}\n\n/** Manage and query turn events and their lifecycle statuses within a scoped event store */\nexport interface TurnEventStore {\n append(turnId: string, events: BufferedTurnEvent[]): Promise<void>\n read(turnId: string, fromSeq: number): Promise<BufferedTurnEvent[]>\n /** Record turn lifecycle. `scopeId` (a thread/session id) is optional and lets\n * {@link TurnEventStore.listRunning} rediscover this turn after a client reload\n * loses the turnId; stores that don't track scope ignore it. */\n setStatus(turnId: string, status: TurnStatus, scopeId?: string): Promise<void>\n getStatus(turnId: string): Promise<TurnStatus | null>\n /** Unexpired running turnIds for a scope, newest first — so a reloaded client\n * (clientRunId lost) can find and resume the in-flight turn without reviving\n * a row abandoned by a dead process. Optional: a store records it only if\n * `setStatus` was given a `scopeId`. */\n listRunning?(scopeId: string): Promise<string[]>\n}\n\n/** Configure running-turn lease evaluation. The clock is injectable so store\n * contract tests do not sleep. Keep the default in production unless a\n * deployment also tunes the buffer's renewal interval. */\nexport interface TurnEventStoreOptions {\n runningTurnLeaseMs?: number\n now?: () => number\n}\n\n// ── coalescing ────────────────────────────────────────────────────────────\n\ntype AnyRecord = Record<string, unknown>\n\nfunction deltaTypeOf(ev: unknown): 'text' | 'reasoning' | null {\n const e = ev as AnyRecord | null\n if (!e || typeof e !== 'object') return null\n const inner = (e.kind === 'event' ? (e.event as AnyRecord | undefined) : e) as AnyRecord | undefined\n if (!inner || typeof inner !== 'object') return null\n if ((inner.type === 'text' || inner.type === 'reasoning') && typeof inner.text === 'string') {\n return inner.type\n }\n return null\n}\n\n/** Merge consecutive text/reasoning deltas of the same type into one event.\n * Concatenation-preserving: replaying the coalesced stream produces the same\n * accumulated text as the original. */\nexport function coalesceDeltas(events: unknown[]): unknown[] {\n const out: unknown[] = []\n for (const ev of events) {\n const type = deltaTypeOf(ev)\n const prev = out[out.length - 1]\n if (type && prev && deltaTypeOf(prev) === type) {\n const read = (x: unknown): AnyRecord =>\n ((x as AnyRecord).kind === 'event' ? (x as AnyRecord).event : x) as AnyRecord\n const merged = JSON.parse(JSON.stringify(prev)) as AnyRecord\n read(merged).text = String(read(prev).text) + String(read(ev).text)\n out[out.length - 1] = merged\n continue\n }\n out.push(ev)\n }\n return out\n}\n\nfunction asPartUpdate(ev: unknown): { partId: unknown; delta: unknown } | null {\n const e = ev as AnyRecord | null\n if (!e || typeof e !== 'object' || e.type !== 'message.part.updated') return null\n const data = e.data as AnyRecord | undefined\n if (!data || typeof data !== 'object') return null\n const part = data.part as AnyRecord | undefined\n const partId = part?.id ?? data.partId ?? part?.partId ?? null\n return { partId, delta: data.delta }\n}\n\n/**\n * Coalesce consecutive `message.part.updated` deltas for the SAME part into one\n * event. agent-runtime products stream `ChatStreamEvent` NDJSON\n * (`{type:'message.part.updated', data:{part, delta}}`); pumped through the\n * buffer with the default tool-loop coalescer, every per-token delta persists as\n * its own row because that coalescer never recognizes the shape. Pass this as\n * {@link PumpBufferedTurnOptions.coalesce} instead.\n *\n * Concatenation-preserving for BOTH consumer styles: the merged event keeps the\n * LATEST event's `data.part` (already the cumulative accumulation) and sets\n * `data.delta` to the concatenation of the merged deltas, so a client that\n * appends `delta` and one that reads the cumulative `part` both reconstruct the\n * identical final text.\n */\nexport function coalesceChatStreamEvents(events: unknown[]): unknown[] {\n const out: unknown[] = []\n for (const ev of events) {\n const cur = asPartUpdate(ev)\n const prevEv = out[out.length - 1]\n const prev = prevEv ? asPartUpdate(prevEv) : null\n if (cur && prev && cur.partId != null && cur.partId === prev.partId) {\n // Base the merged row on the latest event (its `part` is the most complete\n // accumulation); carry forward the summed delta.\n const merged = JSON.parse(JSON.stringify(ev)) as AnyRecord\n ;(merged.data as AnyRecord).delta = String(prev.delta ?? '') + String(cur.delta ?? '')\n out[out.length - 1] = merged\n continue\n }\n out.push(ev)\n }\n return out\n}\n\n// ── buffering core (the tap) ────────────────────────────────────────────────\n\n/** Define options for buffering and flushing turn events with optional live client delivery and event coalescing */\nexport interface BufferedTurnOptions {\n store: TurnEventStore\n turnId: string\n /** Deliver one serialized line to the live client. Throwing here (client\n * disconnected) does NOT stop buffering — events keep persisting. */\n write?: (line: string) => Promise<void> | void\n /** Flush buffered events to the store at most this often. Default 400ms. */\n flushIntervalMs?: number\n /** Per-flush coalescer. Default {@link coalesceDeltas} (tool-loop text/reasoning\n * deltas). agent-runtime products streaming `ChatStreamEvent` pass\n * {@link coalesceChatStreamEvents} so per-token deltas don't each persist as a\n * row. Must be concatenation-preserving. */\n coalesce?: (events: unknown[]) => unknown[]\n /** Optional scope (thread/session id) recorded with the turn status, so\n * {@link TurnEventStore.listRunning} can find this turn after a reload. */\n scopeId?: string\n /** How often to renew the running-turn lease while a producer is alive.\n * Default {@link DEFAULT_RUNNING_TURN_RENEW_INTERVAL_MS}. */\n runningTurnRenewIntervalMs?: number\n}\n\n/** A push-driven buffer for a turn whose producer the caller does NOT own. */\nexport interface BufferedTurnTap {\n /** Buffer one event: persist (coalesced, on the flush window) + best-effort\n * live-deliver. Wire to a push source's per-event hook (e.g. agent-runtime\n * `handleChatTurn`'s `hooks.onEvent`). Marks the turn 'running' on first call. */\n onEvent(raw: unknown): Promise<void>\n /** Settle the turn: final flush + set status. Call after the producer resolves\n * ('complete') or rejects ('error'). 'error' flushes what was produced first. */\n done(status?: Extract<TurnStatus, 'complete' | 'error'>): Promise<void>\n}\n\n/**\n * The buffering core. Sequence-numbers every event, delivers it to `write`\n * (best-effort — a disconnected client never stops buffering), and flushes to\n * the store in coalesced batches. Drives both transports:\n *\n * • {@link pumpBufferedTurn} — when you OWN an `AsyncIterable` producer.\n * • this tap (`onEvent`/`done`) — when the producer owns iteration and only\n * hands you a push callback (agent-runtime `handleChatTurn`'s `hooks.onEvent`\n * + the finished body). Durability stays here in the shell; the engine needs\n * no `TurnEventStore` seam.\n */\nexport function createBufferedTurnTap(opts: BufferedTurnOptions): BufferedTurnTap {\n const flushIntervalMs = opts.flushIntervalMs ?? 400\n const coalesce = opts.coalesce ?? coalesceDeltas\n const startedAt = Date.now()\n let seq = 0\n let clientGone = false\n let pending: unknown[] = []\n let lastFlush = Date.now()\n let started = false\n let settled = false\n let renewalTimer: ReturnType<typeof setTimeout> | undefined\n let renewal: Promise<void> = Promise.resolve()\n\n function clearRenewalTimer(): void {\n if (renewalTimer !== undefined) clearTimeout(renewalTimer)\n renewalTimer = undefined\n }\n\n function scheduleRenewal(): void {\n if (settled || !opts.scopeId) return\n const intervalMs = Math.max(\n 1,\n opts.runningTurnRenewIntervalMs ?? DEFAULT_RUNNING_TURN_RENEW_INTERVAL_MS,\n )\n renewalTimer = setTimeout(() => {\n renewalTimer = undefined\n if (settled) return\n renewal = opts.store\n .setStatus(opts.turnId, 'running', opts.scopeId)\n .catch(() => {})\n .then(scheduleRenewal)\n }, intervalMs)\n // A deliberately abandoned tap in a Node test must not keep the process\n // alive until the production renewal interval elapses.\n if (typeof renewalTimer === 'object' && 'unref' in renewalTimer) {\n renewalTimer.unref()\n }\n }\n\n async function flush(): Promise<void> {\n if (pending.length === 0) return\n const batch = coalesce(pending)\n pending = []\n const rows = batch.map((ev) => ({ seq: ++seq, event: JSON.stringify(ev) }))\n await opts.store.append(opts.turnId, rows)\n lastFlush = Date.now()\n }\n\n async function ensureStarted(): Promise<void> {\n if (started) return\n started = true\n await opts.store.setStatus(opts.turnId, 'running', opts.scopeId)\n scheduleRenewal()\n }\n\n return {\n async onEvent(raw) {\n await ensureStarted()\n // Stamp ms-since-turn-start so any stored turn is replayable AND traceable\n // (see ../trace) from the same buffered rows.\n const ev = raw && typeof raw === 'object' ? { ...(raw as Record<string, unknown>), _t: Date.now() - startedAt } : raw\n pending.push(ev)\n if (!clientGone && opts.write) {\n try {\n // Live delivery carries a provisional ordering hint, not the persisted\n // seq (coalescing changes seq assignment); clients resume with the\n // seqs from replay, or 0 for \"everything\".\n await opts.write(JSON.stringify(ev))\n } catch {\n clientGone = true\n }\n }\n if (Date.now() - lastFlush >= flushIntervalMs) await flush()\n },\n async done(status = 'complete') {\n await ensureStarted()\n settled = true\n clearRenewalTimer()\n await renewal\n if (status === 'error') {\n await flush().catch(() => {})\n await opts.store.setStatus(opts.turnId, 'error', opts.scopeId).catch(() => {})\n return\n }\n await flush()\n await opts.store.setStatus(opts.turnId, 'complete', opts.scopeId)\n },\n }\n}\n\n// ── pump (producer side) ──────────────────────────────────────────────────\n\n/** Define options to pump data from an asynchronous iterable source with buffered turn control */\nexport interface PumpBufferedTurnOptions extends BufferedTurnOptions {\n source: AsyncIterable<unknown>\n}\n\n/**\n * Drive a turn to completion regardless of the live client, when you OWN the\n * producer as an `AsyncIterable`. A thin driver over {@link createBufferedTurnTap}.\n * Returns a promise that resolves when the turn finishes — hand it to\n * `ctx.waitUntil` so a disconnect can't kill the turn. Never rejects on\n * client-write failure; a source error marks the turn 'error' (after flushing\n * what was produced) and rethrows.\n */\nexport async function pumpBufferedTurn(opts: PumpBufferedTurnOptions): Promise<void> {\n const tap = createBufferedTurnTap(opts)\n try {\n for await (const raw of opts.source) await tap.onEvent(raw)\n await tap.done('complete')\n } catch (err) {\n await tap.done('error')\n throw err\n }\n}\n\n// ── replay (consumer side) ────────────────────────────────────────────────\n\n/** Define options for replaying turn events with control over sequence, polling, and timeout */\nexport interface ReplayTurnEventsOptions {\n store: TurnEventStore\n turnId: string\n /** Replay strictly after this sequence number (0 = from the beginning). */\n fromSeq?: number\n /** Poll cadence while the turn is still running. Default 500ms. */\n pollMs?: number\n /** Give up following a 'running' turn after this long. Default 120s. */\n timeoutMs?: number\n}\n\n/**\n * Yield buffered events after `fromSeq`, then keep polling while the turn is\n * still 'running' until it completes, errors, or times out. Terminates with a\n * final `{seq: -1, event: '{\"type\":\"turn_status\",...}'}` marker so clients\n * know why the replay ended.\n */\nexport async function* replayTurnEvents(opts: ReplayTurnEventsOptions): AsyncGenerator<BufferedTurnEvent> {\n const pollMs = opts.pollMs ?? 500\n const timeoutMs = opts.timeoutMs ?? 120_000\n let cursor = opts.fromSeq ?? 0\n const deadline = Date.now() + timeoutMs\n\n for (;;) {\n const batch = await opts.store.read(opts.turnId, cursor)\n for (const row of batch) {\n cursor = Math.max(cursor, row.seq)\n yield row\n }\n const status = await opts.store.getStatus(opts.turnId)\n if (status !== 'running') {\n yield { seq: -1, event: JSON.stringify({ type: 'turn_status', status: status ?? 'unknown' }) }\n return\n }\n if (Date.now() >= deadline) {\n yield { seq: -1, event: JSON.stringify({ type: 'turn_status', status: 'timeout' }) }\n return\n }\n await new Promise((r) => setTimeout(r, pollMs))\n }\n}\n\n/**\n * Serialize a replayed row for the wire, stamping the buffer ordinal ONTO the\n * line so a reconnecting client can continue from `?fromSeq=<lastSeq>`.\n *\n * The seq lives on the {@link BufferedTurnEvent} row wrapper, not inside the\n * serialized event — `flush()` builds `{seq: ++seq, event: JSON.stringify(ev)}`.\n * A route that enqueues `row.event` alone therefore emits lines with no seq at\n * all, and every client cursor silently pins to 0: each reconnect refetches the\n * whole turn and re-applies every delta onto already-rendered state. This\n * restores the contract `web-react/chat-stream` already documents (\"replayed\n * lines carry an extra `seq` — transparently ignored\").\n *\n * The `{seq: -1}` `turn_status` sentinel is passed through unstamped: it is a\n * terminator, not a cursor position, and stamping it would move a client's\n * cursor to -1.\n *\n * Fail-soft by construction — a line that is not a JSON object passes through\n * verbatim. A stamping bug must degrade to today's behaviour, never break a\n * replay.\n */\nexport function stampReplaySeq(row: BufferedTurnEvent): string {\n if (row.seq <= 0) return row.event\n const line = row.event\n // Cheap splice instead of parse+stringify: these rows are already canonical\n // JSON objects from `JSON.stringify`, and replay is a hot per-event path.\n if (line.charCodeAt(0) !== 0x7b /* { */) return line\n const rest = line.slice(1)\n return rest.trimStart().startsWith('}')\n ? `{\"seq\":${row.seq}${rest}`\n : `{\"seq\":${row.seq},${rest}`\n}\n\n// ── D1 store ──────────────────────────────────────────────────────────────\n\n/** Minimal structural D1 contract (Cloudflare `D1Database` satisfies it). */\nexport interface D1LikeForTurns {\n prepare(sql: string): {\n bind(...values: unknown[]): {\n run(): Promise<unknown>\n all<T = Record<string, unknown>>(): Promise<{ results: T[] }>\n first<T = Record<string, unknown>>(): Promise<T | null>\n }\n }\n}\n\n/** Schema for the D1 store — append to the product's migrations. */\nexport const TURN_EVENTS_MIGRATION_SQL = `\nCREATE TABLE IF NOT EXISTS turn_events (\n turnId TEXT NOT NULL,\n seq INTEGER NOT NULL,\n event TEXT NOT NULL,\n PRIMARY KEY (turnId, seq)\n);\nCREATE TABLE IF NOT EXISTS turn_status (\n turnId TEXT PRIMARY KEY,\n status TEXT NOT NULL,\n scopeId TEXT,\n updatedAt TEXT NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_turn_status_scope ON turn_status (scopeId, status);\n`\n\n/** For deployments whose `turn_status` table predates `scopeId`/`listRunning` —\n * run once to add the column (the CREATE above already includes it for new\n * deployments). SQLite ignores a duplicate-add error if already applied. */\nexport const TURN_STATUS_SCOPE_MIGRATION_SQL = `ALTER TABLE turn_status ADD COLUMN scopeId TEXT;`\n\n/** Resolve a TurnEventStore that appends and reads turn events using a D1-like database interface */\nexport function createD1TurnEventStore(\n db: D1LikeForTurns,\n options: TurnEventStoreOptions = {},\n): TurnEventStore {\n const now = options.now ?? Date.now\n const runningTurnLeaseMs = Math.max(\n 1,\n options.runningTurnLeaseMs ?? DEFAULT_RUNNING_TURN_LEASE_MS,\n )\n return {\n async append(turnId, events) {\n if (!events.length) return\n // One multi-row insert per flush window keeps write volume bounded.\n const placeholders = events.map(() => '(?, ?, ?)').join(', ')\n const values = events.flatMap((e) => [turnId, e.seq, e.event])\n await db.prepare(`INSERT OR IGNORE INTO turn_events (turnId, seq, event) VALUES ${placeholders}`).bind(...values).run()\n },\n async read(turnId, fromSeq) {\n const { results } = await db\n .prepare('SELECT seq, event FROM turn_events WHERE turnId = ? AND seq > ? ORDER BY seq ASC')\n .bind(turnId, fromSeq)\n .all<{ seq: number; event: string }>()\n return results\n },\n async setStatus(turnId, status, scopeId) {\n // COALESCE preserves a scopeId set on the initial 'running' write when a\n // later 'complete'/'error' write passes none.\n await db\n .prepare(\n 'INSERT INTO turn_status (turnId, status, scopeId, updatedAt) VALUES (?, ?, ?, ?) ON CONFLICT(turnId) DO UPDATE SET status = excluded.status, scopeId = COALESCE(excluded.scopeId, turn_status.scopeId), updatedAt = excluded.updatedAt',\n )\n .bind(turnId, status, scopeId ?? null, new Date(now()).toISOString())\n .run()\n },\n async getStatus(turnId) {\n const row = await db.prepare('SELECT status FROM turn_status WHERE turnId = ?').bind(turnId).first<{ status: TurnStatus }>()\n return row?.status ?? null\n },\n async listRunning(scopeId) {\n const { results } = await db\n .prepare(\n \"SELECT turnId FROM turn_status WHERE scopeId = ? AND status = 'running' AND updatedAt >= ? ORDER BY updatedAt DESC, rowid DESC\",\n )\n .bind(scopeId, new Date(now() - runningTurnLeaseMs).toISOString())\n .all<{ turnId: string }>()\n return results.map((r) => r.turnId)\n },\n }\n}\n\n/** In-memory store for tests and keyless local dev. */\nexport function createMemoryTurnEventStore(\n options: TurnEventStoreOptions = {},\n): TurnEventStore {\n const events = new Map<string, BufferedTurnEvent[]>()\n const status = new Map<string, TurnStatus>()\n const scopes = new Map<string, string>()\n const order: string[] = []\n const updatedAt = new Map<string, number>()\n const now = options.now ?? Date.now\n const runningTurnLeaseMs = Math.max(\n 1,\n options.runningTurnLeaseMs ?? DEFAULT_RUNNING_TURN_LEASE_MS,\n )\n return {\n async append(turnId, rows) {\n const list = events.get(turnId) ?? []\n list.push(...rows)\n events.set(turnId, list)\n },\n async read(turnId, fromSeq) {\n return (events.get(turnId) ?? []).filter((e) => e.seq > fromSeq)\n },\n async setStatus(turnId, s, scopeId) {\n status.set(turnId, s)\n if (scopeId) scopes.set(turnId, scopeId)\n if (!order.includes(turnId)) order.push(turnId)\n updatedAt.set(turnId, now())\n },\n async getStatus(turnId) {\n return status.get(turnId) ?? null\n },\n async listRunning(scopeId) {\n const cutoff = now() - runningTurnLeaseMs\n return order\n .filter(\n (turnId) =>\n status.get(turnId) === 'running' &&\n scopes.get(turnId) === scopeId &&\n (updatedAt.get(turnId) ?? Number.NEGATIVE_INFINITY) >= cutoff,\n )\n .sort((left, right) => {\n const updatedDelta = (updatedAt.get(right) ?? 0) - (updatedAt.get(left) ?? 0)\n return updatedDelta || order.indexOf(right) - order.indexOf(left)\n })\n },\n }\n}\n"],"mappings":";AAuBO,IAAM,gCAAgC,IAAI;AAI1C,IAAM,yCAAyC;AAqCtD,SAAS,YAAY,IAA0C;AAC7D,QAAM,IAAI;AACV,MAAI,CAAC,KAAK,OAAO,MAAM,SAAU,QAAO;AACxC,QAAM,QAAS,EAAE,SAAS,UAAW,EAAE,QAAkC;AACzE,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,OAAK,MAAM,SAAS,UAAU,MAAM,SAAS,gBAAgB,OAAO,MAAM,SAAS,UAAU;AAC3F,WAAO,MAAM;AAAA,EACf;AACA,SAAO;AACT;AAKO,SAAS,eAAe,QAA8B;AAC3D,QAAM,MAAiB,CAAC;AACxB,aAAW,MAAM,QAAQ;AACvB,UAAM,OAAO,YAAY,EAAE;AAC3B,UAAM,OAAO,IAAI,IAAI,SAAS,CAAC;AAC/B,QAAI,QAAQ,QAAQ,YAAY,IAAI,MAAM,MAAM;AAC9C,YAAM,OAAO,CAAC,MACV,EAAgB,SAAS,UAAW,EAAgB,QAAQ;AAChE,YAAM,SAAS,KAAK,MAAM,KAAK,UAAU,IAAI,CAAC;AAC9C,WAAK,MAAM,EAAE,OAAO,OAAO,KAAK,IAAI,EAAE,IAAI,IAAI,OAAO,KAAK,EAAE,EAAE,IAAI;AAClE,UAAI,IAAI,SAAS,CAAC,IAAI;AACtB;AAAA,IACF;AACA,QAAI,KAAK,EAAE;AAAA,EACb;AACA,SAAO;AACT;AAEA,SAAS,aAAa,IAAyD;AAC7E,QAAM,IAAI;AACV,MAAI,CAAC,KAAK,OAAO,MAAM,YAAY,EAAE,SAAS,uBAAwB,QAAO;AAC7E,QAAM,OAAO,EAAE;AACf,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,QAAM,OAAO,KAAK;AAClB,QAAM,SAAS,MAAM,MAAM,KAAK,UAAU,MAAM,UAAU;AAC1D,SAAO,EAAE,QAAQ,OAAO,KAAK,MAAM;AACrC;AAgBO,SAAS,yBAAyB,QAA8B;AACrE,QAAM,MAAiB,CAAC;AACxB,aAAW,MAAM,QAAQ;AACvB,UAAM,MAAM,aAAa,EAAE;AAC3B,UAAM,SAAS,IAAI,IAAI,SAAS,CAAC;AACjC,UAAM,OAAO,SAAS,aAAa,MAAM,IAAI;AAC7C,QAAI,OAAO,QAAQ,IAAI,UAAU,QAAQ,IAAI,WAAW,KAAK,QAAQ;AAGnE,YAAM,SAAS,KAAK,MAAM,KAAK,UAAU,EAAE,CAAC;AAC3C,MAAC,OAAO,KAAmB,QAAQ,OAAO,KAAK,SAAS,EAAE,IAAI,OAAO,IAAI,SAAS,EAAE;AACrF,UAAI,IAAI,SAAS,CAAC,IAAI;AACtB;AAAA,IACF;AACA,QAAI,KAAK,EAAE;AAAA,EACb;AACA,SAAO;AACT;AAgDO,SAAS,sBAAsB,MAA4C;AAChF,QAAM,kBAAkB,KAAK,mBAAmB;AAChD,QAAM,WAAW,KAAK,YAAY;AAClC,QAAM,YAAY,KAAK,IAAI;AAC3B,MAAI,MAAM;AACV,MAAI,aAAa;AACjB,MAAI,UAAqB,CAAC;AAC1B,MAAI,YAAY,KAAK,IAAI;AACzB,MAAI,UAAU;AACd,MAAI,UAAU;AACd,MAAI;AACJ,MAAI,UAAyB,QAAQ,QAAQ;AAE7C,WAAS,oBAA0B;AACjC,QAAI,iBAAiB,OAAW,cAAa,YAAY;AACzD,mBAAe;AAAA,EACjB;AAEA,WAAS,kBAAwB;AAC/B,QAAI,WAAW,CAAC,KAAK,QAAS;AAC9B,UAAM,aAAa,KAAK;AAAA,MACtB;AAAA,MACA,KAAK,8BAA8B;AAAA,IACrC;AACA,mBAAe,WAAW,MAAM;AAC9B,qBAAe;AACf,UAAI,QAAS;AACb,gBAAU,KAAK,MACZ,UAAU,KAAK,QAAQ,WAAW,KAAK,OAAO,EAC9C,MAAM,MAAM;AAAA,MAAC,CAAC,EACd,KAAK,eAAe;AAAA,IACzB,GAAG,UAAU;AAGb,QAAI,OAAO,iBAAiB,YAAY,WAAW,cAAc;AAC/D,mBAAa,MAAM;AAAA,IACrB;AAAA,EACF;AAEA,iBAAe,QAAuB;AACpC,QAAI,QAAQ,WAAW,EAAG;AAC1B,UAAM,QAAQ,SAAS,OAAO;AAC9B,cAAU,CAAC;AACX,UAAM,OAAO,MAAM,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,OAAO,KAAK,UAAU,EAAE,EAAE,EAAE;AAC1E,UAAM,KAAK,MAAM,OAAO,KAAK,QAAQ,IAAI;AACzC,gBAAY,KAAK,IAAI;AAAA,EACvB;AAEA,iBAAe,gBAA+B;AAC5C,QAAI,QAAS;AACb,cAAU;AACV,UAAM,KAAK,MAAM,UAAU,KAAK,QAAQ,WAAW,KAAK,OAAO;AAC/D,oBAAgB;AAAA,EAClB;AAEA,SAAO;AAAA,IACL,MAAM,QAAQ,KAAK;AACjB,YAAM,cAAc;AAGpB,YAAM,KAAK,OAAO,OAAO,QAAQ,WAAW,EAAE,GAAI,KAAiC,IAAI,KAAK,IAAI,IAAI,UAAU,IAAI;AAClH,cAAQ,KAAK,EAAE;AACf,UAAI,CAAC,cAAc,KAAK,OAAO;AAC7B,YAAI;AAIF,gBAAM,KAAK,MAAM,KAAK,UAAU,EAAE,CAAC;AAAA,QACrC,QAAQ;AACN,uBAAa;AAAA,QACf;AAAA,MACF;AACA,UAAI,KAAK,IAAI,IAAI,aAAa,gBAAiB,OAAM,MAAM;AAAA,IAC7D;AAAA,IACA,MAAM,KAAK,SAAS,YAAY;AAC9B,YAAM,cAAc;AACpB,gBAAU;AACV,wBAAkB;AAClB,YAAM;AACN,UAAI,WAAW,SAAS;AACtB,cAAM,MAAM,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAC5B,cAAM,KAAK,MAAM,UAAU,KAAK,QAAQ,SAAS,KAAK,OAAO,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAC7E;AAAA,MACF;AACA,YAAM,MAAM;AACZ,YAAM,KAAK,MAAM,UAAU,KAAK,QAAQ,YAAY,KAAK,OAAO;AAAA,IAClE;AAAA,EACF;AACF;AAiBA,eAAsB,iBAAiB,MAA8C;AACnF,QAAM,MAAM,sBAAsB,IAAI;AACtC,MAAI;AACF,qBAAiB,OAAO,KAAK,OAAQ,OAAM,IAAI,QAAQ,GAAG;AAC1D,UAAM,IAAI,KAAK,UAAU;AAAA,EAC3B,SAAS,KAAK;AACZ,UAAM,IAAI,KAAK,OAAO;AACtB,UAAM;AAAA,EACR;AACF;AAsBA,gBAAuB,iBAAiB,MAAkE;AACxG,QAAM,SAAS,KAAK,UAAU;AAC9B,QAAM,YAAY,KAAK,aAAa;AACpC,MAAI,SAAS,KAAK,WAAW;AAC7B,QAAM,WAAW,KAAK,IAAI,IAAI;AAE9B,aAAS;AACP,UAAM,QAAQ,MAAM,KAAK,MAAM,KAAK,KAAK,QAAQ,MAAM;AACvD,eAAW,OAAO,OAAO;AACvB,eAAS,KAAK,IAAI,QAAQ,IAAI,GAAG;AACjC,YAAM;AAAA,IACR;AACA,UAAM,SAAS,MAAM,KAAK,MAAM,UAAU,KAAK,MAAM;AACrD,QAAI,WAAW,WAAW;AACxB,YAAM,EAAE,KAAK,IAAI,OAAO,KAAK,UAAU,EAAE,MAAM,eAAe,QAAQ,UAAU,UAAU,CAAC,EAAE;AAC7F;AAAA,IACF;AACA,QAAI,KAAK,IAAI,KAAK,UAAU;AAC1B,YAAM,EAAE,KAAK,IAAI,OAAO,KAAK,UAAU,EAAE,MAAM,eAAe,QAAQ,UAAU,CAAC,EAAE;AACnF;AAAA,IACF;AACA,UAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC;AAAA,EAChD;AACF;AAsBO,SAAS,eAAe,KAAgC;AAC7D,MAAI,IAAI,OAAO,EAAG,QAAO,IAAI;AAC7B,QAAM,OAAO,IAAI;AAGjB,MAAI,KAAK,WAAW,CAAC,MAAM,IAAc,QAAO;AAChD,QAAM,OAAO,KAAK,MAAM,CAAC;AACzB,SAAO,KAAK,UAAU,EAAE,WAAW,GAAG,IAClC,UAAU,IAAI,GAAG,GAAG,IAAI,KACxB,UAAU,IAAI,GAAG,IAAI,IAAI;AAC/B;AAgBO,IAAM,4BAA4B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBlC,IAAM,kCAAkC;AAGxC,SAAS,uBACd,IACA,UAAiC,CAAC,GAClB;AAChB,QAAM,MAAM,QAAQ,OAAO,KAAK;AAChC,QAAM,qBAAqB,KAAK;AAAA,IAC9B;AAAA,IACA,QAAQ,sBAAsB;AAAA,EAChC;AACA,SAAO;AAAA,IACL,MAAM,OAAO,QAAQ,QAAQ;AAC3B,UAAI,CAAC,OAAO,OAAQ;AAEpB,YAAM,eAAe,OAAO,IAAI,MAAM,WAAW,EAAE,KAAK,IAAI;AAC5D,YAAM,SAAS,OAAO,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC;AAC7D,YAAM,GAAG,QAAQ,iEAAiE,YAAY,EAAE,EAAE,KAAK,GAAG,MAAM,EAAE,IAAI;AAAA,IACxH;AAAA,IACA,MAAM,KAAK,QAAQ,SAAS;AAC1B,YAAM,EAAE,QAAQ,IAAI,MAAM,GACvB,QAAQ,kFAAkF,EAC1F,KAAK,QAAQ,OAAO,EACpB,IAAoC;AACvC,aAAO;AAAA,IACT;AAAA,IACA,MAAM,UAAU,QAAQ,QAAQ,SAAS;AAGvC,YAAM,GACH;AAAA,QACC;AAAA,MACF,EACC,KAAK,QAAQ,QAAQ,WAAW,MAAM,IAAI,KAAK,IAAI,CAAC,EAAE,YAAY,CAAC,EACnE,IAAI;AAAA,IACT;AAAA,IACA,MAAM,UAAU,QAAQ;AACtB,YAAM,MAAM,MAAM,GAAG,QAAQ,iDAAiD,EAAE,KAAK,MAAM,EAAE,MAA8B;AAC3H,aAAO,KAAK,UAAU;AAAA,IACxB;AAAA,IACA,MAAM,YAAY,SAAS;AACzB,YAAM,EAAE,QAAQ,IAAI,MAAM,GACvB;AAAA,QACC;AAAA,MACF,EACC,KAAK,SAAS,IAAI,KAAK,IAAI,IAAI,kBAAkB,EAAE,YAAY,CAAC,EAChE,IAAwB;AAC3B,aAAO,QAAQ,IAAI,CAAC,MAAM,EAAE,MAAM;AAAA,IACpC;AAAA,EACF;AACF;AAGO,SAAS,2BACd,UAAiC,CAAC,GAClB;AAChB,QAAM,SAAS,oBAAI,IAAiC;AACpD,QAAM,SAAS,oBAAI,IAAwB;AAC3C,QAAM,SAAS,oBAAI,IAAoB;AACvC,QAAM,QAAkB,CAAC;AACzB,QAAM,YAAY,oBAAI,IAAoB;AAC1C,QAAM,MAAM,QAAQ,OAAO,KAAK;AAChC,QAAM,qBAAqB,KAAK;AAAA,IAC9B;AAAA,IACA,QAAQ,sBAAsB;AAAA,EAChC;AACA,SAAO;AAAA,IACL,MAAM,OAAO,QAAQ,MAAM;AACzB,YAAM,OAAO,OAAO,IAAI,MAAM,KAAK,CAAC;AACpC,WAAK,KAAK,GAAG,IAAI;AACjB,aAAO,IAAI,QAAQ,IAAI;AAAA,IACzB;AAAA,IACA,MAAM,KAAK,QAAQ,SAAS;AAC1B,cAAQ,OAAO,IAAI,MAAM,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,MAAM,OAAO;AAAA,IACjE;AAAA,IACA,MAAM,UAAU,QAAQ,GAAG,SAAS;AAClC,aAAO,IAAI,QAAQ,CAAC;AACpB,UAAI,QAAS,QAAO,IAAI,QAAQ,OAAO;AACvC,UAAI,CAAC,MAAM,SAAS,MAAM,EAAG,OAAM,KAAK,MAAM;AAC9C,gBAAU,IAAI,QAAQ,IAAI,CAAC;AAAA,IAC7B;AAAA,IACA,MAAM,UAAU,QAAQ;AACtB,aAAO,OAAO,IAAI,MAAM,KAAK;AAAA,IAC/B;AAAA,IACA,MAAM,YAAY,SAAS;AACzB,YAAM,SAAS,IAAI,IAAI;AACvB,aAAO,MACJ;AAAA,QACC,CAAC,WACC,OAAO,IAAI,MAAM,MAAM,aACvB,OAAO,IAAI,MAAM,MAAM,YACtB,UAAU,IAAI,MAAM,KAAK,OAAO,sBAAsB;AAAA,MAC3D,EACC,KAAK,CAAC,MAAM,UAAU;AACrB,cAAM,gBAAgB,UAAU,IAAI,KAAK,KAAK,MAAM,UAAU,IAAI,IAAI,KAAK;AAC3E,eAAO,gBAAgB,MAAM,QAAQ,KAAK,IAAI,MAAM,QAAQ,IAAI;AAAA,MAClE,CAAC;AAAA,IACL;AAAA,EACF;AACF;","names":[]}
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
// src/sandbox/index.ts
|
|
23
23
|
import {
|
|
24
24
|
Sandbox
|
|
25
|
-
} from "@tangle-network/sandbox";
|
|
25
|
+
} from "@tangle-network/sandbox/core";
|
|
26
26
|
import { createHash } from "crypto";
|
|
27
27
|
|
|
28
28
|
// src/sandbox/outcome.ts
|
|
@@ -2038,4 +2038,4 @@ export {
|
|
|
2038
2038
|
isTerminalPromptEvent,
|
|
2039
2039
|
detectInteractiveQuestion
|
|
2040
2040
|
};
|
|
2041
|
-
//# sourceMappingURL=chunk-
|
|
2041
|
+
//# sourceMappingURL=chunk-6W5Y4J2X.js.map
|