@orkestrel/agent 0.0.26 → 0.0.28

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.
@@ -146,6 +146,8 @@ var INVALID_RELAY_STATUS = 400;
146
146
  * carried with no body and answered for an aborted inbound read as well.
147
147
  */
148
148
  var OVERSIZED_RELAY_STATUS = 413;
149
+ /** Names the System One decision endpoint shared by compatible servers. */
150
+ var SYSTEM_ONE_PATH = "/v1/systemone";
149
151
  //#endregion
150
152
  //#region src/core/errors.ts
151
153
  /**
@@ -361,6 +363,75 @@ var ProviderError = class extends Error {
361
363
  function isProviderError(value) {
362
364
  return (0, _orkestrel_contract.isInstance)(value, ProviderError);
363
365
  }
366
+ /**
367
+ * Reports a judge call cancelled by the caller's signal or its deadline, carrying the
368
+ * {@link JudgeResult} merged from the calls that completed before the cancel and the
369
+ * machine-readable `code` `'ABORT'`.
370
+ *
371
+ * @remarks
372
+ * `partial` keeps the answers and the usage of every completed call, so usage reported by
373
+ * completed calls is retained; a cancel before the first call carries an empty partial. `cause` holds the failure the
374
+ * cancel superseded when a throw raced the abort, and is undefined when the cancel was the only
375
+ * failure.
376
+ */
377
+ var JudgeAbortError = class extends Error {
378
+ /** Names the machine-readable condition — `'ABORT'`: a judge call cancelled mid-flight. */
379
+ code = "ABORT";
380
+ partial;
381
+ constructor(partial, options) {
382
+ super("judge call aborted", options);
383
+ this.name = "JudgeAbortError";
384
+ this.partial = partial;
385
+ }
386
+ };
387
+ /**
388
+ * Narrows a caught value to a {@link JudgeAbortError} through `instanceof`, so a `catch` can
389
+ * recover its `partial` result.
390
+ *
391
+ * @param value - The caught value
392
+ * @returns True if the value is a {@link JudgeAbortError}; false otherwise
393
+ * @example
394
+ * ```ts
395
+ * isJudgeAbortError(new JudgeAbortError({ model: 'tev1:0.8b', answers: {} })) // true
396
+ * ```
397
+ */
398
+ function isJudgeAbortError(value) {
399
+ return (0, _orkestrel_contract.isInstance)(value, JudgeAbortError);
400
+ }
401
+ /**
402
+ * Reports a coded judge failure with its HTTP status and underlying cause when available.
403
+ *
404
+ * @remarks
405
+ * The judge engine throws this error for a non-OK HTTP response, a missing or unparsable
406
+ * response body, and a request refused before inference. Concrete wire decoders also use it for
407
+ * a response they cannot read and for a wire limit. `status` is present only for an `HTTP`
408
+ * failure; other codes leave it undefined.
409
+ */
410
+ var JudgeError = class extends Error {
411
+ /** Names the machine-readable condition — `'HTTP'`: a non-OK response; `'PROTOCOL'`: a missing, unparsable, or unreadable response body; `'QUESTION'`: a request refused before inference. */
412
+ code;
413
+ /** Holds the response status for an HTTP failure, or undefined for other codes. */
414
+ status;
415
+ constructor(code, message, options) {
416
+ super(message, options);
417
+ this.name = "JudgeError";
418
+ this.code = code;
419
+ this.status = options?.status;
420
+ }
421
+ };
422
+ /**
423
+ * Narrows a caught value to the judge failure class through `instanceof`.
424
+ *
425
+ * @param value - The caught value
426
+ * @returns True if the value is a {@link JudgeError}; false otherwise
427
+ * @example
428
+ * ```ts
429
+ * isJudgeError(new JudgeError('HTTP', 'judge error: 429', { status: 429 })) // true
430
+ * ```
431
+ */
432
+ function isJudgeError(value) {
433
+ return (0, _orkestrel_contract.isInstance)(value, JudgeError);
434
+ }
364
435
  //#endregion
365
436
  //#region src/core/shapers.ts
366
437
  /**
@@ -1260,6 +1331,273 @@ async function* readChunks(body, signal) {
1260
1331
  }
1261
1332
  }
1262
1333
  }
1334
+ /**
1335
+ * Derives the winner, its probability, the published confidence, and a score answer's expected
1336
+ * level from a judge answer's distribution.
1337
+ *
1338
+ * @remarks
1339
+ * The winner is the first strictly greatest candidate in enumeration order: an option name in
1340
+ * criteria order, a level index, or `'false'` then `'true'` for a noul, so a noul of exactly 0.5
1341
+ * names `'false'`. Confidence follows the TypeSafe formulas, whatever confidence a server sent: a
1342
+ * choice reads `(max(p) - 1/n) / (1 - 1/n)`, a noul reads `|2p - 1|` (the choice formula at
1343
+ * n = 2), and a score reads `max(0, 1 - spread / even_spread)`, where `spread` sums each level's
1344
+ * probability times its distance from the winning level and `even_spread` is the mean distance
1345
+ * from the middle level. `score` is the expected level `sum(i * p_i)` and is set only for a score
1346
+ * answer.
1347
+ *
1348
+ * @param answer - The answer whose distribution is read
1349
+ * @returns The derived measures
1350
+ * @throws JudgeError Thrown with code `PROTOCOL` when a choice or score answer has fewer than 2
1351
+ * candidates, because the confidence formulas divide by the candidate count
1352
+ * @example
1353
+ * ```ts
1354
+ * computeReading({ form: 'choice', probabilities: { billing: 0.88, technical: 0.12, sales: 0 } })
1355
+ * // { winner: 'billing', probability: 0.88, confidence: 0.82 } to two decimals
1356
+ * computeReading({ form: 'score', probabilities: [0, 0.57, 0.43] })
1357
+ * // { winner: '1', probability: 0.57, confidence: 0.355, score: 1.43 } to three decimals
1358
+ * computeReading({ form: 'noul', noul: 0.5 }) // { winner: 'false', probability: 0.5, confidence: 0 }
1359
+ * ```
1360
+ */
1361
+ function computeReading(answer) {
1362
+ if (answer.form === "noul") {
1363
+ const yes = answer.noul > .5;
1364
+ return {
1365
+ winner: yes ? "true" : "false",
1366
+ probability: yes ? answer.noul : 1 - answer.noul,
1367
+ confidence: Math.abs(2 * answer.noul - 1)
1368
+ };
1369
+ }
1370
+ const candidates = answer.form === "choice" ? Object.entries(answer.probabilities) : answer.probabilities.map((probability, level) => [String(level), probability]);
1371
+ const count = candidates.length;
1372
+ if (count < 2) throw new JudgeError("PROTOCOL", "judge error: an answer needs at least 2 candidates");
1373
+ let winner = "";
1374
+ let index = 0;
1375
+ let probability = -Infinity;
1376
+ for (const [position, [name, candidate]] of candidates.entries()) if (candidate > probability) {
1377
+ winner = name;
1378
+ index = position;
1379
+ probability = candidate;
1380
+ }
1381
+ if (answer.form === "choice") return {
1382
+ winner,
1383
+ probability,
1384
+ confidence: (probability - 1 / count) / (1 - 1 / count)
1385
+ };
1386
+ let spread = 0;
1387
+ let even = 0;
1388
+ let score = 0;
1389
+ for (const [level, candidate] of answer.probabilities.entries()) {
1390
+ spread += candidate * Math.abs(level - index);
1391
+ even += Math.abs(level - (count - 1) / 2);
1392
+ score += level * candidate;
1393
+ }
1394
+ return {
1395
+ winner,
1396
+ probability,
1397
+ confidence: Math.max(0, 1 - spread / (even / count)),
1398
+ score
1399
+ };
1400
+ }
1401
+ /**
1402
+ * Merges the results of a judge request's calls into one result.
1403
+ *
1404
+ * @remarks
1405
+ * Pure and total. Answers and refusals are joined by question id; `refusals` is omitted when no
1406
+ * call refused a question. Each call's usage passes through {@link sanitizeUsage} and the totals
1407
+ * add through {@link sumUsage}; `usage` is omitted when no call reported one. The model is the
1408
+ * first call's, or the given model when the list is empty, as the empty partial of a cancel
1409
+ * before the first call requires.
1410
+ *
1411
+ * @param model - The judge's configured model, reported when no call completed
1412
+ * @param results - The completed calls' results in call order
1413
+ * @returns The merged result
1414
+ * @example
1415
+ * ```ts
1416
+ * buildJudgeResult('tev1:0.8b', []) // { model: 'tev1:0.8b', answers: {} }
1417
+ * buildJudgeResult('jev-latest', [
1418
+ * { model: 'jev-1.13.0', answers: { urgent: { form: 'noul', noul: 0.95 } } },
1419
+ * { model: 'jev-1.13.0', answers: {}, refusals: { team: { missing: ['sales'] } } },
1420
+ * ])
1421
+ * // { model: 'jev-1.13.0', answers: { urgent: ... }, refusals: { team: { missing: ['sales'] } } }
1422
+ * ```
1423
+ */
1424
+ function buildJudgeResult(model, results) {
1425
+ let answers = {};
1426
+ let refusals = {};
1427
+ let usage;
1428
+ for (const result of results) {
1429
+ answers = {
1430
+ ...answers,
1431
+ ...result.answers
1432
+ };
1433
+ refusals = {
1434
+ ...refusals,
1435
+ ...result.refusals
1436
+ };
1437
+ if (result.usage !== void 0) usage = sumUsage(usage, sanitizeUsage(result.usage));
1438
+ }
1439
+ return {
1440
+ model: results[0]?.model ?? model,
1441
+ answers,
1442
+ ...Object.keys(refusals).length === 0 ? {} : { refusals },
1443
+ ...usage === void 0 ? {} : { usage }
1444
+ };
1445
+ }
1446
+ /**
1447
+ * Builds a request's JSON headers, awaiting the caller's header hook inside the call's
1448
+ * cancellation bound.
1449
+ *
1450
+ * @remarks
1451
+ * The headers start with `Content-Type: application/json`; each entry the hook returns is set
1452
+ * over them, so the hook overrides the content type only when it returns that header. The hook
1453
+ * receives `signal` and races its abort, and the abort listener is removed on every exit. Both
1454
+ * HTTP engines, the provider and the judge, read their headers here.
1455
+ *
1456
+ * @param hook - The caller's header hook, or undefined for the JSON content type alone
1457
+ * @param signal - The call's combined caller and deadline signal
1458
+ * @returns The request headers
1459
+ * @throws Thrown when the signal aborts before the hook settles, with the signal's reason, and
1460
+ * when the hook throws or rejects, with the hook's own failure
1461
+ * @example
1462
+ * ```ts
1463
+ * const signal = new AbortController().signal
1464
+ * const headers = await readHeaders(() => ({ authorization: 'Bearer KEY' }), signal)
1465
+ * headers.get('authorization') // 'Bearer KEY'
1466
+ * headers.get('content-type') // 'application/json'
1467
+ * ```
1468
+ */
1469
+ async function readHeaders(hook, signal) {
1470
+ const headers = new Headers({ "Content-Type": "application/json" });
1471
+ if (hook === void 0) return headers;
1472
+ const cleanup = new AbortController();
1473
+ const aborted = Promise.withResolvers();
1474
+ signal.addEventListener("abort", () => aborted.reject(signal.reason), {
1475
+ once: true,
1476
+ signal: cleanup.signal
1477
+ });
1478
+ try {
1479
+ signal.throwIfAborted();
1480
+ const entries = await Promise.race([Promise.resolve().then(() => hook(signal)), aborted.promise]);
1481
+ for (const [key, value] of Object.entries(entries)) headers.set(key, value);
1482
+ return headers;
1483
+ } finally {
1484
+ cleanup.abort();
1485
+ }
1486
+ }
1487
+ /**
1488
+ * Owns a value by serializing it to JSON and parsing the text, so the copy shares nothing with its source.
1489
+ *
1490
+ * @remarks
1491
+ * A value that JSON cannot carry, such as a cycle or a bigint, and a value that serializes to
1492
+ * nothing, such as a function, both return undefined, so a guard over the result refuses them.
1493
+ * A proxied value serializes through its traps, where a structured clone refuses it.
1494
+ *
1495
+ * @param value - The value to own
1496
+ * @returns The owned JSON copy, or undefined when the value is not JSON
1497
+ * @example
1498
+ * ```ts
1499
+ * copyJSON({ state: 'A ticket.' }) // { state: 'A ticket.' }
1500
+ * copyJSON(() => 1) // undefined
1501
+ * ```
1502
+ */
1503
+ function copyJSON(value) {
1504
+ const text = (0, _orkestrel_contract.attempt)(() => JSON.stringify(value));
1505
+ if (!text.success || typeof text.value !== "string") return void 0;
1506
+ return (0, _orkestrel_contract.parseJSON)(text.value);
1507
+ }
1508
+ /**
1509
+ * Projects a judge question onto the System One wire while preserving omitted members.
1510
+ *
1511
+ * @param question - The domain question to send
1512
+ * @returns The wire question with `type` replacing `form`
1513
+ * @example
1514
+ * ```ts
1515
+ * questionToSystemOne({ form: 'noul' }) // { type: 'noul' }
1516
+ * ```
1517
+ */
1518
+ function questionToSystemOne(question) {
1519
+ return {
1520
+ type: question.form,
1521
+ ...question.instructions === void 0 ? {} : { instructions: question.instructions },
1522
+ ...question.criteria === void 0 ? {} : { criteria: question.criteria }
1523
+ };
1524
+ }
1525
+ /**
1526
+ * Extracts a System One distribution in question criteria order and drops server measures.
1527
+ *
1528
+ * @remarks
1529
+ * Every requested candidate must have a finite probability in [0, 1]; the helper checks each
1530
+ * requested candidate itself and reads no other member. Score maps and arrays project onto the
1531
+ * requested levels. Additional candidates are ignored, sums are unconstrained, and probabilities
1532
+ * are preserved without normalization.
1533
+ *
1534
+ * @param answer - The wire answer to decode
1535
+ * @param question - The question defining the form and candidate order
1536
+ * @returns The domain answer, or undefined for a mismatched form or a requested candidate whose
1537
+ * probability is missing, not finite, or outside [0, 1]
1538
+ * @example
1539
+ * ```ts
1540
+ * extractSystemOneAnswer({ type: 'noul', noul: 0.9 }, { form: 'noul' })
1541
+ * // { form: 'noul', noul: 0.9 }
1542
+ * ```
1543
+ */
1544
+ function extractSystemOneAnswer(answer, question) {
1545
+ const bounded = (0, _orkestrel_contract.boundsOf)(0, 1);
1546
+ if (answer.type === "noul" && question.form === "noul") return bounded(answer.noul) ? {
1547
+ form: "noul",
1548
+ noul: answer.noul
1549
+ } : void 0;
1550
+ if (answer.type === "choice" && question.form === "choice") {
1551
+ const entries = [];
1552
+ for (const label of Object.keys(question.criteria)) {
1553
+ if (!Object.hasOwn(answer.probabilities, label)) return void 0;
1554
+ const probability = answer.probabilities[label];
1555
+ if (!bounded(probability)) return void 0;
1556
+ entries.push([label, probability]);
1557
+ }
1558
+ return {
1559
+ form: "choice",
1560
+ probabilities: Object.fromEntries(entries)
1561
+ };
1562
+ }
1563
+ if (answer.type !== "score" || question.form !== "score") return void 0;
1564
+ const probabilities = [];
1565
+ for (let level = 0; level < question.criteria.length; level++) {
1566
+ if (!Object.hasOwn(answer.probabilities, level)) return void 0;
1567
+ const probability = (0, _orkestrel_contract.isArray)(answer.probabilities) ? answer.probabilities[level] : answer.probabilities[String(level)];
1568
+ if (!bounded(probability)) return void 0;
1569
+ probabilities.push(probability);
1570
+ }
1571
+ return {
1572
+ form: "score",
1573
+ probabilities
1574
+ };
1575
+ }
1576
+ /**
1577
+ * Maps complete System One token counts onto validated token usage.
1578
+ *
1579
+ * @param usage - The optional wire token counts
1580
+ * @returns Token usage, or undefined for missing, null, negative, or non-finite counts
1581
+ * @example
1582
+ * ```ts
1583
+ * extractSystemOneUsage({ input_tokens: 975, output_tokens: 4 })
1584
+ * // { prompt: 975, completion: 4, total: 979 }
1585
+ * ```
1586
+ */
1587
+ function extractSystemOneUsage(usage) {
1588
+ const extracted = (0, _orkestrel_contract.attempt)(() => {
1589
+ if (usage === void 0) return void 0;
1590
+ const { input_tokens: prompt, output_tokens: completion } = usage;
1591
+ if (!(0, _orkestrel_contract.isNumber)(prompt) || !(0, _orkestrel_contract.isNumber)(completion)) return void 0;
1592
+ const result = {
1593
+ prompt,
1594
+ completion,
1595
+ total: prompt + completion
1596
+ };
1597
+ return (0, _orkestrel_budget.isTokenUsage)(result) ? result : void 0;
1598
+ });
1599
+ return extracted.success ? extracted.value : void 0;
1600
+ }
1263
1601
  //#endregion
1264
1602
  //#region src/core/conversations/Conversation.ts
1265
1603
  /**
@@ -2110,7 +2448,8 @@ var Agent = class {
2110
2448
  broke = true;
2111
2449
  break;
2112
2450
  }
2113
- const advertised = filterAllowList(this.#context.scope?.tools, tools.definitions(), (definition) => definition.name);
2451
+ const names = this.#context.scope?.tools?.slice();
2452
+ const advertised = filterAllowList(names, tools.definitions(), (definition) => definition.name);
2114
2453
  const definitions = advertised.length > 0 ? advertised : void 0;
2115
2454
  let charged = 0;
2116
2455
  let turnContent = "";
@@ -2164,8 +2503,10 @@ var Agent = class {
2164
2503
  usage: resultUsage
2165
2504
  };
2166
2505
  }
2167
- if (result.tools !== void 0 && result.tools.length > 0) {
2168
- const authorization = this.#authorize(result.tools);
2506
+ if (definitions === void 0) for (const call of result.tools ?? []) this.#emitter.emit("deny", call, "no tool is advertised in the active scope");
2507
+ else if (result.tools !== void 0 && result.tools.length > 0) {
2508
+ const admitted = filterAllowList(names, result.tools, (call) => call.name);
2509
+ const authorization = this.#authorize(result.tools, admitted);
2169
2510
  if (abort.signal.aborted) {
2170
2511
  partial = true;
2171
2512
  broke = true;
@@ -2190,7 +2531,7 @@ var Agent = class {
2190
2531
  };
2191
2532
  const toolMessage = this.#context.messages.add({
2192
2533
  role: "tool",
2193
- content: outcomeResult.success ? JSON.stringify(outcomeResult.value) : outcomeResult.error
2534
+ content: outcomeResult.success ? typeof outcomeResult.value === "string" ? outcomeResult.value : JSON.stringify(outcomeResult.value) : outcomeResult.error
2194
2535
  });
2195
2536
  messages.push(toolMessage);
2196
2537
  }
@@ -2237,24 +2578,33 @@ var Agent = class {
2237
2578
  messages.splice(0, messages.length, ...this.#context.build(this.#provider.format));
2238
2579
  return false;
2239
2580
  }
2240
- #authorize(calls) {
2241
- if (this.#authority === void 0) return void 0;
2581
+ #authorize(calls, admitted) {
2242
2582
  const authority = this.#authority;
2243
- const allowed = [];
2583
+ const allowed = /* @__PURE__ */ new Map();
2244
2584
  const denials = /* @__PURE__ */ new Map();
2245
- for (const call of calls) {
2585
+ for (const [index, call] of calls.entries()) {
2586
+ if (!admitted.includes(call)) {
2587
+ const reason = `${call.name} is not in the active scope`;
2588
+ denials.set(index, denyCall(call, reason));
2589
+ this.#emitter.emit("deny", call, reason);
2590
+ continue;
2591
+ }
2592
+ if (authority === void 0) {
2593
+ allowed.set(index, call);
2594
+ continue;
2595
+ }
2246
2596
  let decision;
2247
2597
  try {
2248
2598
  decision = authority.evaluate({ call });
2249
2599
  } catch (error) {
2250
2600
  const reason = (0, _orkestrel_workflow.errorToMessage)(error);
2251
- denials.set(call.id, denyCall(call, reason));
2601
+ denials.set(index, denyCall(call, reason));
2252
2602
  this.#emitter.emit("deny", call, reason);
2253
2603
  continue;
2254
2604
  }
2255
- if (decision.allowed) allowed.push(call);
2605
+ if (decision.allowed) allowed.set(index, call);
2256
2606
  else {
2257
- denials.set(call.id, denyCall(call, decision.reason));
2607
+ denials.set(index, denyCall(call, decision.reason));
2258
2608
  this.#emitter.emit("deny", call, decision.reason);
2259
2609
  }
2260
2610
  }
@@ -2264,12 +2614,16 @@ var Agent = class {
2264
2614
  };
2265
2615
  }
2266
2616
  async #dispatch(tools, calls, signal, authorization) {
2267
- if (authorization === void 0) return tools.execute(calls, { signal });
2268
2617
  const { allowed, denials } = authorization;
2269
- const executed = allowed.length > 0 ? await tools.execute(allowed, { signal }) : [];
2270
- const byId = new Map(denials);
2271
- for (const result of executed) byId.set(result.id, result);
2272
- return calls.map((call) => byId.get(call.id) ?? denyCall(call, void 0));
2618
+ const executed = allowed.size > 0 ? await tools.execute([...allowed.values()], { signal }) : [];
2619
+ const results = new Map(denials);
2620
+ let offset = 0;
2621
+ for (const index of allowed.keys()) {
2622
+ const result = executed[offset];
2623
+ if (result !== void 0) results.set(index, result);
2624
+ offset += 1;
2625
+ }
2626
+ return calls.map((call, index) => results.get(index) ?? denyCall(call, void 0));
2273
2627
  }
2274
2628
  async *#provide(messages, signal, definitions, think, schema, onDelta) {
2275
2629
  const options = {};
@@ -2550,6 +2904,117 @@ function isConversationSnapshot(value) {
2550
2904
  if (!(0, _orkestrel_contract.isArray)(value.sections) || !value.sections.every(isSection)) return false;
2551
2905
  return (0, _orkestrel_contract.isArray)(value.messages) && value.messages.every(isMessage);
2552
2906
  }
2907
+ /**
2908
+ * Checks whether a value is a judge entry: a string, a JSON record, or a JSON array.
2909
+ *
2910
+ * @remarks
2911
+ * Total: a cycle, a non-JSON member, a class instance, and a hostile input return false. `null`
2912
+ * is not an entry; a criteria guard admits it where the protocol keeps it.
2913
+ *
2914
+ * @param value - The unknown entry candidate
2915
+ * @returns True if the value is a string or JSON structure the model can read; false otherwise
2916
+ * @example
2917
+ * ```ts
2918
+ * isJudgeEntry('Customer asks for a refund') // true
2919
+ * isJudgeEntry({ ticket: 4182, tags: ['billing'] }) // true
2920
+ * isJudgeEntry(null) // false
2921
+ * isJudgeEntry({ opened: new Date() }) // false
2922
+ * ```
2923
+ */
2924
+ function isJudgeEntry(value) {
2925
+ if ((0, _orkestrel_contract.isString)(value)) return true;
2926
+ return ((0, _orkestrel_contract.isRecord)(value) || (0, _orkestrel_contract.isArray)(value)) && (0, _orkestrel_contract.isJSONValue)(value);
2927
+ }
2928
+ /**
2929
+ * Checks whether a value is a well-formed judge question of the choice, score, or noul form.
2930
+ *
2931
+ * @remarks
2932
+ * Total: a hostile input returns false. A choice needs at least 2 options and a score at least 2
2933
+ * levels, because the confidence formula divides by the candidate count; a description or a level
2934
+ * can be `null`. `instructions` and noul `criteria` are omitted when absent and never `null`.
2935
+ * Server limits on option, level, and question counts are left to the server.
2936
+ *
2937
+ * @param value - The unknown question candidate
2938
+ * @returns True if the value is a question the judge engine can send; false otherwise
2939
+ * @example
2940
+ * ```ts
2941
+ * isJudgeQuestion({ form: 'choice', criteria: { billing: null, bug: 'Software defect' } }) // true
2942
+ * isJudgeQuestion({ form: 'score', criteria: ['Cosmetic', null, 'Blocking'] }) // true
2943
+ * isJudgeQuestion({ form: 'noul', instructions: 'Is a refund owed?' }) // true
2944
+ * isJudgeQuestion({ form: 'choice', criteria: { billing: null } }) // false
2945
+ * ```
2946
+ */
2947
+ function isJudgeQuestion(value) {
2948
+ const checked = (0, _orkestrel_contract.attempt)(() => {
2949
+ if (!(0, _orkestrel_contract.isRecord)(value)) return false;
2950
+ const { form, instructions, criteria } = value;
2951
+ if (instructions !== void 0 && !isJudgeEntry(instructions)) return false;
2952
+ if (form === "choice") return (0, _orkestrel_contract.isRecord)(criteria) && Object.keys(criteria).length >= 2 && Object.values(criteria).every((0, _orkestrel_contract.nullableOf)(isJudgeEntry));
2953
+ if (form === "score") return (0, _orkestrel_contract.arrayOf)((0, _orkestrel_contract.nullableOf)(isJudgeEntry))(criteria) && criteria.length >= 2;
2954
+ if (form !== "noul") return false;
2955
+ if (criteria === void 0) return true;
2956
+ if (!(0, _orkestrel_contract.isRecord)(criteria)) return false;
2957
+ return (0, _orkestrel_contract.optionalOf)(isJudgeEntry)(criteria.true) && (0, _orkestrel_contract.optionalOf)(isJudgeEntry)(criteria.false);
2958
+ });
2959
+ return checked.success && checked.value;
2960
+ }
2961
+ /**
2962
+ * Checks whether a value is a System One response envelope with optional model and usage.
2963
+ *
2964
+ * @remarks
2965
+ * Answers remain unknown until checked against their questions. Missing and null usage counts
2966
+ * are accepted. Extra members are ignored, and unreadable fields return false.
2967
+ *
2968
+ * @param value - The unknown response candidate
2969
+ * @returns True if the envelope fields have their wire types; false otherwise
2970
+ * @example
2971
+ * ```ts
2972
+ * isSystemOneResponse({ answers: {}, usage: { input_tokens: null } }) // true
2973
+ * ```
2974
+ */
2975
+ function isSystemOneResponse(value) {
2976
+ const checked = (0, _orkestrel_contract.attempt)(() => {
2977
+ if (!(0, _orkestrel_contract.isRecord)(value)) return false;
2978
+ const { model, answers, usage } = value;
2979
+ if (!(0, _orkestrel_contract.optionalOf)(_orkestrel_contract.isString)(model) || !(0, _orkestrel_contract.isRecord)(answers)) return false;
2980
+ if (usage === void 0) return true;
2981
+ if (!(0, _orkestrel_contract.isRecord)(usage)) return false;
2982
+ return (0, _orkestrel_contract.optionalOf)((0, _orkestrel_contract.nullableOf)(_orkestrel_contract.isNumber))(usage.input_tokens) && (0, _orkestrel_contract.optionalOf)((0, _orkestrel_contract.nullableOf)(_orkestrel_contract.isNumber))(usage.output_tokens);
2983
+ });
2984
+ return checked.success && checked.value;
2985
+ }
2986
+ /**
2987
+ * Checks whether a value is a System One answer whose type and distribution the wire can read.
2988
+ *
2989
+ * @remarks
2990
+ * The guard checks `type` and the distribution the wire dereferences: the `noul` number for a
2991
+ * noul, the `probabilities` map for a choice, and the `probabilities` map or dense array for a
2992
+ * score, each probability a finite number in [0, 1]. The `choice`, `score`, `confidence`, and
2993
+ * `legend` members are carried unchecked, because the wire never reads them, so a value this
2994
+ * guard accepts can hold any value in those members. Distribution sums are not constrained;
2995
+ * values are never normalized. Hostile reads return false.
2996
+ *
2997
+ * @param value - The unknown answer candidate
2998
+ * @returns True if the type and the distribution satisfy the wire contract; false otherwise
2999
+ * @example
3000
+ * ```ts
3001
+ * isSystemOneAnswer({ type: 'noul', noul: 0.9 }) // true
3002
+ * isSystemOneAnswer({ type: 'score', probabilities: [0.2, 0.8] }) // true
3003
+ * isSystemOneAnswer({ type: 'noul', noul: 1.1 }) // false
3004
+ * ```
3005
+ */
3006
+ function isSystemOneAnswer(value) {
3007
+ const checked = (0, _orkestrel_contract.attempt)(() => {
3008
+ if (!(0, _orkestrel_contract.isRecord)(value)) return false;
3009
+ const { type } = value;
3010
+ if (type === "noul") return (0, _orkestrel_contract.boundsOf)(0, 1)(value.noul);
3011
+ if (type !== "choice" && type !== "score") return false;
3012
+ const { probabilities } = value;
3013
+ if (type === "score" && (0, _orkestrel_contract.isArray)(probabilities)) return (0, _orkestrel_contract.arrayOf)((0, _orkestrel_contract.boundsOf)(0, 1))(probabilities);
3014
+ return (0, _orkestrel_contract.isRecord)(probabilities) && Object.values(probabilities).every((0, _orkestrel_contract.boundsOf)(0, 1));
3015
+ });
3016
+ return checked.success && checked.value;
3017
+ }
2553
3018
  //#endregion
2554
3019
  //#region src/core/conversations/stores/DatabaseConversationStore.ts
2555
3020
  /**
@@ -2901,7 +3366,7 @@ var AgentProvider = class {
2901
3366
  };
2902
3367
  }
2903
3368
  async #request(request, signal) {
2904
- const headers = await this.#requestHeaders(signal);
3369
+ const headers = await readHeaders(this.#headers, signal);
2905
3370
  signal.throwIfAborted();
2906
3371
  const response = await this.#transport(this.#url + this.#path, {
2907
3372
  method: "POST",
@@ -2924,25 +3389,6 @@ var AgentProvider = class {
2924
3389
  }
2925
3390
  return response;
2926
3391
  }
2927
- async #requestHeaders(signal) {
2928
- const headers = new Headers({ "Content-Type": "application/json" });
2929
- if (this.#headers === void 0) return headers;
2930
- const cleanup = new AbortController();
2931
- const aborted = Promise.withResolvers();
2932
- signal.addEventListener("abort", () => aborted.reject(signal.reason), {
2933
- once: true,
2934
- signal: cleanup.signal
2935
- });
2936
- try {
2937
- signal.throwIfAborted();
2938
- const hook = this.#headers;
2939
- const entries = await Promise.race([Promise.resolve().then(() => hook(signal)), aborted.promise]);
2940
- for (const [key, value] of Object.entries(entries)) headers.set(key, value);
2941
- return headers;
2942
- } finally {
2943
- cleanup.abort();
2944
- }
2945
- }
2946
3392
  };
2947
3393
  //#endregion
2948
3394
  //#region src/core/providers/RelayProvider.ts
@@ -3089,6 +3535,258 @@ var RelayProvider = class extends AgentProvider {
3089
3535
  }
3090
3536
  };
3091
3537
  //#endregion
3538
+ //#region src/core/AgentJudge.ts
3539
+ /**
3540
+ * Implements the bounded HTTP calls, validation, and result merging of a judge behind concrete
3541
+ * wire seams.
3542
+ *
3543
+ * @remarks
3544
+ * A subclass fills `name`, `body`, and `read`; the constructor takes the `batch` switch that
3545
+ * decides whether one call carries every question or each question gets its own call in key
3546
+ * order. `ask` validates the request before any call, builds every body before the first call so
3547
+ * a wire limit refuses the request before inference, and runs the calls one after another, each
3548
+ * under its own deadline folded with the caller's signal. A cancel throws a `JudgeAbortError`
3549
+ * whose `partial` merges the calls that completed. A transport error reaches the caller unchanged.
3550
+ *
3551
+ * @example Writing a judge wire
3552
+ * ```ts
3553
+ * import type { JudgeRequest, JudgeResult } from '@orkestrel/agent'
3554
+ * import { AgentJudge, JudgeError } from '@orkestrel/agent'
3555
+ * import { isFiniteNumber, isRecord } from '@orkestrel/contract'
3556
+ *
3557
+ * // A wire whose server answers one yes/no question per call as { "yes": 0.93 }.
3558
+ * class YesJudge extends AgentJudge {
3559
+ * readonly name = 'yes'
3560
+ * body(request: JudgeRequest): object {
3561
+ * return { model: this.model, state: request.state, questions: request.questions }
3562
+ * }
3563
+ * read(value: unknown, request: JudgeRequest): JudgeResult {
3564
+ * const [id] = Object.keys(request.questions)
3565
+ * if (id === undefined || !isRecord(value) || !isFiniteNumber(value.yes)) {
3566
+ * throw new JudgeError('PROTOCOL', 'judge error: unreadable answer')
3567
+ * }
3568
+ * return { model: this.model, answers: { [id]: { form: 'noul', noul: value.yes } } }
3569
+ * }
3570
+ * }
3571
+ *
3572
+ * const judge = new YesJudge({
3573
+ * url: 'http://localhost:8010',
3574
+ * path: '/v1/yes',
3575
+ * model: 'yes-1',
3576
+ * batch: false,
3577
+ * })
3578
+ * ```
3579
+ */
3580
+ var AgentJudge = class {
3581
+ #id;
3582
+ #url;
3583
+ #path;
3584
+ #model;
3585
+ #timeout;
3586
+ #transport;
3587
+ #headers;
3588
+ #batch;
3589
+ constructor(input) {
3590
+ this.#id = crypto.randomUUID();
3591
+ this.#url = input.url;
3592
+ this.#path = input.path ?? "";
3593
+ this.#model = input.model;
3594
+ this.#timeout = input.timeout ?? 12e4;
3595
+ this.#transport = input.fetch ?? globalThis.fetch.bind(globalThis);
3596
+ this.#headers = input.headers;
3597
+ this.#batch = input.batch ?? true;
3598
+ }
3599
+ /** Exposes the instance's minted UUID. */
3600
+ get id() {
3601
+ return this.#id;
3602
+ }
3603
+ /** Exposes the configured model identity. */
3604
+ get model() {
3605
+ return this.#model;
3606
+ }
3607
+ /**
3608
+ * Asks every question of the request about its state and merges the answers of every call.
3609
+ *
3610
+ * @param request - The state and the questions keyed by caller id
3611
+ * @param signal - The caller's cancellation bound
3612
+ * @returns The merged answers, refusals, and usage of every call
3613
+ * @throws JudgeAbortError Thrown when the caller's signal or a call's deadline fires, carrying
3614
+ * the merged result of the completed calls
3615
+ * @throws JudgeError Thrown with code `QUESTION` for an empty question map, a malformed question
3616
+ * or state, or a wire refusal before any call; with code `HTTP` for a non-OK response; and with
3617
+ * code `PROTOCOL` for a missing or unparsable response body
3618
+ */
3619
+ async ask(request, signal) {
3620
+ if (signal.aborted) throw new JudgeAbortError(buildJudgeResult(this.#model, []));
3621
+ const state = copyJSON(request.state);
3622
+ if (!isJudgeEntry(state)) throw new JudgeError("QUESTION", "judge error: state is not a judge entry");
3623
+ const questions = (0, _orkestrel_contract.isRecord)(request.questions) ? Object.entries(request.questions) : [];
3624
+ if (questions.length === 0) throw new JudgeError("QUESTION", "judge error: no questions");
3625
+ const entries = [];
3626
+ for (const [id, question] of questions) {
3627
+ const copy = copyJSON(question);
3628
+ if (!isJudgeQuestion(copy)) throw new JudgeError("QUESTION", `judge error: question ${id} is malformed`);
3629
+ entries.push([id, copy]);
3630
+ }
3631
+ const owned = {
3632
+ state,
3633
+ questions: Object.fromEntries(entries)
3634
+ };
3635
+ const calls = (this.#batch ? [owned] : entries.map(([id, question]) => ({
3636
+ state,
3637
+ questions: { [id]: question }
3638
+ }))).map((part) => ({
3639
+ request: part,
3640
+ body: JSON.stringify(this.body(part))
3641
+ }));
3642
+ const results = [];
3643
+ for (const call of calls) results.push(await this.#call(call.request, call.body, signal, results));
3644
+ return buildJudgeResult(this.#model, results);
3645
+ }
3646
+ async #call(request, body, signal, completed) {
3647
+ const timeout = new _orkestrel_timeout.Timeout({ ms: this.#timeout });
3648
+ timeout.start();
3649
+ const combined = AbortSignal.any([timeout.signal, signal]);
3650
+ try {
3651
+ const headers = await readHeaders(this.#headers, combined);
3652
+ combined.throwIfAborted();
3653
+ const response = await this.#transport(this.#url + this.#path, {
3654
+ method: "POST",
3655
+ headers,
3656
+ body,
3657
+ signal: combined
3658
+ });
3659
+ if (!response.ok) {
3660
+ let detail;
3661
+ try {
3662
+ detail = response.body === null ? "" : (await readText(response.body, MAX_ERROR_BODY_LENGTH, combined)).text;
3663
+ } catch (cause) {
3664
+ throw new JudgeError("HTTP", `judge error: ${response.status} - (error body unavailable)`, {
3665
+ status: response.status,
3666
+ cause
3667
+ });
3668
+ }
3669
+ combined.throwIfAborted();
3670
+ throw new JudgeError("HTTP", `judge error: ${response.status}${detail === "" ? "" : ` - ${detail}`}`, { status: response.status });
3671
+ }
3672
+ if (response.body === null) throw new JudgeError("PROTOCOL", "judge error: no response body");
3673
+ const text = await readText(response.body, void 0, combined);
3674
+ combined.throwIfAborted();
3675
+ const value = (0, _orkestrel_contract.parseJSON)(text.text);
3676
+ if (value === void 0) throw new JudgeError("PROTOCOL", "judge error: invalid JSON body");
3677
+ const result = this.read(value, request);
3678
+ combined.throwIfAborted();
3679
+ return result;
3680
+ } catch (error) {
3681
+ if (combined.aborted) throw new JudgeAbortError(buildJudgeResult(this.#model, completed), error === combined.reason ? void 0 : { cause: error });
3682
+ throw error;
3683
+ } finally {
3684
+ timeout.clear();
3685
+ }
3686
+ }
3687
+ };
3688
+ //#endregion
3689
+ //#region src/core/providers/SystemOneJudge.ts
3690
+ /**
3691
+ * Carries judge questions over the System One protocol and derives answers from server distributions.
3692
+ *
3693
+ * @remarks
3694
+ * The caller supplies the server origin and model. Every question travels in one request.
3695
+ * Server measures are ignored; the response model is preserved.
3696
+ * `headers` supplies authentication through the shared judge engine.
3697
+ *
3698
+ * @example Asking a System One server a choice, a noul, and a score
3699
+ * ```ts
3700
+ * import { computeReading, createSystemOneJudge } from '@orkestrel/agent'
3701
+ *
3702
+ * const judge = createSystemOneJudge({ url: 'http://localhost:11434', model: 'tev1:0.8b' })
3703
+ * const result = await judge.ask(
3704
+ * {
3705
+ * state: 'Our checkout has returned 500 errors since 9am. I want a refund for today.',
3706
+ * questions: {
3707
+ * label: {
3708
+ * form: 'choice',
3709
+ * instructions: 'Which label fits this ticket?',
3710
+ * criteria: { billing: 'Payments and refunds', bug: 'Software errors', account: null },
3711
+ * },
3712
+ * refund: {
3713
+ * form: 'noul',
3714
+ * instructions: 'Does the customer ask for money back?',
3715
+ * criteria: {
3716
+ * true: 'The customer asks for a refund or for money back.',
3717
+ * false: 'The customer does not ask for money back.',
3718
+ * },
3719
+ * },
3720
+ * severity: {
3721
+ * form: 'score',
3722
+ * instructions: 'How severe is the reported issue?',
3723
+ * criteria: ['Cosmetic; no impact', 'Degraded, workaround exists', 'Blocking; no workaround'],
3724
+ * },
3725
+ * },
3726
+ * },
3727
+ * AbortSignal.timeout(30_000),
3728
+ * )
3729
+ * const readings = Object.fromEntries(
3730
+ * Object.entries(result.answers).map(([id, answer]) => [id, computeReading(answer)]),
3731
+ * )
3732
+ * result.model // 'tev1:0.8b' — the model the server named
3733
+ * result.usage // { prompt: 975, completion: 4, total: 979 }
3734
+ * readings.label // { winner: 'bug', probability: 0.9691, confidence: 0.9536 } to four decimals
3735
+ * readings.refund // { winner: 'true', probability: 0.9979, confidence: 0.9958 } to four decimals
3736
+ * readings.severity // { winner: '1', probability: 0.9494, confidence: 0.9241, score: 0.9919 } to four decimals
3737
+ * ```
3738
+ */
3739
+ var SystemOneJudge = class extends AgentJudge {
3740
+ constructor(options) {
3741
+ super({
3742
+ ...options,
3743
+ path: SYSTEM_ONE_PATH,
3744
+ batch: true
3745
+ });
3746
+ }
3747
+ /** Identifies the System One protocol. */
3748
+ name = "systemone";
3749
+ /**
3750
+ * Projects the state and questions onto the System One request with the configured model.
3751
+ *
3752
+ * @param request - The state and questions keyed by caller id
3753
+ * @returns The System One request body
3754
+ */
3755
+ body(request) {
3756
+ return {
3757
+ state: request.state,
3758
+ model: this.model,
3759
+ questions: Object.fromEntries(Object.entries(request.questions).map(([id, question]) => [id, questionToSystemOne(question)]))
3760
+ };
3761
+ }
3762
+ /**
3763
+ * Decodes requested System One answers and reports the server model and available usage.
3764
+ *
3765
+ * @param value - The parsed response body
3766
+ * @param request - The questions defining the expected answers
3767
+ * @returns The decoded distributions, model, and available usage
3768
+ * @throws JudgeError Thrown with code `PROTOCOL` for a malformed envelope or an invalid answer naming its question id
3769
+ */
3770
+ read(value, request) {
3771
+ if (!isSystemOneResponse(value)) throw new JudgeError("PROTOCOL", "judge error: invalid System One response envelope");
3772
+ const entries = [];
3773
+ for (const [id, question] of Object.entries(request.questions)) {
3774
+ if (!Object.hasOwn(value.answers, id)) throw new JudgeError("PROTOCOL", `judge error: question ${id} has no System One answer`);
3775
+ const wire = value.answers[id];
3776
+ if (!isSystemOneAnswer(wire)) throw new JudgeError("PROTOCOL", `judge error: question ${id} has an invalid System One answer`);
3777
+ const answer = extractSystemOneAnswer(wire, question);
3778
+ if (answer === void 0) throw new JudgeError("PROTOCOL", `judge error: question ${id} has a mismatched or incomplete System One answer`);
3779
+ entries.push([id, answer]);
3780
+ }
3781
+ const usage = extractSystemOneUsage(value.usage);
3782
+ return {
3783
+ model: value.model ?? this.model,
3784
+ answers: Object.fromEntries(entries),
3785
+ ...usage === void 0 ? {} : { usage }
3786
+ };
3787
+ }
3788
+ };
3789
+ //#endregion
3092
3790
  //#region src/core/RelayStream.ts
3093
3791
  /**
3094
3792
  * Streams a provider call as validated NDJSON frames under response backpressure.
@@ -4126,11 +4824,25 @@ function createAgentRunner(options) {
4126
4824
  handler: handleAgentRunnerJob.bind(void 0, registry, partial)
4127
4825
  });
4128
4826
  }
4827
+ /**
4828
+ * Creates a judge that sends every question through the configured System One server.
4829
+ *
4830
+ * @param options - The server origin, model, and optional transport, headers, and timeout
4831
+ * @returns The configured judge behind its shared interface
4832
+ * @example
4833
+ * ```ts
4834
+ * const judge = createSystemOneJudge({ url: 'http://localhost:11434', model: 'tev1:0.8b' })
4835
+ * ```
4836
+ */
4837
+ function createSystemOneJudge(options) {
4838
+ return new SystemOneJudge(options);
4839
+ }
4129
4840
  //#endregion
4130
4841
  exports.Agent = Agent;
4131
4842
  exports.AgentContext = AgentContext;
4132
4843
  exports.AgentError = AgentError;
4133
4844
  exports.AgentJobError = AgentJobError;
4845
+ exports.AgentJudge = AgentJudge;
4134
4846
  exports.AgentProvider = AgentProvider;
4135
4847
  exports.AgentRegistry = AgentRegistry;
4136
4848
  exports.Authority = Authority;
@@ -4149,6 +4861,8 @@ exports.IMAGE_TOKEN_ESTIMATE = IMAGE_TOKEN_ESTIMATE;
4149
4861
  exports.INVALID_RELAY_STATUS = INVALID_RELAY_STATUS;
4150
4862
  exports.Instruction = Instruction;
4151
4863
  exports.InstructionManager = InstructionManager;
4864
+ exports.JudgeAbortError = JudgeAbortError;
4865
+ exports.JudgeError = JudgeError;
4152
4866
  exports.MAX_ERROR_BODY_LENGTH = MAX_ERROR_BODY_LENGTH;
4153
4867
  exports.MESSAGE_TOKEN_OVERHEAD = MESSAGE_TOKEN_OVERHEAD;
4154
4868
  exports.MemoryConversationStore = MemoryConversationStore;
@@ -4159,8 +4873,10 @@ exports.RELAY_CONTENT_TYPE = RELAY_CONTENT_TYPE;
4159
4873
  exports.RELAY_PROVIDER_MESSAGE = RELAY_PROVIDER_MESSAGE;
4160
4874
  exports.RelayProvider = RelayProvider;
4161
4875
  exports.RelayStream = RelayStream;
4876
+ exports.SYSTEM_ONE_PATH = SYSTEM_ONE_PATH;
4162
4877
  exports.Scope = Scope;
4163
4878
  exports.ScopeManager = ScopeManager;
4879
+ exports.SystemOneJudge = SystemOneJudge;
4164
4880
  exports.THINK_CLOSE = THINK_CLOSE;
4165
4881
  exports.THINK_OPEN = THINK_OPEN;
4166
4882
  exports.ThinkSplitter = ThinkSplitter;
@@ -4171,10 +4887,13 @@ exports.agentResultToJSON = agentResultToJSON;
4171
4887
  exports.assembleResult = assembleResult;
4172
4888
  exports.attachImages = attachImages;
4173
4889
  exports.attachUserImages = attachUserImages;
4890
+ exports.buildJudgeResult = buildJudgeResult;
4174
4891
  exports.buildProviderResult = buildProviderResult;
4175
4892
  exports.buildRecapMessage = buildRecapMessage;
4176
4893
  exports.buildSummaryMessage = buildSummaryMessage;
4177
4894
  exports.collectImageData = collectImageData;
4895
+ exports.computeReading = computeReading;
4896
+ exports.copyJSON = copyJSON;
4178
4897
  exports.createAgent = createAgent;
4179
4898
  exports.createAgentContext = createAgentContext;
4180
4899
  exports.createAgentQueue = createAgentQueue;
@@ -4192,10 +4911,13 @@ exports.createRelay = createRelay;
4192
4911
  exports.createRelayProvider = createRelayProvider;
4193
4912
  exports.createScope = createScope;
4194
4913
  exports.createScopeManager = createScopeManager;
4914
+ exports.createSystemOneJudge = createSystemOneJudge;
4195
4915
  exports.createThinkSplitter = createThinkSplitter;
4196
4916
  exports.denyCall = denyCall;
4197
4917
  exports.estimateMessages = estimateMessages;
4198
4918
  exports.estimateTokens = estimateTokens;
4919
+ exports.extractSystemOneAnswer = extractSystemOneAnswer;
4920
+ exports.extractSystemOneUsage = extractSystemOneUsage;
4199
4921
  exports.filterAllowList = filterAllowList;
4200
4922
  exports.handleAgentQueueJob = handleAgentQueueJob;
4201
4923
  exports.handleAgentRunnerJob = handleAgentRunnerJob;
@@ -4204,10 +4926,16 @@ exports.isAgentError = isAgentError;
4204
4926
  exports.isAgentJobError = isAgentJobError;
4205
4927
  exports.isConversationError = isConversationError;
4206
4928
  exports.isConversationSnapshot = isConversationSnapshot;
4929
+ exports.isJudgeAbortError = isJudgeAbortError;
4930
+ exports.isJudgeEntry = isJudgeEntry;
4931
+ exports.isJudgeError = isJudgeError;
4932
+ exports.isJudgeQuestion = isJudgeQuestion;
4207
4933
  exports.isMessage = isMessage;
4208
4934
  exports.isProviderAbortError = isProviderAbortError;
4209
4935
  exports.isProviderError = isProviderError;
4210
4936
  exports.isSection = isSection;
4937
+ exports.isSystemOneAnswer = isSystemOneAnswer;
4938
+ exports.isSystemOneResponse = isSystemOneResponse;
4211
4939
  exports.joinThinking = joinThinking;
4212
4940
  exports.messageContract = messageContract;
4213
4941
  exports.messageShape = messageShape;
@@ -4215,7 +4943,9 @@ exports.providerRequestContract = providerRequestContract;
4215
4943
  exports.providerRequestShape = providerRequestShape;
4216
4944
  exports.providerResultContract = providerResultContract;
4217
4945
  exports.providerResultShape = providerResultShape;
4946
+ exports.questionToSystemOne = questionToSystemOne;
4218
4947
  exports.readChunks = readChunks;
4948
+ exports.readHeaders = readHeaders;
4219
4949
  exports.readText = readText;
4220
4950
  exports.relayFrameContract = relayFrameContract;
4221
4951
  exports.relayFrameShape = relayFrameShape;