@jobshimo/browser-link 0.25.2 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/agent-instructions/content.js +53 -6
  2. package/dist/agent-instructions/content.js.map +1 -1
  3. package/dist/bridge/events.d.ts +1 -1
  4. package/dist/bridge/events.js +11 -1
  5. package/dist/bridge/events.js.map +1 -1
  6. package/dist/bridge/ws-bridge.d.ts +18 -0
  7. package/dist/bridge/ws-bridge.js +24 -0
  8. package/dist/bridge/ws-bridge.js.map +1 -1
  9. package/dist/cdp/detached-flows.d.ts +114 -0
  10. package/dist/cdp/detached-flows.js +242 -0
  11. package/dist/cdp/detached-flows.js.map +1 -0
  12. package/dist/cdp/flow.d.ts +76 -8
  13. package/dist/cdp/flow.js +91 -8
  14. package/dist/cdp/flow.js.map +1 -1
  15. package/dist/cdp/support.js +2 -0
  16. package/dist/cdp/support.js.map +1 -1
  17. package/dist/cdp/transport.js +132 -19
  18. package/dist/cdp/transport.js.map +1 -1
  19. package/dist/extension/background.js +535 -16
  20. package/dist/extension/background.js.map +1 -1
  21. package/dist/extension/flow-registry.d.ts +436 -0
  22. package/dist/extension/flow-registry.js +638 -0
  23. package/dist/extension/flow-registry.js.map +1 -0
  24. package/dist/extension/flow.d.ts +81 -5
  25. package/dist/extension/flow.js +96 -5
  26. package/dist/extension/flow.js.map +1 -1
  27. package/dist/extension/manifest.json +1 -1
  28. package/dist/extension/popup.html +138 -0
  29. package/dist/extension/popup.js +144 -0
  30. package/dist/extension/popup.js.map +1 -1
  31. package/dist/messages.d.ts +32 -1
  32. package/dist/permissions.js +12 -0
  33. package/dist/permissions.js.map +1 -1
  34. package/dist/server.js +10 -1
  35. package/dist/server.js.map +1 -1
  36. package/dist/tools/browser-definitions.js +65 -3
  37. package/dist/tools/browser-definitions.js.map +1 -1
  38. package/dist/tools/browser-dispatch.d.ts +13 -0
  39. package/dist/tools/browser-dispatch.js +190 -17
  40. package/dist/tools/browser-dispatch.js.map +1 -1
  41. package/package.json +1 -1
@@ -0,0 +1,638 @@
1
+ /**
2
+ * In-flight flow registry + per-tab flow history + detached-flow records —
3
+ * the state behind the popup's Flows panel, behind `deps.shouldCancel` (see
4
+ * `flow.ts`), and behind the `flow_status` wire tool that backs
5
+ * `browser.flow_status`.
6
+ *
7
+ * Extracted out of `background.ts` for the same reason `idle-policy.ts`,
8
+ * `reconnect-policy.ts` and `flow-recording-policy.ts` were: the service
9
+ * worker itself is untestable (it reaches for `chrome.*` at import time),
10
+ * while THIS is the part with rules worth pinning down — the lifecycle of a
11
+ * running flow, what a cancel does to an id nobody recognises, the 20-entry
12
+ * history cap, the detached ceiling, and above all WHAT A HISTORY ENTRY IS
13
+ * ALLOWED TO CONTAIN.
14
+ *
15
+ * PRIVACY CONTRACT (same rule as the persistent map, enforced by
16
+ * `toHistoryEntry` and asserted in the tests): a history entry holds UI
17
+ * STRUCTURE AND OUTCOMES ONLY — counts, durations, a step index, a step
18
+ * KIND. Never page text, never a selector, never typed input, never a
19
+ * failure message. Flow error strings routinely quote the page (a
20
+ * `find: multiple-matches` error carries candidate labels), which is
21
+ * exactly why the failure message is dropped here rather than trimmed: the
22
+ * operator needs to know WHICH STEP died, not what the page said.
23
+ *
24
+ * The history lives in `chrome.storage.session` (written by
25
+ * `background.ts`, read by the popup through the same runtime-message
26
+ * channel) and therefore dies with the browser session BY DESIGN. This is
27
+ * an operator view, not an audit log.
28
+ *
29
+ * DETACHED-FLOW RECORDS ARE A DIFFERENT ANIMAL and the distinction matters.
30
+ * A record (`DetachedFlowRecord`, keyed `detached-flow:<flow_id>`) carries
31
+ * the flow's ACTION MANIFEST — the very `results[]` a synchronous flow
32
+ * returns to its caller — plus the failure message, so the agent that
33
+ * launched a detached run can still answer "which things did you just
34
+ * delete?" after the fact. That is page-derived data at rest in
35
+ * `chrome.storage.session`, deliberately, because a detached flow has no
36
+ * open tool call to return it through. It is NEVER shown in the popup, it
37
+ * is only ever handed back over the bridge to an agent that can already
38
+ * read the page, and it dies with the browser session like everything else
39
+ * here.
40
+ */
41
+ /** How many completed flows are kept per tab. Small on purpose — this is a
42
+ * "what just happened on this tab" panel in a 320px-wide popup, not a log. */
43
+ export const MAX_FLOW_HISTORY_PER_TAB = 20;
44
+ /** `chrome.storage.session` key holding one tab's flow history. Keyed by
45
+ * the CHROME tab id (not the browser-link `tab_N` id) because the popup
46
+ * only ever knows the former, and a tab that reconnects under a new
47
+ * browser-link id is still the same tab to the human looking at it. */
48
+ export function flowHistoryKey(tabId) {
49
+ return `flow-history:${tabId}`;
50
+ }
51
+ /** Absolute wall-clock ceiling on a DETACHED flow, from the moment it is
52
+ * registered. Mirrors `MAX_DETACHED_FLOW_TIMEOUT_MS` in the server's
53
+ * `browser-dispatch.ts` (which rejects an over-ceiling flow up front) and
54
+ * `cdp/detached-flows.ts` (the cdp-direct runner's own copy); kept in sync
55
+ * manually, since the three packages share no module for it.
56
+ *
57
+ * A detached flow escapes `MAX_FLOW_TIMEOUT_MS` — nothing is parked on it,
58
+ * so the 60s "do not hold a tool call open longer than this" rule simply
59
+ * does not apply. What it must NOT escape is having an end: a flow that
60
+ * keeps acting with no agent attached and no upper bound is exactly the
61
+ * runaway this whole program exists to make impossible. Past the ceiling
62
+ * the flow self-cancels and reports `stopped_by: 'expired'` — a distinct
63
+ * outcome from a human pressing Stop, because "I ran out of time" and
64
+ * "someone stopped me" are different facts about the same partial work.
65
+ *
66
+ * Enforced as a DEADLINE checked at the same points as the cancel flag
67
+ * (see `shouldStop`), never as a timer: a timer would fire while the
68
+ * runner is mid-step and could not stop anything anyway, and a
69
+ * request-derived `setTimeout` duration is a resource-exhaustion finding
70
+ * waiting to happen. */
71
+ export const MAX_DETACHED_FLOW_MS = 30 * 60_000;
72
+ export function createFlowRegistry() {
73
+ const flows = new Map();
74
+ /** Shared by `cancel` and `cancelForTab`: flip once, remember why. A
75
+ * second call never overwrites the first reason — the first thing that
76
+ * told the flow to stop is the thing that stopped it. */
77
+ function flip(entry, reason) {
78
+ if (entry.cancelled)
79
+ return false;
80
+ entry.cancelled = true;
81
+ entry.stopReason = reason;
82
+ return true;
83
+ }
84
+ return {
85
+ register({ flowId, tabId, steps, title, detached, expiresAt }) {
86
+ const entry = {
87
+ flowId,
88
+ tabId,
89
+ title: title ?? '',
90
+ startedAt: Date.now(),
91
+ steps,
92
+ cancelled: false,
93
+ // Step 0 of N until the runner reports its first step — "not
94
+ // started yet" rather than a fake "step 1".
95
+ progress: { step: 0, steps },
96
+ detached: detached === true,
97
+ ...(detached === true && expiresAt !== undefined ? { expiresAt } : {}),
98
+ };
99
+ flows.set(flowId, entry);
100
+ return entry;
101
+ },
102
+ get(flowId) {
103
+ return flows.get(flowId);
104
+ },
105
+ cancel(flowId, reason = 'cancelled') {
106
+ const entry = flows.get(flowId);
107
+ if (!entry)
108
+ return false;
109
+ return flip(entry, reason);
110
+ },
111
+ cancelForTab(tabId) {
112
+ let cancelled = 0;
113
+ for (const entry of flows.values()) {
114
+ if (entry.tabId !== tabId)
115
+ continue;
116
+ if (flip(entry, 'cancelled'))
117
+ cancelled += 1;
118
+ }
119
+ return cancelled;
120
+ },
121
+ detachedForTab(tabId) {
122
+ for (const entry of flows.values()) {
123
+ if (entry.tabId === tabId && entry.detached)
124
+ return entry;
125
+ }
126
+ return undefined;
127
+ },
128
+ shouldStop(flowId, now) {
129
+ const entry = flows.get(flowId);
130
+ // An id the registry has forgotten is not "still allowed to run" —
131
+ // it is a flow whose bookkeeping is gone, and continuing to dispatch
132
+ // irreversible actions on behalf of something nobody can see or stop
133
+ // is the one outcome this module exists to prevent.
134
+ if (!entry)
135
+ return true;
136
+ if (entry.cancelled)
137
+ return true;
138
+ if (entry.expiresAt !== undefined && now >= entry.expiresAt) {
139
+ flip(entry, 'expired');
140
+ return true;
141
+ }
142
+ return false;
143
+ },
144
+ finish(flowId) {
145
+ const entry = flows.get(flowId);
146
+ flows.delete(flowId);
147
+ return entry;
148
+ },
149
+ listRunning(now) {
150
+ const views = [];
151
+ for (const entry of flows.values()) {
152
+ views.push({
153
+ flowId: entry.flowId,
154
+ tabId: entry.tabId,
155
+ title: entry.title,
156
+ // Clamped at 0: `now` is supplied by the caller and a clock that
157
+ // stepped backwards should read "0s", never "-4s".
158
+ elapsedMs: Math.max(0, now - entry.startedAt),
159
+ steps: entry.steps,
160
+ step: entry.progress.step,
161
+ ...(entry.progress.iteration === undefined
162
+ ? {}
163
+ : { iteration: entry.progress.iteration }),
164
+ ...(entry.progress.iterations === undefined
165
+ ? {}
166
+ : { iterations: entry.progress.iterations }),
167
+ cancelling: entry.cancelled,
168
+ detached: entry.detached,
169
+ });
170
+ }
171
+ // Insertion order is start order (Map preserves it and a flow is
172
+ // registered exactly once), so the oldest — the one most likely to
173
+ // be the runaway — sits at the top of the panel.
174
+ return views;
175
+ },
176
+ };
177
+ }
178
+ /** Sum the completed iterations of every `repeat` step in a finished
179
+ * flow's results. Reads ONLY the numeric `iterations_completed` field off
180
+ * entries that carry one; everything else in a result entry is ignored, so
181
+ * nothing page-derived can reach the history. */
182
+ function countIterations(results) {
183
+ let total;
184
+ for (const entry of results) {
185
+ if (typeof entry !== 'object' || entry === null)
186
+ continue;
187
+ const value = entry.iterations_completed;
188
+ if (typeof value !== 'number' || !Number.isFinite(value))
189
+ continue;
190
+ total = (total ?? 0) + value;
191
+ }
192
+ return total;
193
+ }
194
+ /**
195
+ * Reduce a finished flow to its outcome. The ONE place a `FlowResult` is
196
+ * interpreted, so "what happened" has a single definition.
197
+ *
198
+ * `result === null` means the flow threw instead of returning (a detached
199
+ * debugger, a closed tab). That is a `failed` outcome with no step
200
+ * attribution, which is honest — the runner never got to report one.
201
+ *
202
+ * The runner only ever reports `stopped_by: 'cancelled'`: it flips when
203
+ * `shouldCancel` goes true and has no idea WHY it went true. The registry
204
+ * entry does (`stopReason`), which is what promotes a run that outlived
205
+ * its ceiling from `cancelled` to `expired` here.
206
+ */
207
+ export function summarizeFlow(entry, result) {
208
+ if (result === null) {
209
+ // No result to read, so fall back to the last progress report: the
210
+ // runner had announced step N, which means N-1 finished.
211
+ return {
212
+ outcome: 'failed',
213
+ stepsCompleted: Math.max(0, entry.progress.step - 1),
214
+ ...(entry.stopReason === undefined ? {} : { stoppedBy: entry.stopReason }),
215
+ };
216
+ }
217
+ if (!result.ok) {
218
+ return {
219
+ outcome: 'failed',
220
+ stepsCompleted: result.steps_completed,
221
+ failedStep: result.failed_step,
222
+ // A fixed vocabulary from `stepKind()` ('click', 'wait_for',
223
+ // 'unknown', 'none'), never free text — see the privacy contract.
224
+ failedStepKind: result.step_kind,
225
+ error: result.error,
226
+ };
227
+ }
228
+ const iterations = countIterations(result.results);
229
+ const tail = iterations === undefined ? {} : { iterationsCompleted: iterations };
230
+ if (result.stopped_by !== 'cancelled') {
231
+ return { outcome: 'completed', stepsCompleted: result.steps_completed, ...tail };
232
+ }
233
+ const stoppedBy = entry.stopReason ?? 'cancelled';
234
+ return {
235
+ outcome: stoppedBy === 'expired' ? 'expired' : 'cancelled',
236
+ stepsCompleted: result.steps_completed,
237
+ ...tail,
238
+ stoppedBy,
239
+ };
240
+ }
241
+ /**
242
+ * Reduce a finished flow to a history entry — the ONE place a summary is
243
+ * allowed to turn into POPUP-visible stored state, and therefore the one
244
+ * place the privacy contract has to hold. Nothing is copied across by
245
+ * spread: every field is named, typed and numeric/enum, and the summary's
246
+ * `error` is deliberately not among them.
247
+ */
248
+ export function toHistoryEntry(entry, result, endedAt) {
249
+ const summary = summarizeFlow(entry, result);
250
+ return {
251
+ flowId: entry.flowId,
252
+ startedAt: entry.startedAt,
253
+ durationMs: Math.max(0, endedAt - entry.startedAt),
254
+ steps: entry.steps,
255
+ outcome: summary.outcome,
256
+ stepsCompleted: summary.stepsCompleted,
257
+ ...(summary.iterationsCompleted === undefined
258
+ ? {}
259
+ : { iterationsCompleted: summary.iterationsCompleted }),
260
+ ...(summary.failedStep === undefined ? {} : { failedStep: summary.failedStep }),
261
+ ...(summary.failedStepKind === undefined ? {} : { failedStepKind: summary.failedStepKind }),
262
+ ...(entry.detached ? { detached: true } : {}),
263
+ ...(summary.stoppedBy === undefined ? {} : { stoppedBy: summary.stoppedBy }),
264
+ };
265
+ }
266
+ /** Prepend a finished flow to a tab's history and enforce the cap. Newest
267
+ * first — the panel shows the most recent flow at the top, and the cap
268
+ * drops the oldest. Pure so the cap is testable without touching
269
+ * `chrome.storage`. */
270
+ export function appendFlowHistory(existing, entry) {
271
+ return [entry, ...existing].slice(0, MAX_FLOW_HISTORY_PER_TAB);
272
+ }
273
+ /** Narrow whatever `chrome.storage.session` hands back into a history
274
+ * list. Storage is not a trusted, typed channel (an older extension
275
+ * version may have written a different shape, and the value survives a
276
+ * service-worker restart), so an entry that does not carry the required
277
+ * primitives is dropped rather than rendered as `undefined`. */
278
+ export function parseFlowHistory(raw) {
279
+ if (!Array.isArray(raw))
280
+ return [];
281
+ const parsed = [];
282
+ for (const item of raw) {
283
+ if (typeof item !== 'object' || item === null)
284
+ continue;
285
+ const candidate = item;
286
+ if (typeof candidate.flowId !== 'string')
287
+ continue;
288
+ if (typeof candidate.startedAt !== 'number')
289
+ continue;
290
+ if (typeof candidate.durationMs !== 'number')
291
+ continue;
292
+ if (typeof candidate.steps !== 'number')
293
+ continue;
294
+ if (typeof candidate.stepsCompleted !== 'number')
295
+ continue;
296
+ if (!isFlowOutcome(candidate.outcome))
297
+ continue;
298
+ parsed.push({
299
+ flowId: candidate.flowId,
300
+ startedAt: candidate.startedAt,
301
+ durationMs: candidate.durationMs,
302
+ outcome: candidate.outcome,
303
+ steps: candidate.steps,
304
+ stepsCompleted: candidate.stepsCompleted,
305
+ ...(typeof candidate.iterationsCompleted === 'number'
306
+ ? { iterationsCompleted: candidate.iterationsCompleted }
307
+ : {}),
308
+ ...(typeof candidate.failedStep === 'number' ? { failedStep: candidate.failedStep } : {}),
309
+ ...(typeof candidate.failedStepKind === 'string'
310
+ ? { failedStepKind: candidate.failedStepKind }
311
+ : {}),
312
+ ...(candidate.detached === true ? { detached: true } : {}),
313
+ ...(isFlowStopReason(candidate.stoppedBy) ? { stoppedBy: candidate.stoppedBy } : {}),
314
+ });
315
+ }
316
+ return parsed.slice(0, MAX_FLOW_HISTORY_PER_TAB);
317
+ }
318
+ function isFlowOutcome(value) {
319
+ return (value === 'completed' || value === 'cancelled' || value === 'failed' || value === 'expired');
320
+ }
321
+ function isFlowStopReason(value) {
322
+ return value === 'cancelled' || value === 'expired' || value === 'worker-terminated';
323
+ }
324
+ /** Compact duration for the popup: "0.4s" under a second, "12s" under a
325
+ * minute, "3m 07s" beyond. Seconds are zero-padded in the minute form so
326
+ * the column does not jitter as a flow runs. */
327
+ export function formatFlowDuration(ms) {
328
+ const safe = ms > 0 ? ms : 0;
329
+ if (safe < 1000)
330
+ return `${(safe / 1000).toFixed(1)}s`;
331
+ const totalSeconds = Math.floor(safe / 1000);
332
+ if (totalSeconds < 60)
333
+ return `${totalSeconds}s`;
334
+ const minutes = Math.floor(totalSeconds / 60);
335
+ const seconds = totalSeconds % 60;
336
+ return `${minutes}m ${String(seconds).padStart(2, '0')}s`;
337
+ }
338
+ /** What the popup calls a flow's tab. The title is best-effort (it is
339
+ * captured asynchronously at flow start and can legitimately be empty on
340
+ * a blank or still-loading tab), so the Chrome tab id is the fallback —
341
+ * an unlabelled Stop button next to an unlabelled flow would be exactly
342
+ * the wrong thing to hand someone trying to stop the right one. */
343
+ export function flowTabLabel(view) {
344
+ const trimmed = view.title.trim();
345
+ return trimmed.length > 0 ? trimmed : `Tab ${view.tabId}`;
346
+ }
347
+ /** The progress line for one running flow: "step 2/5" outside a repeat,
348
+ * "step 2/5 · iteration 12/50" inside one, "starting…" before the runner
349
+ * has reported its first step. A detached flow is labelled as such: it is
350
+ * the one row where the human is the ONLY party still watching. */
351
+ export function describeFlowProgress(view) {
352
+ const prefix = view.detached ? 'detached · ' : '';
353
+ if (view.step < 1)
354
+ return `${prefix}starting…`;
355
+ const base = `${prefix}step ${view.step}/${view.steps}`;
356
+ if (view.iteration === undefined || view.iterations === undefined)
357
+ return base;
358
+ return `${base} · iteration ${view.iteration}/${view.iterations}`;
359
+ }
360
+ /** How a stop reason reads in the panel. `worker-terminated` gets a real
361
+ * sentence because it is the one outcome a user cannot otherwise explain:
362
+ * Chrome killed the extension's service worker and the flow died with it. */
363
+ const STOP_REASON_LABEL = {
364
+ cancelled: 'stopped by you',
365
+ expired: 'hit the 30-minute detached ceiling',
366
+ 'worker-terminated': 'extension worker was terminated — not resumed',
367
+ };
368
+ /** The one-line summary for a finished flow: outcome, what it got through,
369
+ * and how long it took. Structure and counts only. */
370
+ export function describeFlowHistoryEntry(entry) {
371
+ const progress = entry.iterationsCompleted === undefined
372
+ ? `${entry.stepsCompleted}/${entry.steps} steps`
373
+ : `${entry.stepsCompleted}/${entry.steps} steps · ${entry.iterationsCompleted} iterations`;
374
+ const failure = entry.outcome === 'failed' && entry.failedStep !== undefined
375
+ ? ` · failed at step ${entry.failedStep + 1}${entry.failedStepKind === undefined ? '' : ` (${entry.failedStepKind})`}`
376
+ : '';
377
+ // Only when it adds something: a `cancelled` entry already says
378
+ // "cancelled" in its own badge, so repeating "stopped by you" there
379
+ // would be noise.
380
+ const reason = entry.stoppedBy !== undefined && entry.stoppedBy !== 'cancelled'
381
+ ? ` · ${STOP_REASON_LABEL[entry.stoppedBy]}`
382
+ : '';
383
+ const detached = entry.detached === true ? 'detached · ' : '';
384
+ return `${detached}${progress} · ${formatFlowDuration(entry.durationMs)}${failure}${reason}`;
385
+ }
386
+ // === Detached-flow records ================================================
387
+ /** `chrome.storage.session` key holding ONE detached flow's record. Keyed
388
+ * by `flow_id` (not by tab): the id is what `browser.flow_status` and
389
+ * `browser.flow_cancel` address, and it stays valid across a tab
390
+ * reconnecting under a new browser-link id. */
391
+ export function detachedFlowKey(flowId) {
392
+ return `detached-flow:${flowId}`;
393
+ }
394
+ const DETACHED_FLOW_KEY_PREFIX = 'detached-flow:';
395
+ /** Inverse of `detachedFlowKey`, for the worker-start sweep over
396
+ * `chrome.storage.session.get(null)`. Returns null for every other key. */
397
+ export function flowIdFromDetachedFlowKey(key) {
398
+ if (!key.startsWith(DETACHED_FLOW_KEY_PREFIX))
399
+ return null;
400
+ const flowId = key.slice(DETACHED_FLOW_KEY_PREFIX.length);
401
+ return flowId.length > 0 ? flowId : null;
402
+ }
403
+ /** How many detached-flow records are kept at once, across every tab.
404
+ * Small on purpose: each terminal record carries a full action manifest,
405
+ * `chrome.storage.session` is a shared, quota-bounded store, and the
406
+ * realistic access pattern is "the agent that launched it polls it, reads
407
+ * the manifest once, and moves on". Oldest-first eviction. */
408
+ export const MAX_DETACHED_FLOW_RECORDS = 10;
409
+ /** Byte ceiling on ONE stored record's serialized manifest. A 500-iteration
410
+ * repeat over a 20-step body can produce a manifest far larger than
411
+ * anything a session store should hold, and losing the whole record to a
412
+ * quota rejection would be strictly worse than losing the manifest: the
413
+ * counts, the outcome and the failing step all still fit. Over the ceiling
414
+ * the manifest is dropped and `manifestTruncated` says so out loud, rather
415
+ * than a silently short array the agent would read as "that's all that
416
+ * happened". */
417
+ export const MAX_DETACHED_MANIFEST_BYTES = 128_000;
418
+ /** The record written the instant a detached flow is launched. Its whole
419
+ * job is to exist: a `running` record with no live registry entry behind it
420
+ * is the ONLY evidence that Chrome killed the service worker mid-flow. */
421
+ export function startedDetachedFlowRecord(entry) {
422
+ return {
423
+ flowId: entry.flowId,
424
+ tabId: entry.tabId,
425
+ state: 'running',
426
+ startedAt: entry.startedAt,
427
+ steps: entry.steps,
428
+ stepsCompleted: 0,
429
+ };
430
+ }
431
+ /** The terminal record, manifest included. `endedAt` is passed in rather
432
+ * than read off the clock so the caller can use the same instant for the
433
+ * record, the history entry and the audit event. */
434
+ export function finishedDetachedFlowRecord(entry, result, endedAt) {
435
+ const summary = summarizeFlow(entry, result);
436
+ const manifest = result !== null && result.ok ? result.results : [];
437
+ // Measured on the manifest alone: the rest of the record is a handful of
438
+ // numbers and a bounded error string, and charging them against the
439
+ // ceiling would make the cutoff depend on an error message's length.
440
+ const oversized = measureJsonBytes(manifest) > MAX_DETACHED_MANIFEST_BYTES;
441
+ return {
442
+ flowId: entry.flowId,
443
+ tabId: entry.tabId,
444
+ state: summary.outcome,
445
+ startedAt: entry.startedAt,
446
+ endedAt,
447
+ steps: entry.steps,
448
+ stepsCompleted: summary.stepsCompleted,
449
+ ...(summary.iterationsCompleted === undefined
450
+ ? {}
451
+ : { iterationsCompleted: summary.iterationsCompleted }),
452
+ ...(summary.stoppedBy === undefined ? {} : { stoppedBy: summary.stoppedBy }),
453
+ ...(summary.error === undefined ? {} : { error: summary.error }),
454
+ ...(oversized ? { manifestTruncated: true } : { manifest }),
455
+ };
456
+ }
457
+ /** Serialized size of a value, or `Infinity` when it cannot be serialized
458
+ * at all (a cycle, a BigInt). Unserializable is treated as over the
459
+ * ceiling: the alternative is letting `chrome.storage.session.set` throw
460
+ * and losing the entire record. */
461
+ function measureJsonBytes(value) {
462
+ try {
463
+ const json = JSON.stringify(value);
464
+ return typeof json === 'string' ? json.length : Number.POSITIVE_INFINITY;
465
+ }
466
+ catch {
467
+ return Number.POSITIVE_INFINITY;
468
+ }
469
+ }
470
+ /**
471
+ * Reconcile a persisted record against the fact that this service worker
472
+ * has no live registry entry for it — i.e. Chrome terminated the worker
473
+ * while the flow was mid-run.
474
+ *
475
+ * THE RULE, and it is not negotiable: mark it `failed` /
476
+ * `'worker-terminated'` and NEVER resume. A detached flow exists to do
477
+ * irreversible bulk work; a resume that could not know how far the dead run
478
+ * actually got would double-act on some prefix of it. A loud, honest
479
+ * failure is strictly better than a silent second pass, and a phantom
480
+ * `running` state — a flow that looks alive while nothing is executing it —
481
+ * is worse than both.
482
+ *
483
+ * The manifest of the dead run is genuinely unrecoverable: it lived in the
484
+ * runner's closure, and there was never a per-step write that could have
485
+ * saved it. `manifestTruncated` says so rather than presenting an empty
486
+ * array as "nothing happened".
487
+ *
488
+ * Returns `null` for a record that is already terminal — a second worker
489
+ * restart must not rewrite an outcome, nor re-fire its audit event.
490
+ */
491
+ export function terminatedDetachedFlowRecord(record, endedAt) {
492
+ if (record.state !== 'running')
493
+ return null;
494
+ const { manifest: _dropped, ...rest } = record;
495
+ return {
496
+ ...rest,
497
+ state: 'failed',
498
+ endedAt,
499
+ stoppedBy: 'worker-terminated',
500
+ manifestTruncated: true,
501
+ eventPending: true,
502
+ };
503
+ }
504
+ /** The same terminal record without its manifest — what gets written when
505
+ * storage refuses the full one. The manifest is the only part large enough
506
+ * to be refused; state, `stoppedBy`, the counters and the error are a few
507
+ * hundred bytes. Losing the manifest is a documented outcome; losing the
508
+ * write is not: the stale `running` record becomes `worker-terminated`. */
509
+ export function minimalDetachedFlowRecord(record) {
510
+ const { manifest: _dropped, ...rest } = record;
511
+ return { ...rest, manifestTruncated: true };
512
+ }
513
+ /** The history entry for a flow the service worker was killed under. The
514
+ * live entry is gone, so this is reconstructed from the persisted record —
515
+ * counts and enums only, same privacy contract as every other entry. */
516
+ export function terminatedFlowHistoryEntry(record) {
517
+ return {
518
+ flowId: record.flowId,
519
+ startedAt: record.startedAt,
520
+ durationMs: Math.max(0, (record.endedAt ?? record.startedAt) - record.startedAt),
521
+ steps: record.steps,
522
+ outcome: 'failed',
523
+ stepsCompleted: record.stepsCompleted,
524
+ detached: true,
525
+ stoppedBy: 'worker-terminated',
526
+ };
527
+ }
528
+ /** Narrow whatever `chrome.storage.session` hands back into a record.
529
+ * Same posture as `parseFlowHistory`: storage is not a typed channel, and a
530
+ * record written by an older extension version must be dropped rather than
531
+ * rendered as a half-`undefined` status payload. */
532
+ export function parseDetachedFlowRecord(raw) {
533
+ if (typeof raw !== 'object' || raw === null)
534
+ return null;
535
+ const c = raw;
536
+ if (typeof c.flowId !== 'string' || c.flowId.length === 0)
537
+ return null;
538
+ if (typeof c.tabId !== 'number')
539
+ return null;
540
+ if (typeof c.startedAt !== 'number')
541
+ return null;
542
+ if (typeof c.steps !== 'number')
543
+ return null;
544
+ if (typeof c.stepsCompleted !== 'number')
545
+ return null;
546
+ if (c.state !== 'running' &&
547
+ c.state !== 'completed' &&
548
+ c.state !== 'cancelled' &&
549
+ c.state !== 'failed' &&
550
+ c.state !== 'expired') {
551
+ return null;
552
+ }
553
+ return {
554
+ flowId: c.flowId,
555
+ tabId: c.tabId,
556
+ state: c.state,
557
+ startedAt: c.startedAt,
558
+ steps: c.steps,
559
+ stepsCompleted: c.stepsCompleted,
560
+ ...(typeof c.endedAt === 'number' ? { endedAt: c.endedAt } : {}),
561
+ ...(typeof c.iterationsCompleted === 'number'
562
+ ? { iterationsCompleted: c.iterationsCompleted }
563
+ : {}),
564
+ ...(isFlowStopReason(c.stoppedBy) ? { stoppedBy: c.stoppedBy } : {}),
565
+ ...(typeof c.error === 'string' ? { error: c.error } : {}),
566
+ ...(Array.isArray(c.manifest) ? { manifest: c.manifest } : {}),
567
+ ...(c.manifestTruncated === true ? { manifestTruncated: true } : {}),
568
+ ...(c.eventPending === true ? { eventPending: true } : {}),
569
+ };
570
+ }
571
+ /** Which stored records to evict so `MAX_DETACHED_FLOW_RECORDS` holds,
572
+ * oldest-start first. RUNNING records are never evicted regardless of age:
573
+ * dropping one would erase the only evidence that a flow was in flight, and
574
+ * a worker restart would then have nothing to mark `worker-terminated`.
575
+ * Records still owing their summary event (`eventPending`) go LAST, reached
576
+ * only when nothing else is left: evicting one destroys an audit event no
577
+ * one has received, but a cap that can never be enforced would block every
578
+ * new record. The exemption lapses when the flush clears the flag. */
579
+ export function detachedFlowRecordsToEvict(records) {
580
+ const terminal = records
581
+ .filter((r) => r.state !== 'running')
582
+ .sort((a, b) => a.startedAt - b.startedAt);
583
+ const keep = Math.max(0, MAX_DETACHED_FLOW_RECORDS - (records.length - terminal.length));
584
+ const excess = terminal.length - keep;
585
+ if (excess <= 0)
586
+ return [];
587
+ return [
588
+ ...terminal.filter((r) => r.eventPending !== true),
589
+ ...terminal.filter((r) => r.eventPending === true),
590
+ ].slice(0, excess);
591
+ }
592
+ /**
593
+ * Build the status payload for a flow id, from whichever source knows
594
+ * about it. The live registry entry wins for a running flow (it has
595
+ * up-to-the-step progress the twice-written record cannot), the persisted
596
+ * record answers for everything that already ended, and an id neither
597
+ * knows about comes back as `state: 'unknown'` rather than as an error.
598
+ */
599
+ export function toFlowStatus(flowId, entry, record) {
600
+ if (entry) {
601
+ return {
602
+ flow_id: flowId,
603
+ state: 'running',
604
+ detached: entry.detached,
605
+ started_at: entry.startedAt,
606
+ steps: entry.steps,
607
+ // The runner reports the step it is ABOUT to dispatch, so N announced
608
+ // means N-1 finished. Same derivation the history uses for a flow
609
+ // that threw.
610
+ steps_completed: Math.max(0, entry.progress.step - 1),
611
+ ...(entry.progress.iteration === undefined
612
+ ? {}
613
+ : { iterations_completed: Math.max(0, entry.progress.iteration - 1) }),
614
+ ...(entry.cancelled ? { cancelling: true } : {}),
615
+ ...(entry.expiresAt === undefined ? {} : { expires_at: entry.expiresAt }),
616
+ };
617
+ }
618
+ if (record) {
619
+ return {
620
+ flow_id: flowId,
621
+ state: record.state,
622
+ detached: true,
623
+ started_at: record.startedAt,
624
+ ...(record.endedAt === undefined ? {} : { ended_at: record.endedAt }),
625
+ steps: record.steps,
626
+ steps_completed: record.stepsCompleted,
627
+ ...(record.iterationsCompleted === undefined
628
+ ? {}
629
+ : { iterations_completed: record.iterationsCompleted }),
630
+ ...(record.stoppedBy === undefined ? {} : { stopped_by: record.stoppedBy }),
631
+ ...(record.error === undefined ? {} : { error: record.error }),
632
+ ...(record.manifest === undefined ? {} : { manifest: record.manifest }),
633
+ ...(record.manifestTruncated === true ? { manifest_truncated: true } : {}),
634
+ };
635
+ }
636
+ return { flow_id: flowId, state: 'unknown', detached: false };
637
+ }
638
+ //# sourceMappingURL=flow-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flow-registry.js","sourceRoot":"","sources":["../src/flow-registry.ts"],"names":[],"mappings":"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"}