@orkestrel/agent 0.0.39 → 0.0.41

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.
@@ -1,6 +1,6 @@
1
- import { ContractError, INTRINSICS, arrayOf, arrayShape, attempt, booleanShape, boundsOf, canonicalStringify, cloneJSONValue, createContract, describeFaults, errorToMessage, failure, holds, isArray, isBoolean, isError, isFiniteNumber, isFunction, isJSONValue, isNumber, isObject, isRecord, isString, jsonShape, literalShape, nullableOf, nullableShape, numberShape, objectOf, objectShape, optionalOf, optionalShape, parseJSON, parseJSONAs, parseJSONValue, preview, rawShape, recordOf, recordShape, schemaToShape, stringShape, success, unionOf, unionShape } from "@orkestrel/contract";
1
+ import { ContractError, INTRINSICS, arrayOf, arrayShape, attempt, booleanShape, boundsOf, canonicalStringify, cloneJSONValue, createContract, describeFaults, errorToMessage, failure, holds, isArray, isBoolean, isError, isFiniteNumber, isFunction, isJSONValue, isNumber, isObject, isPromiseLike, isRecord, isString, jsonShape, literalShape, nullableOf, nullableShape, numberShape, objectOf, objectShape, optionalOf, optionalShape, parseJSON, parseJSONAs, parseJSONValue, preview, rawShape, recordOf, recordShape, schemaToShape, stringShape, success, unionOf, unionShape } from "@orkestrel/contract";
2
2
  import { createTokenBudget, createTokenMeter, isBudget, isBudgetAmount, isBudgetError, isTokenUsage } from "@orkestrel/budget";
3
- import { ToolManager, createTool, createToolManager, isToolCall } from "@orkestrel/tool";
3
+ import { ToolManager, createTool, createToolManager, isToolCall, isToolError } from "@orkestrel/tool";
4
4
  import { createNDJSONParser } from "@orkestrel/ndjson";
5
5
  import { Timeout, createTimeout } from "@orkestrel/timeout";
6
6
  import { createDatabase, createMemoryDriver } from "@orkestrel/database";
@@ -8908,7 +8908,7 @@ var DETERMINISTIC_JUDGE_ERROR = /invalid or duplicate top logprob token/;
8908
8908
  * carrying the machine-readable `code`.
8909
8909
  *
8910
8910
  * @remarks
8911
- * A threshold, share, capacity, limit, topic, lookup, question, or gauge outside its bounds is a
8911
+ * A threshold, share, capacity, limit, topic, lookup, write, question, or gauge outside its bounds is a
8912
8912
  * programmer error and throws this with the matching {@link LedgerErrorCode}. A calibration call that reports
8913
8913
  * no prompt usage throws this with `'GAUGE'`. A snapshot option that `isLedgerSnapshot` refuses
8914
8914
  * throws this with `'SNAPSHOT'`, its `context` holding the `faults`. Narrow a caught value with {@link isLedgerError} and
@@ -8952,7 +8952,7 @@ function isLedgerError(value) {
8952
8952
  return holds(() => {
8953
8953
  if (!(value instanceof Error && INTRINSICS.prototype(value) !== Error.prototype && INTRINSICS.describe(value, Symbol.for("@orkestrel/agent.LedgerError"))?.value === value)) return false;
8954
8954
  const code = INTRINSICS.reflect.read(value, "code");
8955
- if (!(code === "REQUEST" || code === "THRESHOLD" || code === "SHARE" || code === "CAPACITY" || code === "LIMIT" || code === "TOPIC" || code === "LOOKUP" || code === "GAUGE" || code === "REFERENCE" || code === "SNAPSHOT" || code === "QUESTION")) return false;
8955
+ if (!(code === "REQUEST" || code === "THRESHOLD" || code === "SHARE" || code === "CAPACITY" || code === "LIMIT" || code === "TOPIC" || code === "LOOKUP" || code === "GAUGE" || code === "REFERENCE" || code === "SNAPSHOT" || code === "QUESTION" || code === "WRITE")) return false;
8956
8956
  const context = INTRINSICS.reflect.read(value, "context");
8957
8957
  return context === void 0 || isObject(context) || isFunction(context);
8958
8958
  });
@@ -8982,7 +8982,43 @@ function digestLedgerWindow(exchanges, messages) {
8982
8982
  return values.some((value) => value === void 0) ? void 0 : canonicalStringify([values, exchanges.map((entry) => [entry.copy ?? null, entry.lines])]);
8983
8983
  });
8984
8984
  if (!encoded.success || encoded.value === void 0) return void 0;
8985
- const hash = [...encoded.value].reduce((prior, character) => {
8985
+ return computeLedgerChecksum(encoded.value);
8986
+ }
8987
+ /**
8988
+ * Digests tool definitions as the JSON a provider request carries them, in order.
8989
+ *
8990
+ * @remarks
8991
+ * A ledger stores this digest with its gauge, so a hydrate reuses the gauge only for the same
8992
+ * definitions: an added, removed, reordered, or edited definition changes the digest. The digest is
8993
+ * a {@link computeLedgerChecksum} checksum and makes no security claim.
8994
+ *
8995
+ * @param definitions - The tool definitions, as a tool manager's `definitions` method returns them
8996
+ * @returns The 16-digit hexadecimal digest, or undefined when JSON cannot render the definitions
8997
+ * @example
8998
+ * ```ts
8999
+ * digestLedgerTools([{ name: 'lookup_order', description: 'Read an order by its id.' }]) // '1eff754a69b7128c'
9000
+ * ```
9001
+ */
9002
+ function digestLedgerTools(definitions) {
9003
+ const text = renderJSON(definitions);
9004
+ return text === void 0 ? void 0 : computeLedgerChecksum(text);
9005
+ }
9006
+ /**
9007
+ * Computes a checksum of text: two 32-bit FNV-1a lanes over its code points, as 16 hexadecimal digits.
9008
+ *
9009
+ * @remarks
9010
+ * The checksum detects an edit, can collide, and makes no security claim. Over ASCII text, the first
9011
+ * lane is the standard 32-bit FNV-1a hash.
9012
+ *
9013
+ * @param text - The text to checksum
9014
+ * @returns The 16-digit lowercase hexadecimal checksum
9015
+ * @example
9016
+ * ```ts
9017
+ * computeLedgerChecksum('a') // 'e40c292ce457465e'
9018
+ * ```
9019
+ */
9020
+ function computeLedgerChecksum(text) {
9021
+ const hash = [...text].reduce((prior, character) => {
8986
9022
  const code = character.codePointAt(0) ?? 0;
8987
9023
  return {
8988
9024
  first: Math.imul(prior.first ^ code, 16777619) >>> 0,
@@ -8997,19 +9033,21 @@ function digestLedgerWindow(exchanges, messages) {
8997
9033
  /**
8998
9034
  * Collects stored messages eligible to open the recall offer.
8999
9035
  * @remarks
9000
- * Notes and findings without a result do not open the offer. User statements, including quiet
9001
- * statements available through recall's word route, can open it; this helper does not filter results.
9036
+ * Notes, findings without a result, and findings {@link collectRetiredFindings} names do not open
9037
+ * the offer. User statements, including quiet statements available through recall's word route,
9038
+ * can open it; this helper does not filter results.
9002
9039
  * @param input - The messages, findings, and classification
9003
9040
  * @param requests - The marked request ids
9004
9041
  * @param notes - The annotation ids to exclude
9005
- * @returns User statements, lookup findings with results, marked requests, and nonblank call-free replies
9042
+ * @returns User statements, lookup findings with results that no write superseded and no later finding replaced, marked requests, and nonblank call-free replies
9006
9043
  * @example
9007
9044
  * ```ts
9008
9045
  * collectRecallMessages(input, new Set(), new Set()) // eligible stored messages
9009
9046
  * ```
9010
9047
  */
9011
9048
  function collectRecallMessages(input, requests, notes) {
9012
- const findings = new Set(input.findings.filter((finding) => finding.result !== void 0).map((finding) => finding.id));
9049
+ const retired = collectRetiredFindings(input.messages, input.findings);
9050
+ const findings = new Set(input.findings.filter((finding) => finding.result !== void 0 && !retired.has(finding.id)).map((finding) => finding.id));
9013
9051
  return input.messages.filter((message) => !notes.has(message.id) && (requests.has(message.id) || message.role === "assistant" && !hasCalls(message) && message.content.trim() !== "" || message.role === "user" && !hasCalls(message) || message.role === "tool" && findings.has(message.id)));
9014
9052
  }
9015
9053
  /**
@@ -9907,6 +9945,7 @@ function matchesCutLine(line) {
9907
9945
  * @param name - The lookup tool name
9908
9946
  * @param args - The arguments the call carried; arguments JSON cannot carry leave the call as its bare tool name, per {@link renderLookupCall}
9909
9947
  * @param state - What became of the result: `failed`, `empty`, `shown` in the records, or `hidden` from them
9948
+ * @param write - If `true`, the call is a write's, whose `empty` state reads `changed nothing`; if `false`, a lookup's, whose `empty` state reads `no record`. Default: `false`
9910
9949
  * @returns The stub text, which carries the call and its state
9911
9950
  *
9912
9951
  * @example
@@ -9915,10 +9954,10 @@ function matchesCutLine(line) {
9915
9954
  * // 'lookup_order {"id":"BW-5512"}: result not shown; call recall with BW-5512'
9916
9955
  * ```
9917
9956
  */
9918
- function renderStub(name, args, state) {
9957
+ function renderStub(name, args, state, write = false) {
9919
9958
  const head = renderLookupCall(name, args);
9920
9959
  if (state === "failed") return `${head}: failed`;
9921
- if (state === "empty") return `${head}: no record`;
9960
+ if (state === "empty") return write ? `${head}: changed nothing` : `${head}: no record`;
9922
9961
  if (state === "shown") return `${head}: result shown under Records below`;
9923
9962
  return `${head}: result not shown; call recall with ${(Object.values(args).find(isString) ?? "").trim() || "its id"}`;
9924
9963
  }
@@ -9965,7 +10004,8 @@ function fitSlope(groups) {
9965
10004
  * or superseded. When the whole run is superseded, its first finding reaches that final filter.
9966
10005
  * Different text or an undefined result replaces the earlier run, so an empty lookup leaves the
9967
10006
  * earlier result out of every record. A thrown lookup leaves the earlier result live. A finding
9968
- * with no canonical identity participates in no replacement.
10007
+ * with no canonical identity participates in no replacement. A finding a write retired is
10008
+ * superseded once {@link mergeSupersessions} merges the write's edges into the classification.
9969
10009
  *
9970
10010
  * @param input - The messages, findings, and classification to read
9971
10011
  * @returns The live message ids
@@ -9992,6 +10032,100 @@ function collectLive(input) {
9992
10032
  return [...new Map(input.messages.map((message) => [message.id, message])).values()].filter((message) => message.role === "user" || message.role === "tool" && current.has(message.id)).map((message) => message.id).filter((id) => !excluded.has(id) && !classification.quiet.has(id) && (classification.supersessions.get(id) ?? []).length === 0);
9993
10033
  }
9994
10034
  /**
10035
+ * Collects each finding a write made stale, mapped to the writes that supersede it.
10036
+ *
10037
+ * @remarks
10038
+ * A write is a finding with `targets`. It supersedes another finding whose names meet its targets
10039
+ * when that finding comes before it in conversation order, or sits in its own tool group and
10040
+ * carries no targets, because one batch dispatches its calls at once. So the later of two writes in
10041
+ * one group supersedes the earlier, and never the reverse. A finding's names are its result's `ids`
10042
+ * and owner ids, or, for an empty finding, the ids its string arguments name, as `extractLiterals`
10043
+ * reads them. Names and targets compare exactly. A write never supersedes itself, a finding with
10044
+ * empty `targets` supersedes nothing, and a finding the messages don't hold takes no part.
10045
+ *
10046
+ * @param messages - The conversation's messages, which fix each finding's position and tool group
10047
+ * @param findings - The lookup and write findings of those messages
10048
+ * @returns Each superseded finding's id, in conversation order, mapped to the ids of the writes that supersede it, in conversation order
10049
+ *
10050
+ * @example
10051
+ * ```ts
10052
+ * collectWriteSupersessions(messages, findings) // Map { 'tool-1' => ['tool-2'] } when tool-2 wrote what tool-1 read
10053
+ * ```
10054
+ */
10055
+ function collectWriteSupersessions(messages, findings) {
10056
+ if (!findings.some((finding) => (finding.targets ?? []).length > 0)) return /* @__PURE__ */ new Map();
10057
+ const positions = new Map(messages.map((message, at) => [message.id, at]));
10058
+ const groups = new Map(collectToolGroups(messages).flatMap((group, at) => group.map((message) => [message.id, at])));
10059
+ const held = findings.filter((finding) => positions.has(finding.id)).toSorted((left, right) => (positions.get(left.id) ?? 0) - (positions.get(right.id) ?? 0));
10060
+ const writes = held.filter((finding) => (finding.targets ?? []).length > 0);
10061
+ return new Map(held.flatMap((finding) => {
10062
+ const names = new Set(finding.result === void 0 ? Object.values(finding.arguments).filter(isString).flatMap((value) => [...extractLiterals(value).ids]) : [...finding.result.ids, ...finding.result.owners.map((owner) => owner.id)]);
10063
+ const at = positions.get(finding.id) ?? 0;
10064
+ const group = groups.get(finding.id);
10065
+ const targeted = (finding.targets ?? []).length > 0;
10066
+ const superseders = writes.filter((write) => write.id !== finding.id && (write.targets ?? []).some((target) => names.has(target)) && ((positions.get(write.id) ?? 0) > at || !targeted && group !== void 0 && groups.get(write.id) === group));
10067
+ return superseders.length === 0 ? [] : [[finding.id, superseders.map((write) => write.id)]];
10068
+ }));
10069
+ }
10070
+ /**
10071
+ * Collects the findings a write superseded or a later finding of the same lookup replaced.
10072
+ *
10073
+ * @remarks
10074
+ * A write supersedes the findings {@link collectWriteSupersessions} maps. A later finding of the
10075
+ * same canonical lookup identity, as {@link identifyLookup} writes it, replaces an earlier one when
10076
+ * its text differs or it holds no result, and a later finding with the same text replaces nothing.
10077
+ * A finding with no canonical identity is never replaced, and a finding the messages don't hold
10078
+ * takes no part. A supersession the judge files is not read here.
10079
+ *
10080
+ * @param messages - The conversation's messages, which fix each finding's position
10081
+ * @param findings - The lookup and write findings of those messages
10082
+ * @returns The ids of the superseded and replaced findings
10083
+ *
10084
+ * @example
10085
+ * ```ts
10086
+ * collectRetiredFindings(messages, findings) // Set { 'tool-1' } when tool-2 wrote what tool-1 read
10087
+ * ```
10088
+ */
10089
+ function collectRetiredFindings(messages, findings) {
10090
+ const positions = new Map(messages.map((message, at) => [message.id, at]));
10091
+ const held = findings.filter((finding) => positions.has(finding.id));
10092
+ const replaced = [...Map.groupBy(held, (finding) => identifyLookup(finding.name, finding.arguments) ?? finding).values()].flatMap((group) => {
10093
+ const ordered = group.toSorted((left, right) => (positions.get(left.id) ?? 0) - (positions.get(right.id) ?? 0));
10094
+ const last = ordered.at(-1);
10095
+ const boundary = last === void 0 ? -1 : ordered.findLastIndex((finding) => finding.result === void 0 || finding.text !== last.text);
10096
+ return ordered.slice(0, Math.min(boundary + 1, ordered.length - 1)).map((finding) => finding.id);
10097
+ });
10098
+ return /* @__PURE__ */ new Set([...collectWriteSupersessions(messages, findings).keys(), ...replaced]);
10099
+ }
10100
+ /**
10101
+ * Merges supersession edges into a classification, adding each edge to both its supersessions and its amendments.
10102
+ *
10103
+ * @remarks
10104
+ * Each earlier message's later ids stay distinct and in conversation order, so every supersession
10105
+ * is still an amendment, as the classifier files one. An empty edge map returns the classification
10106
+ * itself.
10107
+ *
10108
+ * @param classification - The judged classification
10109
+ * @param edges - The later ids that supersede each earlier id, such as {@link collectWriteSupersessions} returns
10110
+ * @param messages - The conversation's messages, which order the later ids
10111
+ * @returns The classification with the edges merged
10112
+ *
10113
+ * @example
10114
+ * ```ts
10115
+ * mergeSupersessions(classification, new Map([['tool-1', ['tool-2']]]), messages).supersessions.get('tool-1') // ['tool-2']
10116
+ * ```
10117
+ */
10118
+ function mergeSupersessions(classification, edges, messages) {
10119
+ if (edges.size === 0) return classification;
10120
+ const positions = new Map(messages.map((message, at) => [message.id, at]));
10121
+ const [amendments = classification.amendments, supersessions = classification.supersessions] = [classification.amendments, classification.supersessions].map((laters) => new Map([.../* @__PURE__ */ new Set([...laters.keys(), ...edges.keys()])].map((earlier) => [earlier, [.../* @__PURE__ */ new Set([...laters.get(earlier) ?? [], ...edges.get(earlier) ?? []])].toSorted((left, right) => (positions.get(left) ?? 0) - (positions.get(right) ?? 0))])));
10122
+ return {
10123
+ ...classification,
10124
+ amendments,
10125
+ supersessions
10126
+ };
10127
+ }
10128
+ /**
9995
10129
  * Places a message on the record keys it joins.
9996
10130
  *
9997
10131
  * @remarks
@@ -10651,7 +10785,8 @@ var Gauge = class {
10651
10785
  // src/core/ledgers/LedgerPass.ts
10652
10786
  /**
10653
10787
  * Holds what one pass of a ledger request keeps: the selection its run adopted last, the
10654
- * conversation boundary its calls count additions from, its summed usage, and its gauge calls.
10788
+ * conversation boundary its calls count additions from, its summed usage, its gauge calls, and the
10789
+ * abort that ends its run.
10655
10790
  *
10656
10791
  * @remarks
10657
10792
  * A `LedgerRequest` opens one pass per agent run and exposes only projections of it, so no public
@@ -10665,6 +10800,7 @@ var LedgerPass = class {
10665
10800
  #selection;
10666
10801
  #boundary;
10667
10802
  #usage;
10803
+ #abort = createAbort();
10668
10804
  /**
10669
10805
  * Opens the pass at a conversation boundary.
10670
10806
  *
@@ -10698,6 +10834,22 @@ var LedgerPass = class {
10698
10834
  return this.#usage;
10699
10835
  }
10700
10836
  /**
10837
+ * Returns the signal `abort` aborts.
10838
+ *
10839
+ * @returns The pass's abort signal
10840
+ */
10841
+ get signal() {
10842
+ return this.#abort.signal;
10843
+ }
10844
+ /**
10845
+ * Reports whether `abort` ended the pass.
10846
+ *
10847
+ * @returns True if the pass was aborted; false otherwise
10848
+ */
10849
+ get aborted() {
10850
+ return this.#abort.signal.aborted;
10851
+ }
10852
+ /**
10701
10853
  * Returns the call in flight.
10702
10854
  *
10703
10855
  * @returns The latest call the pass observed, or undefined before one
@@ -10791,6 +10943,10 @@ var LedgerPass = class {
10791
10943
  think(thinking) {
10792
10944
  this.#log = [...this.#log, { thinking }];
10793
10945
  }
10946
+ /** Aborts the pass's run through `signal`, with the reason `'stray'`; a later call changes nothing. */
10947
+ abort() {
10948
+ this.#abort.abort("stray");
10949
+ }
10794
10950
  };
10795
10951
  // src/core/ledgers/LedgerRequest.ts
10796
10952
  /**
@@ -10824,6 +10980,7 @@ var LedgerRequest = class {
10824
10980
  #passes = [];
10825
10981
  #attempts = [];
10826
10982
  #admitted = /* @__PURE__ */ new Set();
10983
+ #denials = /* @__PURE__ */ new Set();
10827
10984
  #listed = [];
10828
10985
  #recalls = 0;
10829
10986
  #closed = false;
@@ -10905,6 +11062,22 @@ var LedgerRequest = class {
10905
11062
  get attempts() {
10906
11063
  return this.#attempts;
10907
11064
  }
11065
+ /**
11066
+ * Returns the signal `abort` aborts for the current pass's run.
11067
+ *
11068
+ * @returns The current pass's abort signal, or undefined before a pass
11069
+ */
11070
+ get signal() {
11071
+ return this.#passes.at(-1)?.signal;
11072
+ }
11073
+ /**
11074
+ * Reports whether `abort` ended the current pass.
11075
+ *
11076
+ * @returns True if the current pass was aborted; false otherwise, and before a pass
11077
+ */
11078
+ get aborted() {
11079
+ return this.#passes.at(-1)?.aborted ?? false;
11080
+ }
10908
11081
  enter(conversation) {
10909
11082
  if (this.#message !== void 0) return this.#message;
10910
11083
  this.#message = conversation.add({
@@ -10919,6 +11092,9 @@ var LedgerRequest = class {
10919
11092
  adopt(selection, boundary) {
10920
11093
  this.#passes.at(-1)?.adopt(selection, boundary);
10921
11094
  }
11095
+ abort() {
11096
+ this.#passes.at(-1)?.abort();
11097
+ }
10922
11098
  observe(call, position) {
10923
11099
  const draft = this.#draft;
10924
11100
  this.#sent = draft !== void 0 && this.selection?.messages.some((message) => message.id === draft.copy.id && message.content === draft.copy.content) ? draft : void 0;
@@ -10953,6 +11129,18 @@ var LedgerRequest = class {
10953
11129
  if (this.#admitted.has(key)) throw new Error(LEDGER_NOTES.repeat);
10954
11130
  this.#admitted = /* @__PURE__ */ new Set([...this.#admitted, key]);
10955
11131
  }
11132
+ release(name, args) {
11133
+ const key = canonicalStringify([name, args]);
11134
+ this.#admitted = new Set([...this.#admitted].filter((admitted) => admitted !== key));
11135
+ }
11136
+ deny(name, args) {
11137
+ const key = canonicalStringify([name, args]);
11138
+ if (key !== void 0) this.#denials = /* @__PURE__ */ new Set([...this.#denials, key]);
11139
+ }
11140
+ denied(name, args) {
11141
+ const key = canonicalStringify([name, args]);
11142
+ return key !== void 0 && this.#denials.has(key);
11143
+ }
10956
11144
  recall() {
10957
11145
  if (this.#recalls >= this.#limit) this.#closed = true;
10958
11146
  if (this.#closed) throw new Error(LEDGER_NOTES.closure);
@@ -11128,7 +11316,8 @@ var ledgerSnapshotShape = objectShape({
11128
11316
  failures: arrayShape(stringShape()),
11129
11317
  gauge: optionalShape(objectShape({
11130
11318
  scale: numberShape(),
11131
- overhead: numberShape()
11319
+ overhead: numberShape(),
11320
+ digest: stringShape()
11132
11321
  })),
11133
11322
  window: optionalShape(ledgerWindowShape)
11134
11323
  });
@@ -11203,8 +11392,8 @@ function isLedgerQuestion(value) {
11203
11392
  * `conversation` must pass `isConversationSnapshot`, and `requests`, `notes`, and `failures` must be
11204
11393
  * dense arrays of strings. Each request and note id must name a `user` message of
11205
11394
  * `conversation.messages` and each failure id a `tool` message, so an id only a section original
11206
- * holds is refused. A `gauge`, when present, must hold a finite `scale` greater than 0 and a finite
11207
- * `overhead` of at least 0. The guard is exact: an unknown member and an own `undefined` `gauge` return
11395
+ * holds is refused. A `gauge`, when present, must hold a finite `scale` greater than 0, a finite
11396
+ * `overhead` of at least 0, and a string `digest`. The guard is exact: an unknown member and an own `undefined` `gauge` return
11208
11397
  * false, at the snapshot and in its `gauge`. Unreadable members and untrusted inputs return false. The
11209
11398
  * guard is structural: it admits a conversation holding a stored `system` role
11210
11399
  * or a repeated id, which `requireConversationSnapshot` and the `Conversation` constructor then
@@ -11230,9 +11419,9 @@ function isLedgerSnapshot(value) {
11230
11419
  const isIds = arrayOf(isString);
11231
11420
  if (!isIds(requests) || !isIds(notes) || !isIds(failures)) return false;
11232
11421
  if (gauge !== void 0) {
11233
- if (!isRecord(gauge) || Object.keys(gauge).length !== 2) return false;
11234
- const { scale, overhead } = gauge;
11235
- if (!isFiniteNumber(scale) || scale <= 0 || !isFiniteNumber(overhead) || overhead < 0) return false;
11422
+ if (!isRecord(gauge) || Object.keys(gauge).length !== 3) return false;
11423
+ const { scale, overhead, digest } = gauge;
11424
+ if (!isFiniteNumber(scale) || scale <= 0 || !isFiniteNumber(overhead) || overhead < 0 || !isString(digest)) return false;
11236
11425
  }
11237
11426
  const roles = new Map(conversation.messages.map((message) => [message.id, message.role]));
11238
11427
  return requests.every((id) => roles.get(id) === "user") && notes.every((id) => roles.get(id) === "user") && failures.every((id) => roles.get(id) === "tool") && (window === void 0 || isLedgerWindowSnapshot(window));
@@ -11265,6 +11454,7 @@ var Ledger = class {
11265
11454
  #scans = /* @__PURE__ */ new Set();
11266
11455
  #calibrations = /* @__PURE__ */ new Set();
11267
11456
  #gauge;
11457
+ #digest;
11268
11458
  #window = [];
11269
11459
  #evidence = collectLedgerEvidence([], /* @__PURE__ */ new Set(), []);
11270
11460
  /**
@@ -11272,6 +11462,7 @@ var Ledger = class {
11272
11462
  * @param provider - The provider that serves the conversation
11273
11463
  * @param options - The filing policy, capacity, and request bounds
11274
11464
  * @throws {LedgerError} Thrown when a threshold, share, capacity, limit, topic, lookup, or gauge is invalid
11465
+ * @throws {LedgerError} Thrown when a write lacks the `consequential` annotation, writes come without `agent.authority`, or a write is named `recall` or shares a lookup's or another write's name (code `'WRITE'`)
11275
11466
  * @throws {LedgerError} Thrown when `predict` is not a nonnegative safe integer less than `capacity` (code `'CAPACITY'`)
11276
11467
  * @throws {LedgerError} Thrown when `questions` fails `isLedgerQuestion` (code `'QUESTION'`)
11277
11468
  * @throws {LedgerError} Thrown when `snapshot` is malformed or marks an id its conversation does not hold (code `'SNAPSHOT'`, its faults under `context.faults`)
@@ -11297,6 +11488,12 @@ var Ledger = class {
11297
11488
  if (topics.some((name) => name.trim() === "") || new Set(topics).size !== topics.length) throw new LedgerError("TOPIC", "topic names must be nonempty and unique");
11298
11489
  const lookups = ["recall", ...Array.from(options.lookups ?? [], (lookup) => lookup.tool.name)];
11299
11490
  if (new Set(lookups).size !== lookups.length) throw new LedgerError("LOOKUP", "lookup names must be unique and cannot be recall");
11491
+ const writes = Array.from(options.writes ?? [], (write) => write.tool);
11492
+ const names = [...lookups, ...writes.map((tool) => tool.name)];
11493
+ if (new Set(names).size !== names.length) throw new LedgerError("WRITE", "write names must be unique and cannot be recall or the name of a lookup");
11494
+ if (writes.some((tool) => tool.annotations?.consequential !== true)) throw new LedgerError("WRITE", "a write tool must carry annotations.consequential: true, so the host approves each call");
11495
+ const host = options.agent?.authority;
11496
+ if (writes.length > 0 && host === void 0) throw new LedgerError("WRITE", "writes need an agent.authority that gates each call");
11300
11497
  if (!isLedgerQuestion(options.questions)) throw new LedgerError("QUESTION", "questions must hold a choice category question whose criteria describe every category, a non-empty topic string, and noul amends and supersedes questions; pass the LEDGER_QUESTIONS constant for the measured wording");
11301
11498
  const snapshot = options.snapshot;
11302
11499
  if (snapshot !== void 0 && !isLedgerSnapshot(snapshot)) {
@@ -11311,13 +11508,6 @@ var Ledger = class {
11311
11508
  ...DEFAULT_LEDGER_SHARE,
11312
11509
  ...options.share
11313
11510
  };
11314
- const gauge = options.gauge ?? snapshot?.gauge;
11315
- if (gauge !== void 0) this.#gauge = new Gauge({
11316
- ...gauge,
11317
- capacity: options.capacity,
11318
- predict: this.#predict,
11319
- replay: this.#replay
11320
- });
11321
11511
  const conversations = createConversationManager();
11322
11512
  this.#conversation = snapshot === void 0 ? conversations.add() : conversations.add({ snapshot: snapshot.conversation });
11323
11513
  conversations.switch(this.#conversation.id);
@@ -11338,14 +11528,14 @@ var Ledger = class {
11338
11528
  ...options.agent?.strict === void 0 ? {} : { strict: options.agent.strict }
11339
11529
  });
11340
11530
  const tools = createToolManager();
11341
- for (const lookup of options.lookups ?? []) tools.add(createTool({
11342
- name: lookup.tool.name,
11343
- ...lookup.tool.parameters === void 0 ? {} : { parameters: lookup.tool.parameters },
11344
- ...lookup.tool.description === void 0 ? {} : { description: lookup.tool.description },
11345
- ...lookup.tool.summary === void 0 ? {} : { summary: lookup.tool.summary },
11346
- ...lookup.tool.title === void 0 ? {} : { title: lookup.tool.title },
11347
- ...lookup.tool.annotations === void 0 ? {} : { annotations: lookup.tool.annotations },
11348
- execute: (args, context) => this.#lookup(lookup.tool.name, args, context)
11531
+ for (const { tool } of [...options.lookups ?? [], ...options.writes ?? []]) tools.add(createTool({
11532
+ name: tool.name,
11533
+ ...tool.parameters === void 0 ? {} : { parameters: tool.parameters },
11534
+ ...tool.description === void 0 ? {} : { description: tool.description },
11535
+ ...tool.summary === void 0 ? {} : { summary: tool.summary },
11536
+ ...tool.title === void 0 ? {} : { title: tool.title },
11537
+ ...tool.annotations === void 0 ? {} : { annotations: tool.annotations },
11538
+ execute: (args, context) => this.#lookup(tool.name, args, context)
11349
11539
  }));
11350
11540
  tools.add(createTool({
11351
11541
  name: "recall",
@@ -11356,33 +11546,60 @@ var Ledger = class {
11356
11546
  }) }),
11357
11547
  execute: (args) => this.#recall(args)
11358
11548
  }));
11549
+ const advertised = new Authority({
11550
+ rules: [{
11551
+ match: (context) => names.includes(context.call.name),
11552
+ zone: "ledger",
11553
+ verdict: "allow"
11554
+ }],
11555
+ fallback: {
11556
+ zone: "ledger",
11557
+ verdict: "deny",
11558
+ reason: "the ledger advertises no such tool"
11559
+ }
11560
+ });
11359
11561
  this.#agent = createAgent(provider, {
11360
11562
  ...options.agent,
11361
11563
  conversations,
11362
11564
  system: options.system,
11363
11565
  tools,
11364
- authority: createAuthority({
11365
- rules: [{
11366
- match: (context) => lookups.includes(context.call.name),
11367
- zone: "ledger",
11368
- verdict: "allow"
11369
- }],
11370
- fallback: {
11566
+ authority: {
11567
+ evaluate: (context) => this.#current()?.denied(context.call.name, context.call.arguments) === true ? {
11371
11568
  zone: "ledger",
11372
11569
  verdict: "deny",
11373
- reason: "the ledger advertises no such tool"
11374
- }
11375
- }),
11570
+ reason: LEDGER_NOTES.repeat
11571
+ } : host === void 0 || context.call.name === "recall" || !names.includes(context.call.name) ? advertised.evaluate(context) : host.evaluate(context),
11572
+ ask: (context, decision, signal) => this.#current()?.denied(context.call.name, context.call.arguments) === true ? Promise.resolve(false) : host?.ask === void 0 ? advertised.ask(context, decision, signal) : host.ask(context, decision, signal)
11573
+ },
11376
11574
  limit: options.agent?.limit ?? 8,
11377
11575
  select: (conversation, request, signal) => this.#select(conversation, request, signal)
11378
11576
  });
11379
- this.#agent.emitter.on("tool", (_call, result) => {
11577
+ const digest = digestLedgerTools(this.#agent.context.tools.definitions());
11578
+ const gauge = options.gauge ?? (snapshot?.gauge?.digest === digest ? snapshot?.gauge : void 0);
11579
+ if (gauge !== void 0) {
11580
+ this.#gauge = new Gauge({
11581
+ scale: gauge.scale,
11582
+ overhead: gauge.overhead,
11583
+ capacity: options.capacity,
11584
+ predict: this.#predict,
11585
+ replay: this.#replay
11586
+ });
11587
+ this.#digest = digest;
11588
+ }
11589
+ this.#agent.emitter.on("deny", (call, _reason, decision) => {
11590
+ if (decision !== void 0 && call.name !== "recall" && names.includes(call.name)) this.#current()?.deny(call.name, call.arguments);
11591
+ });
11592
+ this.#agent.emitter.on("tool", (call, result) => {
11380
11593
  this.#flush();
11381
11594
  this.#pending.set(this.#conversation.messages().length, result);
11382
- if (!result.success && result.error === LEDGER_NOTES.repeat) {
11595
+ const stopped = denyCall(call, LEDGER_NOTES.repeat);
11596
+ if (!result.success && (result.error === LEDGER_NOTES.repeat || !stopped.success && result.error === stopped.error)) {
11383
11597
  this.#current()?.close();
11384
11598
  this.#agent.abort("repeat");
11385
11599
  }
11600
+ const request = this.#current();
11601
+ if (request?.calls.at(-1)?.tools === 0) request.abort();
11602
+ this.#release(call, result);
11386
11603
  });
11387
11604
  this.#agent.emitter.on("select", (selection) => {
11388
11605
  if (isLedgerError(selection.fault) && selection.fault.code === "REQUEST") return;
@@ -11443,6 +11660,7 @@ var Ledger = class {
11443
11660
  const conversation = this.#conversation.snapshot();
11444
11661
  const ids = conversation.messages.map((message) => message.id);
11445
11662
  const gauge = this.gauge;
11663
+ const priced = this.#digest;
11446
11664
  const window = {
11447
11665
  exchanges: this.#window.map((entry) => ({
11448
11666
  request: entry.request.id,
@@ -11463,7 +11681,10 @@ var Ledger = class {
11463
11681
  requests: ids.filter((id) => this.#requests.has(id)),
11464
11682
  notes: ids.filter((id) => this.#annotations.has(id)),
11465
11683
  failures: ids.filter((id) => this.#results.get(id)?.success === false),
11466
- ...gauge === void 0 ? {} : { gauge },
11684
+ ...gauge === void 0 || priced === void 0 ? {} : { gauge: {
11685
+ ...gauge,
11686
+ digest: priced
11687
+ } },
11467
11688
  ...window.exchanges.length === 0 || digest === void 0 ? {} : { window: {
11468
11689
  ...window,
11469
11690
  digest
@@ -11472,6 +11693,7 @@ var Ledger = class {
11472
11693
  }
11473
11694
  /**
11474
11695
  * Appends a request and serves it, recovering an unfinished or rejected first pass with one answer pass.
11696
+ * In either pass, a call the model makes in a turn that advertises no tools runs nothing and ends the pass after that generation, whatever the agent's `limit`, and when every call of that turn failed, the ledger stores the reply without its calls: a reply with text ends the pass complete, and one without text ends it partial with empty content.
11475
11697
  * A calibration call that reports no prompt usage, or a prompt usage of 0 or less, rejects with `LedgerError` code `'GAUGE'`; a provider error during calibration rejects with that error.
11476
11698
  * When the answer pass rejects, `respond` rejects with that error, and under the default `strict` a judge failure or another selection failure rejects it too. A rejection belongs to its own call: the ledger stays free, and the next call asks the judge again about whatever is still unanswered.
11477
11699
  * @param content - The request text
@@ -11491,14 +11713,14 @@ var Ledger = class {
11491
11713
  if (this.#gauge === void 0) await this.#measureGauge(caller);
11492
11714
  const entered = request.enter(this.#conversation);
11493
11715
  this.#requests.add(entered.id);
11494
- const first = await this.#generatePass(request, caller, this.#options.think).catch((error) => {
11716
+ const first = this.#dropStrayCalls(request, await this.#generatePass(request, caller, this.#options.think).catch((error) => {
11495
11717
  if (request.faulted(error)) throw error;
11496
11718
  return {
11497
11719
  content: "",
11498
11720
  partial: true,
11499
11721
  ...request.usage === void 0 ? {} : { usage: request.usage }
11500
11722
  };
11501
- });
11723
+ }));
11502
11724
  const passes = [first];
11503
11725
  if (!caller.aborted && (first.partial || first.content.trim() === "")) {
11504
11726
  const recovery = this.#buildDigest(request, entered);
@@ -11526,7 +11748,7 @@ var Ledger = class {
11526
11748
  });
11527
11749
  this.#agent.context.apply(answer);
11528
11750
  try {
11529
- passes.push(await this.#generatePass(request, caller, false));
11751
+ passes.push(this.#dropStrayCalls(request, await this.#generatePass(request, caller, false)));
11530
11752
  } finally {
11531
11753
  if (this.#agent.context.scope === answer) this.#agent.context.apply(previous);
11532
11754
  }
@@ -11563,7 +11785,8 @@ var Ledger = class {
11563
11785
  }],
11564
11786
  judgments: []
11565
11787
  }), this.#replay).slice(0, -1);
11566
- const priced = await this.#provider.generate(messages, signal, this.#agent.context.tools.definitions(), { think: false });
11788
+ const definitions = this.#agent.context.tools.definitions();
11789
+ const priced = await this.#provider.generate(messages, signal, definitions, { think: false });
11567
11790
  const bare = await this.#provider.generate(messages, signal, void 0, { think: false });
11568
11791
  if (!isFiniteNumber(priced.usage?.prompt) || priced.usage.prompt <= 0 || !isFiniteNumber(bare.usage?.prompt) || bare.usage.prompt <= 0) throw new LedgerError("GAUGE", "calibration requires prompt usage from both calls");
11569
11792
  this.#gauge = new Gauge({
@@ -11573,6 +11796,7 @@ var Ledger = class {
11573
11796
  scale: bare.usage.prompt / estimateMessages(messages),
11574
11797
  overhead: Math.max(0, priced.usage.prompt - bare.usage.prompt)
11575
11798
  });
11799
+ this.#digest = digestLedgerTools(definitions);
11576
11800
  return {
11577
11801
  scale: this.#gauge.scale,
11578
11802
  overhead: this.#gauge.overhead
@@ -11584,12 +11808,35 @@ var Ledger = class {
11584
11808
  }
11585
11809
  async #generatePass(request, signal, think) {
11586
11810
  request.open(this.#conversation.messages().length);
11811
+ const stop = request.signal;
11587
11812
  return await this.#agent.generate({
11588
- signal,
11813
+ signal: stop === void 0 ? signal : AbortSignal.any([signal, stop]),
11589
11814
  conversation: this.#conversation,
11590
11815
  ...think === void 0 ? {} : { think }
11591
11816
  });
11592
11817
  }
11818
+ #dropStrayCalls(request, pass) {
11819
+ const boundary = request.boundary;
11820
+ if (!request.aborted || boundary === void 0) return pass;
11821
+ this.#flush();
11822
+ const messages = this.#conversation.messages().slice(boundary);
11823
+ const at = messages.findLastIndex((message) => message.role === "assistant");
11824
+ const reply = messages[at];
11825
+ const replies = messages.slice(at + 1);
11826
+ if (reply === void 0 || !hasCalls(reply) || !replies.every((message) => message.role === "tool" && this.#results.get(message.id)?.success === false)) return pass;
11827
+ this.#conversation.remove([reply.id, ...replies.map((message) => message.id)]);
11828
+ for (const message of replies) this.#results.delete(message.id);
11829
+ this.#conversation.add({
11830
+ role: "assistant",
11831
+ content: reply.content,
11832
+ ...reply.thinking === void 0 ? {} : { thinking: reply.thinking }
11833
+ });
11834
+ return {
11835
+ ...pass,
11836
+ content: reply.content,
11837
+ partial: reply.content.trim() === ""
11838
+ };
11839
+ }
11593
11840
  #current() {
11594
11841
  const [request] = this.#live;
11595
11842
  return request?.message === void 0 ? void 0 : request;
@@ -11679,27 +11926,36 @@ var Ledger = class {
11679
11926
  return collectToolGroups(messages).flatMap((group) => group.slice(1).flatMap((message) => {
11680
11927
  const call = findLedgerCall(group, message);
11681
11928
  const lookup = this.#options.lookups?.find((one) => one.tool.name === call?.name);
11682
- if (call === void 0 || lookup === void 0 || this.#results.get(message.id)?.success === false) return [];
11683
- const read = attempt(() => lookup.read(call.arguments, message.content));
11684
- if (!read.success) {
11929
+ const write = this.#options.writes?.find((one) => one.tool.name === call?.name);
11930
+ if (call === void 0 || lookup === void 0 && write === void 0 || this.#results.get(message.id)?.success === false) return [];
11931
+ const read = attempt(() => lookup?.read(call.arguments, message.content));
11932
+ const written = attempt(() => write?.read(call.arguments, message.content));
11933
+ if (!read.success || !written.success) {
11934
+ const error = read.success ? written.success ? void 0 : written.error : read.error;
11685
11935
  this.#results.set(message.id, {
11686
11936
  success: false,
11687
11937
  id: call.id,
11688
11938
  name: call.name,
11689
- error: isError(read.error) ? read.error.message : String(read.error)
11939
+ error: isError(error) ? error.message : String(error)
11690
11940
  });
11691
11941
  return [];
11692
11942
  }
11943
+ const literals = Object.values(call.arguments).filter(isString).flatMap((value) => [...extractLiterals(value).ids]);
11693
11944
  const result = read.value;
11945
+ const change = written.value;
11694
11946
  return [{
11695
11947
  id: message.id,
11696
11948
  name: call.name,
11697
11949
  arguments: call.arguments,
11698
11950
  text: message.content,
11699
- result: result === void 0 ? void 0 : {
11951
+ result: result !== void 0 ? {
11700
11952
  ...result,
11701
- ids: [.../* @__PURE__ */ new Set([...result.ids, ...Object.values(call.arguments).filter(isString).flatMap((value) => [...extractLiterals(value).ids])])]
11702
- }
11953
+ ids: [.../* @__PURE__ */ new Set([...result.ids, ...literals])]
11954
+ } : change !== void 0 ? {
11955
+ ids: [.../* @__PURE__ */ new Set([...change.ids, ...literals])],
11956
+ owners: change.owners
11957
+ } : void 0,
11958
+ ...change === void 0 ? {} : { targets: [...change.targets] }
11703
11959
  }];
11704
11960
  }));
11705
11961
  }
@@ -11711,7 +11967,7 @@ var Ledger = class {
11711
11967
  messages: scan.messages,
11712
11968
  findings: scan.findings,
11713
11969
  entities: new Map(scan.messages.map((message) => [message.id, [...matchEntities(scan.index, message.content, true)]])),
11714
- classification: this.#classifier.classification()
11970
+ classification: mergeSupersessions(this.#classifier.classification(), collectWriteSupersessions(scan.messages, scan.findings), scan.messages)
11715
11971
  };
11716
11972
  return {
11717
11973
  input,
@@ -12062,7 +12318,7 @@ var Ledger = class {
12062
12318
  kept: (leader.calls ?? []).flatMap((call) => {
12063
12319
  const message = group.slice(1).find((result) => findLedgerCall(group, result) === call);
12064
12320
  const result = message === void 0 ? void 0 : this.#results.get(message.id);
12065
- return message === void 0 || !this.#options.lookups?.some((lookup) => lookup.tool.name === call.name) || result?.success === false && result.error === LEDGER_NOTES.repeat ? [] : [{
12321
+ return message === void 0 || ![...this.#options.lookups ?? [], ...this.#options.writes ?? []].some((entry) => entry.tool.name === call.name) || result?.success === false && result.error === LEDGER_NOTES.repeat ? [] : [{
12066
12322
  call,
12067
12323
  message
12068
12324
  }];
@@ -12108,19 +12364,48 @@ var Ledger = class {
12108
12364
  #renderTailStub(message, scan, shown) {
12109
12365
  const group = scan.group(message.id);
12110
12366
  const call = group === void 0 ? void 0 : findLedgerCall(group, message);
12367
+ const write = this.#options.writes?.some((one) => one.tool.name === call?.name) === true;
12111
12368
  const finding = scan.finding(message.id);
12112
12369
  const hidden = renderStub(call?.name ?? "tool", call?.arguments ?? {}, "hidden");
12113
12370
  const visible = renderStub(call?.name ?? "tool", call?.arguments ?? {}, "shown");
12114
12371
  return {
12115
12372
  ...message,
12116
- content: renderStub(call?.name ?? "tool", call?.arguments ?? {}, this.#results.get(message.id)?.success === false ? "failed" : finding?.result === void 0 ? "empty" : shown === void 0 ? hidden.length > visible.length ? "hidden" : "shown" : scan.findings.some((other) => shown.has(other.id) && matchesLookup(finding, other)) ? "shown" : "hidden")
12373
+ content: renderStub(call?.name ?? "tool", call?.arguments ?? {}, this.#results.get(message.id)?.success === false ? "failed" : finding?.result === void 0 ? "empty" : shown === void 0 ? hidden.length > visible.length ? "hidden" : "shown" : scan.findings.some((other) => shown.has(other.id) && matchesLookup(finding, other)) ? "shown" : "hidden", write)
12117
12374
  };
12118
12375
  }
12376
+ #release(call, result) {
12377
+ const request = this.#current();
12378
+ const write = this.#options.writes?.find((one) => one.tool.name === call.name);
12379
+ if (request === void 0 || write === void 0 || !result.success) return;
12380
+ const text = settleToolResult(result).content;
12381
+ const read = attempt(() => {
12382
+ const change = write.read(call.arguments, text);
12383
+ return change === void 0 ? [] : [...change.targets];
12384
+ });
12385
+ if (!read.success || read.value.length === 0) return;
12386
+ const messages = this.#conversation.messages();
12387
+ const findings = this.#readLookups(messages);
12388
+ const id = crypto.randomUUID();
12389
+ const edges = collectWriteSupersessions([...messages, {
12390
+ id,
12391
+ role: "tool",
12392
+ content: text,
12393
+ call: call.id
12394
+ }], [...findings, {
12395
+ id,
12396
+ name: call.name,
12397
+ arguments: call.arguments,
12398
+ text,
12399
+ result: void 0,
12400
+ targets: read.value
12401
+ }]);
12402
+ for (const finding of findings) if ((edges.get(finding.id) ?? []).includes(id) && this.#options.lookups?.some((lookup) => lookup.tool.name === finding.name) === true) request.release(finding.name, finding.arguments);
12403
+ }
12119
12404
  #observeTurn() {
12120
12405
  this.#flush();
12121
12406
  const request = this.#current();
12122
12407
  const boundary = request?.boundary;
12123
- if (request === void 0 || boundary === void 0) return;
12408
+ if (request === void 0 || boundary === void 0 || request.aborted) return;
12124
12409
  const additions = this.#conversation.messages().slice(boundary);
12125
12410
  const messages = this.#agent.context.build(request.selection);
12126
12411
  const names = this.#agent.context.scope?.tools;
@@ -12141,10 +12426,17 @@ var Ledger = class {
12141
12426
  return this.#conversation.messages().filter((message) => message.role === "assistant").reduce((longest, message) => message.content.length > longest.length ? message.content : longest, "");
12142
12427
  }
12143
12428
  #lookup(name, args, context) {
12144
- this.#current()?.admit(name, args);
12145
- const lookup = this.#options.lookups?.find((one) => one.tool.name === name);
12146
- if (lookup === void 0) throw new Error(`unknown lookup ${name}`);
12147
- return lookup.tool.execute(args, context);
12429
+ const request = this.#current();
12430
+ request?.admit(name, args);
12431
+ const entry = this.#options.lookups?.find((one) => one.tool.name === name) ?? this.#options.writes?.find((one) => one.tool.name === name);
12432
+ if (entry === void 0) throw new Error(`unknown lookup or write ${name}`);
12433
+ const ran = attempt(() => entry.tool.execute(args, context));
12434
+ if (!ran.success) return this.#reject(request, name, args, ran.error);
12435
+ return isPromiseLike(ran.value) ? Promise.resolve(ran.value).catch((error) => this.#reject(request, name, args, error)) : ran.value;
12436
+ }
12437
+ #reject(request, name, args, error) {
12438
+ if (isToolError(error) && error.code === "ARGUMENTS") request?.release(name, args);
12439
+ throw error;
12148
12440
  }
12149
12441
  #recall(args) {
12150
12442
  const request = this.#current();
@@ -12182,7 +12474,8 @@ var Ledger = class {
12182
12474
  };
12183
12475
  });
12184
12476
  const start = request?.message === void 0 ? -1 : scan.position(request.message.id) ?? -1;
12185
- const listable = new Map(input.messages.filter((message, at) => !this.#requests.has(message.id) && (at < start || message.role === "tool")).map((message) => [message.id, message]));
12477
+ const retired = collectRetiredFindings(scan.messages, scan.findings);
12478
+ const listable = new Map(input.messages.filter((message, at) => !this.#requests.has(message.id) && !retired.has(message.id) && (at < start || message.role === "tool")).map((message) => [message.id, message]));
12186
12479
  const matched = new Set(searches.flatMap((search) => [...search.matched]));
12187
12480
  const unmatched = searches.filter((search) => search.matched.size === 0 && search.words.length > 0);
12188
12481
  const gathered = [...listable.values()].reverse().reduce((state, message) => {
@@ -12303,6 +12596,6 @@ var Ledger = class {
12303
12596
  function createLedger(provider, options) {
12304
12597
  return new Ledger(provider, options);
12305
12598
  }
12306
- export { AUTHORITY_NOTES, Agent, AgentContext, AgentError, AgentFanout, AgentJobError, AgentJudge, AgentProvider, AgentRegistry, Approver, Authority, CONVERSATION_RECAP_PREFIX, CONVERSATION_ROLES, Channel, Classifier, ContextError, Conversation, ConversationError, ConversationManager, DECISIVE_CATEGORIES, DEFAULT_AGENT_LIMIT, DEFAULT_CONVERSATION_KEEP, DEFAULT_LEDGER_LIMIT, DEFAULT_LEDGER_SHARE, DEFAULT_PROVIDER_TIMEOUT, DEFAULT_RECALL_LIMIT, DEFAULT_RELAY_LIMIT, DETERMINISTIC_JUDGE_ERROR, DatabaseConversationStore, DatabaseMemoStore, Gauge, IMAGE_TOKEN_ESTIMATE, INVALID_RELAY_STATUS, Instruction, InstructionManager, JudgeAbortError, JudgeError, JudgmentManager, LEDGER_CATEGORIES, LEDGER_NOTES, LEDGER_OWNER_PREFIX, LEDGER_QUESTIONS, LEDGER_RECALL_LABELS, LEDGER_RULES_KEY, LEDGER_SCALE_DRIFT, LEDGER_SLICE, LEDGER_WORD, Ledger, LedgerError, LedgerRequest, MAX_ERROR_BODY_LENGTH, MEMO_SECTION_HEADER, MESSAGE_ROLES, MESSAGE_TOKEN_OVERHEAD, MemoManager, MemoryConversationStore, MemoryMemoStore, OVERSIZED_RELAY_STATUS, PLACED_CATEGORIES, ProviderAbortError, ProviderError, QUIET_CATEGORIES, REFUSED_RELAY_CONTENT_TYPE, REFUSED_RELAY_STATUS, RELAY_CONTENT_TYPE, RELAY_PROVIDER_MESSAGE, RelayProvider, RelayStream, SCOPE_NOTE, SKILL_SECTION_HEADER, SYSTEM_ONE_PATH, Scope, ScopeManager, SkillManager, SystemOneJudge, THINK_CLOSE, THINK_OPEN, ThinkSplitter, UNAUTHORIZED_RELAY_STATUS, UPSTREAM_RELAY_STATUS, WORKSPACE_SECTION_HEADER, agentAnswerContract, agentAnswerShape, agentChunkToFrame, agentFrameContract, agentFrameShape, agentJobContract, agentJobShape, agentPromptContract, agentPromptShape, agentResultToFrame, agentResultToJSON, agentStreamToFrames, armReaderAbort, assembleResult, attachImages, attachUserImages, buildJudgeResult, buildJudgments, buildLedgerCut, buildLedgerIndex, buildLedgerLineKey, buildLedgerRenderContext, buildLines, buildProviderHeaders, buildProviderResult, buildRecallMessage, buildRecapMessage, buildRecords, buildRelayRequest, buildRequestMessage, buildSummaryMessage, chargeUsage, collectAmendments, collectExchanges, collectImageData, collectLedgerEvidence, collectLedgerLines, collectLedgerReplies, collectLive, collectNames, collectProjectionIds, collectRecallMessages, collectRegistry, collectToolGroups, computeThinking, conversationSnapshotContract, conversationSnapshotShape, copyJSON, createAgent, createAgentContext, createAgentQueue, createAgentRegistry, createAgentRunner, createApprover, createAuthority, createChannel, createConversation, createConversationManager, createDatabaseConversationStore, createDatabaseMemoStore, createInstruction, createInstructionManager, createLedger, createMemoManager, createMemoryConversationStore, createMemoryMemoStore, createRelay, createRelayProvider, createScope, createScopeManager, createSkillManager, createSystemOneJudge, createThinkSplitter, cutListing, denyCall, describeQuestionRefusal, describeRelayRefusal, describeSnapshotRefusal, digestLedgerWindow, estimateMessages, estimateRequest, estimateTokens, extractLiterals, extractQueueOptions, extractSystemOneAnswer, extractSystemOneUsage, extractWords, filterAllowList, findLedgerCall, findLedgerCut, fitSlope, freezeJSON, handleAgentQueueJob, hasCalls, hasHiddenRecall, identifyLookup, inferStop, intersectKeys, isAgentError, isAgentJobError, isAuthorityDecision, isAuthorityVerdict, isContextError, isConversation, isConversationError, isConversationSnapshot, isFraction, isJudgeAbortError, isJudgeAnswer, isJudgeEntry, isJudgeError, isJudgeQuestion, isJudgment, isLedgerError, isLedgerQuestion, isLedgerSnapshot, isLedgerWindowSnapshot, isMemo, isMemoOrigin, isMemoSnapshot, isMessage, isMessageInput, isNonNegativeSafeInteger, isPositiveSafeInteger, isProviderAbortError, isProviderError, isProviderResult, isSection, isSystemOneAnswer, isSystemOneResponse, joinThinking, judgeAnswerContract, judgeAnswerShape, judgeEntryShape, judgeQuestionContract, judgeQuestionShape, judgmentContract, judgmentShape, ledgerSnapshotContract, ledgerSnapshotShape, ledgerWindowContract, ledgerWindowShape, linkOwners, matchEntities, matchesCutLine, matchesJSON, matchesJudgment, matchesLookup, measureAnswer, measureMessage, measureRequest, memoContract, memoShape, memoSnapshotContract, memoSnapshotShape, messageContract, messageInputContract, messageInputShape, messageShape, normalizeArgument, normalizeWord, placeMember, providerRequestContract, providerRequestShape, providerResultContract, providerResultShape, questionToSystemOne, raceSignal, rankLedgerCut, readBodyText, readChunks, readFailure, readJudgeOutcome, readProviderDelta, relayFrameContract, relayFrameShape, releaseReader, removeEntries, renderArguments, renderCauseChain, renderFencedFile, renderIndexEntry, renderJSON, renderLedgerAnnotation, renderLedgerPinned, renderLedgerRecord, renderLookupCall, renderSection, renderStub, renderToolValue, renderTopicNames, requestToRelay, requireAnswer, requireBudget, requireConversation, requireConversationSnapshot, requireCumulative, requireEntry, requireFallback, requireJudgeResult, requireJudgment, requireKeep, requireLimit, requireMessageInput, requireProviderResult, requireRole, requireRules, requireSectionsCap, requireSummary, resolveDecision, resolvePredict, sanitizeToken, sanitizeUsage, scanAmendments, scanJudgmentTies, scanProbability, sectionContract, sectionShape, selectRecords, settleAgentJob, settleToolResult, splitResultLines, splitSentences, splitTopic, splitWords, stripThinking, sumUsage, tokenUsageShape, tokenUsageToJSON, toolCallShape };
12599
+ export { AUTHORITY_NOTES, Agent, AgentContext, AgentError, AgentFanout, AgentJobError, AgentJudge, AgentProvider, AgentRegistry, Approver, Authority, CONVERSATION_RECAP_PREFIX, CONVERSATION_ROLES, Channel, Classifier, ContextError, Conversation, ConversationError, ConversationManager, DECISIVE_CATEGORIES, DEFAULT_AGENT_LIMIT, DEFAULT_CONVERSATION_KEEP, DEFAULT_LEDGER_LIMIT, DEFAULT_LEDGER_SHARE, DEFAULT_PROVIDER_TIMEOUT, DEFAULT_RECALL_LIMIT, DEFAULT_RELAY_LIMIT, DETERMINISTIC_JUDGE_ERROR, DatabaseConversationStore, DatabaseMemoStore, Gauge, IMAGE_TOKEN_ESTIMATE, INVALID_RELAY_STATUS, Instruction, InstructionManager, JudgeAbortError, JudgeError, JudgmentManager, LEDGER_CATEGORIES, LEDGER_NOTES, LEDGER_OWNER_PREFIX, LEDGER_QUESTIONS, LEDGER_RECALL_LABELS, LEDGER_RULES_KEY, LEDGER_SCALE_DRIFT, LEDGER_SLICE, LEDGER_WORD, Ledger, LedgerError, LedgerRequest, MAX_ERROR_BODY_LENGTH, MEMO_SECTION_HEADER, MESSAGE_ROLES, MESSAGE_TOKEN_OVERHEAD, MemoManager, MemoryConversationStore, MemoryMemoStore, OVERSIZED_RELAY_STATUS, PLACED_CATEGORIES, ProviderAbortError, ProviderError, QUIET_CATEGORIES, REFUSED_RELAY_CONTENT_TYPE, REFUSED_RELAY_STATUS, RELAY_CONTENT_TYPE, RELAY_PROVIDER_MESSAGE, RelayProvider, RelayStream, SCOPE_NOTE, SKILL_SECTION_HEADER, SYSTEM_ONE_PATH, Scope, ScopeManager, SkillManager, SystemOneJudge, THINK_CLOSE, THINK_OPEN, ThinkSplitter, UNAUTHORIZED_RELAY_STATUS, UPSTREAM_RELAY_STATUS, WORKSPACE_SECTION_HEADER, agentAnswerContract, agentAnswerShape, agentChunkToFrame, agentFrameContract, agentFrameShape, agentJobContract, agentJobShape, agentPromptContract, agentPromptShape, agentResultToFrame, agentResultToJSON, agentStreamToFrames, armReaderAbort, assembleResult, attachImages, attachUserImages, buildJudgeResult, buildJudgments, buildLedgerCut, buildLedgerIndex, buildLedgerLineKey, buildLedgerRenderContext, buildLines, buildProviderHeaders, buildProviderResult, buildRecallMessage, buildRecapMessage, buildRecords, buildRelayRequest, buildRequestMessage, buildSummaryMessage, chargeUsage, collectAmendments, collectExchanges, collectImageData, collectLedgerEvidence, collectLedgerLines, collectLedgerReplies, collectLive, collectNames, collectProjectionIds, collectRecallMessages, collectRegistry, collectRetiredFindings, collectToolGroups, collectWriteSupersessions, computeLedgerChecksum, computeThinking, conversationSnapshotContract, conversationSnapshotShape, copyJSON, createAgent, createAgentContext, createAgentQueue, createAgentRegistry, createAgentRunner, createApprover, createAuthority, createChannel, createConversation, createConversationManager, createDatabaseConversationStore, createDatabaseMemoStore, createInstruction, createInstructionManager, createLedger, createMemoManager, createMemoryConversationStore, createMemoryMemoStore, createRelay, createRelayProvider, createScope, createScopeManager, createSkillManager, createSystemOneJudge, createThinkSplitter, cutListing, denyCall, describeQuestionRefusal, describeRelayRefusal, describeSnapshotRefusal, digestLedgerTools, digestLedgerWindow, estimateMessages, estimateRequest, estimateTokens, extractLiterals, extractQueueOptions, extractSystemOneAnswer, extractSystemOneUsage, extractWords, filterAllowList, findLedgerCall, findLedgerCut, fitSlope, freezeJSON, handleAgentQueueJob, hasCalls, hasHiddenRecall, identifyLookup, inferStop, intersectKeys, isAgentError, isAgentJobError, isAuthorityDecision, isAuthorityVerdict, isContextError, isConversation, isConversationError, isConversationSnapshot, isFraction, isJudgeAbortError, isJudgeAnswer, isJudgeEntry, isJudgeError, isJudgeQuestion, isJudgment, isLedgerError, isLedgerQuestion, isLedgerSnapshot, isLedgerWindowSnapshot, isMemo, isMemoOrigin, isMemoSnapshot, isMessage, isMessageInput, isNonNegativeSafeInteger, isPositiveSafeInteger, isProviderAbortError, isProviderError, isProviderResult, isSection, isSystemOneAnswer, isSystemOneResponse, joinThinking, judgeAnswerContract, judgeAnswerShape, judgeEntryShape, judgeQuestionContract, judgeQuestionShape, judgmentContract, judgmentShape, ledgerSnapshotContract, ledgerSnapshotShape, ledgerWindowContract, ledgerWindowShape, linkOwners, matchEntities, matchesCutLine, matchesJSON, matchesJudgment, matchesLookup, measureAnswer, measureMessage, measureRequest, memoContract, memoShape, memoSnapshotContract, memoSnapshotShape, mergeSupersessions, messageContract, messageInputContract, messageInputShape, messageShape, normalizeArgument, normalizeWord, placeMember, providerRequestContract, providerRequestShape, providerResultContract, providerResultShape, questionToSystemOne, raceSignal, rankLedgerCut, readBodyText, readChunks, readFailure, readJudgeOutcome, readProviderDelta, relayFrameContract, relayFrameShape, releaseReader, removeEntries, renderArguments, renderCauseChain, renderFencedFile, renderIndexEntry, renderJSON, renderLedgerAnnotation, renderLedgerPinned, renderLedgerRecord, renderLookupCall, renderSection, renderStub, renderToolValue, renderTopicNames, requestToRelay, requireAnswer, requireBudget, requireConversation, requireConversationSnapshot, requireCumulative, requireEntry, requireFallback, requireJudgeResult, requireJudgment, requireKeep, requireLimit, requireMessageInput, requireProviderResult, requireRole, requireRules, requireSectionsCap, requireSummary, resolveDecision, resolvePredict, sanitizeToken, sanitizeUsage, scanAmendments, scanJudgmentTies, scanProbability, sectionContract, sectionShape, selectRecords, settleAgentJob, settleToolResult, splitResultLines, splitSentences, splitTopic, splitWords, stripThinking, sumUsage, tokenUsageShape, tokenUsageToJSON, toolCallShape };
12307
12600
 
12308
12601
  //# sourceMappingURL=index.js.map