@crvouga/mockingbird-service-bedrock 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.
@@ -0,0 +1,4641 @@
1
+ // ../core/dist/clock.js
2
+ var createClock = (source = Date.now) => {
3
+ let offsetMs = 0;
4
+ let frozenAt;
5
+ const now = () => frozenAt ?? source() + offsetMs;
6
+ return {
7
+ now,
8
+ set: (epochMs) => {
9
+ if (frozenAt !== void 0)
10
+ frozenAt = epochMs;
11
+ else
12
+ offsetMs = epochMs - source();
13
+ },
14
+ advance: (deltaMs) => {
15
+ if (frozenAt !== void 0)
16
+ frozenAt += deltaMs;
17
+ else
18
+ offsetMs += deltaMs;
19
+ },
20
+ freeze: () => {
21
+ frozenAt = now();
22
+ },
23
+ unfreeze: () => {
24
+ if (frozenAt === void 0)
25
+ return;
26
+ offsetMs = frozenAt - source();
27
+ frozenAt = void 0;
28
+ },
29
+ reset: () => {
30
+ offsetMs = 0;
31
+ frozenAt = void 0;
32
+ },
33
+ state: () => ({ now: now(), frozen: frozenAt !== void 0, offsetMs })
34
+ };
35
+ };
36
+
37
+ // ../core/dist/collection.js
38
+ var Collection = class {
39
+ sqlite;
40
+ namespace;
41
+ name;
42
+ constructor(sqlite, namespace, name) {
43
+ this.sqlite = sqlite;
44
+ this.namespace = namespace;
45
+ this.name = name;
46
+ }
47
+ bumpCollectionSeq() {
48
+ const row = this.sqlite.prepare("SELECT value FROM mockingbird_sequences WHERE namespace = ? AND name = ? AND kind = 'collection'").get(this.namespace, this.name);
49
+ const next = (row?.value ?? 0) + 1;
50
+ this.sqlite.prepare(`INSERT INTO mockingbird_sequences (namespace, name, kind, value) VALUES (?, ?, 'collection', ?)
51
+ ON CONFLICT(namespace, name, kind) DO UPDATE SET value = excluded.value`).run(this.namespace, this.name, next);
52
+ return next;
53
+ }
54
+ nextSequence() {
55
+ return this.sqlite.transaction(() => this.bumpCollectionSeq());
56
+ }
57
+ get(id) {
58
+ const row = this.sqlite.prepare("SELECT value FROM mockingbird_records WHERE namespace = ? AND collection = ? AND id = ?").get(this.namespace, this.name, id);
59
+ if (!row)
60
+ return void 0;
61
+ return JSON.parse(row.value).value;
62
+ }
63
+ has(id) {
64
+ const row = this.sqlite.prepare("SELECT 1 AS ok FROM mockingbird_records WHERE namespace = ? AND collection = ? AND id = ?").get(this.namespace, this.name, id);
65
+ return row !== void 0;
66
+ }
67
+ /** Insert a new record, assigning it the next sequence number. */
68
+ insert(id, value) {
69
+ return this.sqlite.transaction(() => {
70
+ const seq = this.bumpCollectionSeq();
71
+ const stored = { seq, value };
72
+ this.sqlite.prepare(`INSERT INTO mockingbird_records (namespace, collection, id, seq, value)
73
+ VALUES (?, ?, ?, ?, ?)
74
+ ON CONFLICT(namespace, collection, id) DO UPDATE SET seq = excluded.seq, value = excluded.value`).run(this.namespace, this.name, id, seq, JSON.stringify(stored));
75
+ return stored;
76
+ });
77
+ }
78
+ /** Replace an existing record's value, keeping its position. */
79
+ update(id, value) {
80
+ return this.sqlite.transaction(() => {
81
+ const row = this.sqlite.prepare("SELECT seq, value FROM mockingbird_records WHERE namespace = ? AND collection = ? AND id = ?").get(this.namespace, this.name, id);
82
+ if (!row)
83
+ return void 0;
84
+ const stored = { seq: row.seq, value };
85
+ this.sqlite.prepare("UPDATE mockingbird_records SET value = ? WHERE namespace = ? AND collection = ? AND id = ?").run(JSON.stringify(stored), this.namespace, this.name, id);
86
+ return stored;
87
+ });
88
+ }
89
+ delete(id) {
90
+ const result = this.sqlite.prepare("DELETE FROM mockingbird_records WHERE namespace = ? AND collection = ? AND id = ?").run(this.namespace, this.name, id);
91
+ return result.changes > 0;
92
+ }
93
+ /** How many records the collection holds, without reading them. */
94
+ count() {
95
+ const row = this.sqlite.prepare("SELECT COUNT(*) AS n FROM mockingbird_records WHERE namespace = ? AND collection = ?").get(this.namespace, this.name);
96
+ return Number(row?.n ?? 0);
97
+ }
98
+ list(options = {}) {
99
+ const rows = this.sqlite.prepare("SELECT id, seq, value FROM mockingbird_records WHERE namespace = ? AND collection = ?").all(this.namespace, this.name);
100
+ const out = [];
101
+ for (const row of rows) {
102
+ const stored = JSON.parse(row.value);
103
+ if (options.where && !options.where(stored.value, stored.seq))
104
+ continue;
105
+ out.push({ id: row.id, seq: stored.seq, value: stored.value });
106
+ }
107
+ out.sort((a, b) => options.order === "oldest" ? a.seq - b.seq : b.seq - a.seq);
108
+ return out;
109
+ }
110
+ };
111
+
112
+ // ../core/dist/control.js
113
+ var HEALTH_PATH = "/health";
114
+ var ADMIN_PREFIX = "/__admin";
115
+ var ADMIN_KEY_HEADER = "x-mockingbird-admin-key";
116
+ var NAMESPACE_HEADER = "x-mockingbird-namespace";
117
+ var json = (status, body) => new Response(JSON.stringify(body), {
118
+ status,
119
+ headers: { "content-type": "application/json" }
120
+ });
121
+ var adminError = (status, message) => json(status, { error: { type: "mockingbird_admin", message } });
122
+ var UNITS = {
123
+ ms: 1,
124
+ s: 1e3,
125
+ m: 6e4,
126
+ h: 36e5,
127
+ d: 864e5
128
+ };
129
+ var parseDuration = (value) => {
130
+ if (typeof value === "number" && Number.isFinite(value))
131
+ return value;
132
+ if (typeof value !== "string")
133
+ return void 0;
134
+ const match = /^(-?\d+(?:\.\d+)?)\s*(ms|s|m|h|d)$/.exec(value.trim());
135
+ if (!match)
136
+ return void 0;
137
+ return Number(match[1]) * UNITS[match[2]];
138
+ };
139
+ var parseInstant = (value) => {
140
+ if (typeof value === "number" && Number.isFinite(value))
141
+ return value;
142
+ if (typeof value !== "string")
143
+ return void 0;
144
+ const parsed = Date.parse(value);
145
+ return Number.isNaN(parsed) ? void 0 : parsed;
146
+ };
147
+ var matchRoute = (pattern, path) => {
148
+ const want = pattern.split("/").filter(Boolean);
149
+ const have = path.split("/").filter(Boolean);
150
+ if (want.length !== have.length)
151
+ return void 0;
152
+ const params = {};
153
+ for (let i = 0; i < want.length; i++) {
154
+ const segment = want[i];
155
+ const actual = have[i];
156
+ if (segment.startsWith(":"))
157
+ params[segment.slice(1)] = decodeURIComponent(actual);
158
+ else if (segment !== actual)
159
+ return void 0;
160
+ }
161
+ return params;
162
+ };
163
+ var readJson = async (request) => {
164
+ const text2 = await request.text();
165
+ if (text2.trim() === "")
166
+ return void 0;
167
+ return JSON.parse(text2);
168
+ };
169
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
170
+ var createControlPlane = (context) => {
171
+ const snapshots = /* @__PURE__ */ new Map();
172
+ let snapshotCounter = 0;
173
+ const headerNamespace = (request) => request.headers.get(NAMESPACE_HEADER) ?? context.defaultNamespace;
174
+ const adminNamespace = (request, url) => url.searchParams.get("namespace") ?? headerNamespace(request);
175
+ const builtin = {
176
+ "GET /": () => json(200, {
177
+ service: context.name,
178
+ routes: [...Object.keys(builtin), ...Object.keys(context.routes)].sort()
179
+ }),
180
+ "POST /reset": async ({ url, namespace }) => {
181
+ const target = url.searchParams.get("all") === "1" ? "*" : namespace;
182
+ await context.reset(target);
183
+ return json(200, { status: "ok", reset: target === "*" ? context.namespaces() : [target] });
184
+ },
185
+ "GET /namespaces": () => json(200, { default: context.defaultNamespace, namespaces: context.namespaces() }),
186
+ "GET /clock": () => json(200, context.clock.state()),
187
+ "POST /clock": ({ body }) => {
188
+ if (!isRecord(body))
189
+ return adminError(400, "expected a JSON object");
190
+ if (body.reset === true)
191
+ context.clock.reset();
192
+ if (body.set !== void 0) {
193
+ const instant = parseInstant(body.set);
194
+ if (instant === void 0)
195
+ return adminError(400, "set: expected epoch ms or ISO-8601");
196
+ context.clock.set(instant);
197
+ }
198
+ if (body.advance !== void 0) {
199
+ const delta = parseDuration(body.advance);
200
+ if (delta === void 0)
201
+ return adminError(400, 'advance: expected ms or "15m"-style');
202
+ context.clock.advance(delta);
203
+ }
204
+ if (body.freeze === true)
205
+ context.clock.freeze();
206
+ if (body.freeze === false)
207
+ context.clock.unfreeze();
208
+ return json(200, context.clock.state());
209
+ },
210
+ "GET /faults": () => json(200, { faults: context.faults.list() }),
211
+ "POST /faults": ({ body, namespace }) => {
212
+ if (isRecord(body) && typeof body.preset === "string") {
213
+ if (!context.applyPreset)
214
+ return adminError(400, `${context.name} has no fault presets`);
215
+ const { preset, ...overrides } = body;
216
+ try {
217
+ return json(201, {
218
+ preset,
219
+ rules: context.applyPreset(preset, namespace, overrides)
220
+ });
221
+ } catch (error) {
222
+ return adminError(404, error instanceof Error ? error.message : String(error));
223
+ }
224
+ }
225
+ if (!isRecord(body) || typeof body.status !== "number" && typeof body.delayMs !== "number" && typeof body.latencyMs !== "number" && body.drop !== true && typeof body.effect !== "string") {
226
+ return adminError(400, "a fault needs a numeric status, a delayMs/latencyMs, drop: true, an effect, or a preset");
227
+ }
228
+ const rule = {
229
+ // Scoped to the caller's namespace unless it asks for every one, so one worker's
230
+ // injected failure never lands on another's request.
231
+ namespace,
232
+ ...body,
233
+ id: typeof body.id === "string" ? body.id : `fault_${context.faults.list().length + 1}`
234
+ };
235
+ return json(201, context.faults.add(rule));
236
+ },
237
+ "DELETE /faults": ({ url }) => {
238
+ const id = url.searchParams.get("id");
239
+ if (id === null) {
240
+ context.faults.clear();
241
+ return json(200, { status: "ok" });
242
+ }
243
+ return context.faults.remove(id) ? json(200, { status: "ok" }) : adminError(404, `no fault ${id}`);
244
+ },
245
+ "POST /snapshots": ({ namespace }) => {
246
+ const point = context.timeTravel.checkpoint(namespace, "main");
247
+ context.timeTravel.retain(namespace, point.id);
248
+ snapshotCounter++;
249
+ const id = `snap_${snapshotCounter}`;
250
+ snapshots.set(id, { namespace, checkpoint: point.id });
251
+ return json(201, { id, namespace, records: point.records ?? 0 });
252
+ },
253
+ "POST /snapshots/:id/restore": ({ params, namespace }) => {
254
+ const alias = snapshots.get(params.id);
255
+ if (!alias)
256
+ return adminError(404, `no snapshot ${params.id}`);
257
+ if (alias.namespace !== namespace) {
258
+ return adminError(409, `snapshot ${params.id} belongs to namespace ${alias.namespace}`);
259
+ }
260
+ context.timeTravel.checkout(alias.checkpoint, { namespace, branch: "main" });
261
+ return json(200, { status: "ok", id: params.id, namespace });
262
+ },
263
+ "DELETE /snapshots/:id": ({ params }) => {
264
+ const id = params.id;
265
+ const alias = snapshots.get(id);
266
+ if (!alias)
267
+ return adminError(404, `no snapshot ${id}`);
268
+ snapshots.delete(id);
269
+ context.timeTravel.release(alias.namespace, alias.checkpoint);
270
+ return json(200, { status: "ok" });
271
+ },
272
+ "GET /timeline": ({ namespace }) => json(200, context.timeTravel.inspect(namespace)),
273
+ "POST /checkpoints": ({ body, namespace }) => {
274
+ const branch = isRecord(body) && typeof body.branch === "string" ? body.branch : "main";
275
+ try {
276
+ return json(201, context.timeTravel.checkpoint(namespace, branch));
277
+ } catch (error) {
278
+ return adminError(409, error instanceof Error ? error.message : String(error));
279
+ }
280
+ },
281
+ "POST /branches/:name": ({ params, body, namespace }) => {
282
+ const at = isRecord(body) && typeof body.at === "string" ? body.at : void 0;
283
+ try {
284
+ return json(201, context.timeTravel.branch(params.name, {
285
+ namespace,
286
+ ...at !== void 0 ? { at } : {}
287
+ }));
288
+ } catch (error) {
289
+ return adminError(409, error instanceof Error ? error.message : String(error));
290
+ }
291
+ },
292
+ "POST /branches/:name/checkout": ({ params, body, namespace }) => {
293
+ if (!isRecord(body) || typeof body.checkpoint !== "string") {
294
+ return adminError(400, 'expected {"checkpoint":"cp_..."}');
295
+ }
296
+ try {
297
+ context.timeTravel.checkout(body.checkpoint, {
298
+ namespace,
299
+ branch: params.name
300
+ });
301
+ return json(200, { status: "ok", branch: params.name, checkpoint: body.checkpoint });
302
+ } catch (error) {
303
+ return adminError(409, error instanceof Error ? error.message : String(error));
304
+ }
305
+ },
306
+ "GET /requests": ({ url, namespace }) => {
307
+ const status = url.searchParams.get("status");
308
+ const since = url.searchParams.get("since");
309
+ const limit = url.searchParams.get("limit");
310
+ const sinceMs = since === null ? void 0 : parseInstant(/^\d+$/.test(since) ? Number(since) : since);
311
+ if (since !== null && sinceMs === void 0) {
312
+ return adminError(400, "since: expected epoch ms or ISO-8601");
313
+ }
314
+ if (status !== null && !/^\d{3}$/.test(status))
315
+ return adminError(400, "status: expected an HTTP status");
316
+ if (limit !== null && !/^\d+$/.test(limit))
317
+ return adminError(400, "limit: expected a count");
318
+ const operationId = url.searchParams.get("operationId");
319
+ const everyNamespace = url.searchParams.get("all") === "1";
320
+ return json(200, {
321
+ size: context.journal.size,
322
+ requests: context.journal.list({
323
+ ...everyNamespace ? {} : { namespace },
324
+ ...operationId !== null ? { operationId } : {},
325
+ ...status !== null ? { status: Number(status) } : {},
326
+ ...sinceMs !== void 0 ? { since: sinceMs } : {},
327
+ ...limit !== null ? { limit: Number(limit) } : {}
328
+ })
329
+ });
330
+ },
331
+ "DELETE /requests": ({ url, namespace }) => {
332
+ context.journal.clear(url.searchParams.get("all") === "1" ? void 0 : namespace);
333
+ return json(200, { status: "ok" });
334
+ },
335
+ "GET /metrics": () => json(200, context.metrics.report()),
336
+ "DELETE /metrics": () => {
337
+ context.metrics.reset();
338
+ return json(200, { status: "ok" });
339
+ }
340
+ };
341
+ const routes = [...Object.entries(context.routes), ...Object.entries(builtin)].map(([key, handler]) => {
342
+ const space = key.indexOf(" ");
343
+ return { method: key.slice(0, space), pattern: key.slice(space + 1), handler };
344
+ });
345
+ return {
346
+ namespaceOf: headerNamespace,
347
+ async handle(request) {
348
+ const url = new URL(request.url);
349
+ if (url.pathname === HEALTH_PATH && request.method === "GET") {
350
+ return json(200, {
351
+ status: "ok",
352
+ service: context.name,
353
+ uptimeMs: context.wallNow() - context.startedAt,
354
+ clock: context.clock.state(),
355
+ namespaces: context.namespaces().length,
356
+ ...context.describe()
357
+ });
358
+ }
359
+ if (url.pathname !== ADMIN_PREFIX && !url.pathname.startsWith(`${ADMIN_PREFIX}/`)) {
360
+ return void 0;
361
+ }
362
+ if (context.adminKey !== void 0 && request.headers.get(ADMIN_KEY_HEADER) !== context.adminKey) {
363
+ return adminError(401, `missing or wrong ${ADMIN_KEY_HEADER}`);
364
+ }
365
+ const path = url.pathname.slice(ADMIN_PREFIX.length) || "/";
366
+ for (const route of routes) {
367
+ if (route.method !== request.method)
368
+ continue;
369
+ const params = matchRoute(route.pattern, path);
370
+ if (!params)
371
+ continue;
372
+ let body;
373
+ try {
374
+ body = await readJson(request);
375
+ } catch {
376
+ return adminError(400, "request body is not valid JSON");
377
+ }
378
+ return route.handler({
379
+ request,
380
+ url,
381
+ params,
382
+ namespace: adminNamespace(request, url),
383
+ body
384
+ });
385
+ }
386
+ return adminError(404, `no admin route ${request.method} ${path}; GET ${ADMIN_PREFIX} lists them`);
387
+ }
388
+ };
389
+ };
390
+
391
+ // ../core/dist/credentials.js
392
+ var sigV4AccessKeyId = (request) => {
393
+ const header = request.headers.get("authorization");
394
+ const fromHeader = header ? /Credential=([^/,\s]+)\//.exec(header)?.[1] : void 0;
395
+ if (fromHeader)
396
+ return fromHeader;
397
+ const query = new URL(request.url).searchParams.get("X-Amz-Credential");
398
+ return query ? query.split("/")[0] ?? void 0 : void 0;
399
+ };
400
+ var createCredentialRegistry = () => {
401
+ const map = /* @__PURE__ */ new Map();
402
+ return {
403
+ set: (credential, namespace) => {
404
+ map.set(credential, namespace);
405
+ },
406
+ get: (credential) => map.get(credential),
407
+ remove: (credential) => map.delete(credential),
408
+ clear: () => map.clear(),
409
+ entries: () => [...map].map(([credential, namespace]) => ({ credential, namespace })).sort((a, b) => a.credential.localeCompare(b.credential))
410
+ };
411
+ };
412
+ var maskCredential = (credential) => credential.length <= 8 ? `${credential.slice(0, 2)}\u2026` : `${credential.slice(0, 6)}\u2026${credential.slice(-2)}`;
413
+
414
+ // ../core/dist/rng.js
415
+ var seedFrom = (value) => {
416
+ let hash = 2166136261;
417
+ for (let i = 0; i < value.length; i++) {
418
+ hash ^= value.charCodeAt(i);
419
+ hash = Math.imul(hash, 16777619);
420
+ }
421
+ return hash >>> 0;
422
+ };
423
+ var createRng = (seed = 0) => {
424
+ const numeric = typeof seed === "string" ? seedFrom(seed) : seed >>> 0;
425
+ let state = numeric;
426
+ const next = () => {
427
+ state = state + 1831565813 >>> 0;
428
+ let t = state;
429
+ t = Math.imul(t ^ t >>> 15, t | 1);
430
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
431
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
432
+ };
433
+ return {
434
+ next,
435
+ int: (min, max) => min + Math.floor(next() * (max - min + 1)),
436
+ reset: () => {
437
+ state = numeric;
438
+ },
439
+ state: () => state,
440
+ setState: (next2) => {
441
+ if (!Number.isSafeInteger(next2) || next2 < 0 || next2 > 4294967295) {
442
+ throw new RangeError("rng state must be an unsigned 32-bit integer");
443
+ }
444
+ state = next2 >>> 0;
445
+ },
446
+ seed: numeric
447
+ };
448
+ };
449
+
450
+ // ../core/dist/faults.js
451
+ var matches = (rule, candidate) => {
452
+ if (rule.namespace !== void 0 && rule.namespace !== "*" && rule.namespace !== candidate.namespace) {
453
+ return false;
454
+ }
455
+ if (rule.operationId !== void 0 && rule.operationId !== candidate.operationId)
456
+ return false;
457
+ if (rule.method !== void 0 && rule.method.toUpperCase() !== candidate.method.toUpperCase()) {
458
+ return false;
459
+ }
460
+ if (rule.pathPrefix !== void 0 && !candidate.path.startsWith(rule.pathPrefix))
461
+ return false;
462
+ return true;
463
+ };
464
+ var faultResponse = (rule) => {
465
+ const status = rule.status ?? 500;
466
+ const headers = { "content-type": "application/json", ...rule.headers };
467
+ if (typeof rule.body === "string")
468
+ return new Response(rule.body, { status, headers });
469
+ if (rule.body === null)
470
+ return new Response(null, { status, headers: rule.headers ?? {} });
471
+ const body = rule.body === void 0 ? { detail: "Injected by Mockingbird" } : rule.body;
472
+ return new Response(JSON.stringify(body), { status, headers });
473
+ };
474
+ var createFaultRegistry = (rng = createRng(0), sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) => {
475
+ const entries = [];
476
+ return {
477
+ add(rule) {
478
+ const existing = entries.findIndex((e) => e.rule.id === rule.id);
479
+ const entry = { rule, remaining: rule.count ?? null, hits: 0 };
480
+ if (existing >= 0)
481
+ entries[existing] = entry;
482
+ else
483
+ entries.push(entry);
484
+ return rule;
485
+ },
486
+ list: () => entries.map((e) => ({ ...e.rule, remaining: e.remaining, hits: e.hits })),
487
+ remove(id) {
488
+ const index = entries.findIndex((e) => e.rule.id === id);
489
+ if (index < 0)
490
+ return false;
491
+ entries.splice(index, 1);
492
+ return true;
493
+ },
494
+ clear() {
495
+ entries.length = 0;
496
+ },
497
+ async take(candidate) {
498
+ const hits = [];
499
+ for (const entry of entries) {
500
+ if (entry.remaining === 0)
501
+ continue;
502
+ if (!matches(entry.rule, candidate))
503
+ continue;
504
+ const rate = entry.rule.rate ?? 1;
505
+ if (rng.next() >= rate)
506
+ continue;
507
+ entry.hits++;
508
+ if (entry.remaining !== null)
509
+ entry.remaining--;
510
+ const delay = entry.rule.delayMs ?? entry.rule.latencyMs;
511
+ if (delay !== void 0 && delay > 0) {
512
+ await sleep(delay);
513
+ }
514
+ const hit = { id: entry.rule.id };
515
+ if (entry.rule.effect !== void 0) {
516
+ hit.effect = { name: entry.rule.effect, params: entry.rule.params ?? {} };
517
+ }
518
+ if (entry.rule.drop === true)
519
+ hit.drop = true;
520
+ else if (entry.rule.status !== void 0)
521
+ hit.response = faultResponse(entry.rule);
522
+ hits.push(hit);
523
+ if (hit.drop || hit.response)
524
+ break;
525
+ }
526
+ return hits;
527
+ }
528
+ };
529
+ };
530
+
531
+ // ../../openapi/core/dist/refs.js
532
+ var OpenAPIReferenceError = class extends Error {
533
+ ref;
534
+ constructor(ref) {
535
+ super(`unresolvable $ref: ${ref}`);
536
+ this.ref = ref;
537
+ this.name = "OpenAPIReferenceError";
538
+ }
539
+ };
540
+ var unescapePointer = (segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~");
541
+ var isReference = (value) => typeof value === "object" && value !== null && typeof value.$ref === "string";
542
+ var resolveRef = (document2, ref) => {
543
+ if (!ref.startsWith("#/"))
544
+ throw new OpenAPIReferenceError(ref);
545
+ let cursor = document2;
546
+ for (const raw of ref.slice(2).split("/")) {
547
+ const segment = unescapePointer(raw);
548
+ if (typeof cursor !== "object" || cursor === null || !(segment in cursor)) {
549
+ throw new OpenAPIReferenceError(ref);
550
+ }
551
+ cursor = cursor[segment];
552
+ }
553
+ if (cursor === void 0)
554
+ throw new OpenAPIReferenceError(ref);
555
+ return cursor;
556
+ };
557
+ var deref = (document2, value) => {
558
+ let current = value;
559
+ const seen = /* @__PURE__ */ new Set();
560
+ while (isReference(current)) {
561
+ if (seen.has(current.$ref))
562
+ throw new OpenAPIReferenceError(`${current.$ref} (cycle)`);
563
+ seen.add(current.$ref);
564
+ current = resolveRef(document2, current.$ref);
565
+ }
566
+ return current;
567
+ };
568
+
569
+ // ../../openapi/core/dist/types.js
570
+ var HTTP_METHODS = [
571
+ "get",
572
+ "put",
573
+ "post",
574
+ "delete",
575
+ "options",
576
+ "head",
577
+ "patch",
578
+ "trace"
579
+ ];
580
+
581
+ // ../../openapi/core/dist/document.js
582
+ var mergeParameters = (document2, item, own) => {
583
+ const merged = /* @__PURE__ */ new Map();
584
+ for (const raw of item.parameters ?? []) {
585
+ const parameter = deref(document2, raw);
586
+ merged.set(`${parameter.in}:${parameter.name}`, parameter);
587
+ }
588
+ for (const raw of own ?? []) {
589
+ const parameter = deref(document2, raw);
590
+ merged.set(`${parameter.in}:${parameter.name}`, parameter);
591
+ }
592
+ return [...merged.values()];
593
+ };
594
+ var listOperations = (document2) => {
595
+ const operations = [];
596
+ for (const [path, item] of Object.entries(document2.paths)) {
597
+ for (const method of HTTP_METHODS) {
598
+ const operation = item[method];
599
+ if (operation?.operationId === void 0)
600
+ continue;
601
+ const responses = {};
602
+ for (const [status, response] of Object.entries(operation.responses)) {
603
+ responses[status] = deref(document2, response);
604
+ }
605
+ operations.push({
606
+ operationId: operation.operationId,
607
+ method,
608
+ path,
609
+ operation,
610
+ parameters: mergeParameters(document2, item, operation.parameters),
611
+ requestBody: operation.requestBody === void 0 ? void 0 : deref(document2, operation.requestBody),
612
+ responses
613
+ });
614
+ }
615
+ }
616
+ return operations;
617
+ };
618
+
619
+ // ../../openapi/core/dist/schema.js
620
+ var resolveSchema = (document2, schema) => {
621
+ let current = schema;
622
+ const seen = /* @__PURE__ */ new Set();
623
+ while (typeof current.$ref === "string") {
624
+ const ref = current.$ref;
625
+ if (seen.has(ref))
626
+ break;
627
+ seen.add(ref);
628
+ const { $ref: _ignored, ...siblings } = current;
629
+ const target = resolveRef(document2, ref);
630
+ current = { ...target, ...siblings };
631
+ }
632
+ if (current.nullable === true) {
633
+ const { nullable: _nullable, ...rest } = current;
634
+ const types = schemaTypes(rest);
635
+ if (types.length > 0 && !types.includes("null"))
636
+ current = { ...rest, type: [...types, "null"] };
637
+ else
638
+ current = rest;
639
+ }
640
+ return current;
641
+ };
642
+ var schemaTypes = (schema) => {
643
+ if (Array.isArray(schema.type))
644
+ return schema.type;
645
+ if (schema.type !== void 0)
646
+ return [schema.type];
647
+ const inferred = [];
648
+ if (schema.properties || schema.required || schema.additionalProperties !== void 0)
649
+ inferred.push("object");
650
+ if (schema.items || schema.prefixItems || schema.minItems !== void 0 || schema.maxItems !== void 0)
651
+ inferred.push("array");
652
+ if (schema.minLength !== void 0 || schema.maxLength !== void 0 || schema.pattern !== void 0)
653
+ inferred.push("string");
654
+ if (schema.minimum !== void 0 || schema.maximum !== void 0 || schema.multipleOf !== void 0)
655
+ inferred.push("number");
656
+ return inferred;
657
+ };
658
+ var jsonTypeOf = (value) => {
659
+ if (value === null)
660
+ return "null";
661
+ if (Array.isArray(value))
662
+ return "array";
663
+ switch (typeof value) {
664
+ case "string":
665
+ return "string";
666
+ case "boolean":
667
+ return "boolean";
668
+ case "number":
669
+ return Number.isInteger(value) ? "integer" : "number";
670
+ case "object":
671
+ return "object";
672
+ default:
673
+ return "undefined";
674
+ }
675
+ };
676
+ var deepEqual = (a, b) => {
677
+ if (a === b)
678
+ return true;
679
+ if (typeof a !== typeof b || a === null || b === null)
680
+ return false;
681
+ if (Array.isArray(a)) {
682
+ return Array.isArray(b) && a.length === b.length && a.every((item, i) => deepEqual(item, b[i]));
683
+ }
684
+ if (typeof a === "object" && typeof b === "object" && !Array.isArray(b)) {
685
+ const ka = Object.keys(a);
686
+ const kb = Object.keys(b);
687
+ return ka.length === kb.length && ka.every((k) => deepEqual(a[k], b[k]));
688
+ }
689
+ return false;
690
+ };
691
+ var FORMAT_PATTERNS = {
692
+ uuid: /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
693
+ date: /^\d{4}-\d{2}-\d{2}$/,
694
+ "date-time": /^\d{4}-\d{2}-\d{2}[Tt ]\d{2}:\d{2}:\d{2}(\.\d+)?([Zz]|[+-]\d{2}:\d{2})$/,
695
+ email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
696
+ uri: /^[a-zA-Z][a-zA-Z0-9+.-]*:[^\s]*$/,
697
+ ipv4: /^(25[0-5]|2[0-4]\d|1?\d?\d)(\.(25[0-5]|2[0-4]\d|1?\d?\d)){3}$/
698
+ };
699
+ var graphemeLength = (value) => [...value].length;
700
+ var validateValue = (document2, schema, value, path = []) => {
701
+ const errors = [];
702
+ const s = resolveSchema(document2, schema);
703
+ const fail2 = (message) => errors.push({ path, message });
704
+ const actual = jsonTypeOf(value);
705
+ if (actual === "undefined") {
706
+ fail2("value is undefined");
707
+ return errors;
708
+ }
709
+ const types = schemaTypes(s);
710
+ if (types.length > 0) {
711
+ const ok = types.some((t) => t === actual || t === "number" && actual === "integer");
712
+ if (!ok) {
713
+ fail2(`expected type ${types.join("|")}, got ${actual}`);
714
+ return errors;
715
+ }
716
+ }
717
+ if (s.enum && !(value === null && types.includes("null")) && !s.enum.some((candidate) => deepEqual(candidate, value))) {
718
+ fail2("value not in enum");
719
+ }
720
+ if (s.const !== void 0 && !deepEqual(s.const, value))
721
+ fail2("value does not equal const");
722
+ if (typeof value === "string") {
723
+ const length = graphemeLength(value);
724
+ if (s.minLength !== void 0 && length < s.minLength)
725
+ fail2(`length ${length} < minLength ${s.minLength}`);
726
+ if (s.maxLength !== void 0 && length > s.maxLength)
727
+ fail2(`length ${length} > maxLength ${s.maxLength}`);
728
+ if (s.pattern !== void 0) {
729
+ try {
730
+ if (!new RegExp(s.pattern, "u").test(value))
731
+ fail2(`does not match pattern ${s.pattern}`);
732
+ } catch {
733
+ }
734
+ }
735
+ if (s.format !== void 0) {
736
+ const pattern = FORMAT_PATTERNS[s.format];
737
+ if (pattern && !pattern.test(value))
738
+ fail2(`does not match format ${s.format}`);
739
+ }
740
+ }
741
+ if (typeof value === "number") {
742
+ if (s.minimum !== void 0 && value < s.minimum)
743
+ fail2(`${value} < minimum ${s.minimum}`);
744
+ if (s.maximum !== void 0 && value > s.maximum)
745
+ fail2(`${value} > maximum ${s.maximum}`);
746
+ if (s.exclusiveMinimum !== void 0 && value <= s.exclusiveMinimum)
747
+ fail2(`${value} <= exclusiveMinimum ${s.exclusiveMinimum}`);
748
+ if (s.exclusiveMaximum !== void 0 && value >= s.exclusiveMaximum)
749
+ fail2(`${value} >= exclusiveMaximum ${s.exclusiveMaximum}`);
750
+ if (s.multipleOf !== void 0 && Math.abs(value / s.multipleOf - Math.round(value / s.multipleOf)) > 1e-9) {
751
+ fail2(`${value} is not a multiple of ${s.multipleOf}`);
752
+ }
753
+ }
754
+ if (Array.isArray(value)) {
755
+ if (s.minItems !== void 0 && value.length < s.minItems)
756
+ fail2(`${value.length} items < minItems ${s.minItems}`);
757
+ if (s.maxItems !== void 0 && value.length > s.maxItems)
758
+ fail2(`${value.length} items > maxItems ${s.maxItems}`);
759
+ if (s.uniqueItems && value.some((item, i) => value.slice(0, i).some((prev) => deepEqual(prev, item))))
760
+ fail2("items are not unique");
761
+ value.forEach((item, i) => {
762
+ const itemSchema = s.prefixItems?.[i] ?? s.items;
763
+ if (itemSchema)
764
+ errors.push(...validateValue(document2, itemSchema, item, [...path, i]));
765
+ });
766
+ }
767
+ if (actual === "object") {
768
+ const record = value;
769
+ const keys = Object.keys(record);
770
+ for (const name of s.required ?? [])
771
+ if (!(name in record))
772
+ fail2(`missing required property ${name}`);
773
+ if (s.minProperties !== void 0 && keys.length < s.minProperties)
774
+ fail2(`${keys.length} properties < minProperties ${s.minProperties}`);
775
+ if (s.maxProperties !== void 0 && keys.length > s.maxProperties)
776
+ fail2(`${keys.length} properties > maxProperties ${s.maxProperties}`);
777
+ for (const key of keys) {
778
+ const property = s.properties?.[key];
779
+ if (property) {
780
+ errors.push(...validateValue(document2, property, record[key], [...path, key]));
781
+ continue;
782
+ }
783
+ if (s.additionalProperties === false)
784
+ fail2(`unexpected property ${key}`);
785
+ else if (typeof s.additionalProperties === "object") {
786
+ errors.push(...validateValue(document2, s.additionalProperties, record[key], [...path, key]));
787
+ }
788
+ if (s.propertyNames) {
789
+ const nameErrors = validateValue(document2, s.propertyNames, key, [...path, key]);
790
+ if (nameErrors.length > 0)
791
+ fail2(`property name ${key} is invalid: ${nameErrors[0]?.message}`);
792
+ }
793
+ }
794
+ }
795
+ if (s.allOf)
796
+ for (const branch of s.allOf)
797
+ errors.push(...validateValue(document2, branch, value, path));
798
+ if (s.anyOf && !s.anyOf.some((branch) => validateValue(document2, branch, value).length === 0))
799
+ fail2("matches no anyOf branch");
800
+ if (s.oneOf) {
801
+ const matches3 = s.oneOf.filter((branch) => validateValue(document2, branch, value).length === 0).length;
802
+ if (matches3 !== 1)
803
+ fail2(`matches ${matches3} oneOf branches, expected exactly 1`);
804
+ }
805
+ if (s.not && validateValue(document2, s.not, value).length === 0)
806
+ fail2("matches forbidden `not` schema");
807
+ return errors;
808
+ };
809
+
810
+ // ../../http/codec/dist/form.js
811
+ var parsePath = (rawKey) => {
812
+ const open = rawKey.indexOf("[");
813
+ if (open === -1)
814
+ return [rawKey];
815
+ const path = [rawKey.slice(0, open)];
816
+ const rest = rawKey.slice(open);
817
+ const pattern = /\[([^\]]*)\]/g;
818
+ let match = pattern.exec(rest);
819
+ let consumed = 0;
820
+ while (match !== null) {
821
+ if (match.index !== consumed)
822
+ return [rawKey];
823
+ path.push(match[1] ?? "");
824
+ consumed = match.index + match[0].length;
825
+ match = pattern.exec(rest);
826
+ }
827
+ if (consumed !== rest.length)
828
+ return [rawKey];
829
+ return path;
830
+ };
831
+ var isIndex = (segment) => /^(0|[1-9][0-9]*)$/.test(segment);
832
+ var put = (target, key, value) => {
833
+ if (key === "__proto__") {
834
+ Object.defineProperty(target, key, {
835
+ value,
836
+ enumerable: true,
837
+ writable: true,
838
+ configurable: true
839
+ });
840
+ return;
841
+ }
842
+ ;
843
+ target[key] = value;
844
+ };
845
+ var assign = (target, path, value) => {
846
+ let cursor = target;
847
+ for (let i = 0; i < path.length; i++) {
848
+ const segment = path[i];
849
+ const last = i === path.length - 1;
850
+ if (Array.isArray(cursor)) {
851
+ const index = segment === "" ? cursor.length : isIndex(segment) ? Number(segment) : void 0;
852
+ if (index === void 0)
853
+ return;
854
+ if (last) {
855
+ put(cursor, index, value);
856
+ return;
857
+ }
858
+ const next = Object.hasOwn(cursor, index) ? cursor[index] : void 0;
859
+ if (next === void 0 || typeof next === "string") {
860
+ const created = path[i + 1] === "" || isIndex(path[i + 1]) ? [] : {};
861
+ put(cursor, index, created);
862
+ cursor = created;
863
+ } else {
864
+ cursor = next;
865
+ }
866
+ continue;
867
+ }
868
+ if (typeof cursor === "string")
869
+ return;
870
+ if (last) {
871
+ put(cursor, segment, value);
872
+ return;
873
+ }
874
+ const nextSegment = path[i + 1];
875
+ const existing = Object.hasOwn(cursor, segment) ? cursor[segment] : void 0;
876
+ if (existing === void 0 || typeof existing === "string") {
877
+ const created = nextSegment === "" || isIndex(nextSegment) ? [] : {};
878
+ put(cursor, segment, created);
879
+ cursor = created;
880
+ } else {
881
+ cursor = existing;
882
+ }
883
+ }
884
+ };
885
+ var decodeFormPairs = (pairs) => {
886
+ const out = {};
887
+ for (const [rawKey, value] of pairs)
888
+ assign(out, parsePath(rawKey), value);
889
+ return densify(out);
890
+ };
891
+ var densify = (value) => {
892
+ if (typeof value === "string")
893
+ return value;
894
+ if (Array.isArray(value))
895
+ return value.filter((item) => item !== void 0).map(densify);
896
+ const out = {};
897
+ for (const [key, item] of Object.entries(value))
898
+ put(out, key, densify(item));
899
+ return out;
900
+ };
901
+ var decodeForm = (text2) => {
902
+ const source = text2.startsWith("?") ? text2.slice(1) : text2;
903
+ return decodeFormPairs(new URLSearchParams(source).entries());
904
+ };
905
+
906
+ // ../../http/codec/dist/content.js
907
+ var JSON_MEDIA_TYPE = "application/json";
908
+ var FORM_MEDIA_TYPE = "application/x-www-form-urlencoded";
909
+ var mediaTypeOf = (contentType) => {
910
+ if (!contentType)
911
+ return void 0;
912
+ const essence = contentType.split(";")[0]?.trim().toLowerCase();
913
+ return essence ? essence : void 0;
914
+ };
915
+ var isJsonMediaType = (mediaType) => mediaType === JSON_MEDIA_TYPE || mediaType.endsWith("+json") || mediaType === "text/json";
916
+ var utf8 = new TextDecoder("utf-8", { fatal: false });
917
+ var decodeBody = (contentType, bytes) => {
918
+ if (bytes.byteLength === 0)
919
+ return { kind: "empty" };
920
+ const mediaType = mediaTypeOf(contentType);
921
+ if (mediaType === void 0)
922
+ return { kind: "bytes", value: bytes };
923
+ if (isJsonMediaType(mediaType)) {
924
+ const text2 = utf8.decode(bytes);
925
+ try {
926
+ return { kind: "json", value: JSON.parse(text2) };
927
+ } catch (error) {
928
+ return {
929
+ kind: "invalid",
930
+ mediaType,
931
+ text: text2,
932
+ error: error instanceof Error ? error.message : String(error)
933
+ };
934
+ }
935
+ }
936
+ if (mediaType === FORM_MEDIA_TYPE) {
937
+ return { kind: "form", value: decodeForm(utf8.decode(bytes)) };
938
+ }
939
+ if (mediaType.startsWith("text/"))
940
+ return { kind: "text", value: utf8.decode(bytes) };
941
+ return { kind: "bytes", value: bytes };
942
+ };
943
+ var readBody = async (message) => {
944
+ const bytes = new Uint8Array(await message.arrayBuffer());
945
+ return decodeBody(message.headers.get("content-type"), bytes);
946
+ };
947
+
948
+ // ../core/dist/http.js
949
+ var jsonRes = (status, body, headers = {}) => new Response(JSON.stringify(body), {
950
+ status,
951
+ headers: { "content-type": JSON_MEDIA_TYPE, ...headers }
952
+ });
953
+ var HttpError = class extends Error {
954
+ status;
955
+ body;
956
+ headers;
957
+ constructor(status, body, headers = {}) {
958
+ super(`HTTP ${status}`);
959
+ this.status = status;
960
+ this.body = body;
961
+ this.headers = headers;
962
+ this.name = "HttpError";
963
+ }
964
+ toResponse() {
965
+ const contentType = this.headers["content-type"]?.split(";", 1)[0]?.trim().toLowerCase();
966
+ if (contentType === "text/plain") {
967
+ return new Response(String(this.body), {
968
+ status: this.status,
969
+ headers: this.headers
970
+ });
971
+ }
972
+ return jsonRes(this.status, this.body, this.headers);
973
+ }
974
+ };
975
+
976
+ // ../core/dist/ids.js
977
+ var ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
978
+ var mix = (input) => {
979
+ let hash = 2166136261;
980
+ for (let i = 0; i < input.length; i++) {
981
+ hash ^= input.charCodeAt(i);
982
+ hash = Math.imul(hash, 16777619) >>> 0;
983
+ }
984
+ hash ^= hash >>> 16;
985
+ hash = Math.imul(hash, 2246822507) >>> 0;
986
+ hash ^= hash >>> 13;
987
+ return hash >>> 0;
988
+ };
989
+ var opaqueToken = (input, length) => {
990
+ let out = "";
991
+ let round = 0;
992
+ while (out.length < length) {
993
+ let hash = mix(`${input}:${round++}`);
994
+ for (let i = 0; i < 5 && out.length < length; i++) {
995
+ out += ALPHABET.charAt(hash % ALPHABET.length);
996
+ hash = Math.floor(hash / ALPHABET.length);
997
+ }
998
+ }
999
+ return out;
1000
+ };
1001
+ var IdSequence = class {
1002
+ sqlite;
1003
+ namespace;
1004
+ salt;
1005
+ constructor(sqlite, namespace, salt = "mockingbird") {
1006
+ this.sqlite = sqlite;
1007
+ this.namespace = namespace;
1008
+ this.salt = salt;
1009
+ }
1010
+ next(prefix, length = 14) {
1011
+ return this.sqlite.transaction(() => {
1012
+ const row = this.sqlite.prepare("SELECT value FROM mockingbird_sequences WHERE namespace = ? AND name = ? AND kind = 'id'").get(this.namespace, prefix);
1013
+ const value = (row?.value ?? 0) + 1;
1014
+ this.sqlite.prepare(`INSERT INTO mockingbird_sequences (namespace, name, kind, value) VALUES (?, ?, 'id', ?)
1015
+ ON CONFLICT(namespace, name, kind) DO UPDATE SET value = excluded.value`).run(this.namespace, prefix, value);
1016
+ return `${prefix}${opaqueToken(`${this.salt}:${prefix}:${value}`, length)}`;
1017
+ });
1018
+ }
1019
+ };
1020
+
1021
+ // ../core/dist/journal.js
1022
+ var DEFAULT_JOURNAL_SIZE = 1e3;
1023
+ var createJournal = (size = DEFAULT_JOURNAL_SIZE) => {
1024
+ const capacity = Math.max(0, Math.floor(size));
1025
+ const rings = /* @__PURE__ */ new Map();
1026
+ let sequence = 0;
1027
+ const order = /* @__PURE__ */ new WeakMap();
1028
+ const inOrder = (ring) => ring.entries.length < capacity ? ring.entries : [...ring.entries.slice(ring.next), ...ring.entries.slice(0, ring.next)];
1029
+ return {
1030
+ size: capacity,
1031
+ record(entry) {
1032
+ if (capacity === 0)
1033
+ return;
1034
+ order.set(entry, sequence++);
1035
+ let ring = rings.get(entry.namespace);
1036
+ if (!ring) {
1037
+ ring = { entries: [], next: 0 };
1038
+ rings.set(entry.namespace, ring);
1039
+ }
1040
+ if (ring.entries.length < capacity)
1041
+ ring.entries.push(entry);
1042
+ else {
1043
+ ring.entries[ring.next] = entry;
1044
+ ring.next = (ring.next + 1) % capacity;
1045
+ }
1046
+ },
1047
+ list(query = {}) {
1048
+ const source = query.namespace !== void 0 ? inOrder(rings.get(query.namespace) ?? { entries: [], next: 0 }) : [...rings.values()].flatMap(inOrder).sort((a, b) => (order.get(a) ?? 0) - (order.get(b) ?? 0));
1049
+ const matched = source.filter((entry) => (query.operationId === void 0 || entry.operationId === query.operationId) && (query.status === void 0 || entry.status === query.status) && (query.since === void 0 || Date.parse(entry.at) >= query.since));
1050
+ return query.limit !== void 0 ? matched.slice(-Math.max(0, query.limit)) : matched;
1051
+ },
1052
+ clear(namespace) {
1053
+ if (namespace === void 0)
1054
+ rings.clear();
1055
+ else
1056
+ rings.delete(namespace);
1057
+ }
1058
+ };
1059
+ };
1060
+ var notes = /* @__PURE__ */ new WeakMap();
1061
+ var annotateResponse = (response, extra) => {
1062
+ const existing = notes.get(response);
1063
+ notes.set(response, {
1064
+ ...existing,
1065
+ ...extra,
1066
+ ...existing?.ids || extra.ids ? { ids: { ...existing?.ids, ...extra.ids } } : {}
1067
+ });
1068
+ return response;
1069
+ };
1070
+ var responseNotes = (response) => notes.get(response);
1071
+
1072
+ // ../core/dist/metrics.js
1073
+ var createMetrics = () => {
1074
+ let requests = 0;
1075
+ let faults = 0;
1076
+ let totalDurationMs = 0;
1077
+ const byOperation = /* @__PURE__ */ new Map();
1078
+ const unmatched = /* @__PURE__ */ new Map();
1079
+ return {
1080
+ record(entry) {
1081
+ requests++;
1082
+ totalDurationMs += entry.durationMs;
1083
+ if (entry.faultId !== void 0)
1084
+ faults++;
1085
+ const key = `${entry.operationId ?? "(unmatched)"} ${entry.status}`;
1086
+ byOperation.set(key, (byOperation.get(key) ?? 0) + 1);
1087
+ if (entry.unmatched) {
1088
+ const route = `${entry.method} ${entry.path}`;
1089
+ unmatched.set(route, (unmatched.get(route) ?? 0) + 1);
1090
+ }
1091
+ },
1092
+ report: () => ({
1093
+ requests,
1094
+ byOperation: Object.fromEntries([...byOperation].sort(([a], [b]) => a.localeCompare(b))),
1095
+ unmatched: [...unmatched].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([route, count]) => {
1096
+ const space = route.indexOf(" ");
1097
+ return { method: route.slice(0, space), path: route.slice(space + 1), count };
1098
+ }),
1099
+ faults,
1100
+ totalDurationMs
1101
+ }),
1102
+ reset() {
1103
+ requests = 0;
1104
+ faults = 0;
1105
+ totalDurationMs = 0;
1106
+ byOperation.clear();
1107
+ unmatched.clear();
1108
+ }
1109
+ };
1110
+ };
1111
+
1112
+ // ../../core/dist/timeline.js
1113
+ var BRANCH_PATTERN = /^[A-Za-z0-9_.-]{1,64}$/;
1114
+ var Timeline = class {
1115
+ maxCheckpoints;
1116
+ now;
1117
+ makeId;
1118
+ nodes = /* @__PURE__ */ new Map();
1119
+ heads = /* @__PURE__ */ new Map();
1120
+ /** Unreferenced nodes in the exact order they became collectible. */
1121
+ evictable = /* @__PURE__ */ new Set();
1122
+ /** Branch heads plus explicit retainers. Absent means zero. */
1123
+ references = /* @__PURE__ */ new Map();
1124
+ explicitPins = /* @__PURE__ */ new Map();
1125
+ sequence = 0;
1126
+ constructor(options = {}) {
1127
+ const max = options.maxCheckpoints ?? 1e3;
1128
+ if (!Number.isSafeInteger(max) || max < 1)
1129
+ throw new RangeError("maxCheckpoints must be a positive integer");
1130
+ this.maxCheckpoints = max;
1131
+ this.now = options.now ?? (() => this.sequence);
1132
+ this.makeId = options.id ?? ((sequence) => `cp_${sequence.toString(36).padStart(8, "0")}`);
1133
+ }
1134
+ /** Capture a new immutable value and move `branch` to it. */
1135
+ commit(value, options = {}) {
1136
+ const branch = options.branch ?? "main";
1137
+ this.assertBranch(branch);
1138
+ const parent = options.parent === void 0 ? this.heads.get(branch) ?? null : options.parent;
1139
+ if (parent !== null && !this.nodes.has(parent))
1140
+ throw new RangeError(`no checkpoint ${parent}`);
1141
+ const id = this.makeId(++this.sequence);
1142
+ if (this.nodes.has(id))
1143
+ throw new RangeError(`duplicate checkpoint id ${id}`);
1144
+ const checkpoint = Object.freeze({ id, branch, parent, at: this.now(), value });
1145
+ this.nodes.set(id, checkpoint);
1146
+ this.moveHead(branch, id);
1147
+ this.collect(this.maxCheckpoints);
1148
+ return checkpoint;
1149
+ }
1150
+ /** Create a branch pointer without copying its checkpoint value. */
1151
+ fork(branch, options = {}) {
1152
+ this.assertBranch(branch);
1153
+ if (this.heads.has(branch))
1154
+ throw new RangeError(`branch already exists: ${branch}`);
1155
+ const from = options.from ?? this.heads.get("main");
1156
+ if (from === void 0)
1157
+ return void 0;
1158
+ const checkpoint = this.get(from);
1159
+ this.moveHead(branch, checkpoint.id);
1160
+ return checkpoint;
1161
+ }
1162
+ /** Move a branch pointer to an existing checkpoint. */
1163
+ checkout(branch, id) {
1164
+ this.assertBranch(branch);
1165
+ const checkpoint = this.get(id);
1166
+ this.moveHead(branch, checkpoint.id);
1167
+ return checkpoint;
1168
+ }
1169
+ get(id) {
1170
+ const checkpoint = this.nodes.get(id);
1171
+ if (!checkpoint)
1172
+ throw new RangeError(`no checkpoint ${id}`);
1173
+ return checkpoint;
1174
+ }
1175
+ head(branch = "main") {
1176
+ const id = this.heads.get(branch);
1177
+ return id === void 0 ? void 0 : this.get(id);
1178
+ }
1179
+ hasBranch(branch) {
1180
+ return this.heads.has(branch);
1181
+ }
1182
+ branches() {
1183
+ return Object.freeze(Object.fromEntries([...this.heads].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)));
1184
+ }
1185
+ checkpoints() {
1186
+ return [...this.nodes.values()];
1187
+ }
1188
+ /** Number of retained checkpoints without allocating an array. */
1189
+ get size() {
1190
+ return this.nodes.size;
1191
+ }
1192
+ /** Pin a checkpoint independently of branch heads (used by compatibility snapshot handles). */
1193
+ retain(id) {
1194
+ const checkpoint = this.get(id);
1195
+ this.explicitPins.set(id, (this.explicitPins.get(id) ?? 0) + 1);
1196
+ this.addReference(id);
1197
+ return checkpoint;
1198
+ }
1199
+ /** Release one explicit pin. Branch heads remain pinned until moved or deleted. */
1200
+ release(id) {
1201
+ if (!this.nodes.has(id))
1202
+ return false;
1203
+ const pins = this.explicitPins.get(id) ?? 0;
1204
+ if (pins === 0)
1205
+ return false;
1206
+ if (pins === 1)
1207
+ this.explicitPins.delete(id);
1208
+ else
1209
+ this.explicitPins.set(id, pins - 1);
1210
+ this.removeReference(id);
1211
+ this.collect(this.maxCheckpoints);
1212
+ return true;
1213
+ }
1214
+ deleteBranch(branch) {
1215
+ if (branch === "main")
1216
+ throw new RangeError("cannot delete main branch");
1217
+ const previous = this.heads.get(branch);
1218
+ const deleted = this.heads.delete(branch);
1219
+ if (previous !== void 0)
1220
+ this.removeReference(previous);
1221
+ this.collect(this.maxCheckpoints);
1222
+ return deleted;
1223
+ }
1224
+ /**
1225
+ * Deterministically discard oldest unpinned checkpoints. Collection is O(number removed):
1226
+ * commits never scan pinned nodes or the retained history. Parents are metadata rather than a
1227
+ * storage dependency, so a retained node remains usable after pruning.
1228
+ */
1229
+ gc(max = this.maxCheckpoints) {
1230
+ if (!Number.isSafeInteger(max) || max < 1)
1231
+ throw new RangeError("max must be a positive integer");
1232
+ const removed = [];
1233
+ this.collect(max, removed);
1234
+ return removed;
1235
+ }
1236
+ collect(max, removed) {
1237
+ while (this.nodes.size > max && this.evictable.size > 0) {
1238
+ const id = this.evictable.values().next().value;
1239
+ this.evictable.delete(id);
1240
+ this.nodes.delete(id);
1241
+ removed?.push(id);
1242
+ }
1243
+ }
1244
+ moveHead(branch, id) {
1245
+ const previous = this.heads.get(branch);
1246
+ if (previous === id)
1247
+ return;
1248
+ if (previous !== void 0)
1249
+ this.removeReference(previous);
1250
+ this.heads.set(branch, id);
1251
+ this.addReference(id);
1252
+ }
1253
+ addReference(id) {
1254
+ this.references.set(id, (this.references.get(id) ?? 0) + 1);
1255
+ this.evictable.delete(id);
1256
+ }
1257
+ removeReference(id) {
1258
+ const next = (this.references.get(id) ?? 0) - 1;
1259
+ if (next > 0)
1260
+ this.references.set(id, next);
1261
+ else {
1262
+ this.references.delete(id);
1263
+ if (this.nodes.has(id))
1264
+ this.evictable.add(id);
1265
+ }
1266
+ }
1267
+ assertBranch(branch) {
1268
+ if (!BRANCH_PATTERN.test(branch))
1269
+ throw new RangeError(`branch must match ${BRANCH_PATTERN}`);
1270
+ }
1271
+ };
1272
+
1273
+ // ../../sqlite/dist/default.js
1274
+ import { Database } from "@crvouga/mockingbird-service-sqlite";
1275
+ var createDefaultSqlite = () => new Database();
1276
+ var resolveSqlite = (sqlite) => sqlite ?? createDefaultSqlite();
1277
+
1278
+ // ../../sqlite/dist/migrate.js
1279
+ var ensureMigrationsTable = (sqlite) => {
1280
+ sqlite.exec(`
1281
+ CREATE TABLE IF NOT EXISTS schema_migrations (
1282
+ id TEXT PRIMARY KEY NOT NULL,
1283
+ applied_at INTEGER NOT NULL
1284
+ )
1285
+ `);
1286
+ };
1287
+ var migrate = (sqlite, migrations) => {
1288
+ ensureMigrationsTable(sqlite);
1289
+ const applied = new Set(sqlite.prepare("SELECT id FROM schema_migrations").all().map((row) => row.id));
1290
+ const pending = migrations.filter((migration) => !applied.has(migration.id));
1291
+ if (pending.length === 0)
1292
+ return;
1293
+ const insert = sqlite.prepare("INSERT INTO schema_migrations (id, applied_at) VALUES (?, ?)");
1294
+ const now = Math.floor(Date.now() / 1e3);
1295
+ sqlite.transaction(() => {
1296
+ for (const migration of pending) {
1297
+ sqlite.exec(migration.sql);
1298
+ insert.run(migration.id, now);
1299
+ }
1300
+ });
1301
+ };
1302
+
1303
+ // ../../sqlite/dist/schema.js
1304
+ var CORE_MIGRATIONS = [
1305
+ {
1306
+ id: "20260322_core_records_sequences",
1307
+ sql: `
1308
+ CREATE TABLE IF NOT EXISTS mockingbird_records (
1309
+ namespace TEXT NOT NULL,
1310
+ collection TEXT NOT NULL,
1311
+ id TEXT NOT NULL,
1312
+ seq INTEGER NOT NULL,
1313
+ value TEXT NOT NULL,
1314
+ PRIMARY KEY (namespace, collection, id)
1315
+ );
1316
+ CREATE INDEX IF NOT EXISTS mockingbird_records_seq
1317
+ ON mockingbird_records (namespace, collection, seq);
1318
+ CREATE TABLE IF NOT EXISTS mockingbird_sequences (
1319
+ namespace TEXT NOT NULL,
1320
+ name TEXT NOT NULL,
1321
+ kind TEXT NOT NULL,
1322
+ value INTEGER NOT NULL,
1323
+ PRIMARY KEY (namespace, name, kind)
1324
+ );
1325
+ `
1326
+ }
1327
+ ];
1328
+ var migrateCore = (sqlite) => {
1329
+ migrate(sqlite, CORE_MIGRATIONS);
1330
+ };
1331
+ var clearNamespace = (sqlite, namespace) => {
1332
+ sqlite.transaction(() => {
1333
+ sqlite.prepare("DELETE FROM mockingbird_records WHERE namespace = ?").run(namespace);
1334
+ sqlite.prepare("DELETE FROM mockingbird_sequences WHERE namespace = ?").run(namespace);
1335
+ });
1336
+ };
1337
+
1338
+ // ../../openapi/metadata/dist/types.js
1339
+ var EXTENSION_KEYS = {
1340
+ operation: "x-mockingbird",
1341
+ resource: "x-mockingbird-resource",
1342
+ resourceRef: "x-mockingbird-resource-ref",
1343
+ volatile: "x-mockingbird-volatile",
1344
+ scope: "x-mockingbird-scope",
1345
+ unsupported: "x-mockingbird-unsupported",
1346
+ parityHeader: "x-mockingbird-parity-header"
1347
+ };
1348
+
1349
+ // ../../openapi/metadata/dist/read.js
1350
+ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1351
+ var extensionOf = (holder, key) => holder[key];
1352
+ var operationMetadata = (operation) => {
1353
+ const raw = extensionOf(operation, EXTENSION_KEYS.operation);
1354
+ const ext = isRecord2(raw) ? raw : {};
1355
+ const supported = ext.supported ?? true;
1356
+ const parity = ext.parity ?? {};
1357
+ return {
1358
+ supported,
1359
+ reason: typeof ext.reason === "string" ? ext.reason : void 0,
1360
+ parity: {
1361
+ enabled: supported && (parity.enabled ?? true),
1362
+ safe: parity.safe ?? true,
1363
+ reason: typeof parity.reason === "string" ? parity.reason : void 0
1364
+ }
1365
+ };
1366
+ };
1367
+
1368
+ // ../core/dist/service.js
1369
+ import { Hono } from "hono";
1370
+ var defineOperations = (handlers) => handlers;
1371
+ var OperationRegistryError = class extends Error {
1372
+ problems;
1373
+ constructor(problems) {
1374
+ super(`operation registry is inconsistent:
1375
+ ${problems.map((p) => ` - ${p}`).join("\n")}`);
1376
+ this.problems = problems;
1377
+ this.name = "OperationRegistryError";
1378
+ }
1379
+ };
1380
+ var verifyOperations = (document2, handlers) => {
1381
+ const problems = [];
1382
+ const operations = listOperations(document2);
1383
+ const seen = /* @__PURE__ */ new Set();
1384
+ for (const operation of operations) {
1385
+ if (seen.has(operation.operationId))
1386
+ problems.push(`duplicate operationId ${operation.operationId}`);
1387
+ seen.add(operation.operationId);
1388
+ const supported = operationMetadata(operation.operation).supported;
1389
+ const handler = handlers[operation.operationId];
1390
+ if (supported && !handler)
1391
+ problems.push(`supported operation ${operation.operationId} has no handler`);
1392
+ if (!supported && handler)
1393
+ problems.push(`operation ${operation.operationId} is marked unsupported but has a handler`);
1394
+ }
1395
+ for (const id of Object.keys(handlers)) {
1396
+ if (!seen.has(id))
1397
+ problems.push(`handler ${id} has no OpenAPI operation`);
1398
+ }
1399
+ return problems;
1400
+ };
1401
+ var honoPath = (template) => template.replace(/\{([^}]+)\}/g, ":$1");
1402
+ var routeOrder = (a, b) => {
1403
+ const sa = a.path.split("/");
1404
+ const sb = b.path.split("/");
1405
+ for (let i = 0; i < Math.max(sa.length, sb.length); i++) {
1406
+ const x = sa[i] ?? "";
1407
+ const y = sb[i] ?? "";
1408
+ const px = x.startsWith("{");
1409
+ const py = y.startsWith("{");
1410
+ if (px !== py)
1411
+ return px ? 1 : -1;
1412
+ if (x !== y)
1413
+ return x < y ? -1 : 1;
1414
+ }
1415
+ return 0;
1416
+ };
1417
+ var queryOf = (url) => decodeFormPairs(url.searchParams.entries());
1418
+ var bootSqlite = (sqlite) => {
1419
+ const client = resolveSqlite(sqlite);
1420
+ migrateCore(client);
1421
+ return client;
1422
+ };
1423
+ var createService = (options) => {
1424
+ const problems = verifyOperations(options.document, options.handlers);
1425
+ if (problems.length > 0)
1426
+ throw new OperationRegistryError(problems);
1427
+ migrateCore(options.sqlite);
1428
+ const now = options.now ?? (() => Date.now());
1429
+ const app = new Hono();
1430
+ app.notFound((c) => options.notFound(c.req.raw));
1431
+ app.onError((error, c) => options.onError(error, c.req.raw));
1432
+ const operations = [...listOperations(options.document)].sort(routeOrder);
1433
+ for (const operation of operations) {
1434
+ const metadata = operationMetadata(operation.operation);
1435
+ const handler = options.handlers[operation.operationId];
1436
+ const route = async (c) => {
1437
+ const request = c.req.raw;
1438
+ if (!metadata.supported || !handler) {
1439
+ return options.unsupported ? options.unsupported(request, operation) : options.notFound(request);
1440
+ }
1441
+ const url = new URL(request.url);
1442
+ const context = {
1443
+ request,
1444
+ url,
1445
+ params: c.req.param(),
1446
+ query: queryOf(url),
1447
+ body: await readBody(request),
1448
+ sqlite: options.sqlite,
1449
+ namespace: options.namespace,
1450
+ operation,
1451
+ document: options.document,
1452
+ now
1453
+ };
1454
+ const short = await options.before?.(context);
1455
+ if (short)
1456
+ return short;
1457
+ return handler(context);
1458
+ };
1459
+ app.on(operation.method.toUpperCase(), honoPath(operation.path), route);
1460
+ }
1461
+ return {
1462
+ app,
1463
+ sqlite: options.sqlite,
1464
+ namespace: options.namespace,
1465
+ fetch: async (request) => app.fetch(request),
1466
+ reset: async () => {
1467
+ clearNamespace(options.sqlite, options.namespace);
1468
+ }
1469
+ };
1470
+ };
1471
+
1472
+ // ../core/dist/snapshot.js
1473
+ var snapshotNamespace = (sqlite, namespace) => ({
1474
+ namespace,
1475
+ records: sqlite.prepare("SELECT collection, id, seq, value FROM mockingbird_records WHERE namespace = ? ORDER BY collection, seq").all(namespace),
1476
+ sequences: sqlite.prepare("SELECT name, kind, value FROM mockingbird_sequences WHERE namespace = ? ORDER BY name, kind").all(namespace)
1477
+ });
1478
+ var restoreNamespace = (sqlite, namespace, snapshot) => {
1479
+ sqlite.transaction(() => {
1480
+ sqlite.prepare("DELETE FROM mockingbird_records WHERE namespace = ?").run(namespace);
1481
+ sqlite.prepare("DELETE FROM mockingbird_sequences WHERE namespace = ?").run(namespace);
1482
+ const record = sqlite.prepare("INSERT INTO mockingbird_records (namespace, collection, id, seq, value) VALUES (?, ?, ?, ?, ?)");
1483
+ for (const row of snapshot.records) {
1484
+ record.run(namespace, row.collection, row.id, row.seq, row.value);
1485
+ }
1486
+ const sequence = sqlite.prepare("INSERT INTO mockingbird_sequences (namespace, name, kind, value) VALUES (?, ?, ?, ?)");
1487
+ for (const row of snapshot.sequences) {
1488
+ sequence.run(namespace, row.name, row.kind, row.value);
1489
+ }
1490
+ });
1491
+ };
1492
+
1493
+ // ../core/dist/version.js
1494
+ var PACKAGE_VERSION = true ? "0.1.0" : UNRELEASED_VERSION;
1495
+
1496
+ // ../core/dist/signing.js
1497
+ var encoder = new TextEncoder();
1498
+ var toHex = (bytes) => [...bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
1499
+ var sha = async (algorithm, input) => toHex(await crypto.subtle.digest(algorithm, typeof input === "string" ? encoder.encode(input) : input));
1500
+
1501
+ // ../core/dist/webhooks.js
1502
+ var json2 = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
1503
+ var adminError2 = (status, message) => json2(status, { error: { type: "mockingbird_admin", message } });
1504
+ var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1505
+ var parseEndpoint = (value) => {
1506
+ if (!isRecord3(value) || typeof value.url !== "string")
1507
+ return "each endpoint needs a url";
1508
+ try {
1509
+ new URL(value.url);
1510
+ } catch {
1511
+ return `not a URL: ${value.url}`;
1512
+ }
1513
+ const endpoint = { url: value.url };
1514
+ if (typeof value.id === "string")
1515
+ endpoint.id = value.id;
1516
+ if (typeof value.secret === "string")
1517
+ endpoint.secret = value.secret;
1518
+ if (typeof value.signUrl === "string")
1519
+ endpoint.signUrl = value.signUrl;
1520
+ const events = value.events ?? value.enabledEvents;
1521
+ if (Array.isArray(events))
1522
+ endpoint.events = events.map(String);
1523
+ if (isRecord3(value.tags)) {
1524
+ endpoint.tags = Object.fromEntries(Object.entries(value.tags).map(([k, v]) => [k, String(v)]));
1525
+ }
1526
+ if (typeof value.account === "string")
1527
+ endpoint.tags = { ...endpoint.tags, account: value.account };
1528
+ if (isRecord3(value.headers)) {
1529
+ endpoint.headers = Object.fromEntries(Object.entries(value.headers).map(([k, v]) => [k, String(v)]));
1530
+ }
1531
+ return endpoint;
1532
+ };
1533
+ var webhookAdminRoutes = (hub) => ({
1534
+ "GET /webhooks": ({ url, namespace }) => json2(200, {
1535
+ deliveries: hub.deliveries(url.searchParams.get("all") === "1" ? void 0 : namespace).filter((d) => {
1536
+ const type = url.searchParams.get("type");
1537
+ return type === null || d.type === type;
1538
+ })
1539
+ }),
1540
+ "GET /webhooks/events": ({ url, namespace }) => {
1541
+ const type = url.searchParams.get("type");
1542
+ return json2(200, {
1543
+ events: hub.messages(url.searchParams.get("all") === "1" ? void 0 : namespace).filter((m) => type === null || m.type === type).map((m) => ({ ...m, payload: parsePayload(m) }))
1544
+ });
1545
+ },
1546
+ "POST /webhooks/:id/replay": async ({ params }) => {
1547
+ const replayed = await hub.replay(params.id);
1548
+ return replayed ? json2(200, replayed) : adminError2(404, `no delivery ${params.id}`);
1549
+ },
1550
+ "POST /webhooks/flush": async () => {
1551
+ await hub.flush();
1552
+ return json2(200, { status: "ok" });
1553
+ },
1554
+ "POST /webhooks/faults": ({ body, namespace }) => {
1555
+ if (!isRecord3(body) || !["duplicate", "reorder", "drop"].includes(String(body.mode))) {
1556
+ return adminError2(400, "mode must be duplicate, reorder or drop");
1557
+ }
1558
+ const fault = { mode: body.mode };
1559
+ if (typeof body.count === "number")
1560
+ fault.count = body.count;
1561
+ hub.fault(namespace, fault);
1562
+ return json2(201, { namespace, ...fault });
1563
+ },
1564
+ "GET /webhook-endpoints": ({ namespace }) => json2(200, {
1565
+ endpoints: hub.endpoints(namespace).map(({ secret, ...rest }) => ({
1566
+ ...rest,
1567
+ secret: secret ? "(set)" : null
1568
+ }))
1569
+ }),
1570
+ "PUT /webhook-endpoints": ({ body, namespace }) => {
1571
+ const list = Array.isArray(body) ? body : isRecord3(body) ? body.endpoints : void 0;
1572
+ if (!Array.isArray(list))
1573
+ return adminError2(400, "expected [{url, secret?, events?}]");
1574
+ const parsed = [];
1575
+ for (const each of list) {
1576
+ const endpoint = parseEndpoint(each);
1577
+ if (typeof endpoint === "string")
1578
+ return adminError2(400, endpoint);
1579
+ parsed.push(endpoint);
1580
+ }
1581
+ const set = hub.setEndpoints(namespace, parsed);
1582
+ return json2(200, { endpoints: set.map((e) => ({ ...e, secret: e.secret ? "(set)" : null })) });
1583
+ },
1584
+ "DELETE /webhook-endpoints": ({ namespace }) => {
1585
+ hub.setEndpoints(namespace, []);
1586
+ return json2(200, { status: "ok" });
1587
+ }
1588
+ });
1589
+ var parsePayload = (message) => {
1590
+ if (message.contentType.startsWith("application/json")) {
1591
+ try {
1592
+ return JSON.parse(message.body);
1593
+ } catch {
1594
+ return message.body;
1595
+ }
1596
+ }
1597
+ if (message.contentType.startsWith("application/x-www-form-urlencoded")) {
1598
+ return Object.fromEntries(new URLSearchParams(message.body));
1599
+ }
1600
+ return message.body;
1601
+ };
1602
+
1603
+ // ../core/dist/runtime.js
1604
+ var MOCKINGBIRD_HEADER = "x-mockingbird";
1605
+ var BRANCH_HEADER = "x-mockingbird-branch";
1606
+ var AT_HEADER = "x-mockingbird-at";
1607
+ var CHECKPOINT_HEADER = "x-mockingbird-checkpoint";
1608
+ var DEFAULT_NAMESPACE = "default";
1609
+ var NAMESPACE_PATTERN = /^[A-Za-z0-9_.-]{1,64}$/;
1610
+ var PATH_PREFIX = /^\/ns\/([^/]+)(\/.*)?$/;
1611
+ var BRANCH_PATTERN2 = /^[A-Za-z0-9_.-]{1,64}$/;
1612
+ var MUTATING_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
1613
+ var effects = /* @__PURE__ */ new WeakMap();
1614
+ var reuseSorted = (fresh, previous, compare, equal) => {
1615
+ if (!previous || previous.length === 0)
1616
+ return fresh.map((row) => Object.freeze(row));
1617
+ const result = new Array(fresh.length);
1618
+ let unchanged = fresh.length === previous.length;
1619
+ let oldIndex = 0;
1620
+ for (let index = 0; index < fresh.length; index++) {
1621
+ const row = fresh[index];
1622
+ while (oldIndex < previous.length && compare(previous[oldIndex], row) < 0) {
1623
+ oldIndex++;
1624
+ }
1625
+ const old = previous[oldIndex];
1626
+ result[index] = old !== void 0 && compare(old, row) === 0 && equal(old, row) ? old : Object.freeze(row);
1627
+ if (result[index] !== previous[index])
1628
+ unchanged = false;
1629
+ }
1630
+ return unchanged ? previous : result;
1631
+ };
1632
+ var faultEffects = (request) => (effects.get(request) ?? []).filter((e) => e !== void 0);
1633
+ var faultEffect = (request, name) => faultEffects(request).find((e) => e.name === name)?.params;
1634
+ var DroppedConnectionError = class extends TypeError {
1635
+ code = "MOCKINGBIRD_DROP";
1636
+ constructor() {
1637
+ super("fetch failed: connection dropped by Mockingbird fault");
1638
+ this.name = "TypeError";
1639
+ }
1640
+ };
1641
+ var operationMatcher = (document2) => {
1642
+ const matchers = listOperations(document2).map((operation) => ({
1643
+ operationId: operation.operationId,
1644
+ method: operation.method.toUpperCase(),
1645
+ pattern: new RegExp(`^${operation.path.split("/").map((segment) => segment.startsWith("{") ? "[^/]+" : segment.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("/")}/?$`),
1646
+ params: (operation.path.match(/\{/g) ?? []).length
1647
+ })).sort((a, b) => a.params - b.params);
1648
+ return (request, path) => matchers.find((m) => m.method === request.method && m.pattern.test(path))?.operationId;
1649
+ };
1650
+ var createRuntime = (options) => {
1651
+ const sqlite = bootSqlite(options.sqlite);
1652
+ const clock = options.clock ?? createClock();
1653
+ const rng = createRng(options.seed ?? 0);
1654
+ const wallNow = options.io?.wallNow ?? Date.now;
1655
+ const monotonicNow = options.io?.monotonicNow ?? (() => performance.now());
1656
+ const sleep = options.io?.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
1657
+ const faults = createFaultRegistry(createRng(options.seed ?? 0), sleep);
1658
+ const metrics = createMetrics();
1659
+ const journal = createJournal(options.journalSize ?? DEFAULT_JOURNAL_SIZE);
1660
+ const version = options.version ?? PACKAGE_VERSION;
1661
+ const instances = /* @__PURE__ */ new Map();
1662
+ const publicNamespaces = /* @__PURE__ */ new Set();
1663
+ const branchRngs = /* @__PURE__ */ new Map();
1664
+ const timelines = /* @__PURE__ */ new Map();
1665
+ const branchStorage = /* @__PURE__ */ new Map();
1666
+ const captured = /* @__PURE__ */ new Map();
1667
+ const credentials = createCredentialRegistry();
1668
+ const operationIdFor = options.document ? operationMatcher(options.document) : () => void 0;
1669
+ const storageNamespace = (name) => name === DEFAULT_NAMESPACE ? options.name : `${options.name}:${name}`;
1670
+ const instanceFor = (key, publicNamespace = key, isolatedRng) => {
1671
+ const existing = instances.get(key);
1672
+ if (existing)
1673
+ return existing;
1674
+ if (!NAMESPACE_PATTERN.test(key) || !NAMESPACE_PATTERN.test(publicNamespace)) {
1675
+ throw new RangeError(`namespace must match ${NAMESPACE_PATTERN}: ${JSON.stringify(publicNamespace)}`);
1676
+ }
1677
+ const created = options.create({
1678
+ namespace: storageNamespace(key),
1679
+ publicNamespace,
1680
+ sqlite,
1681
+ clock,
1682
+ rng: isolatedRng ?? rng
1683
+ });
1684
+ instances.set(key, created);
1685
+ publicNamespaces.add(publicNamespace);
1686
+ if (isolatedRng)
1687
+ branchRngs.set(key, isolatedRng);
1688
+ return created;
1689
+ };
1690
+ const instance = (name = DEFAULT_NAMESPACE) => instanceFor(name);
1691
+ const capture = (storage) => {
1692
+ const fresh = snapshotNamespace(sqlite, storageNamespace(storage));
1693
+ const previous = captured.get(storage);
1694
+ const snapshot2 = {
1695
+ namespace: fresh.namespace,
1696
+ records: reuseSorted(fresh.records, previous?.records, (left, right) => left.collection < right.collection ? -1 : left.collection > right.collection ? 1 : left.seq - right.seq, (left, right) => left.id === right.id && left.value === right.value),
1697
+ sequences: reuseSorted(fresh.sequences, previous?.sequences, (left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : left.kind < right.kind ? -1 : left.kind > right.kind ? 1 : 0, (left, right) => left.value === right.value)
1698
+ };
1699
+ Object.freeze(snapshot2.records);
1700
+ Object.freeze(snapshot2.sequences);
1701
+ Object.freeze(snapshot2);
1702
+ captured.set(storage, snapshot2);
1703
+ return Object.freeze({
1704
+ snapshot: snapshot2,
1705
+ clock: Object.freeze(clock.state()),
1706
+ rngState: (branchRngs.get(storage) ?? rng).state()
1707
+ });
1708
+ };
1709
+ const timeline = (name = DEFAULT_NAMESPACE) => {
1710
+ let found = timelines.get(name);
1711
+ if (found)
1712
+ return found;
1713
+ instance(name);
1714
+ found = new Timeline({
1715
+ now: clock.now,
1716
+ ...options.maxCheckpoints !== void 0 ? { maxCheckpoints: options.maxCheckpoints } : {}
1717
+ });
1718
+ found.commit(capture(name));
1719
+ timelines.set(name, found);
1720
+ return found;
1721
+ };
1722
+ const physicalBranch = (namespace, branch2) => {
1723
+ if (branch2 === "main")
1724
+ return namespace;
1725
+ const mapKey = `${namespace}\0${branch2}`;
1726
+ const existing = branchStorage.get(mapKey);
1727
+ if (existing)
1728
+ return existing;
1729
+ const key = `branch_${seedFrom(`${options.name}\0${namespace}\0${branch2}`).toString(36)}`;
1730
+ branchStorage.set(mapKey, key);
1731
+ return key;
1732
+ };
1733
+ const ensureBranch = (namespace, branch2, at) => {
1734
+ if (!BRANCH_PATTERN2.test(branch2))
1735
+ throw new RangeError(`branch must match ${BRANCH_PATTERN2}`);
1736
+ const history = timeline(namespace);
1737
+ if (branch2 === "main") {
1738
+ if (at !== void 0) {
1739
+ const point = history.checkout("main", at);
1740
+ restoreNamespace(sqlite, storageNamespace(namespace), point.value.snapshot);
1741
+ captured.set(namespace, point.value.snapshot);
1742
+ rng.setState(point.value.rngState);
1743
+ clock.set(point.value.clock.now);
1744
+ if (point.value.clock.frozen)
1745
+ clock.freeze();
1746
+ else
1747
+ clock.unfreeze();
1748
+ }
1749
+ return namespace;
1750
+ }
1751
+ const storage = physicalBranch(namespace, branch2);
1752
+ if (!history.hasBranch(branch2)) {
1753
+ if (at === void 0)
1754
+ history.commit(capture(namespace));
1755
+ const point = history.fork(branch2, at === void 0 ? {} : { from: at });
1756
+ const branchRng = createRng(options.seed ?? 0);
1757
+ if (point)
1758
+ branchRng.setState(point.value.rngState);
1759
+ instanceFor(storage, namespace, branchRng);
1760
+ if (point)
1761
+ restoreNamespace(sqlite, storageNamespace(storage), point.value.snapshot);
1762
+ if (point)
1763
+ captured.set(storage, point.value.snapshot);
1764
+ } else if (at !== void 0 && history.head(branch2)?.id !== at) {
1765
+ const point = history.checkout(branch2, at);
1766
+ if (!instances.has(storage)) {
1767
+ const branchRng = createRng(options.seed ?? 0);
1768
+ branchRng.setState(point.value.rngState);
1769
+ instanceFor(storage, namespace, branchRng);
1770
+ }
1771
+ branchRngs.get(storage)?.setState(point.value.rngState);
1772
+ restoreNamespace(sqlite, storageNamespace(storage), point.value.snapshot);
1773
+ captured.set(storage, point.value.snapshot);
1774
+ } else {
1775
+ if (!instances.has(storage)) {
1776
+ const point = history.head(branch2);
1777
+ const branchRng = createRng(options.seed ?? 0);
1778
+ if (point)
1779
+ branchRng.setState(point.value.rngState);
1780
+ instanceFor(storage, namespace, branchRng);
1781
+ }
1782
+ }
1783
+ return storage;
1784
+ };
1785
+ const checkpoint = (namespace = DEFAULT_NAMESPACE, branch2 = "main") => {
1786
+ const storage = ensureBranch(namespace, branch2);
1787
+ return timeline(namespace).commit(capture(storage), { branch: branch2 });
1788
+ };
1789
+ const branch = (name, branchOptions = {}) => {
1790
+ const namespace = branchOptions.namespace ?? DEFAULT_NAMESPACE;
1791
+ ensureBranch(namespace, name, branchOptions.at);
1792
+ const head = timeline(namespace).head(name);
1793
+ if (!head)
1794
+ throw new RangeError(`branch ${name} has no checkpoint`);
1795
+ return head;
1796
+ };
1797
+ const checkout = (checkpointId, checkoutOptions = {}) => {
1798
+ const namespace = checkoutOptions.namespace ?? DEFAULT_NAMESPACE;
1799
+ const branchName = checkoutOptions.branch ?? "main";
1800
+ const history = timeline(namespace);
1801
+ const point = history.checkout(branchName, checkpointId);
1802
+ const storage = ensureBranch(namespace, branchName);
1803
+ restoreNamespace(sqlite, storageNamespace(storage), point.value.snapshot);
1804
+ captured.set(storage, point.value.snapshot);
1805
+ clock.set(point.value.clock.now);
1806
+ if (point.value.clock.frozen)
1807
+ clock.freeze();
1808
+ else
1809
+ clock.unfreeze();
1810
+ (branchRngs.get(storage) ?? rng).setState(point.value.rngState);
1811
+ };
1812
+ const reset = async (name = DEFAULT_NAMESPACE) => {
1813
+ if (name === "*") {
1814
+ options.webhooks?.clear();
1815
+ for (const each of instances.values())
1816
+ await each.reset();
1817
+ timelines.clear();
1818
+ branchStorage.clear();
1819
+ branchRngs.clear();
1820
+ captured.clear();
1821
+ return;
1822
+ }
1823
+ options.webhooks?.clear(name);
1824
+ const target = instances.get(name);
1825
+ if (target)
1826
+ await target.reset();
1827
+ else
1828
+ clearNamespace(sqlite, storageNamespace(name));
1829
+ for (const [mapping, storage] of branchStorage) {
1830
+ if (!mapping.startsWith(`${name}\0`))
1831
+ continue;
1832
+ const branchInstance = instances.get(storage);
1833
+ if (branchInstance)
1834
+ await branchInstance.reset();
1835
+ else
1836
+ clearNamespace(sqlite, storageNamespace(storage));
1837
+ branchStorage.delete(mapping);
1838
+ branchRngs.delete(storage);
1839
+ captured.delete(storage);
1840
+ }
1841
+ timelines.delete(name);
1842
+ captured.delete(name);
1843
+ };
1844
+ const snapshot = (name = DEFAULT_NAMESPACE) => {
1845
+ return checkpoint(name, "main").value.snapshot;
1846
+ };
1847
+ const restore = (from, name = DEFAULT_NAMESPACE) => {
1848
+ instance(name);
1849
+ restoreNamespace(sqlite, storageNamespace(name), from);
1850
+ captured.set(name, from);
1851
+ const history = timelines.get(name);
1852
+ if (history)
1853
+ history.commit(capture(name), { branch: "main" });
1854
+ else
1855
+ timeline(name);
1856
+ };
1857
+ const runtime = {
1858
+ name: options.name,
1859
+ sqlite,
1860
+ clock,
1861
+ faults,
1862
+ metrics,
1863
+ journal,
1864
+ rng,
1865
+ credentials,
1866
+ webhooks: options.webhooks,
1867
+ applyPreset: (name, namespace = DEFAULT_NAMESPACE, overrides = {}) => {
1868
+ const preset = options.presets?.[name];
1869
+ if (!preset)
1870
+ throw new RangeError(`no fault preset ${JSON.stringify(name)}`);
1871
+ const added = (preset.rules ?? []).map((rule, index) => faults.add({
1872
+ namespace,
1873
+ ...rule,
1874
+ ...overrides,
1875
+ preset: name,
1876
+ id: `${overrides.id ?? name}${(preset.rules ?? []).length > 1 ? `_${index + 1}` : ""}`
1877
+ }));
1878
+ if (preset.webhook && options.webhooks) {
1879
+ options.webhooks.fault(namespace, {
1880
+ ...preset.webhook,
1881
+ ...overrides.count !== void 0 ? { count: overrides.count } : {}
1882
+ });
1883
+ }
1884
+ return added;
1885
+ },
1886
+ instance,
1887
+ namespaces: () => [...publicNamespaces].sort(),
1888
+ reset,
1889
+ snapshot,
1890
+ restore,
1891
+ checkpoint,
1892
+ branch,
1893
+ checkout,
1894
+ timeline,
1895
+ fetch: async (incoming) => {
1896
+ let request = incoming;
1897
+ const prefixed = PATH_PREFIX.exec(new URL(request.url).pathname);
1898
+ if (prefixed) {
1899
+ const url2 = new URL(request.url);
1900
+ url2.pathname = prefixed[2] ?? "/";
1901
+ const headers = new Headers(request.headers);
1902
+ if (!headers.has(NAMESPACE_HEADER)) {
1903
+ headers.set(NAMESPACE_HEADER, decodeURIComponent(prefixed[1]));
1904
+ }
1905
+ const hasBody = request.method !== "GET" && request.method !== "HEAD";
1906
+ request = new Request(url2, {
1907
+ method: request.method,
1908
+ headers,
1909
+ ...hasBody ? { body: await request.arrayBuffer() } : {},
1910
+ signal: request.signal
1911
+ });
1912
+ }
1913
+ let namespace = control.namespaceOf(request);
1914
+ if (!request.headers.has(NAMESPACE_HEADER) && options.credential) {
1915
+ const credential = options.credential(request);
1916
+ const mapped = credential !== void 0 ? credentials.get(credential) : void 0;
1917
+ if (mapped !== void 0)
1918
+ namespace = mapped;
1919
+ }
1920
+ const selectedBranch = request.headers.get(BRANCH_HEADER) ?? "main";
1921
+ const at = request.headers.get(AT_HEADER) ?? void 0;
1922
+ const stamp = (response2) => {
1923
+ const value = NAMESPACE_PATTERN.test(namespace) ? `${options.name}@${version}; ns=${namespace}` : `${options.name}@${version}`;
1924
+ try {
1925
+ response2.headers.set(MOCKINGBIRD_HEADER, value);
1926
+ return response2;
1927
+ } catch {
1928
+ const copy = new Response(response2.body, response2);
1929
+ copy.headers.set(MOCKINGBIRD_HEADER, value);
1930
+ return copy;
1931
+ }
1932
+ };
1933
+ const handled = await control.handle(request);
1934
+ if (handled)
1935
+ return stamp(handled);
1936
+ const started = monotonicNow();
1937
+ const url = new URL(request.url);
1938
+ const operationId = operationIdFor(request, url.pathname);
1939
+ const log = (status, faultId, response2) => {
1940
+ const noted = response2 ? responseNotes(response2) : void 0;
1941
+ const entry = {
1942
+ service: options.name,
1943
+ namespace,
1944
+ operationId,
1945
+ method: request.method,
1946
+ path: url.pathname,
1947
+ status,
1948
+ durationMs: Math.round((monotonicNow() - started) * 100) / 100,
1949
+ unmatched: options.document !== void 0 && operationId === void 0,
1950
+ ...faultId !== void 0 ? { faultId } : {},
1951
+ ...noted?.ids && Object.keys(noted.ids).length > 0 ? { ids: noted.ids } : {},
1952
+ ...noted?.adopted ? { adopted: true } : {}
1953
+ };
1954
+ metrics.record(entry);
1955
+ journal.record({ ...entry, at: new Date(clock.now()).toISOString() });
1956
+ options.onLog?.(entry);
1957
+ };
1958
+ if (!NAMESPACE_PATTERN.test(namespace)) {
1959
+ log(400);
1960
+ return stamp(new Response(JSON.stringify({
1961
+ error: {
1962
+ type: "mockingbird_admin",
1963
+ message: `${NAMESPACE_HEADER} must match ${NAMESPACE_PATTERN}`
1964
+ }
1965
+ }), { status: 400, headers: { "content-type": "application/json" } }));
1966
+ }
1967
+ if (!BRANCH_PATTERN2.test(selectedBranch)) {
1968
+ log(400);
1969
+ return stamp(adminFail(400, `${BRANCH_HEADER} must match ${BRANCH_PATTERN2}`));
1970
+ }
1971
+ let storage;
1972
+ try {
1973
+ if (at !== void 0 && selectedBranch === "main" && !MUTATING_METHODS.has(request.method)) {
1974
+ const point = timeline(namespace).get(at);
1975
+ storage = physicalBranch(namespace, `at_${at}`);
1976
+ let viewRng = branchRngs.get(storage);
1977
+ if (!viewRng) {
1978
+ viewRng = createRng(options.seed ?? 0);
1979
+ instanceFor(storage, namespace, viewRng);
1980
+ }
1981
+ viewRng.setState(point.value.rngState);
1982
+ restoreNamespace(sqlite, storageNamespace(storage), point.value.snapshot);
1983
+ captured.set(storage, point.value.snapshot);
1984
+ } else {
1985
+ storage = ensureBranch(namespace, selectedBranch, at);
1986
+ }
1987
+ } catch (error) {
1988
+ log(409);
1989
+ return stamp(adminFail(409, error instanceof Error ? error.message : String(error)));
1990
+ }
1991
+ const hits = await faults.take({
1992
+ operationId,
1993
+ method: request.method,
1994
+ path: url.pathname,
1995
+ namespace
1996
+ });
1997
+ const final = hits.find((hit) => hit.drop || hit.response);
1998
+ if (final?.drop) {
1999
+ log(0, final.id);
2000
+ throw new DroppedConnectionError();
2001
+ }
2002
+ if (final?.response) {
2003
+ log(final.response.status, final.id);
2004
+ return stamp(final.response);
2005
+ }
2006
+ const fired = hits.filter((hit) => hit.effect !== void 0);
2007
+ if (fired.length > 0)
2008
+ effects.set(request, fired.map((hit) => hit.effect));
2009
+ let response = await instanceFor(storage, namespace).fetch(request);
2010
+ if (MUTATING_METHODS.has(request.method) && response.status >= 200 && response.status < 400) {
2011
+ const point = timeline(namespace).commit(capture(storage), { branch: selectedBranch });
2012
+ response = mutableResponse(response);
2013
+ response.headers.set(CHECKPOINT_HEADER, point.id);
2014
+ }
2015
+ if (selectedBranch !== "main") {
2016
+ response = mutableResponse(response);
2017
+ response.headers.set(BRANCH_HEADER, selectedBranch);
2018
+ }
2019
+ if (at !== void 0) {
2020
+ response = mutableResponse(response);
2021
+ response.headers.set(AT_HEADER, at);
2022
+ }
2023
+ log(response.status, fired[0]?.id, response);
2024
+ return stamp(response);
2025
+ }
2026
+ };
2027
+ const control = createControlPlane({
2028
+ name: options.name,
2029
+ startedAt: wallNow(),
2030
+ wallNow,
2031
+ clock,
2032
+ faults,
2033
+ metrics,
2034
+ journal,
2035
+ defaultNamespace: DEFAULT_NAMESPACE,
2036
+ namespaces: runtime.namespaces,
2037
+ reset,
2038
+ timeTravel: {
2039
+ checkpoint: (name, branchName) => {
2040
+ const point = checkpoint(name, branchName);
2041
+ return {
2042
+ id: point.id,
2043
+ branch: point.branch,
2044
+ parent: point.parent,
2045
+ at: point.at,
2046
+ records: point.value.snapshot.records.length
2047
+ };
2048
+ },
2049
+ branch: (branchName, branchOptions) => {
2050
+ const point = branch(branchName, branchOptions);
2051
+ return { id: point.id, branch: point.branch, parent: point.parent, at: point.at };
2052
+ },
2053
+ checkout: (checkpointId, checkoutOptions) => checkout(checkpointId, checkoutOptions),
2054
+ retain: (name, checkpointId) => {
2055
+ timeline(name).retain(checkpointId);
2056
+ },
2057
+ release: (name, checkpointId) => timeline(name).release(checkpointId),
2058
+ inspect: (name) => {
2059
+ const history = timeline(name);
2060
+ return {
2061
+ branches: history.branches(),
2062
+ checkpoints: history.checkpoints().map(({ id, branch: branchName, parent, at }) => ({
2063
+ id,
2064
+ branch: branchName,
2065
+ parent,
2066
+ at
2067
+ }))
2068
+ };
2069
+ }
2070
+ },
2071
+ describe: options.describe ?? (() => ({})),
2072
+ ...options.presets ? {
2073
+ applyPreset: (name, namespace, overrides) => runtime.applyPreset(name, namespace, overrides)
2074
+ } : {},
2075
+ routes: {
2076
+ ...credentialRoutes(credentials),
2077
+ ...options.presets ? presetRoutes(options.presets, runtime) : {},
2078
+ ...options.webhooks ? webhookAdminRoutes(options.webhooks) : {},
2079
+ ...options.admin?.(runtime) ?? {}
2080
+ },
2081
+ adminKey: options.adminKey
2082
+ });
2083
+ return runtime;
2084
+ };
2085
+ var mutableResponse = (response) => {
2086
+ try {
2087
+ response.headers.set("x-mockingbird-mutable-probe", "1");
2088
+ response.headers.delete("x-mockingbird-mutable-probe");
2089
+ return response;
2090
+ } catch {
2091
+ return new Response(response.body, response);
2092
+ }
2093
+ };
2094
+ var adminJson = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
2095
+ var adminFail = (status, message) => adminJson(status, { error: { type: "mockingbird_admin", message } });
2096
+ var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2097
+ var credentialRoutes = (registry) => ({
2098
+ "GET /credentials": () => adminJson(200, {
2099
+ credentials: registry.entries().map(({ credential, namespace }) => ({
2100
+ credential: maskCredential(credential),
2101
+ namespace
2102
+ }))
2103
+ }),
2104
+ "PUT /credentials": ({ body, namespace }) => {
2105
+ const pairs = [];
2106
+ const list = Array.isArray(body) ? body : isObject(body) ? body.credentials : void 0;
2107
+ if (Array.isArray(list)) {
2108
+ for (const each of list) {
2109
+ if (typeof each === "string")
2110
+ pairs.push([each, namespace]);
2111
+ else if (isObject(each) && typeof each.credential === "string") {
2112
+ pairs.push([
2113
+ each.credential,
2114
+ typeof each.namespace === "string" ? each.namespace : namespace
2115
+ ]);
2116
+ } else
2117
+ return adminFail(400, "each entry is a credential string or {credential, namespace}");
2118
+ }
2119
+ } else if (isObject(list)) {
2120
+ for (const [credential, target] of Object.entries(list)) {
2121
+ if (typeof target !== "string")
2122
+ return adminFail(400, `namespace for ${credential} must be a string`);
2123
+ pairs.push([credential, target]);
2124
+ }
2125
+ } else if (isObject(body) && typeof body.credential === "string") {
2126
+ pairs.push([body.credential, typeof body.namespace === "string" ? body.namespace : namespace]);
2127
+ } else {
2128
+ return adminFail(400, 'expected {"credentials": {"<credential>": "<namespace>"}}');
2129
+ }
2130
+ for (const [credential, target] of pairs) {
2131
+ if (!NAMESPACE_PATTERN.test(target))
2132
+ return adminFail(400, `bad namespace ${JSON.stringify(target)}`);
2133
+ registry.set(credential, target);
2134
+ }
2135
+ return adminJson(200, { mapped: pairs.length });
2136
+ },
2137
+ "DELETE /credentials": ({ url }) => {
2138
+ const credential = url.searchParams.get("credential");
2139
+ if (credential === null)
2140
+ registry.clear();
2141
+ else
2142
+ registry.remove(credential);
2143
+ return adminJson(200, { status: "ok" });
2144
+ }
2145
+ });
2146
+ var presetRoutes = (presets, runtime) => ({
2147
+ "GET /faults/presets": () => adminJson(200, {
2148
+ presets: Object.entries(presets).map(([name, preset]) => ({ name, ...preset }))
2149
+ }),
2150
+ "POST /faults/presets/:name": ({ params, body, namespace }) => {
2151
+ const name = params.name;
2152
+ if (!presets[name])
2153
+ return adminFail(404, `no fault preset ${name}; GET /__admin/faults/presets`);
2154
+ const overrides = isObject(body) ? body : {};
2155
+ return adminJson(201, { preset: name, rules: runtime.applyPreset(name, namespace, overrides) });
2156
+ }
2157
+ });
2158
+
2159
+ // ../core/dist/validation.js
2160
+ var bodyIssues = (context, contentType = "application/json") => {
2161
+ const requestBody = context.operation.operation.requestBody;
2162
+ if (!requestBody)
2163
+ return [];
2164
+ const resolved = deref(context.document, requestBody);
2165
+ const schema = resolved.content?.[contentType]?.schema;
2166
+ if (!schema)
2167
+ return [];
2168
+ const value = context.body.kind === "json" || context.body.kind === "form" ? context.body.value : void 0;
2169
+ if (context.body.kind === "invalid") {
2170
+ return [{ path: "", message: `request body is not valid ${context.body.mediaType}` }];
2171
+ }
2172
+ if (value === void 0) {
2173
+ return resolved.required ? [{ path: "", message: "request body is required" }] : [];
2174
+ }
2175
+ return validateValue(context.document, resolveSchema(context.document, schema), value).map((issue) => ({ path: issue.path.join("."), message: issue.message }));
2176
+ };
2177
+
2178
+ // src/analyze.ts
2179
+ var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2180
+ var array = (value) => Array.isArray(value) ? value : [];
2181
+ var ValidationProblem = class extends Error {
2182
+ constructor(message) {
2183
+ super(message);
2184
+ this.name = "ValidationProblem";
2185
+ }
2186
+ };
2187
+ var NO_PREFILL = /claude-(?:sonnet|opus)-4-[5-9]|claude-opus-4-1|claude-(?:sonnet|opus|haiku)-[5-9]/i;
2188
+ var ONE_SAMPLING_PARAM = /claude-(?:sonnet|opus|haiku)-4-[5-9]|claude-opus-4-1|claude-(?:sonnet|opus|haiku)-[5-9]/i;
2189
+ var parseSchema = (value) => {
2190
+ if (typeof value !== "string") return value;
2191
+ try {
2192
+ return JSON.parse(value);
2193
+ } catch {
2194
+ return {};
2195
+ }
2196
+ };
2197
+ var textOf = (blocks) => blocks.map((block) => isRecord4(block) && typeof block.text === "string" ? block.text : "").filter((text2) => text2.length > 0).join("\n");
2198
+ var conversationFacts = (messages) => {
2199
+ let lastSaid = -1;
2200
+ for (let i = messages.length - 1; i >= 0; i--) {
2201
+ const message = messages[i];
2202
+ if (message.role === "user" && message.hasContent) {
2203
+ lastSaid = i;
2204
+ break;
2205
+ }
2206
+ }
2207
+ const turnIndex = messages.slice(lastSaid + 1).filter((m) => m.role === "assistant").length;
2208
+ const toolNames = /* @__PURE__ */ new Map();
2209
+ for (const message of messages)
2210
+ for (const use of message.toolUses) toolNames.set(use.id, use.name);
2211
+ const last = messages.at(-1);
2212
+ const toolResults = last?.role === "user" ? last.toolResultIds.map((id) => toolNames.get(id)).filter((n) => !!n) : [];
2213
+ return {
2214
+ turnIndex,
2215
+ toolResults,
2216
+ lastUserText: lastSaid >= 0 ? messages[lastSaid].text : ""
2217
+ };
2218
+ };
2219
+ var checkConversation = (messages, modelId, hasToolConfig) => {
2220
+ if (messages.length === 0 || messages[0]?.role !== "user") {
2221
+ throw new ValidationProblem(
2222
+ "A conversation must start with a user message. Try again with a conversation that starts with a user message."
2223
+ );
2224
+ }
2225
+ for (let i = 1; i < messages.length; i++) {
2226
+ if (messages[i]?.role === messages[i - 1]?.role) {
2227
+ throw new ValidationProblem(
2228
+ "A conversation must alternate between user and assistant roles. Make sure the conversation alternates between user and assistant roles and try again."
2229
+ );
2230
+ }
2231
+ }
2232
+ const usesTools = messages.some((m) => m.toolUses.length > 0 || m.toolResultIds.length > 0);
2233
+ if (usesTools && !hasToolConfig) {
2234
+ throw new ValidationProblem(
2235
+ "The toolConfig field must be defined when using toolUse and toolResult content blocks."
2236
+ );
2237
+ }
2238
+ for (let i = 0; i < messages.length; i++) {
2239
+ const message = messages[i];
2240
+ if (message.role === "user" && message.hasDocument && message.text.length === 0) {
2241
+ throw new ValidationProblem(
2242
+ `The model returned the following errors: messages.${i}: A text block must be included alongside a document block.`
2243
+ );
2244
+ }
2245
+ if (message.role !== "assistant" || message.toolUses.length === 0) continue;
2246
+ const next = messages[i + 1];
2247
+ if (!next) continue;
2248
+ const missing = message.toolUses.filter((use) => !next.toolResultIds.includes(use.id));
2249
+ if (missing.length > 0) {
2250
+ throw new ValidationProblem(
2251
+ `Expected toolResult blocks at messages.${i + 1}.content for the following Ids: ${missing.map((m) => m.id).join(", ")}`
2252
+ );
2253
+ }
2254
+ }
2255
+ for (let i = 0; i < messages.length; i++) {
2256
+ const message = messages[i];
2257
+ if (message.toolResultIds.length === 0) continue;
2258
+ const previous = messages[i - 1];
2259
+ const known = new Set(previous?.toolUses.map((use) => use.id) ?? []);
2260
+ const orphan = message.toolResultIds.find((id) => !known.has(id));
2261
+ if (orphan !== void 0) {
2262
+ throw new ValidationProblem(
2263
+ `messages.${i}.content: unexpected tool_use_id found in tool_result blocks: ${orphan}. Each tool_result block must have a corresponding tool_use block in the previous message.`
2264
+ );
2265
+ }
2266
+ }
2267
+ if (messages.at(-1)?.role === "assistant" && NO_PREFILL.test(modelId)) {
2268
+ throw new ValidationProblem(
2269
+ "This model does not support assistant message prefill. The conversation must end with a user message."
2270
+ );
2271
+ }
2272
+ };
2273
+ var converseMessage = (value, index) => {
2274
+ if (!isRecord4(value) || value.role !== "user" && value.role !== "assistant") {
2275
+ throw new ValidationProblem(`messages.${index}.role: must be one of [user, assistant]`);
2276
+ }
2277
+ const content = array(value.content);
2278
+ if (content.length === 0) {
2279
+ throw new ValidationProblem(
2280
+ `messages.${index}.content: The content field in the Message object at messages.${index} is empty. Add a ContentBlock object to the content field and try again.`
2281
+ );
2282
+ }
2283
+ const out = {
2284
+ role: value.role,
2285
+ text: textOf(content),
2286
+ toolUses: [],
2287
+ toolResultIds: [],
2288
+ hasContent: false,
2289
+ hasDocument: false,
2290
+ hasImage: false,
2291
+ hasCachePoint: false,
2292
+ chars: 0
2293
+ };
2294
+ for (const block of content) {
2295
+ if (!isRecord4(block)) continue;
2296
+ out.chars += JSON.stringify(block).length;
2297
+ if (typeof block.text === "string") out.hasContent = true;
2298
+ if (isRecord4(block.document)) {
2299
+ out.hasDocument = true;
2300
+ out.hasContent = true;
2301
+ }
2302
+ if (isRecord4(block.image)) {
2303
+ out.hasImage = true;
2304
+ out.hasContent = true;
2305
+ }
2306
+ if (isRecord4(block.video)) out.hasContent = true;
2307
+ if (isRecord4(block.cachePoint)) out.hasCachePoint = true;
2308
+ if (isRecord4(block.toolUse)) {
2309
+ out.toolUses.push({
2310
+ id: String(block.toolUse.toolUseId ?? ""),
2311
+ name: String(block.toolUse.name ?? "")
2312
+ });
2313
+ }
2314
+ if (isRecord4(block.toolResult)) out.toolResultIds.push(String(block.toolResult.toolUseId ?? ""));
2315
+ }
2316
+ return out;
2317
+ };
2318
+ var analyzeConverse = (operation, modelId, body) => {
2319
+ if (!isRecord4(body))
2320
+ throw new ValidationProblem(
2321
+ "Malformed input request, please reformat your input and try again."
2322
+ );
2323
+ const messages = array(body.messages).map(converseMessage);
2324
+ const system = array(body.system);
2325
+ const toolConfig = isRecord4(body.toolConfig) ? body.toolConfig : void 0;
2326
+ const toolSchemas = {};
2327
+ for (const tool of array(toolConfig?.tools)) {
2328
+ if (!isRecord4(tool) || !isRecord4(tool.toolSpec)) continue;
2329
+ const spec = tool.toolSpec;
2330
+ const schema = isRecord4(spec.inputSchema) ? spec.inputSchema.json : void 0;
2331
+ toolSchemas[String(spec.name)] = parseSchema(schema) ?? {};
2332
+ }
2333
+ const tools = Object.keys(toolSchemas);
2334
+ const choice = isRecord4(toolConfig?.toolChoice) ? toolConfig.toolChoice : void 0;
2335
+ let toolChoice;
2336
+ if (choice) {
2337
+ if (isRecord4(choice.tool)) toolChoice = `tool:${String(choice.tool.name)}`;
2338
+ else if (choice.any !== void 0) toolChoice = "any";
2339
+ else if (choice.auto !== void 0) toolChoice = "auto";
2340
+ }
2341
+ if (toolConfig && tools.length === 0) {
2342
+ throw new ValidationProblem(
2343
+ "The value at toolConfig.tools failed to satisfy constraint: Member must have length greater than or equal to 1"
2344
+ );
2345
+ }
2346
+ if (toolChoice?.startsWith("tool:") && !tools.includes(toolChoice.slice(5))) {
2347
+ throw new ValidationProblem(
2348
+ `The provided toolChoice ${toolChoice.slice(5)} is not a tool in toolConfig.tools.`
2349
+ );
2350
+ }
2351
+ const inference = isRecord4(body.inferenceConfig) ? body.inferenceConfig : {};
2352
+ const additional = isRecord4(body.additionalModelRequestFields) ? body.additionalModelRequestFields : {};
2353
+ const hasTemperature = inference.temperature !== void 0 || additional.temperature !== void 0;
2354
+ const hasTopP = inference.topP !== void 0 || additional.top_p !== void 0;
2355
+ if (hasTemperature && hasTopP && ONE_SAMPLING_PARAM.test(modelId)) {
2356
+ throw new ValidationProblem(
2357
+ "The model returned the following errors: `temperature` and `top_p` cannot both be specified for this model. Please use only one."
2358
+ );
2359
+ }
2360
+ checkConversation(messages, modelId, toolConfig !== void 0);
2361
+ let structured;
2362
+ const outputConfig = isRecord4(body.outputConfig) ? body.outputConfig : void 0;
2363
+ const textFormat = isRecord4(outputConfig?.textFormat) ? outputConfig.textFormat : void 0;
2364
+ const jsonSchema = textFormat && isRecord4(textFormat.structure) && isRecord4(textFormat.structure.jsonSchema) ? textFormat.structure.jsonSchema : void 0;
2365
+ const nativeFormat = isRecord4(additional.output_config) && isRecord4(additional.output_config.format) ? additional.output_config.format : void 0;
2366
+ if (jsonSchema) {
2367
+ structured = {
2368
+ form: "outputConfig",
2369
+ schema: parseSchema(jsonSchema.schema),
2370
+ ...typeof jsonSchema.name === "string" ? { name: jsonSchema.name } : {}
2371
+ };
2372
+ } else if (nativeFormat && nativeFormat.type === "json_schema") {
2373
+ structured = { form: "outputFormat", schema: parseSchema(nativeFormat.schema) };
2374
+ } else if (toolChoice?.startsWith("tool:")) {
2375
+ const tool = toolChoice.slice(5);
2376
+ structured = { form: "tool", tool, schema: toolSchemas[tool] };
2377
+ } else if (toolChoice === "any" && tools.length > 0) {
2378
+ const tool = tools.includes("json") ? "json" : tools[0];
2379
+ structured = { form: "tool", tool, schema: toolSchemas[tool] };
2380
+ }
2381
+ const facts = conversationFacts(messages);
2382
+ const systemText = textOf(system);
2383
+ return {
2384
+ operation,
2385
+ modelId,
2386
+ lastUserText: facts.lastUserText,
2387
+ systemText,
2388
+ tools,
2389
+ toolSchemas,
2390
+ toolChoice,
2391
+ hasDocument: messages.some((m) => m.hasDocument),
2392
+ hasImage: messages.some((m) => m.hasImage),
2393
+ hasCachePoint: messages.some((m) => m.hasCachePoint) || system.some((b) => isRecord4(b) && isRecord4(b.cachePoint)) || array(toolConfig?.tools).some((t) => isRecord4(t) && isRecord4(t.cachePoint)),
2394
+ hasGuardrail: isRecord4(body.guardrailConfig),
2395
+ toolResults: facts.toolResults,
2396
+ turnIndex: facts.turnIndex,
2397
+ structured,
2398
+ inputChars: messages.reduce((sum, m) => sum + m.chars, 0) + systemText.length + JSON.stringify(toolSchemas).length
2399
+ };
2400
+ };
2401
+ var anthropicMessage = (value, index) => {
2402
+ if (!isRecord4(value) || value.role !== "user" && value.role !== "assistant") {
2403
+ throw new ValidationProblem(`messages.${index}.role: Input should be 'user' or 'assistant'`);
2404
+ }
2405
+ const blocks = typeof value.content === "string" ? [{ type: "text", text: value.content }] : array(value.content);
2406
+ if (blocks.length === 0)
2407
+ throw new ValidationProblem(`messages.${index}: all messages must have non-empty content`);
2408
+ const out = {
2409
+ role: value.role,
2410
+ text: textOf(blocks.filter((b) => isRecord4(b) && b.type === "text")),
2411
+ toolUses: [],
2412
+ toolResultIds: [],
2413
+ hasContent: false,
2414
+ hasDocument: false,
2415
+ hasImage: false,
2416
+ hasCachePoint: false,
2417
+ chars: 0
2418
+ };
2419
+ for (const block of blocks) {
2420
+ if (!isRecord4(block)) continue;
2421
+ out.chars += JSON.stringify(block).length;
2422
+ if (isRecord4(block.cache_control)) out.hasCachePoint = true;
2423
+ switch (block.type) {
2424
+ case "text":
2425
+ out.hasContent = true;
2426
+ break;
2427
+ case "document":
2428
+ out.hasDocument = true;
2429
+ out.hasContent = true;
2430
+ break;
2431
+ case "image":
2432
+ out.hasImage = true;
2433
+ out.hasContent = true;
2434
+ break;
2435
+ case "tool_use":
2436
+ out.toolUses.push({ id: String(block.id ?? ""), name: String(block.name ?? "") });
2437
+ break;
2438
+ case "tool_result":
2439
+ out.toolResultIds.push(String(block.tool_use_id ?? ""));
2440
+ break;
2441
+ }
2442
+ }
2443
+ return out;
2444
+ };
2445
+ var analyzeAnthropic = (modelId, body) => {
2446
+ if (!isRecord4(body))
2447
+ throw new ValidationProblem(
2448
+ "Malformed input request, please reformat your input and try again."
2449
+ );
2450
+ if (typeof body.anthropic_version !== "string") {
2451
+ throw new ValidationProblem(
2452
+ "Malformed input request: #: required key [anthropic_version] not found, please reformat your input and try again."
2453
+ );
2454
+ }
2455
+ if (typeof body.max_tokens !== "number") {
2456
+ throw new ValidationProblem(
2457
+ "Malformed input request: #: required key [max_tokens] not found, please reformat your input and try again."
2458
+ );
2459
+ }
2460
+ if (body.temperature !== void 0 && body.top_p !== void 0 && ONE_SAMPLING_PARAM.test(modelId)) {
2461
+ throw new ValidationProblem(
2462
+ "`temperature` and `top_p` cannot both be specified for this model. Please use only one."
2463
+ );
2464
+ }
2465
+ const messages = array(body.messages).map(anthropicMessage);
2466
+ const toolSchemas = {};
2467
+ for (const tool of array(body.tools)) {
2468
+ if (isRecord4(tool) && typeof tool.name === "string")
2469
+ toolSchemas[tool.name] = tool.input_schema ?? {};
2470
+ }
2471
+ checkConversation(messages, modelId, true);
2472
+ const system = typeof body.system === "string" ? body.system : textOf(array(body.system));
2473
+ const choice = isRecord4(body.tool_choice) ? body.tool_choice : void 0;
2474
+ const toolChoice = choice?.type === "tool" ? `tool:${String(choice.name)}` : typeof choice?.type === "string" ? choice.type : void 0;
2475
+ const tools = Object.keys(toolSchemas);
2476
+ let structured;
2477
+ const format = isRecord4(body.output_config) && isRecord4(body.output_config.format) ? body.output_config.format : void 0;
2478
+ if (format?.type === "json_schema") structured = { form: "outputFormat", schema: format.schema };
2479
+ else if (toolChoice?.startsWith("tool:")) {
2480
+ const tool = toolChoice.slice(5);
2481
+ structured = { form: "tool", tool, schema: toolSchemas[tool] };
2482
+ }
2483
+ const facts = conversationFacts(messages);
2484
+ return {
2485
+ operation: "InvokeModel",
2486
+ modelId,
2487
+ lastUserText: facts.lastUserText,
2488
+ systemText: system,
2489
+ tools,
2490
+ toolSchemas,
2491
+ toolChoice,
2492
+ hasDocument: messages.some((m) => m.hasDocument),
2493
+ hasImage: messages.some((m) => m.hasImage),
2494
+ hasCachePoint: messages.some((m) => m.hasCachePoint),
2495
+ hasGuardrail: false,
2496
+ toolResults: facts.toolResults,
2497
+ turnIndex: facts.turnIndex,
2498
+ structured,
2499
+ inputChars: messages.reduce((sum, m) => sum + m.chars, 0) + system.length
2500
+ };
2501
+ };
2502
+ var analyzeHarness = (harnessArn, body) => {
2503
+ if (!isRecord4(body) || !Array.isArray(body.messages) || body.messages.length === 0) {
2504
+ throw new ValidationProblem(
2505
+ "1 validation error detected: Value null at 'messages' failed to satisfy constraint: Member must not be null"
2506
+ );
2507
+ }
2508
+ const messages = array(body.messages).map(converseMessage);
2509
+ const facts = conversationFacts(messages);
2510
+ const system = typeof body.systemPrompt === "string" ? body.systemPrompt : textOf(array(body.systemPrompt));
2511
+ return {
2512
+ operation: "InvokeHarness",
2513
+ modelId: harnessArn,
2514
+ lastUserText: facts.lastUserText,
2515
+ systemText: system,
2516
+ tools: array(body.tools).map((tool) => isRecord4(tool) && typeof tool.name === "string" ? tool.name : void 0).filter((name) => name !== void 0),
2517
+ toolSchemas: {},
2518
+ toolChoice: void 0,
2519
+ hasDocument: false,
2520
+ hasImage: false,
2521
+ hasCachePoint: false,
2522
+ hasGuardrail: false,
2523
+ toolResults: facts.toolResults,
2524
+ turnIndex: facts.turnIndex,
2525
+ structured: void 0,
2526
+ inputChars: messages.reduce((sum, m) => sum + m.chars, 0) + system.length
2527
+ };
2528
+ };
2529
+
2530
+ // src/generated/openapi.ts
2531
+ var document = JSON.parse(`{"openapi":"3.1.0","info":{"title":"Amazon Bedrock Runtime + AgentCore harness (Mockingbird subset)","description":"The Bedrock Runtime data plane our consumer calls: Converse, ConverseStream (event\\nstream), InvokeModel (Anthropic Messages bodies and Titan text embeddings), and\\nInvokeModelWithBidirectionalStream (Nova Sonic, HTTP/2 duplex), plus the AgentCore\\n\`InvokeHarness\` event stream. Hand-trimmed from the Smithy models behind\\n\`@aws-sdk/client-bedrock-runtime@3.1132.0\` and \`@aws-sdk/client-bedrock-agentcore@3.1074.0\`\\nto the fields our consumer sends and reads.\\n","version":"2023-09-30","x-mockingbird-upstream":{"note":"Shapes follow the AWS SDK v3 Smithy schemas (restJson1). Errors carry \`x-amzn-ErrorType: <Name>:http://internal.amazon.com/coral/com.amazon.bedrock/\` and a \`{\\"message\\"}\` body, as Bedrock sends them."}},"servers":[{"url":"https://bedrock-runtime.us-east-1.amazonaws.com"}],"security":[{"sigv4":[]}],"paths":{"/model/{modelId}/converse":{"parameters":[{"$ref":"#/components/parameters/ModelId"}],"post":{"operationId":"Converse","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConverseRequest"}}}},"responses":{"200":{"description":"The assistant message, stop reason, usage and (with trace enabled) the guardrail trace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConverseResponse"}}}},"400":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"408":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"},"500":{"$ref":"#/components/responses/Error"},"503":{"$ref":"#/components/responses/Error"}}}},"/model/{modelId}/converse-stream":{"parameters":[{"$ref":"#/components/parameters/ModelId"}],"post":{"operationId":"ConverseStream","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConverseRequest"}}}},"responses":{"200":{"description":"An \`application/vnd.amazon.eventstream\` body: messageStart, contentBlockStart (tool use), contentBlockDelta (text, toolUse.input, reasoningContent), contentBlockStop, messageStop, metadata; or an exception frame mid-stream.","content":{"application/vnd.amazon.eventstream":{"schema":{"type":"string","format":"binary"}}}},"400":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"408":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"},"500":{"$ref":"#/components/responses/Error"},"503":{"$ref":"#/components/responses/Error"}}}},"/model/{modelId}/invoke":{"parameters":[{"$ref":"#/components/parameters/ModelId"}],"post":{"operationId":"InvokeModel","description":"Titan text embeddings (\`amazon.titan-embed-text-*\`: \`{inputText, dimensions, normalize}\` \u2192 \`{embedding, inputTextTokenCount}\`) or an Anthropic Messages body for a Claude model (\`{anthropic_version, max_tokens, system, messages}\` \u2192 a Messages response).","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/TitanEmbedRequest"},{"$ref":"#/components/schemas/AnthropicRequest"}]}}}},"responses":{"200":{"description":"The model's native response body.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/TitanEmbedResponse"},{"$ref":"#/components/schemas/AnthropicResponse"}]}}}},"400":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"408":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"},"500":{"$ref":"#/components/responses/Error"},"503":{"$ref":"#/components/responses/Error"}}}},"/model/{modelId}/invoke-with-response-stream":{"parameters":[{"$ref":"#/components/parameters/ModelId"}],"post":{"operationId":"InvokeModelWithResponseStream","x-mockingbird":{"supported":false,"reason":"No consumer calls it; chat streaming goes through ConverseStream."},"responses":{"200":{"description":"Event stream of model chunks.","content":{"application/vnd.amazon.eventstream":{"schema":{"type":"string","format":"binary"}}}}}}},"/model/{modelId}/invoke-with-bidirectional-stream":{"parameters":[{"$ref":"#/components/parameters/ModelId"}],"post":{"operationId":"InvokeModelWithBidirectionalStream","description":"Nova Sonic. HTTP/2 duplex: the request body is an event stream of \`chunk\` events (\`{bytes: base64(JSON {event})}\`, SigV4-wrapped) that stays open for the session; the response streams \`chunk\` events back while it does.","x-mockingbird":{"supported":true,"parity":{"enabled":false,"reason":"A duplex HTTP/2 session; random request bodies cannot drive it."}},"requestBody":{"required":true,"content":{"application/vnd.amazon.eventstream":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"Event stream of \`chunk\` events (completionStart, contentStart, textOutput, audioOutput, toolUse, contentEnd, usageEvent, completionEnd).","content":{"application/vnd.amazon.eventstream":{"schema":{"type":"string","format":"binary"}}}},"400":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"},"500":{"$ref":"#/components/responses/Error"},"503":{"$ref":"#/components/responses/Error"}}}},"/harnesses/invoke":{"post":{"operationId":"InvokeHarness","description":"AgentCore data plane (\`bedrock-agentcore.<region>.amazonaws.com\`).","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"harnessArn","in":"query","required":true,"schema":{"type":"string","enum":["arn:aws:bedrock-agentcore:us-east-1:123456789012:harness/erx-prescreen"]}},{"name":"X-Amzn-Bedrock-AgentCore-Runtime-Session-Id","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-zA-Z0-9-]{33,100}$"}},{"name":"X-Amzn-Bedrock-AgentCore-Runtime-User-Id","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-zA-Z0-9_.@-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HarnessRequest"}}}},"responses":{"200":{"description":"Event stream (messageStart, contentBlockDelta text/toolResult, contentBlockStop, messageStop, metadata), or validationException / internalServerException / runtimeClientError frames.","content":{"application/vnd.amazon.eventstream":{"schema":{"type":"string","format":"binary"}}}},"400":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"},"500":{"$ref":"#/components/responses/Error"},"503":{"$ref":"#/components/responses/Error"}}}}},"components":{"securitySchemes":{"sigv4":{"type":"apiKey","in":"header","name":"Authorization","description":"AWS SigV4 (service \`bedrock\` / \`bedrock-agentcore\`). Accepted without verification; the access key id selects a namespace."}},"parameters":{"ModelId":{"name":"modelId","in":"path","required":true,"description":"A model id, inference-profile id (\`global.\` / \`us.\` prefixes) or a URL-encoded inference-profile / foundation-model ARN. Any value is accepted; the enum only steers generated requests.","schema":{"type":"string","enum":["global.anthropic.claude-sonnet-4-6","us.anthropic.claude-haiku-4-5-20251001-v1:0","us.anthropic.claude-sonnet-4-20250514-v1:0","amazon.titan-embed-text-v2:0","arn:aws:bedrock:us-east-1:123456789012:inference-profile/us.amazon.nova-pro-v1:0"]}}},"responses":{"Error":{"description":"A Bedrock error (type in \`x-amzn-ErrorType\`).","headers":{"x-amzn-ErrorType":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"schemas":{"ErrorBody":{"type":"object","required":["message"],"properties":{"message":{"type":"string","x-mockingbird-volatile":{"kind":"opaque"}}}},"TextBlock":{"type":"object","required":["text"],"properties":{"text":{"type":"string","minLength":1}}},"CachePointBlock":{"type":"object","required":["cachePoint"],"properties":{"cachePoint":{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["default"]},"ttl":{"type":"string","enum":["5m","1h"]}}}}},"ToolUseBlock":{"type":"object","required":["toolUse"],"properties":{"toolUse":{"type":"object","required":["toolUseId","name","input"],"properties":{"toolUseId":{"type":"string","minLength":1},"name":{"type":"string","minLength":1},"input":{}}}}},"ToolResultBlock":{"type":"object","required":["toolResult"],"properties":{"toolResult":{"type":"object","required":["toolUseId","content"],"properties":{"toolUseId":{"type":"string","minLength":1},"content":{"type":"array","minItems":1,"items":{"type":"object","properties":{"text":{"type":"string"},"json":{}}}},"status":{"type":"string","enum":["success","error"]}}}}},"DocumentBlock":{"type":"object","required":["document"],"properties":{"document":{"type":"object","required":["format","name","source"],"properties":{"format":{"type":"string","enum":["pdf","csv","doc","docx","xls","xlsx","html","txt","md"]},"name":{"type":"string","minLength":1},"source":{"type":"object","properties":{"bytes":{"type":"string","contentEncoding":"base64"}}}}}}},"ImageBlock":{"type":"object","required":["image"],"properties":{"image":{"type":"object","required":["format","source"],"properties":{"format":{"type":"string","enum":["png","jpeg","gif","webp"]},"source":{"type":"object","properties":{"bytes":{"type":"string","contentEncoding":"base64"}}}}}}},"GuardContentBlock":{"type":"object","required":["guardContent"],"properties":{"guardContent":{"type":"object"}}},"ContentBlock":{"anyOf":[{"$ref":"#/components/schemas/TextBlock"},{"$ref":"#/components/schemas/ToolUseBlock"},{"$ref":"#/components/schemas/ToolResultBlock"},{"$ref":"#/components/schemas/DocumentBlock"},{"$ref":"#/components/schemas/ImageBlock"},{"$ref":"#/components/schemas/CachePointBlock"},{"$ref":"#/components/schemas/GuardContentBlock"},{"type":"object","required":["reasoningContent"],"properties":{"reasoningContent":{"type":"object"}}},{"type":"object","required":["video"],"properties":{"video":{"type":"object"}}}]},"Message":{"type":"object","required":["role","content"],"properties":{"role":{"type":"string","enum":["user","assistant"]},"content":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/ContentBlock"}}}},"ToolSpec":{"type":"object","required":["toolSpec"],"properties":{"toolSpec":{"type":"object","required":["name","inputSchema"],"properties":{"name":{"type":"string","pattern":"^[a-zA-Z0-9_-]{1,64}$"},"description":{"type":"string"},"inputSchema":{"type":"object","required":["json"],"properties":{"json":{}}}}}}},"ConverseRequest":{"type":"object","required":["messages"],"properties":{"messages":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/Message"}},"system":{"type":"array","items":{"anyOf":[{"$ref":"#/components/schemas/TextBlock"},{"$ref":"#/components/schemas/CachePointBlock"},{"$ref":"#/components/schemas/GuardContentBlock"}]}},"inferenceConfig":{"type":"object","properties":{"maxTokens":{"type":"integer","minimum":1},"temperature":{"type":"number","minimum":0,"maximum":1},"topP":{"type":"number","minimum":0,"maximum":1},"stopSequences":{"type":"array","maxItems":4,"items":{"type":"string"}}}},"toolConfig":{"type":"object","required":["tools"],"properties":{"tools":{"type":"array","minItems":1,"items":{"anyOf":[{"$ref":"#/components/schemas/ToolSpec"},{"$ref":"#/components/schemas/CachePointBlock"}]}},"toolChoice":{"oneOf":[{"type":"object","required":["auto"],"properties":{"auto":{"type":"object"}}},{"type":"object","required":["any"],"properties":{"any":{"type":"object"}}},{"type":"object","required":["tool"],"properties":{"tool":{"type":"object","required":["name"],"properties":{"name":{"type":"string"}}}}}]}}},"guardrailConfig":{"type":"object","required":["guardrailIdentifier","guardrailVersion"],"properties":{"guardrailIdentifier":{"type":"string","minLength":1},"guardrailVersion":{"type":"string","minLength":1},"trace":{"type":"string","enum":["enabled","disabled","enabled_full"]},"streamProcessingMode":{"type":"string","enum":["sync","async"]}}},"additionalModelRequestFields":{"type":"object"},"outputConfig":{"type":"object","properties":{"textFormat":{"type":"object","required":["type","structure"],"properties":{"type":{"type":"string","enum":["json_schema"]},"structure":{"type":"object","required":["jsonSchema"],"properties":{"jsonSchema":{"type":"object","required":["schema"],"properties":{"schema":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"}}}}}}}}},"requestMetadata":{"type":"object","additionalProperties":{"type":"string"}},"performanceConfig":{"type":"object","properties":{"latency":{"type":"string","enum":["standard","optimized"]}}}}},"Usage":{"type":"object","required":["inputTokens","outputTokens","totalTokens"],"properties":{"inputTokens":{"type":"integer","x-mockingbird-volatile":{"kind":"opaque"}},"outputTokens":{"type":"integer","x-mockingbird-volatile":{"kind":"opaque"}},"totalTokens":{"type":"integer","x-mockingbird-volatile":{"kind":"opaque"}},"cacheReadInputTokens":{"type":"integer","x-mockingbird-volatile":{"kind":"opaque"}},"cacheWriteInputTokens":{"type":"integer","x-mockingbird-volatile":{"kind":"opaque"}}}},"ConverseResponse":{"type":"object","required":["output","stopReason","usage","metrics"],"properties":{"output":{"type":"object","required":["message"],"properties":{"message":{"type":"object","required":["role","content"],"properties":{"role":{"type":"string","enum":["assistant"]},"content":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string","x-mockingbird-volatile":{"kind":"opaque"}},"toolUse":{"type":"object","required":["toolUseId","name","input"],"properties":{"toolUseId":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"name":{"type":"string"},"input":{"x-mockingbird-volatile":{"kind":"opaque"}}}},"reasoningContent":{"type":"object"}}}}}}}},"stopReason":{"type":"string","enum":["end_turn","tool_use","max_tokens","stop_sequence","guardrail_intervened","content_filtered"]},"usage":{"$ref":"#/components/schemas/Usage"},"metrics":{"type":"object","required":["latencyMs"],"properties":{"latencyMs":{"type":"integer","x-mockingbird-volatile":{"kind":"opaque"}}}},"trace":{"type":"object","properties":{"guardrail":{"type":"object"}}}}},"TitanEmbedRequest":{"type":"object","required":["inputText"],"properties":{"inputText":{"type":"string","minLength":1,"maxLength":50000},"dimensions":{"type":"integer","enum":[256,512,1024]},"normalize":{"type":"boolean"},"embeddingTypes":{"type":"array","items":{"type":"string","enum":["float","binary"]}}}},"TitanEmbedResponse":{"type":"object","required":["embedding","inputTextTokenCount"],"properties":{"embedding":{"type":"array","items":{"type":"number","x-mockingbird-volatile":{"kind":"opaque"}}},"inputTextTokenCount":{"type":"integer","x-mockingbird-volatile":{"kind":"opaque"}},"embeddingsByType":{"type":"object"}}},"AnthropicRequest":{"type":"object","required":["anthropic_version","max_tokens","messages"],"properties":{"anthropic_version":{"type":"string","enum":["bedrock-2023-05-31"]},"max_tokens":{"type":"integer","minimum":1},"temperature":{"type":"number","minimum":0,"maximum":1},"system":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"object"}}]},"messages":{"type":"array","minItems":1,"items":{"type":"object","required":["role","content"],"properties":{"role":{"type":"string","enum":["user","assistant"]},"content":{"anyOf":[{"type":"string","minLength":1},{"type":"array","minItems":1,"items":{"type":"object","required":["type"]}}]}}}}}},"AnthropicResponse":{"type":"object","required":["id","type","role","model","content","stop_reason","usage"],"properties":{"id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"type":{"type":"string","enum":["message"]},"role":{"type":"string","enum":["assistant"]},"model":{"type":"string"},"content":{"type":"array","items":{"type":"object","required":["type"],"properties":{"type":{"type":"string"},"text":{"type":"string","x-mockingbird-volatile":{"kind":"opaque"}}}}},"stop_reason":{"type":"string"},"stop_sequence":{"type":["string","null"]},"usage":{"type":"object","required":["input_tokens","output_tokens"],"properties":{"input_tokens":{"type":"integer","x-mockingbird-volatile":{"kind":"opaque"}},"output_tokens":{"type":"integer","x-mockingbird-volatile":{"kind":"opaque"}}}}}},"HarnessRequest":{"type":"object","required":["messages"],"properties":{"messages":{"type":"array","minItems":1,"items":{"type":"object","required":["role","content"],"properties":{"role":{"type":"string","enum":["user","assistant"]},"content":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/ContentBlock"}}}}},"systemPrompt":{"type":"array","items":{"$ref":"#/components/schemas/TextBlock"}},"maxIterations":{"type":"integer","minimum":1,"maximum":50}}}}}}`);
2532
+ var operationIds = ["Converse", "ConverseStream", "InvokeModel", "InvokeModelWithResponseStream", "InvokeModelWithBidirectionalStream", "InvokeHarness"];
2533
+ var supportedOperationIds = ["Converse", "ConverseStream", "InvokeModel", "InvokeModelWithBidirectionalStream", "InvokeHarness"];
2534
+
2535
+ // src/schema-sample.ts
2536
+ var isSchema = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2537
+ var FORMATS = {
2538
+ "date-time": "2026-01-01T00:00:00.000Z",
2539
+ date: "2026-01-01",
2540
+ time: "00:00:00",
2541
+ email: "user@example.com",
2542
+ uri: "https://example.com/",
2543
+ url: "https://example.com/",
2544
+ uuid: "00000000-0000-4000-8000-000000000000",
2545
+ ipv4: "127.0.0.1"
2546
+ };
2547
+ var resolveRef2 = (root, ref) => {
2548
+ if (!ref.startsWith("#")) return void 0;
2549
+ let node = root;
2550
+ for (const part of ref.slice(1).split("/").filter(Boolean)) {
2551
+ if (!isSchema(node)) return void 0;
2552
+ node = node[decodeURIComponent(part.replace(/~1/g, "/").replace(/~0/g, "~"))];
2553
+ }
2554
+ return isSchema(node) ? node : void 0;
2555
+ };
2556
+ var typesOf = (schema) => {
2557
+ if (Array.isArray(schema.type))
2558
+ return schema.type.filter((t) => typeof t === "string");
2559
+ if (typeof schema.type === "string") return [schema.type];
2560
+ if (isSchema(schema.properties) || Array.isArray(schema.required)) return ["object"];
2561
+ if (schema.items !== void 0) return ["array"];
2562
+ return [];
2563
+ };
2564
+ var sampleNumber = (schema, integer) => {
2565
+ const min = typeof schema.minimum === "number" ? schema.minimum : void 0;
2566
+ const exclusiveMin = typeof schema.exclusiveMinimum === "number" ? schema.exclusiveMinimum : schema.exclusiveMinimum === true && min !== void 0 ? min : void 0;
2567
+ const max = typeof schema.maximum === "number" ? schema.maximum : void 0;
2568
+ const exclusiveMax = typeof schema.exclusiveMaximum === "number" ? schema.exclusiveMaximum : void 0;
2569
+ let value = 0;
2570
+ if (exclusiveMin !== void 0)
2571
+ value = integer ? Math.floor(exclusiveMin) + 1 : exclusiveMin + (max !== void 0 ? Math.min(1, (max - exclusiveMin) / 2) : 1);
2572
+ else if (min !== void 0) value = integer ? Math.ceil(min) : min;
2573
+ else if (max !== void 0 && max < 0) value = integer ? Math.floor(max) : max;
2574
+ else if (exclusiveMax !== void 0 && exclusiveMax <= 0)
2575
+ value = integer ? Math.ceil(exclusiveMax) - 1 : exclusiveMax - 1;
2576
+ if (typeof schema.multipleOf === "number" && schema.multipleOf > 0) {
2577
+ value = Math.ceil(value / schema.multipleOf) * schema.multipleOf;
2578
+ }
2579
+ return value;
2580
+ };
2581
+ var sampleString = (schema) => {
2582
+ const format = typeof schema.format === "string" ? FORMATS[schema.format] : void 0;
2583
+ let value = format ?? "";
2584
+ const min = typeof schema.minLength === "number" ? schema.minLength : 0;
2585
+ if (!format && typeof schema.pattern === "string") {
2586
+ const pattern = new RegExp(schema.pattern);
2587
+ value = ["x", "a", "A", "0", "a1", "x".repeat(Math.max(1, min))].find((c) => pattern.test(c)) ?? "x";
2588
+ }
2589
+ while (value.length < min) value += "x";
2590
+ if (typeof schema.maxLength === "number" && value.length > schema.maxLength) {
2591
+ value = value.slice(0, schema.maxLength);
2592
+ }
2593
+ return value;
2594
+ };
2595
+ var sampleSchema = (schema, root = schema, depth = 0) => {
2596
+ if (!isSchema(schema) || depth > 32) return null;
2597
+ const rootSchema = isSchema(root) ? root : {};
2598
+ if (typeof schema.$ref === "string") {
2599
+ return sampleSchema(resolveRef2(rootSchema, schema.$ref), rootSchema, depth + 1);
2600
+ }
2601
+ if ("const" in schema) return schema.const;
2602
+ if (Array.isArray(schema.enum) && schema.enum.length > 0) return schema.enum[0];
2603
+ if (Array.isArray(schema.allOf) && schema.allOf.length > 0) {
2604
+ const merged = { ...schema };
2605
+ delete merged.allOf;
2606
+ for (const part of schema.allOf) {
2607
+ const resolved = isSchema(part) && typeof part.$ref === "string" ? resolveRef2(rootSchema, part.$ref) : part;
2608
+ if (!isSchema(resolved)) continue;
2609
+ for (const [key, value] of Object.entries(resolved)) {
2610
+ if (key === "properties" && isSchema(merged.properties) && isSchema(value)) {
2611
+ merged.properties = { ...merged.properties, ...value };
2612
+ } else if (key === "required" && Array.isArray(merged.required) && Array.isArray(value)) {
2613
+ merged.required = [.../* @__PURE__ */ new Set([...merged.required, ...value])];
2614
+ } else merged[key] = value;
2615
+ }
2616
+ }
2617
+ return sampleSchema(merged, rootSchema, depth + 1);
2618
+ }
2619
+ const union = Array.isArray(schema.anyOf) ? schema.anyOf : Array.isArray(schema.oneOf) ? schema.oneOf : void 0;
2620
+ if (union && union.length > 0) {
2621
+ const concrete = union.find((branch) => !(isSchema(branch) && branch.type === "null"));
2622
+ return sampleSchema(concrete ?? union[0], rootSchema, depth + 1);
2623
+ }
2624
+ const types = typesOf(schema);
2625
+ const type = types.find((t) => t !== "null") ?? types[0];
2626
+ switch (type) {
2627
+ case "object": {
2628
+ const properties = isSchema(schema.properties) ? schema.properties : {};
2629
+ const required = Array.isArray(schema.required) ? schema.required.filter((key) => typeof key === "string") : [];
2630
+ const out = {};
2631
+ for (const key of required)
2632
+ out[key] = sampleSchema(properties[key] ?? {}, rootSchema, depth + 1);
2633
+ const minProperties = typeof schema.minProperties === "number" ? schema.minProperties : 0;
2634
+ for (const key of Object.keys(properties)) {
2635
+ if (Object.keys(out).length >= minProperties) break;
2636
+ if (!(key in out)) out[key] = sampleSchema(properties[key], rootSchema, depth + 1);
2637
+ }
2638
+ return out;
2639
+ }
2640
+ case "array": {
2641
+ const min = typeof schema.minItems === "number" ? schema.minItems : 0;
2642
+ const items = Array.isArray(schema.prefixItems) ? schema.prefixItems : void 0;
2643
+ const out = [];
2644
+ for (let i = 0; i < min; i++) {
2645
+ const itemSchema = items?.[i] ?? (Array.isArray(schema.items) ? schema.items[i] : schema.items);
2646
+ let value = sampleSchema(itemSchema ?? {}, rootSchema, depth + 1);
2647
+ if (schema.uniqueItems === true && isSchema(itemSchema)) {
2648
+ const choices = Array.isArray(itemSchema.enum) ? itemSchema.enum : void 0;
2649
+ if (choices && choices.length > i) value = choices[i];
2650
+ else if (typeof value === "string") value = `${value}${i}`;
2651
+ else if (typeof value === "number") value = value + i;
2652
+ }
2653
+ out.push(value);
2654
+ }
2655
+ return out;
2656
+ }
2657
+ case "string":
2658
+ return sampleString(schema);
2659
+ case "integer":
2660
+ return sampleNumber(schema, true);
2661
+ case "number":
2662
+ return sampleNumber(schema, false);
2663
+ case "boolean":
2664
+ return false;
2665
+ case "null":
2666
+ return null;
2667
+ default:
2668
+ return {};
2669
+ }
2670
+ };
2671
+
2672
+ // src/scripts.ts
2673
+ var MODEL_OPERATIONS = [
2674
+ "Converse",
2675
+ "ConverseStream",
2676
+ "InvokeModel",
2677
+ "InvokeModelWithBidirectionalStream",
2678
+ "InvokeHarness"
2679
+ ];
2680
+ var STOP_REASONS = [
2681
+ "end_turn",
2682
+ "tool_use",
2683
+ "max_tokens",
2684
+ "stop_sequence",
2685
+ "guardrail_intervened",
2686
+ "content_filtered"
2687
+ ];
2688
+ var TURN_FAULTS = [
2689
+ "throttling",
2690
+ "validation",
2691
+ "access_denied",
2692
+ "model_timeout",
2693
+ "service_unavailable",
2694
+ "internal_server",
2695
+ "mid_stream_exception",
2696
+ "max_tokens",
2697
+ "truncated_frame",
2698
+ "latency"
2699
+ ];
2700
+ var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2701
+ var escapeGlob = (value) => new RegExp(
2702
+ `^${value.split("*").map((part) => part.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*")}$`,
2703
+ "i"
2704
+ );
2705
+ var globMatch = (pattern, modelId) => escapeGlob(pattern).test(modelId);
2706
+ var textMatches = (rule, value) => {
2707
+ if (typeof rule === "string") return value.toLowerCase().includes(rule.toLowerCase());
2708
+ if (rule.contains !== void 0 && !value.toLowerCase().includes(rule.contains.toLowerCase())) {
2709
+ return false;
2710
+ }
2711
+ if (rule.regex !== void 0 && !new RegExp(rule.regex, rule.flags ?? "i").test(value))
2712
+ return false;
2713
+ return true;
2714
+ };
2715
+ var matches2 = (match, call, context) => {
2716
+ if (!match) return true;
2717
+ if (match.modelId !== void 0 && !globMatch(match.modelId, call.modelId)) return false;
2718
+ if (match.operation !== void 0) {
2719
+ const ops = Array.isArray(match.operation) ? match.operation : [match.operation];
2720
+ if (!ops.includes(call.operation)) return false;
2721
+ }
2722
+ if (match.lastUserText !== void 0 && !textMatches(match.lastUserText, call.lastUserText)) {
2723
+ return false;
2724
+ }
2725
+ if (match.systemHash !== void 0 && match.systemHash.toLowerCase() !== context.systemHash) {
2726
+ return false;
2727
+ }
2728
+ if (match.toolsInclude?.some((name) => !call.tools.includes(name))) return false;
2729
+ if (match.toolChoice !== void 0) {
2730
+ const want = ["auto", "any", "none"].includes(match.toolChoice) ? match.toolChoice : `tool:${match.toolChoice}`;
2731
+ if ((call.toolChoice ?? "auto") !== want) return false;
2732
+ }
2733
+ if (match.hasDocument !== void 0 && match.hasDocument !== call.hasDocument) return false;
2734
+ if (match.hasImage !== void 0 && match.hasImage !== call.hasImage) return false;
2735
+ if (match.callIndex !== void 0 && match.callIndex !== context.callIndex) return false;
2736
+ return true;
2737
+ };
2738
+ var selectTurn = (script, call) => {
2739
+ const turn = script.turns[call.turnIndex];
2740
+ if (!turn) return void 0;
2741
+ if (turn.expectToolResult && !call.toolResults.includes(turn.expectToolResult.name)) {
2742
+ return void 0;
2743
+ }
2744
+ return turn;
2745
+ };
2746
+ var fail = (path, message) => `${path}: ${message}`;
2747
+ var checkTurn = (turn, path) => {
2748
+ if (!isRecord5(turn)) return fail(path, "a turn is an object");
2749
+ const known = /* @__PURE__ */ new Set([
2750
+ "text",
2751
+ "chunkSize",
2752
+ "delayMsPerChunk",
2753
+ "reasoning",
2754
+ "toolUse",
2755
+ "json",
2756
+ "stopReason",
2757
+ "guardrail",
2758
+ "usage",
2759
+ "expectToolResult",
2760
+ "fault",
2761
+ "userTranscript",
2762
+ "toolResult"
2763
+ ]);
2764
+ for (const key of Object.keys(turn)) {
2765
+ if (!known.has(key)) return fail(`${path}.${key}`, "unknown turn field");
2766
+ }
2767
+ if (turn.text !== void 0 && typeof turn.text !== "string")
2768
+ return fail(`${path}.text`, "string");
2769
+ for (const key of ["chunkSize", "delayMsPerChunk"]) {
2770
+ const value = turn[key];
2771
+ if (value !== void 0 && (typeof value !== "number" || value < (key === "chunkSize" ? 1 : 0))) {
2772
+ return fail(`${path}.${key}`, key === "chunkSize" ? "a positive number" : "ms \u2265 0");
2773
+ }
2774
+ }
2775
+ if (turn.toolUse !== void 0) {
2776
+ const uses = Array.isArray(turn.toolUse) ? turn.toolUse : [turn.toolUse];
2777
+ for (const [i, use] of uses.entries()) {
2778
+ if (!isRecord5(use) || typeof use.name !== "string" || use.name === "") {
2779
+ return fail(`${path}.toolUse[${i}]`, "needs a name");
2780
+ }
2781
+ }
2782
+ }
2783
+ if (turn.stopReason !== void 0 && !STOP_REASONS.includes(turn.stopReason)) {
2784
+ return fail(`${path}.stopReason`, `one of ${STOP_REASONS.join(", ")}`);
2785
+ }
2786
+ if (turn.fault !== void 0) {
2787
+ const type = isRecord5(turn.fault) ? turn.fault.type : turn.fault;
2788
+ if (!TURN_FAULTS.includes(type)) {
2789
+ return fail(`${path}.fault`, `one of ${TURN_FAULTS.join(", ")}`);
2790
+ }
2791
+ }
2792
+ if (turn.toolResult !== void 0 && !Array.isArray(turn.toolResult)) {
2793
+ return fail(`${path}.toolResult`, "an array of {text} | {json}");
2794
+ }
2795
+ if (turn.expectToolResult !== void 0 && !(isRecord5(turn.expectToolResult) && typeof turn.expectToolResult.name === "string")) {
2796
+ return fail(`${path}.expectToolResult`, "{name}");
2797
+ }
2798
+ return void 0;
2799
+ };
2800
+ var parseScript = (value, index) => {
2801
+ const path = `scripts[${index}]`;
2802
+ if (!isRecord5(value)) return fail(path, "a script is an object");
2803
+ if (typeof value.id !== "string" || value.id === "")
2804
+ return fail(`${path}.id`, "a non-empty string");
2805
+ if (!Array.isArray(value.turns) || value.turns.length === 0) {
2806
+ return fail(`${path}.turns`, "a non-empty array");
2807
+ }
2808
+ for (const [i, turn] of value.turns.entries()) {
2809
+ const problem = checkTurn(turn, `${path}.turns[${i}]`);
2810
+ if (problem) return problem;
2811
+ }
2812
+ if (value.match !== void 0) {
2813
+ if (!isRecord5(value.match)) return fail(`${path}.match`, "an object");
2814
+ const match = value.match;
2815
+ if (match.operation !== void 0) {
2816
+ const ops = Array.isArray(match.operation) ? match.operation : [match.operation];
2817
+ for (const op of ops) {
2818
+ if (!MODEL_OPERATIONS.includes(op)) {
2819
+ return fail(`${path}.match.operation`, `one of ${MODEL_OPERATIONS.join(", ")}`);
2820
+ }
2821
+ }
2822
+ }
2823
+ if (isRecord5(match.lastUserText) && typeof match.lastUserText.regex === "string") {
2824
+ try {
2825
+ new RegExp(match.lastUserText.regex);
2826
+ } catch {
2827
+ return fail(`${path}.match.lastUserText.regex`, "not a valid regular expression");
2828
+ }
2829
+ }
2830
+ if (match.toolsInclude !== void 0 && !Array.isArray(match.toolsInclude)) {
2831
+ return fail(`${path}.match.toolsInclude`, "string[]");
2832
+ }
2833
+ }
2834
+ if (value.times !== void 0 && (typeof value.times !== "number" || value.times < 1)) {
2835
+ return fail(`${path}.times`, "a positive count");
2836
+ }
2837
+ return value;
2838
+ };
2839
+ var turnFault = (turn) => {
2840
+ if (!turn?.fault) return void 0;
2841
+ return typeof turn.fault === "string" ? { type: turn.fault } : turn.fault;
2842
+ };
2843
+
2844
+ // src/plan.ts
2845
+ var GUARDRAIL_BLOCKED_TEXT = "Sorry, the model cannot answer this question.";
2846
+ var DEFAULT_CHAT_TEXT = "OK.";
2847
+ var DEFAULT_CLASSIFIER = { category: "general", confidence: 0.9 };
2848
+ var DEFAULT_SOAP_NOTE = {
2849
+ sections: [
2850
+ {
2851
+ title: "Subjective",
2852
+ content: "Patient reports no new concerns. [UNCERTAIN] Mock transcript."
2853
+ },
2854
+ { title: "Objective", content: "No examination findings discussed." },
2855
+ {
2856
+ title: "Assessment",
2857
+ content: "Stable. [UNCERTAIN] Generated by the Mockingbird Bedrock mock."
2858
+ },
2859
+ { title: "Plan", content: "Continue current plan; follow up as scheduled." }
2860
+ ],
2861
+ summary: "Routine follow-up visit with no new concerns (mock note)."
2862
+ };
2863
+ var tokens = (chars) => Math.max(1, Math.ceil(chars / 4));
2864
+ var defaultTrace = (guardrailId) => ({
2865
+ guardrail: {
2866
+ actionReason: "Guardrail blocked.",
2867
+ inputAssessment: {
2868
+ [guardrailId ?? "mock-guardrail"]: {
2869
+ topicPolicy: {
2870
+ topics: [{ name: "Medical Advice", type: "DENY", action: "BLOCKED", detected: true }]
2871
+ },
2872
+ invocationMetrics: {
2873
+ guardrailProcessingLatency: 120,
2874
+ usage: {
2875
+ topicPolicyUnits: 1,
2876
+ contentPolicyUnits: 0,
2877
+ wordPolicyUnits: 0,
2878
+ sensitiveInformationPolicyUnits: 0,
2879
+ sensitiveInformationPolicyFreeUnits: 0,
2880
+ contextualGroundingPolicyUnits: 0
2881
+ },
2882
+ guardrailCoverage: { textCharacters: { guarded: 1, total: 1 } }
2883
+ }
2884
+ }
2885
+ }
2886
+ }
2887
+ });
2888
+ var withUsage = (call, blocks, scripted) => {
2889
+ const outputChars = blocks.reduce(
2890
+ (sum, block) => sum + (block.kind === "toolUse" ? JSON.stringify(block.input ?? {}).length : block.kind === "toolResult" ? JSON.stringify(block.content).length : block.text.length),
2891
+ 0
2892
+ );
2893
+ const inputTokens = scripted?.inputTokens ?? tokens(call.inputChars);
2894
+ const outputTokens = scripted?.outputTokens ?? tokens(outputChars);
2895
+ return {
2896
+ inputTokens,
2897
+ outputTokens,
2898
+ totalTokens: scripted?.totalTokens ?? inputTokens + outputTokens,
2899
+ ...scripted?.cacheReadInputTokens !== void 0 || call.hasCachePoint ? { cacheReadInputTokens: scripted?.cacheReadInputTokens ?? 0 } : {},
2900
+ ...scripted?.cacheWriteInputTokens !== void 0 || call.hasCachePoint ? { cacheWriteInputTokens: scripted?.cacheWriteInputTokens ?? 0 } : {}
2901
+ };
2902
+ };
2903
+ var renderJson = (call, value, context) => {
2904
+ const structured = call.structured;
2905
+ if (structured?.form === "tool") {
2906
+ return {
2907
+ blocks: [
2908
+ {
2909
+ kind: "toolUse",
2910
+ toolUseId: context.nextToolUseId(),
2911
+ name: structured.tool,
2912
+ input: value
2913
+ }
2914
+ ],
2915
+ stopReason: "tool_use"
2916
+ };
2917
+ }
2918
+ return { blocks: [{ kind: "text", text: JSON.stringify(value) }], stopReason: "end_turn" };
2919
+ };
2920
+ var capOutput = (blocks, stopReason, maxTokens, force) => {
2921
+ const budget = force ? void 0 : maxTokens !== void 0 ? maxTokens * 4 : void 0;
2922
+ const texts = blocks.filter((b) => b.kind === "text");
2923
+ const length = texts.reduce((sum, b) => sum + b.text.length, 0);
2924
+ if (!force && (budget === void 0 || length <= budget)) return { blocks, stopReason };
2925
+ let remaining = force ? Math.max(1, Math.floor(length / 2)) : budget;
2926
+ const out = [];
2927
+ for (const block of blocks) {
2928
+ if (block.kind === "toolUse" || block.kind === "toolResult") continue;
2929
+ if (block.kind === "reasoning") {
2930
+ out.push(block);
2931
+ continue;
2932
+ }
2933
+ if (remaining <= 0) break;
2934
+ out.push({ kind: "text", text: block.text.slice(0, remaining) });
2935
+ remaining -= block.text.length;
2936
+ }
2937
+ return { blocks: out, stopReason: "max_tokens" };
2938
+ };
2939
+ var planTurn = (scriptId, turn, call, context) => {
2940
+ let blocks = [];
2941
+ let stopReason;
2942
+ let trace;
2943
+ if (turn.reasoning) blocks.push({ kind: "reasoning", text: turn.reasoning });
2944
+ if (turn.guardrail) {
2945
+ const detail = typeof turn.guardrail === "object" ? turn.guardrail : {};
2946
+ blocks.push({ kind: "text", text: detail.text ?? GUARDRAIL_BLOCKED_TEXT });
2947
+ stopReason = "guardrail_intervened";
2948
+ if (context.traceEnabled || detail.trace !== void 0) {
2949
+ trace = detail.trace ?? defaultTrace(context.guardrailId);
2950
+ }
2951
+ } else {
2952
+ if (turn.text !== void 0) blocks.push({ kind: "text", text: turn.text });
2953
+ if (turn.json !== void 0) {
2954
+ const rendered = renderJson(call, turn.json, context);
2955
+ blocks.push(...rendered.blocks);
2956
+ stopReason = rendered.stopReason;
2957
+ }
2958
+ if (turn.toolResult !== void 0) {
2959
+ blocks.push({
2960
+ kind: "toolResult",
2961
+ toolUseId: context.nextToolUseId(),
2962
+ content: turn.toolResult
2963
+ });
2964
+ }
2965
+ const uses = turn.toolUse === void 0 ? [] : Array.isArray(turn.toolUse) ? turn.toolUse : [turn.toolUse];
2966
+ for (const use of uses) {
2967
+ blocks.push({
2968
+ kind: "toolUse",
2969
+ toolUseId: use.toolUseId ?? context.nextToolUseId(),
2970
+ name: use.name,
2971
+ input: use.input ?? {}
2972
+ });
2973
+ stopReason = "tool_use";
2974
+ }
2975
+ }
2976
+ const fault = turnFault(turn);
2977
+ const capped = capOutput(
2978
+ blocks,
2979
+ turn.stopReason ?? stopReason ?? "end_turn",
2980
+ context.maxTokens,
2981
+ fault?.type === "max_tokens"
2982
+ );
2983
+ blocks = capped.blocks;
2984
+ return {
2985
+ scriptId,
2986
+ blocks,
2987
+ stopReason: turn.stopReason ?? capped.stopReason,
2988
+ usage: withUsage(call, blocks, turn.usage),
2989
+ ...trace ? { trace } : {},
2990
+ ...fault && fault.type !== "max_tokens" ? { fault } : {},
2991
+ chunkSize: turn.chunkSize ?? context.chunkSize,
2992
+ delayMsPerChunk: turn.delayMsPerChunk ?? context.delayMsPerChunk,
2993
+ ...turn.userTranscript !== void 0 ? { userTranscript: turn.userTranscript } : {}
2994
+ };
2995
+ };
2996
+ var isClassifier = (call) => call.systemText.includes('"category"') && call.systemText.includes('"confidence"');
2997
+ var planDefault = (call, context, defaultText) => {
2998
+ let blocks;
2999
+ let stopReason = "end_turn";
3000
+ let fallback = "chat";
3001
+ if (call.structured) {
3002
+ const value = sampleSchema(call.structured.schema ?? {});
3003
+ const rendered = renderJson(call, value, context);
3004
+ blocks = rendered.blocks;
3005
+ stopReason = rendered.stopReason;
3006
+ fallback = "structured";
3007
+ } else if (call.operation === "InvokeModel") {
3008
+ blocks = [{ kind: "text", text: JSON.stringify(DEFAULT_SOAP_NOTE) }];
3009
+ fallback = "scribe";
3010
+ } else if (isClassifier(call)) {
3011
+ blocks = [{ kind: "text", text: JSON.stringify(DEFAULT_CLASSIFIER) }];
3012
+ fallback = "classifier";
3013
+ } else {
3014
+ blocks = [{ kind: "text", text: defaultText }];
3015
+ }
3016
+ const capped = capOutput(blocks, stopReason, context.maxTokens, false);
3017
+ return {
3018
+ scriptId: void 0,
3019
+ fallback,
3020
+ blocks: capped.blocks,
3021
+ stopReason: capped.stopReason,
3022
+ usage: withUsage(call, capped.blocks, void 0),
3023
+ chunkSize: context.chunkSize,
3024
+ delayMsPerChunk: context.delayMsPerChunk
3025
+ };
3026
+ };
3027
+ var truncatePlan = (plan) => {
3028
+ const capped = capOutput(plan.blocks, plan.stopReason, void 0, true);
3029
+ return { ...plan, blocks: capped.blocks, stopReason: capped.stopReason };
3030
+ };
3031
+ var chunk = (text2, size) => {
3032
+ if (text2.length === 0) return [""];
3033
+ const out = [];
3034
+ for (let i = 0; i < text2.length; i += Math.max(1, size))
3035
+ out.push(text2.slice(i, i + Math.max(1, size)));
3036
+ return out;
3037
+ };
3038
+
3039
+ // src/eventstream.ts
3040
+ var EventStreamError = class extends Error {
3041
+ constructor(message) {
3042
+ super(message);
3043
+ this.name = "EventStreamError";
3044
+ }
3045
+ };
3046
+ var PRELUDE = 12;
3047
+ var TRAILER = 4;
3048
+ var utf82 = new TextEncoder();
3049
+ var text = new TextDecoder();
3050
+ var CRC_TABLE = (() => {
3051
+ const table = new Uint32Array(256);
3052
+ for (let n = 0; n < 256; n++) {
3053
+ let c = n;
3054
+ for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
3055
+ table[n] = c >>> 0;
3056
+ }
3057
+ return table;
3058
+ })();
3059
+ var crc32 = (bytes) => {
3060
+ let crc = 4294967295;
3061
+ for (let i = 0; i < bytes.length; i++) {
3062
+ crc = CRC_TABLE[(crc ^ bytes[i]) & 255] ^ crc >>> 8;
3063
+ }
3064
+ return (crc ^ 4294967295) >>> 0;
3065
+ };
3066
+ var toBytes = (body) => body === void 0 ? new Uint8Array(0) : typeof body === "string" ? utf82.encode(body) : body;
3067
+ var encodeHeaders = (headers) => {
3068
+ const parts = [];
3069
+ for (const [name, raw] of Object.entries(headers)) {
3070
+ const header = typeof raw === "string" ? { type: "string", value: raw } : raw;
3071
+ const nameBytes = utf82.encode(name);
3072
+ if (nameBytes.length > 255) throw new EventStreamError(`header name too long: ${name}`);
3073
+ let value;
3074
+ switch (header.type) {
3075
+ case "boolean":
3076
+ value = Uint8Array.of(header.value ? 0 : 1);
3077
+ break;
3078
+ case "byte":
3079
+ value = Uint8Array.of(2, header.value & 255);
3080
+ break;
3081
+ case "short": {
3082
+ value = new Uint8Array(3);
3083
+ value[0] = 3;
3084
+ new DataView(value.buffer).setInt16(1, header.value, false);
3085
+ break;
3086
+ }
3087
+ case "integer": {
3088
+ value = new Uint8Array(5);
3089
+ value[0] = 4;
3090
+ new DataView(value.buffer).setInt32(1, header.value, false);
3091
+ break;
3092
+ }
3093
+ case "long": {
3094
+ value = new Uint8Array(9);
3095
+ value[0] = 5;
3096
+ new DataView(value.buffer).setBigInt64(1, header.value, false);
3097
+ break;
3098
+ }
3099
+ case "binary":
3100
+ case "string": {
3101
+ const bytes = header.type === "binary" ? header.value : utf82.encode(header.value);
3102
+ if (bytes.length > 65535) throw new EventStreamError(`header ${name} value too long`);
3103
+ value = new Uint8Array(3 + bytes.length);
3104
+ value[0] = header.type === "binary" ? 6 : 7;
3105
+ new DataView(value.buffer).setUint16(1, bytes.length, false);
3106
+ value.set(bytes, 3);
3107
+ break;
3108
+ }
3109
+ case "timestamp": {
3110
+ value = new Uint8Array(9);
3111
+ value[0] = 8;
3112
+ new DataView(value.buffer).setBigInt64(1, BigInt(header.value.getTime()), false);
3113
+ break;
3114
+ }
3115
+ case "uuid": {
3116
+ const hex = header.value.replace(/-/g, "");
3117
+ if (!/^[0-9a-f]{32}$/i.test(hex)) throw new EventStreamError(`bad uuid header ${name}`);
3118
+ value = new Uint8Array(17);
3119
+ value[0] = 9;
3120
+ for (let i = 0; i < 16; i++) value[i + 1] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
3121
+ break;
3122
+ }
3123
+ }
3124
+ const entry = new Uint8Array(1 + nameBytes.length + value.length);
3125
+ entry[0] = nameBytes.length;
3126
+ entry.set(nameBytes, 1);
3127
+ entry.set(value, 1 + nameBytes.length);
3128
+ parts.push(entry);
3129
+ }
3130
+ return concat(parts);
3131
+ };
3132
+ var concat = (parts) => {
3133
+ const out = new Uint8Array(parts.reduce((sum, part) => sum + part.length, 0));
3134
+ let offset = 0;
3135
+ for (const part of parts) {
3136
+ out.set(part, offset);
3137
+ offset += part.length;
3138
+ }
3139
+ return out;
3140
+ };
3141
+ var encodeMessage = (message) => {
3142
+ const headers = encodeHeaders(message.headers);
3143
+ const body = toBytes(message.body);
3144
+ const total = PRELUDE + headers.length + body.length + TRAILER;
3145
+ const frame = new Uint8Array(total);
3146
+ const view = new DataView(frame.buffer);
3147
+ view.setUint32(0, total, false);
3148
+ view.setUint32(4, headers.length, false);
3149
+ view.setUint32(8, crc32(frame.subarray(0, 8)), false);
3150
+ frame.set(headers, PRELUDE);
3151
+ frame.set(body, PRELUDE + headers.length);
3152
+ view.setUint32(total - TRAILER, crc32(frame.subarray(0, total - TRAILER)), false);
3153
+ return frame;
3154
+ };
3155
+ var decodeHeaders = (bytes) => {
3156
+ const headers = {};
3157
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
3158
+ let at = 0;
3159
+ while (at < bytes.length) {
3160
+ const nameLength = bytes[at];
3161
+ const name = text.decode(bytes.subarray(at + 1, at + 1 + nameLength));
3162
+ at += 1 + nameLength;
3163
+ const type = bytes[at];
3164
+ at += 1;
3165
+ switch (type) {
3166
+ case 0:
3167
+ case 1:
3168
+ headers[name] = { type: "boolean", value: type === 0 };
3169
+ break;
3170
+ case 2:
3171
+ headers[name] = { type: "byte", value: view.getInt8(at) };
3172
+ at += 1;
3173
+ break;
3174
+ case 3:
3175
+ headers[name] = { type: "short", value: view.getInt16(at, false) };
3176
+ at += 2;
3177
+ break;
3178
+ case 4:
3179
+ headers[name] = { type: "integer", value: view.getInt32(at, false) };
3180
+ at += 4;
3181
+ break;
3182
+ case 5:
3183
+ headers[name] = { type: "long", value: view.getBigInt64(at, false) };
3184
+ at += 8;
3185
+ break;
3186
+ case 6:
3187
+ case 7: {
3188
+ const length = view.getUint16(at, false);
3189
+ const value = bytes.slice(at + 2, at + 2 + length);
3190
+ headers[name] = type === 6 ? { type: "binary", value } : { type: "string", value: text.decode(value) };
3191
+ at += 2 + length;
3192
+ break;
3193
+ }
3194
+ case 8:
3195
+ headers[name] = { type: "timestamp", value: new Date(Number(view.getBigInt64(at, false))) };
3196
+ at += 8;
3197
+ break;
3198
+ case 9: {
3199
+ const hex = [...bytes.subarray(at, at + 16)].map((b) => b.toString(16).padStart(2, "0")).join("");
3200
+ headers[name] = {
3201
+ type: "uuid",
3202
+ value: `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`
3203
+ };
3204
+ at += 16;
3205
+ break;
3206
+ }
3207
+ default:
3208
+ throw new EventStreamError(`unknown header type ${type} for ${name}`);
3209
+ }
3210
+ }
3211
+ return headers;
3212
+ };
3213
+ var decodeMessage = (frame) => {
3214
+ if (frame.length < PRELUDE + TRAILER) throw new EventStreamError("frame shorter than a prelude");
3215
+ const view = new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
3216
+ const total = view.getUint32(0, false);
3217
+ const headersLength = view.getUint32(4, false);
3218
+ if (total !== frame.length) throw new EventStreamError(`frame length ${frame.length} \u2260 ${total}`);
3219
+ if (view.getUint32(8, false) !== crc32(frame.subarray(0, 8))) {
3220
+ throw new EventStreamError("prelude checksum mismatch");
3221
+ }
3222
+ if (view.getUint32(total - TRAILER, false) !== crc32(frame.subarray(0, total - TRAILER))) {
3223
+ throw new EventStreamError("message checksum mismatch");
3224
+ }
3225
+ return {
3226
+ headers: decodeHeaders(frame.subarray(PRELUDE, PRELUDE + headersLength)),
3227
+ body: frame.slice(PRELUDE + headersLength, total - TRAILER)
3228
+ };
3229
+ };
3230
+ var FrameReader = class {
3231
+ buffer = new Uint8Array(0);
3232
+ /** Add bytes; returns every frame they complete. */
3233
+ push(chunk2) {
3234
+ this.buffer = this.buffer.length === 0 ? chunk2.slice() : concat([this.buffer, chunk2]);
3235
+ const out = [];
3236
+ while (this.buffer.length >= 4) {
3237
+ const total = new DataView(
3238
+ this.buffer.buffer,
3239
+ this.buffer.byteOffset,
3240
+ this.buffer.byteLength
3241
+ ).getUint32(0, false);
3242
+ if (total < PRELUDE + TRAILER) throw new EventStreamError(`impossible frame length ${total}`);
3243
+ if (this.buffer.length < total) break;
3244
+ out.push(decodeMessage(this.buffer.subarray(0, total)));
3245
+ this.buffer = this.buffer.slice(total);
3246
+ }
3247
+ return out;
3248
+ }
3249
+ /** Bytes of an incomplete frame still waiting for the rest. */
3250
+ get pending() {
3251
+ return this.buffer.length;
3252
+ }
3253
+ };
3254
+ var headerString = (message, name) => {
3255
+ const header = message.headers[name];
3256
+ return header?.type === "string" ? header.value : void 0;
3257
+ };
3258
+ var unwrapSigned = (message) => {
3259
+ if (message.headers[":chunk-signature"] === void 0) return message;
3260
+ if (message.body.length === 0) return null;
3261
+ return decodeMessage(message.body);
3262
+ };
3263
+ var eventFrame = (eventType, payload, contentType = payload instanceof Uint8Array ? "application/octet-stream" : "application/json") => encodeMessage({
3264
+ headers: {
3265
+ ":event-type": eventType,
3266
+ ":content-type": contentType,
3267
+ ":message-type": "event"
3268
+ },
3269
+ body: payload instanceof Uint8Array ? payload : JSON.stringify(payload)
3270
+ });
3271
+ var exceptionFrame = (exceptionType, body) => encodeMessage({
3272
+ headers: {
3273
+ ":exception-type": exceptionType,
3274
+ ":content-type": "application/json",
3275
+ ":message-type": "exception"
3276
+ },
3277
+ body: JSON.stringify(body)
3278
+ });
3279
+ var payloadJson = (message) => {
3280
+ try {
3281
+ return JSON.parse(text.decode(message.body));
3282
+ } catch {
3283
+ return void 0;
3284
+ }
3285
+ };
3286
+ async function* readFrames(body) {
3287
+ if (!body) return;
3288
+ const reader = new FrameReader();
3289
+ const stream = body.getReader();
3290
+ try {
3291
+ for (; ; ) {
3292
+ const { done, value } = await stream.read();
3293
+ if (done) break;
3294
+ for (const frame of reader.push(value)) yield frame;
3295
+ }
3296
+ } finally {
3297
+ stream.releaseLock();
3298
+ }
3299
+ if (reader.pending > 0) throw new EventStreamError("stream ended inside a frame");
3300
+ }
3301
+
3302
+ // src/render.ts
3303
+ var REASONING_SIGNATURE = "mock-reasoning-signature";
3304
+ var converseBlock = (block) => {
3305
+ switch (block.kind) {
3306
+ case "text":
3307
+ return { text: block.text };
3308
+ case "reasoning":
3309
+ return {
3310
+ reasoningContent: { reasoningText: { text: block.text, signature: REASONING_SIGNATURE } }
3311
+ };
3312
+ case "toolUse":
3313
+ return { toolUse: { toolUseId: block.toolUseId, name: block.name, input: block.input } };
3314
+ case "toolResult":
3315
+ return void 0;
3316
+ }
3317
+ };
3318
+ var converseBody = (plan, latencyMs) => ({
3319
+ output: {
3320
+ message: {
3321
+ role: "assistant",
3322
+ content: plan.blocks.map(converseBlock).filter((b) => b !== void 0)
3323
+ }
3324
+ },
3325
+ stopReason: plan.stopReason,
3326
+ usage: plan.usage,
3327
+ metrics: { latencyMs },
3328
+ ...plan.trace ? { trace: plan.trace } : {}
3329
+ });
3330
+ var anthropicBody = (plan, modelId, id) => ({
3331
+ id,
3332
+ type: "message",
3333
+ role: "assistant",
3334
+ model: modelId,
3335
+ content: plan.blocks.map((block) => {
3336
+ switch (block.kind) {
3337
+ case "text":
3338
+ return { type: "text", text: block.text };
3339
+ case "reasoning":
3340
+ return { type: "thinking", thinking: block.text, signature: REASONING_SIGNATURE };
3341
+ case "toolUse":
3342
+ return { type: "tool_use", id: block.toolUseId, name: block.name, input: block.input };
3343
+ default:
3344
+ return void 0;
3345
+ }
3346
+ }).filter((b) => b !== void 0),
3347
+ stop_reason: plan.stopReason === "guardrail_intervened" ? "refusal" : plan.stopReason,
3348
+ stop_sequence: null,
3349
+ usage: {
3350
+ input_tokens: plan.usage.inputTokens,
3351
+ output_tokens: plan.usage.outputTokens,
3352
+ ...plan.usage.cacheReadInputTokens !== void 0 ? { cache_read_input_tokens: plan.usage.cacheReadInputTokens } : {},
3353
+ ...plan.usage.cacheWriteInputTokens !== void 0 ? { cache_creation_input_tokens: plan.usage.cacheWriteInputTokens } : {}
3354
+ }
3355
+ });
3356
+ var MESSAGES = {
3357
+ modelStreamErrorException: "The model stream encountered an error. Try your request again.",
3358
+ internalServerException: "The system encountered an unexpected error during processing. Try your request again.",
3359
+ throttlingException: "Too many requests, please wait before trying again.",
3360
+ validationException: "The input fails to satisfy the constraints specified by the service.",
3361
+ serviceUnavailableException: "Bedrock is unable to process your request.",
3362
+ runtimeClientError: "The harness runtime failed while processing the request."
3363
+ };
3364
+ var paced = (steps, options) => {
3365
+ const fault = options.fault?.type === "mid_stream_exception" || options.fault?.type === "truncated_frame" ? options.fault : void 0;
3366
+ const after = fault?.afterChunks ?? 1;
3367
+ let index = 0;
3368
+ let sent = 0;
3369
+ let done = false;
3370
+ return new ReadableStream({
3371
+ async pull(controller) {
3372
+ if (done) return;
3373
+ const step = steps[index];
3374
+ if (fault && sent >= after && (step === void 0 || step.content)) {
3375
+ done = true;
3376
+ if (fault.type === "mid_stream_exception") {
3377
+ const type = fault.exceptionType ?? options.defaultException;
3378
+ controller.enqueue(
3379
+ exceptionFrame(type, { message: fault.message ?? MESSAGES[type] ?? "Stream failure." })
3380
+ );
3381
+ } else {
3382
+ const next = step?.frame ?? steps.at(-1)?.frame ?? new Uint8Array(16);
3383
+ controller.enqueue(next.subarray(0, Math.max(1, Math.floor(next.length / 2))));
3384
+ }
3385
+ controller.close();
3386
+ return;
3387
+ }
3388
+ if (step === void 0) {
3389
+ done = true;
3390
+ controller.close();
3391
+ return;
3392
+ }
3393
+ if (step.content && options.delayMsPerChunk > 0) {
3394
+ await options.sleep(options.delayMsPerChunk, options.signal);
3395
+ }
3396
+ if (options.signal?.aborted) {
3397
+ done = true;
3398
+ controller.close();
3399
+ return;
3400
+ }
3401
+ controller.enqueue(step.frame);
3402
+ if (step.content) sent++;
3403
+ index++;
3404
+ }
3405
+ });
3406
+ };
3407
+ var converseStreamSteps = (plan, latencyMs) => {
3408
+ const steps = [
3409
+ { frame: eventFrame("messageStart", { role: "assistant" }), content: false }
3410
+ ];
3411
+ plan.blocks.filter((block) => block.kind !== "toolResult").forEach((block, contentBlockIndex) => {
3412
+ if (block.kind === "toolUse") {
3413
+ steps.push({
3414
+ frame: eventFrame("contentBlockStart", {
3415
+ contentBlockIndex,
3416
+ start: { toolUse: { toolUseId: block.toolUseId, name: block.name } }
3417
+ }),
3418
+ content: false
3419
+ });
3420
+ for (const part of chunk(JSON.stringify(block.input ?? {}), Math.max(plan.chunkSize, 8))) {
3421
+ steps.push({
3422
+ frame: eventFrame("contentBlockDelta", {
3423
+ contentBlockIndex,
3424
+ delta: { toolUse: { input: part } }
3425
+ }),
3426
+ content: true
3427
+ });
3428
+ }
3429
+ } else if (block.kind === "reasoning") {
3430
+ for (const part of chunk(block.text, plan.chunkSize)) {
3431
+ steps.push({
3432
+ frame: eventFrame("contentBlockDelta", {
3433
+ contentBlockIndex,
3434
+ delta: { reasoningContent: { text: part } }
3435
+ }),
3436
+ content: true
3437
+ });
3438
+ }
3439
+ steps.push({
3440
+ frame: eventFrame("contentBlockDelta", {
3441
+ contentBlockIndex,
3442
+ delta: { reasoningContent: { signature: REASONING_SIGNATURE } }
3443
+ }),
3444
+ content: false
3445
+ });
3446
+ } else if (block.kind === "text") {
3447
+ for (const part of chunk(block.text, plan.chunkSize)) {
3448
+ steps.push({
3449
+ frame: eventFrame("contentBlockDelta", { contentBlockIndex, delta: { text: part } }),
3450
+ content: true
3451
+ });
3452
+ }
3453
+ }
3454
+ steps.push({ frame: eventFrame("contentBlockStop", { contentBlockIndex }), content: false });
3455
+ });
3456
+ steps.push({ frame: eventFrame("messageStop", { stopReason: plan.stopReason }), content: false });
3457
+ steps.push({
3458
+ frame: eventFrame("metadata", {
3459
+ usage: plan.usage,
3460
+ metrics: { latencyMs },
3461
+ ...plan.trace ? { trace: plan.trace } : {}
3462
+ }),
3463
+ content: false
3464
+ });
3465
+ return steps;
3466
+ };
3467
+ var harnessSteps = (plan, latencyMs) => {
3468
+ const steps = [
3469
+ { frame: eventFrame("messageStart", { role: "assistant" }), content: false }
3470
+ ];
3471
+ plan.blocks.forEach((block, contentBlockIndex) => {
3472
+ if (block.kind === "toolUse") {
3473
+ steps.push({
3474
+ frame: eventFrame("contentBlockStart", {
3475
+ contentBlockIndex,
3476
+ start: { toolUse: { toolUseId: block.toolUseId, name: block.name } }
3477
+ }),
3478
+ content: false
3479
+ });
3480
+ steps.push({
3481
+ frame: eventFrame("contentBlockDelta", {
3482
+ contentBlockIndex,
3483
+ delta: { toolUse: { input: JSON.stringify(block.input ?? {}) } }
3484
+ }),
3485
+ content: true
3486
+ });
3487
+ } else if (block.kind === "toolResult") {
3488
+ steps.push({
3489
+ frame: eventFrame("contentBlockStart", {
3490
+ contentBlockIndex,
3491
+ start: { toolResult: { toolUseId: block.toolUseId, status: "success" } }
3492
+ }),
3493
+ content: false
3494
+ });
3495
+ steps.push({
3496
+ frame: eventFrame("contentBlockDelta", {
3497
+ contentBlockIndex,
3498
+ delta: { toolResult: block.content }
3499
+ }),
3500
+ content: true
3501
+ });
3502
+ } else {
3503
+ for (const part of chunk(block.text, plan.chunkSize)) {
3504
+ steps.push({
3505
+ frame: eventFrame("contentBlockDelta", {
3506
+ contentBlockIndex,
3507
+ delta: block.kind === "reasoning" ? { reasoningContent: { text: part } } : { text: part }
3508
+ }),
3509
+ content: true
3510
+ });
3511
+ }
3512
+ }
3513
+ steps.push({ frame: eventFrame("contentBlockStop", { contentBlockIndex }), content: false });
3514
+ });
3515
+ steps.push({ frame: eventFrame("messageStop", { stopReason: plan.stopReason }), content: false });
3516
+ steps.push({
3517
+ frame: eventFrame("metadata", {
3518
+ usage: {
3519
+ inputTokens: plan.usage.inputTokens,
3520
+ outputTokens: plan.usage.outputTokens,
3521
+ totalTokens: plan.usage.totalTokens
3522
+ },
3523
+ metrics: { latencyMs }
3524
+ }),
3525
+ content: false
3526
+ });
3527
+ return steps;
3528
+ };
3529
+
3530
+ // src/audio.ts
3531
+ var MS_PER_CHARACTER = 60;
3532
+ var pcmTone = (durationMs, sampleRate) => {
3533
+ const samples = Math.max(1, Math.round(sampleRate * durationMs / 1e3));
3534
+ const out = new Uint8Array(samples * 2);
3535
+ const view = new DataView(out.buffer);
3536
+ for (let i = 0; i < samples; i++) {
3537
+ const value = Math.round(Math.sin(2 * Math.PI * 440 * i / sampleRate) * 0.25 * 32767);
3538
+ view.setInt16(i * 2, value, true);
3539
+ }
3540
+ return out;
3541
+ };
3542
+ var speechFor = (text2, sampleRate) => pcmTone(Math.max(1, text2.length) * MS_PER_CHARACTER, sampleRate);
3543
+ var base64 = (bytes) => {
3544
+ let binary = "";
3545
+ for (let i = 0; i < bytes.length; i += 32768) {
3546
+ binary += String.fromCharCode(...bytes.subarray(i, i + 32768));
3547
+ }
3548
+ return btoa(binary);
3549
+ };
3550
+
3551
+ // src/sonic.ts
3552
+ var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3553
+ var utf83 = new TextDecoder();
3554
+ var AUDIO_CHUNK_BYTES = 4800;
3555
+ var sonicSession = (request, modelId, host) => {
3556
+ let controller;
3557
+ let closed = false;
3558
+ const output = new ReadableStream({
3559
+ start(c) {
3560
+ controller = c;
3561
+ },
3562
+ cancel() {
3563
+ closed = true;
3564
+ }
3565
+ });
3566
+ const sessionId = host.nextId("session");
3567
+ const contents = /* @__PURE__ */ new Map();
3568
+ const tools = [];
3569
+ const toolNames = /* @__PURE__ */ new Map();
3570
+ let promptName = "";
3571
+ let systemText = "";
3572
+ let lastUserText = "";
3573
+ let sinceUser = 0;
3574
+ let pendingToolResults = [];
3575
+ let completionId;
3576
+ let outputSampleRate = 24e3;
3577
+ let contentEvents = 0;
3578
+ let fault = host.fault;
3579
+ let chain = Promise.resolve();
3580
+ let inputChars = 0;
3581
+ const close = () => {
3582
+ if (closed) return;
3583
+ closed = true;
3584
+ try {
3585
+ controller.close();
3586
+ } catch {
3587
+ }
3588
+ };
3589
+ const emitRaw = (frame) => {
3590
+ if (!closed) controller.enqueue(frame);
3591
+ };
3592
+ const emit = (event) => emitRaw(
3593
+ eventFrame("chunk", { bytes: base64(new TextEncoder().encode(JSON.stringify({ event }))) })
3594
+ );
3595
+ const emitContent = async (event, delayMs) => {
3596
+ if (closed) return;
3597
+ if (fault && (fault.type === "mid_stream_exception" || fault.type === "truncated_frame")) {
3598
+ if (contentEvents >= (fault.afterChunks ?? 1)) {
3599
+ if (fault.type === "mid_stream_exception") {
3600
+ emitRaw(
3601
+ exceptionFrame(fault.exceptionType ?? "modelStreamErrorException", {
3602
+ message: fault.message ?? "The model stream encountered an error. Try your request again."
3603
+ })
3604
+ );
3605
+ } else {
3606
+ const frame = eventFrame("chunk", {
3607
+ bytes: base64(new TextEncoder().encode(JSON.stringify({ event })))
3608
+ });
3609
+ emitRaw(frame.subarray(0, Math.floor(frame.length / 2)));
3610
+ }
3611
+ close();
3612
+ return;
3613
+ }
3614
+ }
3615
+ if (delayMs > 0) await host.sleep(delayMs, request.signal);
3616
+ emit(event);
3617
+ contentEvents++;
3618
+ };
3619
+ const respond = async (spoken, call) => {
3620
+ const plan = await host.resolve(call);
3621
+ if (plan.fault && !fault) fault = plan.fault;
3622
+ if (fault?.type === "latency") await host.sleep(fault.latencyMs ?? 1e3, request.signal);
3623
+ const base = { sessionId, promptName };
3624
+ if (completionId === void 0) {
3625
+ completionId = host.nextId("completion");
3626
+ emit({ completionStart: { ...base, completionId } });
3627
+ }
3628
+ const ids = { ...base, completionId };
3629
+ if (spoken && plan.userTranscript !== void 0) {
3630
+ const contentId = host.nextId("content");
3631
+ emit({
3632
+ contentStart: {
3633
+ ...ids,
3634
+ contentId,
3635
+ type: "TEXT",
3636
+ role: "USER",
3637
+ textOutputConfiguration: { mediaType: "text/plain" }
3638
+ }
3639
+ });
3640
+ await emitContent(
3641
+ { textOutput: { ...ids, contentId, content: plan.userTranscript, role: "USER" } },
3642
+ 0
3643
+ );
3644
+ emit({ contentEnd: { ...ids, contentId, type: "TEXT", stopReason: "PARTIAL_TURN" } });
3645
+ }
3646
+ for (const block of plan.blocks) {
3647
+ if (closed) return;
3648
+ if (block.kind === "text") {
3649
+ const textId = host.nextId("content");
3650
+ emit({
3651
+ contentStart: {
3652
+ ...ids,
3653
+ contentId: textId,
3654
+ type: "TEXT",
3655
+ role: "ASSISTANT",
3656
+ additionalModelFields: JSON.stringify({ generationStage: "FINAL" }),
3657
+ textOutputConfiguration: { mediaType: "text/plain" }
3658
+ }
3659
+ });
3660
+ await emitContent(
3661
+ { textOutput: { ...ids, contentId: textId, content: block.text, role: "ASSISTANT" } },
3662
+ plan.delayMsPerChunk
3663
+ );
3664
+ emit({
3665
+ contentEnd: { ...ids, contentId: textId, type: "TEXT", stopReason: "PARTIAL_TURN" }
3666
+ });
3667
+ const audioId = host.nextId("content");
3668
+ emit({
3669
+ contentStart: {
3670
+ ...ids,
3671
+ contentId: audioId,
3672
+ type: "AUDIO",
3673
+ role: "ASSISTANT",
3674
+ audioOutputConfiguration: {
3675
+ mediaType: "audio/lpcm",
3676
+ sampleRateHertz: outputSampleRate,
3677
+ sampleSizeBits: 16,
3678
+ channelCount: 1,
3679
+ encoding: "base64",
3680
+ audioType: "SPEECH"
3681
+ }
3682
+ }
3683
+ });
3684
+ const audio = speechFor(block.text, outputSampleRate);
3685
+ for (let at = 0; at < audio.length; at += AUDIO_CHUNK_BYTES) {
3686
+ await emitContent(
3687
+ {
3688
+ audioOutput: {
3689
+ ...ids,
3690
+ contentId: audioId,
3691
+ content: base64(audio.subarray(at, at + AUDIO_CHUNK_BYTES))
3692
+ }
3693
+ },
3694
+ plan.delayMsPerChunk
3695
+ );
3696
+ if (closed) return;
3697
+ }
3698
+ emit({ contentEnd: { ...ids, contentId: audioId, type: "AUDIO", stopReason: "END_TURN" } });
3699
+ } else if (block.kind === "toolUse") {
3700
+ toolNames.set(block.toolUseId, block.name);
3701
+ const toolId = host.nextId("content");
3702
+ emit({
3703
+ contentStart: {
3704
+ ...ids,
3705
+ contentId: toolId,
3706
+ type: "TOOL",
3707
+ role: "TOOL",
3708
+ toolUseOutputConfiguration: { mediaType: "application/json" }
3709
+ }
3710
+ });
3711
+ await emitContent(
3712
+ {
3713
+ toolUse: {
3714
+ ...ids,
3715
+ contentId: toolId,
3716
+ toolName: block.name,
3717
+ toolUseId: block.toolUseId,
3718
+ content: JSON.stringify(block.input ?? {})
3719
+ }
3720
+ },
3721
+ plan.delayMsPerChunk
3722
+ );
3723
+ emit({ contentEnd: { ...ids, contentId: toolId, type: "TOOL", stopReason: "TOOL_USE" } });
3724
+ }
3725
+ }
3726
+ if (closed) return;
3727
+ emit({
3728
+ usageEvent: {
3729
+ ...ids,
3730
+ totalInputTokens: plan.usage.inputTokens,
3731
+ totalOutputTokens: plan.usage.outputTokens,
3732
+ totalTokens: plan.usage.totalTokens,
3733
+ details: {
3734
+ delta: {
3735
+ input: { speechTokens: 0, textTokens: plan.usage.inputTokens },
3736
+ output: { speechTokens: 0, textTokens: plan.usage.outputTokens }
3737
+ }
3738
+ }
3739
+ }
3740
+ });
3741
+ };
3742
+ const schedule = (spoken) => {
3743
+ const call = {
3744
+ operation: "InvokeModelWithBidirectionalStream",
3745
+ modelId,
3746
+ lastUserText,
3747
+ systemText,
3748
+ tools: [...tools],
3749
+ toolSchemas: {},
3750
+ toolChoice: void 0,
3751
+ hasDocument: false,
3752
+ hasImage: false,
3753
+ hasCachePoint: false,
3754
+ hasGuardrail: false,
3755
+ toolResults: pendingToolResults,
3756
+ turnIndex: sinceUser,
3757
+ structured: void 0,
3758
+ inputChars
3759
+ };
3760
+ pendingToolResults = [];
3761
+ sinceUser++;
3762
+ chain = chain.then(() => respond(spoken, call)).catch(() => close());
3763
+ };
3764
+ const handle = (event) => {
3765
+ const [kind] = Object.keys(event);
3766
+ const body = kind ? event[kind] : void 0;
3767
+ if (!kind || !isRecord6(body)) return;
3768
+ switch (kind) {
3769
+ case "promptStart": {
3770
+ promptName = String(body.promptName ?? "");
3771
+ const audio = isRecord6(body.audioOutputConfiguration) ? body.audioOutputConfiguration : void 0;
3772
+ if (typeof audio?.sampleRateHertz === "number") outputSampleRate = audio.sampleRateHertz;
3773
+ const config = isRecord6(body.toolConfiguration) ? body.toolConfiguration : void 0;
3774
+ for (const tool of Array.isArray(config?.tools) ? config.tools : []) {
3775
+ if (isRecord6(tool) && isRecord6(tool.toolSpec) && typeof tool.toolSpec.name === "string") {
3776
+ tools.push(tool.toolSpec.name);
3777
+ }
3778
+ }
3779
+ return;
3780
+ }
3781
+ case "contentStart": {
3782
+ const name = String(body.contentName ?? "");
3783
+ contents.set(name, {
3784
+ type: String(body.type ?? ""),
3785
+ role: String(body.role ?? ""),
3786
+ interactive: body.interactive !== false,
3787
+ text: "",
3788
+ audioChunks: 0
3789
+ });
3790
+ const config = isRecord6(body.toolResultInputConfiguration) ? body.toolResultInputConfiguration : void 0;
3791
+ if (config && typeof config.toolUseId === "string") {
3792
+ const tool = toolNames.get(config.toolUseId);
3793
+ if (tool) pendingToolResults.push(tool);
3794
+ }
3795
+ return;
3796
+ }
3797
+ case "textInput": {
3798
+ const content = contents.get(String(body.contentName ?? ""));
3799
+ const text2 = typeof body.content === "string" ? body.content : "";
3800
+ inputChars += text2.length;
3801
+ if (!content) return;
3802
+ content.text += text2;
3803
+ if (content.role === "SYSTEM") systemText += text2;
3804
+ return;
3805
+ }
3806
+ case "audioInput": {
3807
+ const content = contents.get(String(body.contentName ?? ""));
3808
+ if (!content) return;
3809
+ content.audioChunks++;
3810
+ if (host.audioTurnChunks > 0 && content.audioChunks >= host.audioTurnChunks) {
3811
+ content.audioChunks = 0;
3812
+ lastUserText = "";
3813
+ sinceUser = 0;
3814
+ schedule(true);
3815
+ }
3816
+ return;
3817
+ }
3818
+ case "toolResult": {
3819
+ const text2 = typeof body.content === "string" ? body.content : "";
3820
+ inputChars += text2.length;
3821
+ return;
3822
+ }
3823
+ case "contentEnd": {
3824
+ const content = contents.get(String(body.contentName ?? ""));
3825
+ if (!content) return;
3826
+ if (content.role === "USER" && content.type === "TEXT" && content.interactive) {
3827
+ lastUserText = content.text;
3828
+ sinceUser = 0;
3829
+ schedule(false);
3830
+ } else if (content.role === "USER" && content.type === "AUDIO" && content.audioChunks > 0) {
3831
+ lastUserText = "";
3832
+ sinceUser = 0;
3833
+ schedule(true);
3834
+ } else if (content.type === "TOOL") {
3835
+ schedule(false);
3836
+ }
3837
+ return;
3838
+ }
3839
+ default:
3840
+ return;
3841
+ }
3842
+ };
3843
+ void (async () => {
3844
+ let ended = false;
3845
+ try {
3846
+ for await (const raw of readFrames(request.body)) {
3847
+ const frame = unwrapSigned(raw);
3848
+ if (frame === null) break;
3849
+ if (headerString(frame, ":event-type") !== "chunk") continue;
3850
+ const payload = payloadJson(frame);
3851
+ if (!isRecord6(payload) || typeof payload.bytes !== "string") continue;
3852
+ let decoded;
3853
+ try {
3854
+ decoded = JSON.parse(
3855
+ utf83.decode(Uint8Array.from(atob(payload.bytes), (c) => c.charCodeAt(0)))
3856
+ );
3857
+ } catch {
3858
+ continue;
3859
+ }
3860
+ const event = isRecord6(decoded) && isRecord6(decoded.event) ? decoded.event : decoded;
3861
+ if (!isRecord6(event)) continue;
3862
+ if ("sessionEnd" in event) {
3863
+ ended = true;
3864
+ break;
3865
+ }
3866
+ handle(event);
3867
+ }
3868
+ } catch {
3869
+ }
3870
+ await chain;
3871
+ if (ended && completionId !== void 0 && !closed) {
3872
+ emit({ completionEnd: { sessionId, promptName, completionId, stopReason: "END_TURN" } });
3873
+ }
3874
+ close();
3875
+ })();
3876
+ return output;
3877
+ };
3878
+
3879
+ // src/state.ts
3880
+ var DEFAULT_SETTINGS = {
3881
+ defaultText: "OK.",
3882
+ chunkSize: 16,
3883
+ delayMsPerChunk: 0,
3884
+ audioTurnChunks: 0
3885
+ };
3886
+ var EMPTY_STATS = {
3887
+ calls: 0,
3888
+ scripted: 0,
3889
+ unscripted: 0,
3890
+ byScript: {},
3891
+ byFallback: {},
3892
+ byOperation: {}
3893
+ };
3894
+ var BedrockState = class {
3895
+ constructor(sqlite, namespace, seed) {
3896
+ this.seed = seed;
3897
+ this.scripts = new Collection(sqlite, namespace, "scripts");
3898
+ this.uses = new Collection(sqlite, namespace, "script_uses");
3899
+ this.settings = new Collection(sqlite, namespace, "settings");
3900
+ this.stats = new Collection(sqlite, namespace, "stats");
3901
+ this.ids = new IdSequence(sqlite, namespace, "bedrock");
3902
+ this.ensureSeeded();
3903
+ }
3904
+ seed;
3905
+ scripts;
3906
+ uses;
3907
+ settings;
3908
+ stats;
3909
+ ids;
3910
+ /** Re-apply the configured settings and scripts after a reset. */
3911
+ ensureSeeded() {
3912
+ if (!this.settings.has("settings")) {
3913
+ this.settings.insert("settings", { ...DEFAULT_SETTINGS, ...this.seed.settings });
3914
+ for (const script of this.seed.scripts) this.scripts.insert(script.id, script);
3915
+ }
3916
+ }
3917
+ current() {
3918
+ return this.settings.get("settings") ?? DEFAULT_SETTINGS;
3919
+ }
3920
+ update(patch) {
3921
+ const next = { ...this.current(), ...patch };
3922
+ this.settings.insert("settings", next);
3923
+ return next;
3924
+ }
3925
+ list() {
3926
+ return this.scripts.list({ order: "oldest" }).map((row) => row.value);
3927
+ }
3928
+ /** Replace every script (`PUT`) or add/overwrite by id (`POST`). */
3929
+ put(scripts, replace) {
3930
+ if (replace) {
3931
+ for (const row of this.scripts.list()) this.scripts.delete(row.id);
3932
+ for (const row of this.uses.list()) this.uses.delete(row.id);
3933
+ }
3934
+ for (const script of scripts) this.scripts.insert(script.id, script);
3935
+ return this.list();
3936
+ }
3937
+ remove(id) {
3938
+ const targets = id === void 0 ? this.scripts.list().map((row) => row.id) : [id];
3939
+ let removed = 0;
3940
+ for (const each of targets) {
3941
+ if (this.scripts.delete(each)) removed++;
3942
+ this.uses.delete(each);
3943
+ }
3944
+ return removed;
3945
+ }
3946
+ usesOf(id) {
3947
+ return this.uses.get(id) ?? 0;
3948
+ }
3949
+ use(id) {
3950
+ this.uses.insert(id, this.usesOf(id) + 1);
3951
+ }
3952
+ /** The 0-based index of this model call in the namespace, then count it. */
3953
+ nextCallIndex() {
3954
+ const stats = this.currentStats();
3955
+ this.stats.insert("stats", { ...stats, calls: stats.calls + 1 });
3956
+ return stats.calls;
3957
+ }
3958
+ record(operation, scriptId, fallback) {
3959
+ const stats = this.currentStats();
3960
+ const bump = (map, key) => ({
3961
+ ...map,
3962
+ [key]: (map[key] ?? 0) + 1
3963
+ });
3964
+ this.stats.insert("stats", {
3965
+ ...stats,
3966
+ scripted: stats.scripted + (scriptId !== void 0 ? 1 : 0),
3967
+ unscripted: stats.unscripted + (scriptId === void 0 ? 1 : 0),
3968
+ byScript: scriptId !== void 0 ? bump(stats.byScript, scriptId) : stats.byScript,
3969
+ byFallback: scriptId === void 0 ? bump(stats.byFallback, fallback ?? "chat") : stats.byFallback,
3970
+ byOperation: bump(stats.byOperation, operation)
3971
+ });
3972
+ }
3973
+ currentStats() {
3974
+ return this.stats.get("stats") ?? EMPTY_STATS;
3975
+ }
3976
+ };
3977
+
3978
+ // src/runtime.ts
3979
+ var MODEL_PATH = "/model/";
3980
+ var everyCall = (description, params) => ({
3981
+ description,
3982
+ rules: [
3983
+ { pathPrefix: MODEL_PATH, effect: "bedrock_fault", params },
3984
+ { pathPrefix: "/harnesses/", effect: "bedrock_fault", params }
3985
+ ]
3986
+ });
3987
+ var BEDROCK_PRESETS = {
3988
+ throttling: everyCall(
3989
+ "429 ThrottlingException before the first chunk (our backend retries 3\xD7 with 500 ms\xB72^n backoff)",
3990
+ { type: "throttling" }
3991
+ ),
3992
+ mid_stream_exception: everyCall(
3993
+ "The stream starts, sends one content chunk, then a modelStreamErrorException frame",
3994
+ { type: "mid_stream_exception", afterChunks: 1 }
3995
+ ),
3996
+ mid_stream_throttling: everyCall(
3997
+ "The stream starts, sends one content chunk, then a throttlingException frame",
3998
+ { type: "mid_stream_exception", afterChunks: 1, exceptionType: "throttlingException" }
3999
+ ),
4000
+ validation_exception: everyCall("400 ValidationException", { type: "validation" }),
4001
+ max_tokens: everyCall("Output cut in half and stopReason max_tokens", { type: "max_tokens" }),
4002
+ latency: everyCall("2 s (mock clock) before the response starts", {
4003
+ type: "latency",
4004
+ latencyMs: 2e3
4005
+ }),
4006
+ truncated_frame: everyCall(
4007
+ "The stream ends half-way through a frame (the event-stream decoders throw)",
4008
+ { type: "truncated_frame", afterChunks: 1 }
4009
+ ),
4010
+ model_timeout: everyCall("408 ModelTimeoutException", { type: "model_timeout" }),
4011
+ service_unavailable: everyCall("503 ServiceUnavailableException", {
4012
+ type: "service_unavailable"
4013
+ }),
4014
+ access_denied: everyCall("403 AccessDeniedException", { type: "access_denied" }),
4015
+ internal_server: everyCall("500 InternalServerException", { type: "internal_server" })
4016
+ };
4017
+ var json3 = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
4018
+ var adminError3 = (status, message) => json3(status, { error: { type: "mockingbird_admin", message } });
4019
+ var isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
4020
+ var parseScripts = (body) => {
4021
+ const list = Array.isArray(body) ? body : isRecord7(body) ? body.scripts ?? (body.id ? [body] : void 0) : void 0;
4022
+ if (!Array.isArray(list)) return 'expected {"scripts": [{id, match?, turns: [...]}]}';
4023
+ const out = [];
4024
+ for (const [index, each] of list.entries()) {
4025
+ const parsed = parseScript(each, index);
4026
+ if (typeof parsed === "string") return parsed;
4027
+ out.push(parsed);
4028
+ }
4029
+ const ids = out.map((s) => s.id);
4030
+ const duplicate = ids.find((id, i) => ids.indexOf(id) !== i);
4031
+ return duplicate ? `duplicate script id ${duplicate}` : out;
4032
+ };
4033
+ var SETTING_CHECKS = {
4034
+ defaultText: (value) => typeof value === "string",
4035
+ chunkSize: (value) => typeof value === "number" && value >= 1,
4036
+ delayMsPerChunk: (value) => typeof value === "number" && value >= 0,
4037
+ audioTurnChunks: (value) => typeof value === "number" && value >= 0
4038
+ };
4039
+ var adminRoutes = (runtime) => {
4040
+ const store = (replace) => ({ body, namespace }) => {
4041
+ const scripts = parseScripts(body);
4042
+ if (typeof scripts === "string") return adminError3(400, scripts);
4043
+ return json3(200, { scripts: runtime.instance(namespace).putScripts(scripts, replace) });
4044
+ };
4045
+ return {
4046
+ "GET /scripts": ({ namespace }) => {
4047
+ const api = runtime.instance(namespace);
4048
+ return json3(200, { scripts: api.scripts(), stats: api.stats() });
4049
+ },
4050
+ "PUT /scripts": store(true),
4051
+ "POST /scripts": store(false),
4052
+ "DELETE /scripts": ({ url, namespace }) => json3(200, {
4053
+ removed: runtime.instance(namespace).removeScripts(url.searchParams.get("id") ?? void 0)
4054
+ }),
4055
+ "GET /model-metrics": ({ namespace }) => json3(200, runtime.instance(namespace).stats()),
4056
+ "GET /settings": ({ namespace }) => json3(200, runtime.instance(namespace).state.current()),
4057
+ "PUT /settings": ({ body, namespace }) => {
4058
+ if (!isRecord7(body)) return adminError3(400, "expected a JSON object");
4059
+ const patch = {};
4060
+ for (const [key, value] of Object.entries(body)) {
4061
+ const check = SETTING_CHECKS[key];
4062
+ if (!check) return adminError3(400, `unknown setting ${key}`);
4063
+ if (!check(value)) return adminError3(400, `bad value for ${key}`);
4064
+ patch[key] = value;
4065
+ }
4066
+ return json3(200, runtime.instance(namespace).state.update(patch));
4067
+ }
4068
+ };
4069
+ };
4070
+ var createRuntime2 = (options = {}) => {
4071
+ let runtime;
4072
+ const totals = () => {
4073
+ let scripted = 0;
4074
+ let unscripted = 0;
4075
+ for (const name of runtime?.namespaces() ?? []) {
4076
+ const stats = runtime?.instance(name).stats();
4077
+ scripted += stats?.scripted ?? 0;
4078
+ unscripted += stats?.unscripted ?? 0;
4079
+ }
4080
+ return { scripted, unscripted };
4081
+ };
4082
+ runtime = createRuntime({
4083
+ name: BEDROCK_NAMESPACE,
4084
+ document,
4085
+ ...options.sqlite ? { sqlite: options.sqlite } : {},
4086
+ ...options.clock ? { clock: options.clock } : {},
4087
+ ...options.seed !== void 0 ? { seed: options.seed } : {},
4088
+ ...options.adminKey !== void 0 ? { adminKey: options.adminKey } : {},
4089
+ ...options.onLog ? { onLog: options.onLog } : {},
4090
+ credential: accessKeyCredential,
4091
+ presets: BEDROCK_PRESETS,
4092
+ create: ({ sqlite, namespace, clock }) => new BedrockAPI({
4093
+ sqlite,
4094
+ namespace,
4095
+ now: clock.now,
4096
+ sleep: clockSleep(clock.now),
4097
+ ...options.settings ? { settings: options.settings } : {},
4098
+ ...options.scripts ? { scripts: options.scripts } : {}
4099
+ }),
4100
+ describe: () => ({ scripts: options.scripts?.length ?? 0, modelCalls: totals() }),
4101
+ admin: adminRoutes
4102
+ });
4103
+ return runtime;
4104
+ };
4105
+
4106
+ // src/index.ts
4107
+ var BEDROCK_NAMESPACE = "bedrock";
4108
+ var ERROR_TYPE_SUFFIX = ":http://internal.amazon.com/coral/com.amazon.bedrock/";
4109
+ var TITAN_EMBED = /amazon\.titan-embed-(text|g1-text)/i;
4110
+ var FAULT_ERRORS = {
4111
+ throttling: [429, "ThrottlingException", "Too many requests, please wait before trying again."],
4112
+ validation: [
4113
+ 400,
4114
+ "ValidationException",
4115
+ "The input fails to satisfy the constraints specified by the service."
4116
+ ],
4117
+ access_denied: [
4118
+ 403,
4119
+ "AccessDeniedException",
4120
+ "You don't have access to the model with the specified model ID."
4121
+ ],
4122
+ model_timeout: [
4123
+ 408,
4124
+ "ModelTimeoutException",
4125
+ "Model has timed out in processing the request. Try your request again."
4126
+ ],
4127
+ service_unavailable: [
4128
+ 503,
4129
+ "ServiceUnavailableException",
4130
+ "Bedrock is unable to process your request."
4131
+ ],
4132
+ internal_server: [
4133
+ 500,
4134
+ "InternalServerException",
4135
+ "The system encountered an unexpected error during processing. Try your request again."
4136
+ ]
4137
+ };
4138
+ var bedrockError = (status, type, message, requestId) => new Response(JSON.stringify({ message }), {
4139
+ status,
4140
+ headers: {
4141
+ "content-type": "application/json",
4142
+ "x-amzn-errortype": `${type}${ERROR_TYPE_SUFFIX}`,
4143
+ ...requestId ? { "x-amzn-requestid": requestId } : {}
4144
+ }
4145
+ });
4146
+ var presetFault = (request) => {
4147
+ const params = faultEffect(request, "bedrock_fault");
4148
+ return params && typeof params.type === "string" ? params : void 0;
4149
+ };
4150
+ var accessKeyCredential = sigV4AccessKeyId;
4151
+ var realSleep = (ms, signal) => new Promise((resolve) => {
4152
+ if (ms <= 0 || signal?.aborted) return resolve();
4153
+ const timer = setTimeout(resolve, ms);
4154
+ signal?.addEventListener("abort", () => {
4155
+ clearTimeout(timer);
4156
+ resolve();
4157
+ });
4158
+ });
4159
+ var clockSleep = (now) => (ms, signal) => {
4160
+ if (ms <= 0) return Promise.resolve();
4161
+ const until = now() + ms;
4162
+ return new Promise((resolve) => {
4163
+ const tick = () => {
4164
+ if (signal?.aborted || now() >= until) return resolve();
4165
+ setTimeout(tick, Math.min(5, Math.max(1, until - now())));
4166
+ };
4167
+ tick();
4168
+ });
4169
+ };
4170
+ var isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
4171
+ var utf84 = new TextEncoder();
4172
+ var titanEmbedding = async (inputText, dims = 1024) => {
4173
+ const seed = new Uint8Array(await crypto.subtle.digest("SHA-256", utf84.encode(inputText)));
4174
+ const values = [];
4175
+ for (let block = 0; values.length < dims; block++) {
4176
+ const input = new Uint8Array(seed.length + 4);
4177
+ input.set(seed);
4178
+ new DataView(input.buffer).setUint32(seed.length, block, false);
4179
+ const digest = new DataView(await crypto.subtle.digest("SHA-256", input));
4180
+ for (let at = 0; at + 4 <= 32 && values.length < dims; at += 4) {
4181
+ values.push(digest.getUint32(at, false) / 4294967295 * 2 - 1);
4182
+ }
4183
+ }
4184
+ const norm = Math.sqrt(values.reduce((sum, v) => sum + v * v, 0)) || 1;
4185
+ return values.map((v) => v / norm);
4186
+ };
4187
+ var BedrockAPI = class {
4188
+ app;
4189
+ sqlite;
4190
+ state;
4191
+ service;
4192
+ now;
4193
+ sleep;
4194
+ constructor(options = {}) {
4195
+ const sqlite = bootSqlite(options.sqlite);
4196
+ const namespace = options.namespace ?? BEDROCK_NAMESPACE;
4197
+ this.now = options.now ?? (() => Date.now());
4198
+ this.sleep = options.sleep ?? realSleep;
4199
+ this.state = new BedrockState(sqlite, namespace, {
4200
+ settings: options.settings ?? {},
4201
+ scripts: options.scripts ?? []
4202
+ });
4203
+ const handlers = defineOperations({
4204
+ Converse: (context) => this.converse(context, false),
4205
+ ConverseStream: (context) => this.converse(context, true),
4206
+ InvokeModel: (context) => this.invokeModel(context),
4207
+ // Served by `fetch` before routing, so the duplex body is never buffered.
4208
+ InvokeModelWithBidirectionalStream: (context) => this.bidirectional(context.request, context.params.modelId ?? ""),
4209
+ InvokeHarness: (context) => this.invokeHarness(context)
4210
+ });
4211
+ this.service = createService({
4212
+ document,
4213
+ handlers,
4214
+ sqlite,
4215
+ namespace,
4216
+ now: this.now,
4217
+ notFound: (request) => bedrockError(
4218
+ 404,
4219
+ "UnknownOperationException",
4220
+ `No operation matches ${request.method} ${new URL(request.url).pathname}`
4221
+ ),
4222
+ onError: (error) => {
4223
+ if (error instanceof HttpError) return error.toResponse();
4224
+ if (error instanceof ValidationProblem)
4225
+ return bedrockError(400, "ValidationException", error.message);
4226
+ throw error;
4227
+ }
4228
+ });
4229
+ this.app = this.service.app;
4230
+ this.sqlite = this.service.sqlite;
4231
+ }
4232
+ fetch(request) {
4233
+ const path = new URL(request.url).pathname;
4234
+ const bidi = /^\/model\/([^/]+)\/invoke-with-bidirectional-stream\/?$/.exec(path);
4235
+ if (bidi && request.method === "POST") {
4236
+ return this.bidirectional(request, decodeURIComponent(bidi[1]));
4237
+ }
4238
+ return this.service.fetch(request);
4239
+ }
4240
+ async reset() {
4241
+ await this.service.reset();
4242
+ this.state.ensureSeeded();
4243
+ }
4244
+ // ── admin surface ───────────────────────────────────────────────
4245
+ scripts() {
4246
+ return this.state.list();
4247
+ }
4248
+ putScripts(scripts, replace = true) {
4249
+ return this.state.put(scripts, replace);
4250
+ }
4251
+ removeScripts(id) {
4252
+ return this.state.remove(id);
4253
+ }
4254
+ stats() {
4255
+ return this.state.currentStats();
4256
+ }
4257
+ // ── shared machinery ────────────────────────────────────────────
4258
+ requestId() {
4259
+ const hex = opaqueToken(`request:${this.state.ids.next("rq_", 8)}`, 32).split("").map((c) => (c.charCodeAt(0) % 16).toString(16)).join("");
4260
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
4261
+ }
4262
+ planContext(body, maxTokens) {
4263
+ const settings = this.state.current();
4264
+ const guardrail = isRecord8(body.guardrailConfig) ? body.guardrailConfig : void 0;
4265
+ return {
4266
+ nextToolUseId: () => this.state.ids.next("tooluse_", 22),
4267
+ chunkSize: settings.chunkSize,
4268
+ delayMsPerChunk: settings.delayMsPerChunk,
4269
+ ...typeof guardrail?.guardrailIdentifier === "string" ? { guardrailId: guardrail.guardrailIdentifier } : {},
4270
+ traceEnabled: guardrail?.trace === "enabled" || guardrail?.trace === "enabled_full",
4271
+ ...maxTokens !== void 0 ? { maxTokens } : {}
4272
+ };
4273
+ }
4274
+ /**
4275
+ * First script with a turn for this call, else the default (which `unscripted` may
4276
+ * replace with an operation-specific one); records stats either way.
4277
+ */
4278
+ async resolve(call, context, unscripted = (plan) => plan) {
4279
+ const callIndex = this.state.nextCallIndex();
4280
+ const systemHash = await sha("SHA-256", call.systemText);
4281
+ for (const script of this.state.list()) {
4282
+ if (script.times !== void 0 && this.state.usesOf(script.id) >= script.times) continue;
4283
+ if (!matches2(script.match, call, { systemHash, callIndex })) continue;
4284
+ const turn = selectTurn(script, call);
4285
+ if (!turn) continue;
4286
+ this.state.use(script.id);
4287
+ this.state.record(call.operation, script.id, void 0);
4288
+ return planTurn(script.id, turn, call, context);
4289
+ }
4290
+ const plan = unscripted(planDefault(call, context, this.state.current().defaultText));
4291
+ this.state.record(call.operation, void 0, plan.fallback);
4292
+ return plan;
4293
+ }
4294
+ /** Journal notes: metadata only (never message or prompt text). */
4295
+ notes(response, call, plan) {
4296
+ const flags = [
4297
+ call.hasCachePoint ? "cachePoint" : void 0,
4298
+ call.hasGuardrail ? "guardrail" : void 0,
4299
+ call.hasDocument ? "document" : void 0,
4300
+ call.hasImage ? "image" : void 0,
4301
+ call.structured ? `structured:${call.structured.form}` : void 0
4302
+ ].filter((f) => f !== void 0);
4303
+ return annotateResponse(response, {
4304
+ ids: {
4305
+ modelId: call.modelId,
4306
+ script: plan?.scriptId ?? `unscripted:${plan?.fallback ?? "chat"}`,
4307
+ ...call.tools.length > 0 ? { tools: call.tools.join(",") } : {},
4308
+ ...flags.length > 0 ? { flags: flags.join(",") } : {},
4309
+ ...plan ? {
4310
+ stopReason: plan.stopReason,
4311
+ inputTokens: String(plan.usage.inputTokens),
4312
+ outputTokens: String(plan.usage.outputTokens)
4313
+ } : {}
4314
+ }
4315
+ });
4316
+ }
4317
+ jsonBody(context) {
4318
+ const body = context.body;
4319
+ if (body.kind === "json") return body.value;
4320
+ if (body.kind === "bytes" || body.kind === "text") {
4321
+ try {
4322
+ return JSON.parse(
4323
+ body.kind === "text" ? body.value : new TextDecoder().decode(body.value)
4324
+ );
4325
+ } catch {
4326
+ return void 0;
4327
+ }
4328
+ }
4329
+ return void 0;
4330
+ }
4331
+ /** A pre-stream fault as the vendor's error, or `undefined` to carry on. */
4332
+ async preStream(fault, requestId, signal, streaming) {
4333
+ if (!fault) return void 0;
4334
+ if (fault.type === "latency") {
4335
+ await this.sleep(fault.latencyMs ?? 1e3, signal);
4336
+ return void 0;
4337
+ }
4338
+ const known = FAULT_ERRORS[fault.type];
4339
+ if (known) return bedrockError(known[0], known[1], fault.message ?? known[2], requestId);
4340
+ if (!streaming && (fault.type === "mid_stream_exception" || fault.type === "truncated_frame")) {
4341
+ const [status, type, message] = FAULT_ERRORS.internal_server;
4342
+ return bedrockError(status, type, fault.message ?? message, requestId);
4343
+ }
4344
+ return void 0;
4345
+ }
4346
+ eventStream(body, requestId) {
4347
+ return new Response(body, {
4348
+ status: 200,
4349
+ headers: {
4350
+ "content-type": "application/vnd.amazon.eventstream",
4351
+ "x-amzn-requestid": requestId
4352
+ }
4353
+ });
4354
+ }
4355
+ // ── operations ──────────────────────────────────────────────────
4356
+ async converse(context, streaming) {
4357
+ const requestId = this.requestId();
4358
+ const operation = streaming ? "ConverseStream" : "Converse";
4359
+ const modelId = context.params.modelId ?? "";
4360
+ const body = this.jsonBody(context);
4361
+ if (!isRecord8(body)) {
4362
+ return bedrockError(
4363
+ 400,
4364
+ "ValidationException",
4365
+ "Malformed input request, please reformat your input and try again.",
4366
+ requestId
4367
+ );
4368
+ }
4369
+ const issues = bodyIssues(context).filter(
4370
+ (issue) => issue.message !== "request body is not valid application/json"
4371
+ );
4372
+ if (issues.length > 0) {
4373
+ const first = issues[0];
4374
+ return bedrockError(
4375
+ 400,
4376
+ "ValidationException",
4377
+ `${issues.length} validation error${issues.length > 1 ? "s" : ""} detected: Value at '${first.path || "body"}' failed to satisfy constraint: ${first.message}`,
4378
+ requestId
4379
+ );
4380
+ }
4381
+ let call;
4382
+ try {
4383
+ call = analyzeConverse(operation, modelId, body);
4384
+ } catch (error) {
4385
+ if (error instanceof ValidationProblem)
4386
+ return bedrockError(400, "ValidationException", error.message, requestId);
4387
+ throw error;
4388
+ }
4389
+ const inference = isRecord8(body.inferenceConfig) ? body.inferenceConfig : {};
4390
+ const maxTokens = typeof inference.maxTokens === "number" ? inference.maxTokens : void 0;
4391
+ let plan = await this.resolve(call, this.planContext(body, maxTokens));
4392
+ const fault = presetFault(context.request) ?? plan.fault;
4393
+ if (fault?.type === "max_tokens") plan = truncatePlan(plan);
4394
+ const failed = await this.preStream(fault, requestId, context.request.signal, streaming);
4395
+ if (failed) return this.notes(failed, call, plan);
4396
+ if (!streaming) {
4397
+ return this.notes(
4398
+ jsonRes(200, converseBody(plan, 0), { "x-amzn-requestid": requestId }),
4399
+ call,
4400
+ plan
4401
+ );
4402
+ }
4403
+ const stream = paced(converseStreamSteps(plan, 0), {
4404
+ sleep: this.sleep,
4405
+ delayMsPerChunk: plan.delayMsPerChunk,
4406
+ fault,
4407
+ defaultException: "modelStreamErrorException",
4408
+ signal: context.request.signal
4409
+ });
4410
+ return this.notes(this.eventStream(stream, requestId), call, plan);
4411
+ }
4412
+ async invokeModel(context) {
4413
+ const requestId = this.requestId();
4414
+ const modelId = context.params.modelId ?? "";
4415
+ const body = this.jsonBody(context);
4416
+ if (!isRecord8(body)) {
4417
+ return bedrockError(
4418
+ 400,
4419
+ "ValidationException",
4420
+ "Malformed input request, please reformat your input and try again.",
4421
+ requestId
4422
+ );
4423
+ }
4424
+ if (TITAN_EMBED.test(modelId)) return this.titan(context, modelId, body, requestId);
4425
+ if (!/anthropic|claude/i.test(modelId)) {
4426
+ return bedrockError(
4427
+ 400,
4428
+ "ValidationException",
4429
+ "The provided model identifier is invalid.",
4430
+ requestId
4431
+ );
4432
+ }
4433
+ let call;
4434
+ try {
4435
+ call = analyzeAnthropic(modelId, body);
4436
+ } catch (error) {
4437
+ if (error instanceof ValidationProblem)
4438
+ return bedrockError(400, "ValidationException", error.message, requestId);
4439
+ throw error;
4440
+ }
4441
+ const maxTokens = typeof body.max_tokens === "number" ? body.max_tokens : void 0;
4442
+ let plan = await this.resolve(call, this.planContext(body, maxTokens));
4443
+ const fault = presetFault(context.request) ?? plan.fault;
4444
+ if (fault?.type === "max_tokens") plan = truncatePlan(plan);
4445
+ const failed = await this.preStream(fault, requestId, context.request.signal, false);
4446
+ if (failed) return this.notes(failed, call, plan);
4447
+ return this.notes(
4448
+ jsonRes(200, anthropicBody(plan, modelId, `msg_bdrk_${this.state.ids.next("", 24)}`), {
4449
+ "x-amzn-requestid": requestId,
4450
+ "x-amzn-bedrock-input-token-count": String(plan.usage.inputTokens),
4451
+ "x-amzn-bedrock-output-token-count": String(plan.usage.outputTokens),
4452
+ "x-amzn-bedrock-invocation-latency": "0"
4453
+ }),
4454
+ call,
4455
+ plan
4456
+ );
4457
+ }
4458
+ async titan(context, modelId, body, requestId) {
4459
+ const v2 = /v2/i.test(modelId);
4460
+ const inputText = body.inputText;
4461
+ if (typeof inputText !== "string" || inputText.length === 0) {
4462
+ return bedrockError(
4463
+ 400,
4464
+ "ValidationException",
4465
+ "Malformed input request: #/inputText: expected minLength: 1, actual: 0, please reformat your input and try again.",
4466
+ requestId
4467
+ );
4468
+ }
4469
+ const dimensions = body.dimensions ?? (v2 ? 1024 : 1536);
4470
+ if (v2 && ![256, 512, 1024].includes(dimensions)) {
4471
+ return bedrockError(
4472
+ 400,
4473
+ "ValidationException",
4474
+ `Malformed input request: #/dimensions: ${String(dimensions)} is not a valid enum value, please reformat your input and try again.`,
4475
+ requestId
4476
+ );
4477
+ }
4478
+ this.state.nextCallIndex();
4479
+ this.state.record("InvokeModel", void 0, "titan");
4480
+ const fault = presetFault(context.request);
4481
+ const failed = await this.preStream(fault, requestId, context.request.signal, false);
4482
+ const inputTextTokenCount = Math.max(1, Math.ceil(inputText.length / 4));
4483
+ const notes2 = (response) => annotateResponse(response, {
4484
+ ids: { modelId, script: "unscripted:titan", inputTokens: String(inputTextTokenCount) }
4485
+ });
4486
+ if (failed) return notes2(failed);
4487
+ const embedding = await titanEmbedding(inputText, dimensions);
4488
+ return notes2(
4489
+ jsonRes(
4490
+ 200,
4491
+ {
4492
+ embedding,
4493
+ inputTextTokenCount,
4494
+ ...Array.isArray(body.embeddingTypes) ? { embeddingsByType: { float: embedding } } : {}
4495
+ },
4496
+ {
4497
+ "x-amzn-requestid": requestId,
4498
+ "x-amzn-bedrock-input-token-count": String(inputTextTokenCount),
4499
+ "x-amzn-bedrock-invocation-latency": "0"
4500
+ }
4501
+ )
4502
+ );
4503
+ }
4504
+ async invokeHarness(context) {
4505
+ const requestId = this.requestId();
4506
+ const harnessArn = context.url.searchParams.get("harnessArn") ?? "";
4507
+ if (!harnessArn) {
4508
+ return bedrockError(
4509
+ 400,
4510
+ "ValidationException",
4511
+ "1 validation error detected: Value null at 'harnessArn' failed to satisfy constraint: Member must not be null",
4512
+ requestId
4513
+ );
4514
+ }
4515
+ const body = this.jsonBody(context);
4516
+ let call;
4517
+ try {
4518
+ call = analyzeHarness(harnessArn, body);
4519
+ } catch (error) {
4520
+ if (error instanceof ValidationProblem)
4521
+ return bedrockError(400, "ValidationException", error.message, requestId);
4522
+ throw error;
4523
+ }
4524
+ let plan = await this.resolve(
4525
+ call,
4526
+ this.planContext({}, void 0),
4527
+ (fallback) => this.harnessDefault(call, fallback)
4528
+ );
4529
+ const fault = presetFault(context.request) ?? plan.fault;
4530
+ if (fault?.type === "max_tokens") plan = truncatePlan(plan);
4531
+ const failed = await this.preStream(fault, requestId, context.request.signal, true);
4532
+ if (failed) return this.notes(failed, call, plan);
4533
+ const stream = paced(harnessSteps(plan, 0), {
4534
+ sleep: this.sleep,
4535
+ delayMsPerChunk: plan.delayMsPerChunk,
4536
+ fault,
4537
+ defaultException: "internalServerException",
4538
+ signal: context.request.signal
4539
+ });
4540
+ const response = this.eventStream(stream, requestId);
4541
+ const session = context.request.headers.get("x-amzn-bedrock-agentcore-runtime-session-id");
4542
+ if (session) response.headers.set("x-amzn-bedrock-agentcore-runtime-session-id", session);
4543
+ return this.notes(response, call, plan);
4544
+ }
4545
+ /** The unscripted eRx prescreen answer: eligible for clinician review. */
4546
+ harnessDefault(call, plan) {
4547
+ let productKey = "unknown";
4548
+ try {
4549
+ const parsed = JSON.parse(call.lastUserText);
4550
+ if (typeof parsed.product_key === "string") productKey = parsed.product_key;
4551
+ } catch {
4552
+ }
4553
+ const summary = {
4554
+ status: "eligible_for_clinician_review",
4555
+ summary: "Mock prescreen: no hard stops found; ready for clinician review.",
4556
+ narrative: "Generated by the Mockingbird Bedrock mock. No clinical rules were evaluated.",
4557
+ protocolVersion: `${productKey}-mock-1`,
4558
+ ranAt: new Date(this.now()).toISOString(),
4559
+ hardStops: [],
4560
+ cautions: [],
4561
+ missingData: []
4562
+ };
4563
+ return {
4564
+ ...plan,
4565
+ fallback: "harness",
4566
+ blocks: [{ kind: "text", text: JSON.stringify(summary) }],
4567
+ stopReason: "end_turn"
4568
+ };
4569
+ }
4570
+ async bidirectional(request, modelId) {
4571
+ const requestId = this.requestId();
4572
+ if (!/sonic/i.test(modelId)) {
4573
+ return bedrockError(
4574
+ 400,
4575
+ "ValidationException",
4576
+ `The model ${modelId} does not support bidirectional streaming.`,
4577
+ requestId
4578
+ );
4579
+ }
4580
+ const fault = presetFault(request);
4581
+ if (fault && FAULT_ERRORS[fault.type]) {
4582
+ const failed = await this.preStream(fault, requestId, request.signal, true);
4583
+ if (failed) return annotateResponse(failed, { ids: { modelId } });
4584
+ }
4585
+ const settings = this.state.current();
4586
+ const body = sonicSession(request, modelId, {
4587
+ resolve: (call) => this.resolve(
4588
+ call,
4589
+ {
4590
+ nextToolUseId: () => this.state.ids.next("tooluse_", 22),
4591
+ chunkSize: settings.chunkSize,
4592
+ delayMsPerChunk: settings.delayMsPerChunk,
4593
+ traceEnabled: false
4594
+ },
4595
+ (plan) => ({ ...plan, fallback: "sonic" })
4596
+ ),
4597
+ sleep: this.sleep,
4598
+ nextId: (prefix) => {
4599
+ const hex = opaqueToken(`${prefix}:${this.state.ids.next(`${prefix}_`, 8)}`, 32).split("").map((c) => (c.charCodeAt(0) % 16).toString(16)).join("");
4600
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
4601
+ },
4602
+ audioTurnChunks: settings.audioTurnChunks,
4603
+ ...fault && !FAULT_ERRORS[fault.type] ? { fault } : {}
4604
+ });
4605
+ return annotateResponse(this.eventStream(body, requestId), { ids: { modelId } });
4606
+ }
4607
+ };
4608
+
4609
+ export {
4610
+ document,
4611
+ operationIds,
4612
+ supportedOperationIds,
4613
+ sampleSchema,
4614
+ MODEL_OPERATIONS,
4615
+ STOP_REASONS,
4616
+ TURN_FAULTS,
4617
+ parseScript,
4618
+ GUARDRAIL_BLOCKED_TEXT,
4619
+ DEFAULT_CHAT_TEXT,
4620
+ DEFAULT_CLASSIFIER,
4621
+ DEFAULT_SOAP_NOTE,
4622
+ EventStreamError,
4623
+ crc32,
4624
+ encodeMessage,
4625
+ decodeMessage,
4626
+ FrameReader,
4627
+ unwrapSigned,
4628
+ eventFrame,
4629
+ exceptionFrame,
4630
+ readFrames,
4631
+ DEFAULT_SETTINGS,
4632
+ BEDROCK_PRESETS,
4633
+ createRuntime2 as createRuntime,
4634
+ BEDROCK_NAMESPACE,
4635
+ bedrockError,
4636
+ accessKeyCredential,
4637
+ clockSleep,
4638
+ titanEmbedding,
4639
+ BedrockAPI
4640
+ };
4641
+ //# sourceMappingURL=chunk-O5ZO5KMR.js.map