@workser/runtime 0.0.0-stage → 0.1.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 (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +71 -2
  3. package/dist/cjs/client/follow.js +103 -0
  4. package/dist/cjs/client/index.js +6 -0
  5. package/dist/cjs/contract/actions.js +223 -0
  6. package/dist/cjs/contract/catalog.js +335 -0
  7. package/dist/cjs/contract/index.js +20 -0
  8. package/dist/cjs/contract/ordered-feed.js +94 -0
  9. package/dist/cjs/contract/types.js +9 -0
  10. package/dist/cjs/engine/engine.js +2 -0
  11. package/dist/cjs/index.js +27 -0
  12. package/dist/cjs/internal/async-queue.js +56 -0
  13. package/dist/cjs/internal/sse.js +170 -0
  14. package/dist/cjs/package.json +1 -0
  15. package/dist/cjs/place/place.js +2 -0
  16. package/dist/cjs/runner/index.js +6 -0
  17. package/dist/cjs/runner/journal.js +82 -0
  18. package/dist/cjs/runner/runner-client.js +477 -0
  19. package/dist/client/follow.d.ts +45 -0
  20. package/dist/client/follow.js +100 -0
  21. package/dist/client/follow.js.map +1 -0
  22. package/dist/client/index.d.ts +1 -0
  23. package/dist/client/index.js +2 -0
  24. package/dist/client/index.js.map +1 -0
  25. package/dist/contract/actions.d.ts +73 -0
  26. package/dist/contract/actions.js +217 -0
  27. package/dist/contract/actions.js.map +1 -0
  28. package/dist/contract/catalog.d.ts +97 -0
  29. package/dist/contract/catalog.js +328 -0
  30. package/dist/contract/catalog.js.map +1 -0
  31. package/dist/contract/index.d.ts +4 -0
  32. package/dist/contract/index.js +5 -0
  33. package/dist/contract/index.js.map +1 -0
  34. package/dist/contract/ordered-feed.d.ts +56 -0
  35. package/dist/contract/ordered-feed.js +91 -0
  36. package/dist/contract/ordered-feed.js.map +1 -0
  37. package/dist/contract/types.d.ts +311 -0
  38. package/dist/contract/types.js +9 -0
  39. package/dist/contract/types.js.map +1 -0
  40. package/dist/engine/engine.d.ts +39 -0
  41. package/dist/engine/engine.js +2 -0
  42. package/dist/engine/engine.js.map +1 -0
  43. package/dist/index.d.ts +10 -0
  44. package/dist/index.js +9 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/internal/async-queue.d.ts +14 -0
  47. package/dist/internal/async-queue.js +53 -0
  48. package/dist/internal/async-queue.js.map +1 -0
  49. package/dist/internal/sse.d.ts +57 -0
  50. package/dist/internal/sse.js +164 -0
  51. package/dist/internal/sse.js.map +1 -0
  52. package/dist/place/place.d.ts +81 -0
  53. package/dist/place/place.js +2 -0
  54. package/dist/place/place.js.map +1 -0
  55. package/dist/runner/index.d.ts +2 -0
  56. package/dist/runner/index.js +2 -0
  57. package/dist/runner/index.js.map +1 -0
  58. package/dist/runner/journal.d.ts +32 -0
  59. package/dist/runner/journal.js +79 -0
  60. package/dist/runner/journal.js.map +1 -0
  61. package/dist/runner/runner-client.d.ts +185 -0
  62. package/dist/runner/runner-client.js +473 -0
  63. package/dist/runner/runner-client.js.map +1 -0
  64. package/package.json +30 -4
@@ -0,0 +1,328 @@
1
+ /**
2
+ * The run event catalog: every event a runner may send, and what it must
3
+ * carry. Pure and dependency-free: this file is the source of truth in
4
+ * `@workser/runtime`, and core-api keeps a generated copy of it (plan Phase 0).
5
+ *
6
+ * TWO GATES, ON PURPOSE
7
+ * ---------------------
8
+ * - A runner (`v1/runner/runs/:id/events`) is strict: an unknown type or a
9
+ * malformed payload is refused, so a screen never has to guess what an event
10
+ * means.
11
+ * - The service engine (`internal/runs/:id/events`) is lenient: it already
12
+ * sends ~30 types, some built at runtime, and refusing one would lose a live
13
+ * run's history. Unknown types are accepted and counted (`unknownLegacyType`)
14
+ * so the catalog can grow to cover them before that gate tightens.
15
+ */
16
+ export const RUNNER_PROTOCOL_VERSION = 1;
17
+ /** Largest event payload we keep, serialised. Bigger outputs go to files. */
18
+ export const MAX_EVENT_DATA_BYTES = 64 * 1024;
19
+ /** Largest batch a runner may send in one call. */
20
+ export const MAX_EVENT_BATCH = 100;
21
+ const text = (maxLength = 20_000, required = true) => ({
22
+ type: 'string',
23
+ required,
24
+ maxLength,
25
+ });
26
+ const id = (required = true) => text(120, required);
27
+ /**
28
+ * What each event means, for the people reading it on a screen:
29
+ *
30
+ * - run.* the run's life. Terminal ones end the stream.
31
+ * - step.* one thing the agent did (a command, an edit, a tool call).
32
+ * - message* what the agent said; `.delta` streams it as it is written.
33
+ * - file/diff what changed on disk.
34
+ * - check.result a done-check (tests, typecheck, deck validates, numbers match).
35
+ * - preview.ready a URL the owner can open.
36
+ * - handoff.* a sub-job this run asked for started or ended (core-api
37
+ * writes these on the parent; `run.completed.result` is the
38
+ * child's typed answer).
39
+ * - usage tokens and cost as the runner saw them; billing uses the
40
+ * gateway's own figure, never this one.
41
+ * - log plain lines for the details view.
42
+ */
43
+ export const RUN_EVENT_CATALOG = {
44
+ 'run.started': { engine: text(80, false), place: text(40, false) },
45
+ 'run.paused': { reason: text(500, false) },
46
+ 'run.resumed': {},
47
+ // `result`: the typed answer a handoff's parent asked for (`Handoff.expected`).
48
+ 'run.completed': { summary: text(20_000, false), result: { type: 'object' } },
49
+ 'run.failed': { error_kind: text(80, false), message: text(4_000, false) },
50
+ 'run.cancelled': { reason: text(500, false) },
51
+ // A person stopped the job before it read some messages sent into it (its
52
+ // unacked `message` commands). Core writes it on Stop; nothing new starts.
53
+ // `messages`: [{ pos, text (≤500 chars), author (name or null) }], oldest first.
54
+ 'run.messages_unseen': {
55
+ count: { type: 'number', required: true },
56
+ messages: { type: 'array', required: true },
57
+ },
58
+ // A session-mode job finished a turn and is waiting for the next message.
59
+ 'run.idle': { summary: text(20_000, false), turn: { type: 'number' } },
60
+ // A reply turned into a job (reply-or-job contract §2c): the AI agent called
61
+ // `begin_job`, or the safety net saw an action, a computer, a fan-out, a
62
+ // helper or a long run. Core makes it a job once (migration 300).
63
+ 'run.became_job': {
64
+ reason: { type: 'string', required: true, oneOf: ['begin_job', 'action', 'computer', 'fan_out', 'long', 'helper', 'owner'] },
65
+ title: text(500, false),
66
+ eta: text(200, false),
67
+ },
68
+ // The engine's own session, so a later runner can reattach to it
69
+ // (core-api puts the latest into the brief as `run.resume`).
70
+ 'engine.session': { engine: text(80), session_id: text(200), workdir: text(1_000) },
71
+ // The agent is waiting on a person (`owner`) or on an approval it raised.
72
+ // `run.resumed` says it carried on.
73
+ 'run.waiting': {
74
+ on: { type: 'string', required: true, oneOf: ['owner', 'approval'] },
75
+ ask_id: id(false),
76
+ },
77
+ 'step.started': {
78
+ step_id: id(),
79
+ title: text(500),
80
+ tool: text(120, false),
81
+ input: { type: 'object' },
82
+ // The sub-job this step handed work to, and the step it is nested under.
83
+ child_run_id: id(false),
84
+ parent_step_id: id(false),
85
+ },
86
+ // Live output of a long step. Throttle to about one a second per step.
87
+ 'step.progress': {
88
+ step_id: id(),
89
+ text: text(2_000, false),
90
+ pct: { type: 'number' },
91
+ stdout_tail: text(2_000, false),
92
+ },
93
+ // The agent's to-do list changed (`write_todos`): the whole list, every time.
94
+ 'plan.updated': {
95
+ todos: { type: 'array', required: true },
96
+ },
97
+ 'step.completed': {
98
+ step_id: id(),
99
+ status: { type: 'string', required: true, oneOf: ['ok', 'error', 'skipped'] },
100
+ output: { type: 'object' },
101
+ error: text(4_000, false),
102
+ duration_ms: { type: 'number' },
103
+ },
104
+ message: {
105
+ message_id: id(false),
106
+ role: {
107
+ type: 'string',
108
+ required: true,
109
+ oneOf: ['assistant', 'user', 'system'],
110
+ },
111
+ text: text(),
112
+ },
113
+ 'message.delta': { message_id: id(), text: text() },
114
+ 'file.changed': {
115
+ path: text(1_000),
116
+ change: {
117
+ type: 'string',
118
+ required: true,
119
+ oneOf: ['added', 'modified', 'deleted', 'renamed'],
120
+ },
121
+ },
122
+ 'diff.ready': {
123
+ files: { type: 'number', required: true },
124
+ additions: { type: 'number' },
125
+ deletions: { type: 'number' },
126
+ },
127
+ 'check.result': {
128
+ name: text(200),
129
+ status: {
130
+ type: 'string',
131
+ required: true,
132
+ oneOf: ['pass', 'fail', 'skip'],
133
+ },
134
+ detail: text(8_000, false),
135
+ },
136
+ 'preview.ready': { url: text(2_000) },
137
+ // Written by core-api on the PARENT when a handoff starts and ends.
138
+ 'handoff.created': { child_run_id: id(), goal: text(2_000, false) },
139
+ 'handoff.finished': {
140
+ child_run_id: id(),
141
+ status: {
142
+ type: 'string',
143
+ required: true,
144
+ oneOf: ['completed', 'failed', 'cancelled'],
145
+ },
146
+ },
147
+ usage: {
148
+ model: text(200, false),
149
+ tokens_in: { type: 'number' },
150
+ tokens_out: { type: 'number' },
151
+ cost_usd: { type: 'number' },
152
+ },
153
+ log: {
154
+ level: {
155
+ type: 'string',
156
+ required: true,
157
+ oneOf: ['debug', 'info', 'warn', 'error'],
158
+ },
159
+ text: text(),
160
+ },
161
+ };
162
+ /** Events that end a run. The stream closes after it delivers one. */
163
+ export const TERMINAL_EVENT_TYPES = new Set([
164
+ 'run.completed',
165
+ 'run.failed',
166
+ 'run.cancelled',
167
+ ]);
168
+ /**
169
+ * Types the service engine sends today that aren't runner events. Accepted on
170
+ * the lenient gate without being counted as unknown.
171
+ */
172
+ export const LEGACY_EVENT_TYPES = new Set([
173
+ 'browser.live',
174
+ 'composio.authorization',
175
+ 'computer.busy',
176
+ 'computer.ready',
177
+ 'computer.requested',
178
+ 'computer.viewer.available',
179
+ 'computer.viewer.unavailable',
180
+ 'connector.execution.receipt',
181
+ 'run.capabilities_attached',
182
+ 'run.inference_overridden',
183
+ 'run.limit_reached',
184
+ 'run.llm_call',
185
+ 'run.model_fallback',
186
+ 'run.running',
187
+ 'run.starting',
188
+ 'run.tool_call',
189
+ 'run.tool_refused',
190
+ 'run.training_loaded',
191
+ 'setup.change',
192
+ 'ui.card',
193
+ 'workser_computer.conversation.gate',
194
+ 'workser_computer.route.selected',
195
+ ]);
196
+ /** Is this a well-formed runner event? The strict gate. */
197
+ export function checkRunnerEvent(event) {
198
+ if (!event || typeof event !== 'object') {
199
+ return { ok: false, reason: 'An event must be an object.' };
200
+ }
201
+ if (typeof event.event_id !== 'string' || !event.event_id || event.event_id.length > 40) {
202
+ return { ok: false, reason: 'event_id must be a string of 1–40 characters.' };
203
+ }
204
+ if (!Number.isInteger(event.seq) || event.seq < 1) {
205
+ return { ok: false, reason: 'seq must be a positive whole number.' };
206
+ }
207
+ const shape = RUN_EVENT_CATALOG[event.type];
208
+ if (!shape) {
209
+ return { ok: false, reason: `Unknown event type "${String(event.type)}".` };
210
+ }
211
+ const data = event.data ?? {};
212
+ if (typeof data !== 'object' || Array.isArray(data) || data === null) {
213
+ return { ok: false, reason: `${event.type}: data must be an object.` };
214
+ }
215
+ const size = byteLength(data);
216
+ if (size > MAX_EVENT_DATA_BYTES) {
217
+ return {
218
+ ok: false,
219
+ reason: `${event.type}: data is ${size} bytes; the limit is ${MAX_EVENT_DATA_BYTES}. Put large output in a file.`,
220
+ };
221
+ }
222
+ for (const [field, rule] of Object.entries(shape)) {
223
+ const value = data[field];
224
+ if (value === undefined || value === null) {
225
+ if (rule.required) {
226
+ return { ok: false, reason: `${event.type}: "${field}" is required.` };
227
+ }
228
+ continue;
229
+ }
230
+ const actual = Array.isArray(value) ? 'array' : typeof value;
231
+ if (actual !== rule.type) {
232
+ return {
233
+ ok: false,
234
+ reason: `${event.type}: "${field}" must be ${rule.type}, got ${actual}.`,
235
+ };
236
+ }
237
+ if (rule.type === 'number' && !Number.isFinite(value)) {
238
+ return { ok: false, reason: `${event.type}: "${field}" must be finite.` };
239
+ }
240
+ if (rule.oneOf && !rule.oneOf.includes(value)) {
241
+ return {
242
+ ok: false,
243
+ reason: `${event.type}: "${field}" must be one of ${rule.oneOf.join(', ')}.`,
244
+ };
245
+ }
246
+ if (rule.maxLength && value.length > rule.maxLength) {
247
+ return {
248
+ ok: false,
249
+ reason: `${event.type}: "${field}" is longer than ${rule.maxLength} characters.`,
250
+ };
251
+ }
252
+ }
253
+ return { ok: true };
254
+ }
255
+ /** The lenient gate: only the envelope is checked. */
256
+ export function checkLegacyEvent(event) {
257
+ if (typeof event.event_type !== 'string' || !event.event_type || event.event_type.length > 80) {
258
+ return { ok: false, reason: 'event_type must be a string of 1–80 characters.' };
259
+ }
260
+ if (typeof event.event_id !== 'string' || !event.event_id || event.event_id.length > 40) {
261
+ return { ok: false, reason: 'event_id must be a string of 1–40 characters.' };
262
+ }
263
+ if (!Number.isInteger(event.seq)) {
264
+ return { ok: false, reason: 'seq must be a whole number.' };
265
+ }
266
+ return { ok: true };
267
+ }
268
+ /** A type neither in the catalog nor in the known legacy list. */
269
+ export function unknownLegacyType(type) {
270
+ return !RUN_EVENT_CATALOG[type] && !LEGACY_EVENT_TYPES.has(type);
271
+ }
272
+ /**
273
+ * The run status a runner event moves the run to, or null when it doesn't.
274
+ * Status only moves forward: a terminal run never changes again.
275
+ */
276
+ export function statusForRunnerEvent(type) {
277
+ switch (type) {
278
+ case 'run.started':
279
+ case 'run.resumed':
280
+ return 'RUNNING';
281
+ case 'run.completed':
282
+ return 'COMPLETED';
283
+ case 'run.failed':
284
+ return 'FAILED';
285
+ case 'run.cancelled':
286
+ return 'CANCELLED';
287
+ default:
288
+ return null;
289
+ }
290
+ }
291
+ /**
292
+ * Check a runner batch against the run's cursor (the last seq accepted).
293
+ *
294
+ * Events at or below the cursor are retries and are dropped here — their
295
+ * `event_id` is checked against the stored row by the caller. What remains
296
+ * must continue the cursor exactly: `cursor+1, cursor+2, …`. A gap means the
297
+ * runner lost an event, and it is told where to resend from rather than
298
+ * having the hole papered over.
299
+ */
300
+ export function checkBatchSeq(cursor, events) {
301
+ const sorted = [...events].sort((a, b) => a.seq - b.seq);
302
+ const fresh = sorted.filter((one) => one.seq > cursor);
303
+ let expected = cursor + 1;
304
+ for (const one of fresh) {
305
+ if (one.seq !== expected) {
306
+ return {
307
+ ok: false,
308
+ expected,
309
+ reason: one.seq < expected
310
+ ? `seq ${one.seq} appears twice in this batch.`
311
+ : `seq ${one.seq} skips ahead; expected ${expected}. Resend from ${expected}.`,
312
+ };
313
+ }
314
+ expected += 1;
315
+ }
316
+ return { ok: true, fresh, nextCursor: expected - 1 };
317
+ }
318
+ // TextEncoder, not Buffer: the same file runs in browsers and in core-api.
319
+ const encoder = new TextEncoder();
320
+ function byteLength(value) {
321
+ try {
322
+ return encoder.encode(JSON.stringify(value)).length;
323
+ }
324
+ catch {
325
+ return Number.POSITIVE_INFINITY;
326
+ }
327
+ }
328
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/contract/catalog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export * from './catalog.js';
2
+ export * from './ordered-feed.js';
3
+ export * from './types.js';
4
+ export * from './actions.js';
@@ -0,0 +1,5 @@
1
+ export * from './catalog.js';
2
+ export * from './ordered-feed.js';
3
+ export * from './types.js';
4
+ export * from './actions.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contract/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * One subscriber's view of a positioned log (run events, runner commands):
3
+ * everything after its cursor, in order, once each, with nothing missing.
4
+ *
5
+ * WHY THIS EXISTS
6
+ * ---------------
7
+ * The old stream replayed history, THEN subscribed to live events. Anything
8
+ * written while the history query ran fell between the two and was never
9
+ * shown. And live delivery across instances is a pointer read back from the
10
+ * database, so two events can arrive out of order, and a pointer lost while
11
+ * the listener reconnects is lost for good.
12
+ *
13
+ * The feed fixes all three by trusting only the database's order (`pos`):
14
+ * - live rows are OFFERED from the moment the subscriber exists, and wait
15
+ * behind the history replay rather than racing it;
16
+ * - a row that skips ahead makes the feed read the missing range first;
17
+ * - `catchUp()` (called on a timer) reads anything a lost pointer hid.
18
+ *
19
+ * Holes in `pos` are fine: position N+1 never commits before N has committed
20
+ * or rolled back (migration 239), so "everything after my cursor" read after
21
+ * seeing N+1 is complete.
22
+ */
23
+ export interface Positioned {
24
+ pos: number | null;
25
+ }
26
+ export type FetchAfter<T> = (after: number, limit: number) => Promise<T[]>;
27
+ export declare const FEED_PAGE = 500;
28
+ export declare class OrderedFeed<T extends Positioned> {
29
+ private readonly fetchAfter;
30
+ private readonly emit;
31
+ private readonly onError;
32
+ private last;
33
+ private chain;
34
+ private closed;
35
+ constructor(start: number, fetchAfter: FetchAfter<T>, emit: (row: T) => void, onError?: (error: unknown) => void);
36
+ /** The position of the last row emitted. */
37
+ get cursor(): number;
38
+ /**
39
+ * Replay everything after the start cursor. Call once, right after creating.
40
+ *
41
+ * `show`, when given, hides replayed rows it rejects while still moving the
42
+ * cursor past them — for the old `after_seq` resume, which skipped rows by
43
+ * the writer's own number. Live rows are never filtered.
44
+ */
45
+ start(show?: (row: T) => boolean): Promise<void>;
46
+ /** A live row. Emitted in order; a skip ahead reads the gap first. */
47
+ offer(row: T): Promise<void>;
48
+ /** Read anything after the cursor that live delivery didn't bring. */
49
+ catchUp(): Promise<void>;
50
+ /** Stop emitting. Pending work finishes silently. */
51
+ close(): void;
52
+ private push;
53
+ /** Emit stored rows after the cursor, up to `until` when given. */
54
+ private drain;
55
+ private enqueue;
56
+ }
@@ -0,0 +1,91 @@
1
+ export const FEED_PAGE = 500;
2
+ export class OrderedFeed {
3
+ fetchAfter;
4
+ emit;
5
+ onError;
6
+ last;
7
+ chain = Promise.resolve();
8
+ closed = false;
9
+ constructor(start, fetchAfter, emit, onError = () => undefined) {
10
+ this.fetchAfter = fetchAfter;
11
+ this.emit = emit;
12
+ this.onError = onError;
13
+ this.last = Math.max(0, Math.floor(start || 0));
14
+ }
15
+ /** The position of the last row emitted. */
16
+ get cursor() {
17
+ return this.last;
18
+ }
19
+ /**
20
+ * Replay everything after the start cursor. Call once, right after creating.
21
+ *
22
+ * `show`, when given, hides replayed rows it rejects while still moving the
23
+ * cursor past them — for the old `after_seq` resume, which skipped rows by
24
+ * the writer's own number. Live rows are never filtered.
25
+ */
26
+ start(show) {
27
+ return this.enqueue(() => this.drain(undefined, show));
28
+ }
29
+ /** A live row. Emitted in order; a skip ahead reads the gap first. */
30
+ offer(row) {
31
+ return this.enqueue(async () => {
32
+ const pos = row.pos;
33
+ if (pos == null || pos <= this.last)
34
+ return;
35
+ if (pos > this.last + 1) {
36
+ await this.drain(pos);
37
+ if (pos <= this.last)
38
+ return;
39
+ }
40
+ this.push(row);
41
+ });
42
+ }
43
+ /** Read anything after the cursor that live delivery didn't bring. */
44
+ catchUp() {
45
+ return this.enqueue(() => this.drain());
46
+ }
47
+ /** Stop emitting. Pending work finishes silently. */
48
+ close() {
49
+ this.closed = true;
50
+ }
51
+ push(row, show) {
52
+ if (this.closed || row.pos == null || row.pos <= this.last)
53
+ return;
54
+ this.last = row.pos;
55
+ if (show && !show(row))
56
+ return;
57
+ this.emit(row);
58
+ }
59
+ /** Emit stored rows after the cursor, up to `until` when given. */
60
+ async drain(until, show) {
61
+ for (;;) {
62
+ if (this.closed)
63
+ return;
64
+ const rows = await this.fetchAfter(this.last, FEED_PAGE);
65
+ for (const row of rows) {
66
+ if (row.pos == null)
67
+ continue;
68
+ if (until !== undefined && row.pos > until)
69
+ return;
70
+ this.push(row, show);
71
+ }
72
+ if (rows.length < FEED_PAGE)
73
+ return;
74
+ }
75
+ }
76
+ enqueue(work) {
77
+ const next = this.chain.then(async () => {
78
+ if (this.closed)
79
+ return;
80
+ try {
81
+ await work();
82
+ }
83
+ catch (error) {
84
+ this.onError(error);
85
+ }
86
+ });
87
+ this.chain = next;
88
+ return next;
89
+ }
90
+ }
91
+ //# sourceMappingURL=ordered-feed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ordered-feed.js","sourceRoot":"","sources":["../../src/contract/ordered-feed.ts"],"names":[],"mappings":"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"}