opencode-zen-freex 0.2.16 → 0.2.18

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.
package/tool-response.mjs CHANGED
@@ -19,8 +19,8 @@ function headersFor(response) {
19
19
  }
20
20
 
21
21
  // failure answers the error a client reads. `code` says what went wrong
22
- // where the shape has room for it: only a call of no tool the agent has is
23
- // an undeclared one, and a limit, a truncation or a malformed upstream is
22
+ // where the shape has room for it: only a call the guard cannot name at all
23
+ // is an undeclared one, and a limit, a truncation or a malformed upstream is
24
24
  // not.
25
25
  function failure(protocol, message, stream = false, code = "upstream_error") {
26
26
  if (protocol === "anthropic") return { type: "error", error: { type: "api_error", message } };
@@ -71,10 +71,14 @@ function foldsTo(declared, key) {
71
71
  function declaredNames(policy) {
72
72
  const held = policy?.declared;
73
73
  const names = held instanceof Set ? [...held] : held;
74
+ // A field that is not one of the two shapes gets here as itself: `names` is
75
+ // the field's own value unless it was a Set, so an array never reaches this
76
+ // message (the old `Array.isArray(held) ? "an array of those"` arm could not
77
+ // be read at all).
74
78
  if (!Array.isArray(names))
75
79
  throw new TypeError(
76
80
  `policy.declared must be a Set or an array of tool names, not ${
77
- held === null ? "null" : Array.isArray(held) ? "an array of those" : typeof held
81
+ held === null ? "null" : typeof held
78
82
  }`,
79
83
  );
80
84
  for (const name of names) {
@@ -84,8 +88,15 @@ function declaredNames(policy) {
84
88
  return new Set(names);
85
89
  }
86
90
 
87
- // nameGuard answers the agent's name for a call, or null for a call of no
88
- // tool the agent has (a missing name included).
91
+ // nameGuard answers the agent's name for a call: the agent's spelling when one
92
+ // folds to it, else the name as the model wrote it. A call of a tool the agent
93
+ // has no spelling of (bedit, psh, mcp__netcatcatty__unused) reaches the agent
94
+ // as called, as it does on every other provider: the agent answers it with its
95
+ // own unknown-tool error, and the model corrects the name and goes on (#209).
96
+ // Leaving it out ended an agent's turn as a plain answer, which an autonomous
97
+ // host reads as "the task is done" (#76 fixed the failure that came before
98
+ // this one; that reply is not handed a turn's end any more, it is handed a
99
+ // call). Only a call with no name answers null, and is left out.
89
100
  function nameGuard(policy) {
90
101
  const declared = declaredNames(policy);
91
102
  const lower = (name) => name.toLowerCase();
@@ -97,16 +108,16 @@ function nameGuard(policy) {
97
108
  if (declared.has(name)) return name;
98
109
  const match =
99
110
  byCase.get(lower(name)) ?? (byCase.has(lower(name)) ? null : byShape.get(bare(name)));
100
- return match || null;
111
+ return match || name;
101
112
  };
102
113
  }
103
114
 
104
- // The models also call tools no agent has: bedit, bcp, ash, a mangled
105
- // mcp__netcatcatty__unused (#76). Such a call can't be run or named onto
106
- // one the agent has, and failing the reply for it threw away the whole
107
- // turn. It is left out instead, and the reply says so in its text, where
108
- // the agent's user and the model's next turn both read it. A reply left
109
- // with no call ends as a plain stop.
115
+ // A call with no name can't be run, named onto one the agent has, or answered
116
+ // by any agent: nothing keys a result by it and its own id names nothing. It
117
+ // is left out instead of failing the reply — failing threw away the whole turn
118
+ // (#76) — and the reply says so in its text, where the agent's user and the
119
+ // model's next turn both read it. A reply left with no call ends as a plain
120
+ // stop. Every named call, whatever the name, is handed on as called.
110
121
  export function droppedNote(name) {
111
122
  return typeof name === "string" && name
112
123
  ? `(The model called a tool this agent doesn't have: ${JSON.stringify(name.slice(0, 128))}. The call was left out.)`
@@ -182,6 +193,16 @@ function isCall(protocol, value) {
182
193
  );
183
194
  }
184
195
 
196
+ // What a call is named by, when it is named at all: a non-empty string. ""
197
+ // names nothing a client can key by — the empty id this route was measured
198
+ // putting on a call — and neither does a number or a boolean. Every place
199
+ // that asks whether a call has an identity asks it here, or two calls of one
200
+ // reply share one item (measured: two `"call_id": ""` calls each became the
201
+ // item `msg_`, and their arguments frames could not be matched to either).
202
+ function identity(value) {
203
+ return typeof value === "string" && value !== "" ? value : undefined;
204
+ }
205
+
185
206
  // Counts the ids invented for calls the reply gives nothing to place: a
186
207
  // counter cannot be stable, but it is the only thing that keeps two of them
187
208
  // apart (see inPlaceOf).
@@ -190,10 +211,10 @@ let synthesized = 0;
190
211
  // What a call left out of a Responses or Messages reply becomes: text
191
212
  // saying so, in the call's place, so no later index moves. `at` is where the
192
213
  // call sits in the reply as far as the caller can tell — a frame's
193
- // output_index, or the index the walk found it at. `placed` is the token a
194
- // caller that follows the whole stream already settled on for a call whose
195
- // frames place it nowhere at all (see snapshotStream): without it every
196
- // rewrite of that call invented a new id.
214
+ // output_index, or the index the walk found it at. `placed` is the id a
215
+ // caller that follows the whole stream has already answered for this call
216
+ // (see snapshotStream): without it every rewrite of that call invented a new
217
+ // id.
197
218
  function inPlaceOf(protocol, call, note, at, placed) {
198
219
  if (protocol === "anthropic") return { type: "text", text: note };
199
220
  // An item id is what a Responses client keys the item by, and what the
@@ -206,16 +227,20 @@ function inPlaceOf(protocol, call, note, at, placed) {
206
227
  // and a call the reply places nowhere takes the id its own stream settled
207
228
  // on for it.
208
229
  //
209
- // Both arms still invent ids in one reply, and they still name them under
210
- // different words: a counter that happened to reach another call's index
211
- // would hand two unrelated calls the same item, which is the failure the
212
- // counter was introduced to prevent.
230
+ // The id a stream already answered comes first, before the place the caller
231
+ // knows. The id the call *itself* carries comes last, and only behind the
232
+ // `msg_` the invented kind is named under: a word is not the guard's to hand
233
+ // out twice. A reply can name two items by one word (this route was measured
234
+ // settling on an id only after the frame that opened the call, and nothing
235
+ // stops the same id naming a call the reply keeps), and the note taking that
236
+ // id verbatim would key two items — the note and the item the reply kept —
237
+ // by one word, which is the failure this arm exists to prevent, read back at
238
+ // the other end. Behind the prefix the word cannot collide with an item of
239
+ // the reply, and a call_id the reply used is still named in the id it
240
+ // answers, as it was.
213
241
  const id =
214
- typeof call.id === "string" && call.id
215
- ? call.id
216
- : `msg_${
217
- call.call_id ?? placed ?? (at === undefined ? `anon_${++synthesized}` : `dropped_${at}`)
218
- }`;
242
+ placed ??
243
+ `msg_${identity(call.call_id) ?? (at === undefined ? `anon_${++synthesized}` : `dropped_${at}`)}`;
219
244
  return {
220
245
  type: "message",
221
246
  id,
@@ -237,10 +262,27 @@ function inPlaceOf(protocol, call, note, at, placed) {
237
262
  // the wire leaves with an id: the last-resort kind, process-unique under the
238
263
  // same counter the invented item ids use, so two invented calls cannot share
239
264
  // one.
265
+ //
266
+ // An invented id is only process-unique, and the counter is shared with every
267
+ // earlier reply, so it can reach a word the same reply already placed: an
268
+ // upstream that names its calls call_1, call_2, … names the very ids the
269
+ // counter mints, and a call left unnamed beside those used to be handed one of
270
+ // them — two calls of one reply under one id, which is the same failure the
271
+ // counter exists to prevent, one step further out. The ids the calls being
272
+ // flushed already carry are read first, and an invented id misses all of them.
240
273
  function ensureCallIDs(calls) {
274
+ const held = new Set();
275
+ for (const call of calls) {
276
+ const id = identity(call.id);
277
+ if (id !== undefined) held.add(id);
278
+ }
241
279
  for (const call of calls) {
242
- if (typeof call.id === "string" && call.id !== "") continue;
243
- call.id = `call_${++synthesized}`;
280
+ if (identity(call.id) !== undefined) continue;
281
+ let id;
282
+ do id = `call_${++synthesized}`;
283
+ while (held.has(id));
284
+ held.add(id);
285
+ call.id = id;
244
286
  }
245
287
  }
246
288
 
@@ -334,6 +376,11 @@ function checkSnapshots(data, protocol, checkName, placeOf) {
334
376
  return { changed, kept, left };
335
377
  }
336
378
 
379
+ // The slot a fragmented call or a choice sits in. A frame that names no index
380
+ // is read as the first one, which is what the collapse path reads it as too
381
+ // (stream.mjs's `index`): a fragment carries no other place it could hold, and
382
+ // the two paths over one stream must answer one reply (pinned by the chat test
383
+ // "a fragment that names no index is read the same by both paths").
337
384
  function position(value) {
338
385
  const result = value ?? 0;
339
386
  if (!Number.isSafeInteger(result) || result < 0)
@@ -443,7 +490,16 @@ function chatBuffer(checkName) {
443
490
  const delta = choice.delta;
444
491
  if (!object(delta)) continue;
445
492
  if (typeof delta.content === "string" && delta.content) wrote.add(position(choice.index));
446
- if (delta.tool_calls == null && delta.function_call == null) continue;
493
+ // A `tool_calls` that is there but empty is a call of nothing: a
494
+ // gateway re-emitting the shape with a role-only delta sends one, and
495
+ // no call is being waited for. Reading it as a call opened a round
496
+ // with nothing in it — the choice's `ended` mark was dropped with it,
497
+ // a choice the upstream never sent was flushed at [DONE], and a stream
498
+ // that then closed without the marker failed the whole turn "a tool
499
+ // call unfinished" while nothing was pending. It is left where it is,
500
+ // as a field the guard has nothing to say about.
501
+ const fragments = list(delta.tool_calls);
502
+ if (delta.function_call == null && !fragments.length) continue;
447
503
  changed = true;
448
504
  // A call buffered now opens a round of its own for this choice: a
449
505
  // choice that finished as a plain stop because every call of an
@@ -451,7 +507,7 @@ function chatBuffer(checkName) {
451
507
  const at = position(choice.index);
452
508
  ended.delete(at);
453
509
  const state = stateFor(at, data);
454
- for (const fragment of list(delta.tool_calls)) {
510
+ for (const fragment of fragments) {
455
511
  if (!object(fragment) || (fragment.type != null && fragment.type !== "function")) {
456
512
  throw new GuardError("Unsupported upstream tool delta");
457
513
  }
@@ -534,8 +590,33 @@ function chatBuffer(checkName) {
534
590
  // as text carrying the note, and its arguments are never sent. A message
535
591
  // whose every call was left out stops as a turn's end, not as tool use.
536
592
  function snapshotStream(protocol, checkName) {
537
- const left = new Map(); // output_index or content block index → true
538
- const leftIDs = new Set(); // Responses item ids left out
593
+ // Where a call left out was replaced, by its output_index (or a Messages
594
+ // block's index, which stores true: the id it answers is the text's, not an
595
+ // item's). A copy of the call that lands on the same place answers the id
596
+ // recorded there rather than the one the upstream names it by later.
597
+ const left = new Map();
598
+ // The places the items the reply kept hold. A place is a fact about the
599
+ // reply, so an arguments frame that gives one belongs to whoever holds it —
600
+ // the item kept there, or the call left out there — whatever words either
601
+ // was named by. Reading the place first is what makes the answer the same
602
+ // whichever order the two calls arrived in: two sets subtracted from each
603
+ // other answered by arrival order instead (a kept call named X before a
604
+ // dropped one named X let the dropped call's frames through, and the same
605
+ // reply with the two swapped did not).
606
+ const keptPlaces = new Set();
607
+ // The words the items the reply kept were named by, for the one case a place
608
+ // cannot answer: two items stating one index, one of them replaced by a note
609
+ // (an upstream at odds with itself). There the frame's own word is all that
610
+ // says whose it is, and a word a kept item was named by is that item's —
611
+ // reading the place alone would swallow the declared call's frames with the
612
+ // dropped call's.
613
+ const keptWords = new Set();
614
+ // Every word the calls this stream left out are named by: the ids the guard
615
+ // invented for them, and the ids and call_ids the upstream named them by. A
616
+ // frame that gives no place and arrives outside any item is read by its
617
+ // word, and a word a call left out was named by is never forwarded —
618
+ // reaching the client it would describe an item that was replaced by a note.
619
+ const leftIDs = new Set();
539
620
  // The calls this stream placed nowhere — its frames carried no output_index
540
621
  // and no item id — in the order it opened them. Nothing in those frames says
541
622
  // which item is which, so the order is all the guard has: the k-th item a
@@ -546,11 +627,34 @@ function snapshotStream(protocol, checkName) {
546
627
  // (inPlaceOf), which is also what its arguments frames are matched by. An
547
628
  // entry is settled once the stream has left the call.
548
629
  const nowhere = []; // { token, name, settled }
549
- // The item ids the reply has named on an item of its own: the arguments
550
- // frames of a call the reply opened carry one of these, and are not the
551
- // placeless call's to swallow.
630
+ // What each word the reply has used for a call it left out answers: the id
631
+ // the guard invented for that call, under every word the reply names it by.
632
+ // One call is replaced more than once, and the client keys an item by the id
633
+ // it was handed first — the frames of the measured route carry no id at all
634
+ // until their arguments frames name one, so the id the upstream settled on
635
+ // arrives after the replacement, and answering it then would make one call
636
+ // two items (inPlaceOf).
637
+ const minted = new Map(); // a word the call is named by → the id it answers
638
+ // The item ids the reply has named on an item of its own, and the words of
639
+ // the item the stream is inside. A frame that names one of the latter is that
640
+ // item's — the reply naming the same word elsewhere says nothing about it —
641
+ // while a frame naming one of the former belongs to that other item, which
642
+ // is what keeps an item the guard kept from being read as the one it is
643
+ // inside.
552
644
  const seen = new Set();
645
+ const insideWords = new Set();
553
646
  let open = -1; // the placeless call the stream is inside, or -1
647
+ // The item the stream is inside, as far as an arguments frame that gives no
648
+ // place of its own is concerned: the id the call left out there answers (and
649
+ // the name it was called), or undefined with `insideKept` for an item the
650
+ // reply kept. `closingID`/`closingName` are the same for the frame that
651
+ // closes an item: a copy of a call can arrive with nothing to name it by,
652
+ // and the call being closed is what it is.
653
+ let insideID;
654
+ let insideName;
655
+ let insideKept = false;
656
+ let closingID;
657
+ let closingName;
554
658
  let kept = 0;
555
659
  let dropped = 0;
556
660
  // What a frame says the reply's items are called. A frame that names none of
@@ -565,82 +669,190 @@ function snapshotStream(protocol, checkName) {
565
669
  if (object(value) && typeof value.id === "string" && value.id) seen.add(value.id);
566
670
  }
567
671
  }
568
- // Opens a placeless call of its own and answers the token naming it.
672
+ // Opens a call the reply placed nowhere — a frame with no output_index — and
673
+ // answers the entry that names it. Its id is what the guard answered for the
674
+ // call, filled in by the caller once it knows: the entry is what a later copy
675
+ // of the same call is recognised by, and that copy can carry nothing at all —
676
+ // no word of its own and no index (measured: the frame that closes the call
677
+ // drops the call_id the frame that opened it carried), so the call's name
678
+ // among the entries is the only thing left to read it by.
569
679
  function entering(name, settled = false) {
570
- const token = `anon_${++synthesized}`;
571
- nowhere.push({ token, name, settled });
680
+ const entry = { name, id: undefined, settled };
681
+ nowhere.push(entry);
572
682
  open = nowhere.length - 1;
573
- return token;
683
+ return entry;
574
684
  }
685
+ // The words a frame can name this call by: the item id and the call_id, a
686
+ // non-empty string each (identity), and both name the one call.
687
+ function names(call) {
688
+ return [identity(call.id), identity(call.call_id)].filter((value) => value !== undefined);
689
+ }
690
+ // Whether the call carries a word of its own to be placed by — the test
691
+ // inPlaceOf and ensureCallIDs make, so all three read "" the same way.
575
692
  function identified(call) {
576
- return (typeof call.id === "string" && call.id) || call.call_id != null;
693
+ return identity(call.id) !== undefined || identity(call.call_id) !== undefined;
694
+ }
695
+ // Records that this call answers `id`, under every word the reply has for it:
696
+ // the copies of the call that follow answer the same one.
697
+ function mintedFor(call, id) {
698
+ minted.set(id, id);
699
+ for (const word of names(call)) minted.set(word, id);
700
+ return id;
577
701
  }
578
- // The stream's answer for a call whose frames carry no output_index and no
579
- // item id, or undefined when the call is not one the stream has to place:
580
- // the walk hands every call it leaves out through here.
702
+ // The stream's answer for a call it leaves out: the id this call already
703
+ // answers, or undefined when the stream has none and the call keeps its own.
704
+ // The walk hands every call it leaves out through here.
581
705
  function placeOf(call, at, listed) {
582
- if (protocol !== "responses" || identified(call)) return undefined;
706
+ if (protocol !== "responses") return undefined;
707
+ // A word the reply has already used for a call it left out names that
708
+ // call: answer the id it was handed.
709
+ const bound = names(call)
710
+ .map((word) => minted.get(word))
711
+ .find((id) => id !== undefined);
712
+ if (bound !== undefined) return bound;
583
713
  if (listed) {
584
- // The copy inside a snapshot or the terminal response's output: the call
585
- // sits where the walk found it, and an entry invented when the stream
586
- // opened that call already names that place. A reply that places nothing
587
- // is what this can be read from, so the entry is matched by the place it
588
- // holds when the names agree, and by its name when they do not — the
714
+ // The copy inside a snapshot or the terminal response's output: it sits
715
+ // where the walk found it, and a call the stream already replaced at
716
+ // that place has answered an id there. A reply that places nothing is
717
+ // what the rest can be read from, so the entry is matched by the place
718
+ // it holds when the names agree, and by its name when they do not — the
589
719
  // same call is copied into several snapshots, and every one of them has
590
720
  // to answer the same id. Two calls of one name cannot be told apart
591
- // here, and the last one the stream opened answers for both.
721
+ // here, and the last one the stream opened answers for both, and a call
722
+ // that carries a word of its own is not guessed at from a name.
723
+ const held = left.get(at);
724
+ if (typeof held === "string") return held;
725
+ if (identified(call)) return undefined;
592
726
  const entry =
593
727
  (nowhere[at]?.name === call.name ? nowhere[at] : undefined) ??
594
728
  nowhere.findLast((value) => value.name === call.name);
595
- return entry?.token;
729
+ return entry?.id;
730
+ }
731
+ // A frame's own item. One that gave an index is placed by it: the call the
732
+ // stream already replaced at that place answered an id there, and the
733
+ // dropped_<at> token inPlaceOf mints is what a call the stream never
734
+ // replaced gets.
735
+ if (at !== undefined) {
736
+ const held = left.get(at);
737
+ return typeof held === "string" ? held : undefined;
738
+ }
739
+ const inside = nowhere[open] && !nowhere[open].settled ? nowhere[open] : undefined;
740
+ if (identified(call)) {
741
+ // The frame that closes a call the stream opened with nothing to place it
742
+ // by can be the first of its frames to carry an id (measured). It is the
743
+ // call the stream is inside, and the id already answered is what the
744
+ // client has.
745
+ if (!inside) return undefined;
746
+ inside.settled = true;
747
+ return mintedFor(call, inside.id);
596
748
  }
597
- // A frame's own item. One that gave an index is already placed by it (the
598
- // dropped_<at> token inPlaceOf mints), so only the index-less frames need
599
- // the stream's answer: the call of that name it is inside, or else the
600
- // call it opened last — which is what a frame that repeats no name is.
601
- if (at !== undefined) return undefined;
602
- const entry =
603
- nowhere.findLast((value) => value.name === call.name) ??
604
- (nowhere[open] && !nowhere[open].settled ? nowhere[open] : undefined);
749
+ // A frame with nothing at all to name it by: the call the stream opened
750
+ // last of that name, which is the one it is closing — the id it answered
751
+ // is the one the client was handed, and a later copy of the call has to
752
+ // answer it again rather than mint one of its own.
753
+ const entry = nowhere.findLast((value) => value.name === call.name) ?? inside;
605
754
  if (entry) {
606
755
  entry.settled = true;
607
- return entry.token;
756
+ return mintedFor(call, entry.id);
608
757
  }
758
+ // The call the stream is closing, in a frame that carries nothing to name
759
+ // it by: the id the guard answered for it is what this copy answers, so
760
+ // the call's rewrites stay one item. A frame of another call that only
761
+ // looks like it — same name, no words — is read the same way, and that is
762
+ // the closest thing to a word either of them has.
763
+ if (closingID !== undefined && closingName === call.name) return mintedFor(call, closingID);
609
764
  // A done frame whose added frame was never seen. It is a call of its own,
610
765
  // and the terminal's copy of it still answers this id by name.
611
- return entering(call.name, true);
766
+ const opened = entering(call.name, true);
767
+ opened.id = `msg_anon_${++synthesized}`;
768
+ return mintedFor(call, opened.id);
612
769
  }
613
770
  return {
614
771
  apply(data, type) {
615
772
  if (protocol === "responses") {
616
773
  see(data);
617
- // A frame that opens an item ends the window the last one had: the
618
- // stream is not inside that call any more, so an arguments frame
619
- // naming an item the reply never opened is not that call's to swallow
620
- // (below). The item this frame opens is entered just after this, in
621
- // the frame's own branch.
622
- if (type === "response.output_item.added" && nowhere[open]) nowhere[open].settled = true;
623
774
  const item = data.item;
624
- if (
625
- type === "response.output_item.added" &&
775
+ const opens = type === "response.output_item.added";
776
+ const closes = type === "response.output_item.done";
777
+ // A frame that opens or closes an item ends the window the last one
778
+ // had: the stream is not inside that item any more, so an arguments
779
+ // frame that gives no place of its own is not that item's to answer
780
+ // for (below). The item this frame opens is entered just after this,
781
+ // in the frame's own branch.
782
+ if (opens || closes) {
783
+ // The item the stream was inside, for the frame that closes it: a
784
+ // copy of a call can arrive with nothing at all to name it by — no
785
+ // word of its own and no index — and the call being closed is the
786
+ // one whose id it answers.
787
+ closingID = closes ? insideID : undefined;
788
+ closingName = closes ? insideName : undefined;
789
+ insideID = undefined;
790
+ insideName = undefined;
791
+ insideKept = false;
792
+ insideWords.clear();
793
+ if (opens && nowhere[open]) nowhere[open].settled = true;
794
+ }
795
+ // The item this frame carries, when it is a call with no name: the
796
+ // walk below replaces it — opens or closes it, the copy is the same
797
+ // call — and its words are the ones a note takes the place of. Every
798
+ // other item the frame names is one the reply keeps, and the place it
799
+ // holds is a fact the suppression below reads before any word.
800
+ const leaving =
801
+ (opens || closes) &&
626
802
  object(item) &&
627
803
  item.type === "function_call" &&
628
- checkName(item.name) == null
629
- ) {
804
+ checkName(item.name) == null;
805
+ // A copy of a call left out — the frame that closes it, and every copy
806
+ // of it a snapshot or the terminal carries — holds no place of its own:
807
+ // the place the call holds is the one the frame that opened it was
808
+ // answered at, and a reply at odds with itself (one copy numbered and
809
+ // the next not, or two copies numbered differently) reads back as one
810
+ // item only when the copies leave the placing to the stream's own
811
+ // answer. The id they all answer is what the collapse path finds them
812
+ // by. The frame that opens the call keeps its index: that is where the
813
+ // place comes from.
814
+ if (closes && leaving && data.output_index !== undefined) delete data.output_index;
815
+ if ((opens || closes) && object(item) && !leaving) {
816
+ if (data.output_index !== undefined) keptPlaces.add(data.output_index);
817
+ for (const id of names(item)) keptWords.add(id);
818
+ // A function_call the reply kept: an arguments frame that gives no
819
+ // place of its own is this call's while the stream is inside it.
820
+ if (opens && item.type === "function_call") {
821
+ insideKept = true;
822
+ for (const id of names(item)) insideWords.add(id);
823
+ }
824
+ }
825
+ if (opens && leaving) {
630
826
  const note = droppedNote(item.name);
631
827
  // The frame's output_index is this call's place in the reply, which
632
828
  // is the id its later frames (done, the terminal's output) invent
633
829
  // for it again (inPlaceOf). A frame that gives no index leaves the
634
830
  // place to the stream: the entry opened here is what its later
635
- // frames — and its arguments frames — are matched by.
636
- const placed =
637
- data.output_index === undefined && !identified(item)
638
- ? entering(item.name)
639
- : undefined;
640
- const message = inPlaceOf(protocol, item, note, data.output_index, placed);
641
- left.set(data.output_index, true);
642
- leftIDs.add(message.id);
643
- if (typeof item.id === "string") leftIDs.add(item.id);
831
+ // frames — and its arguments frames — are matched by, and it is
832
+ // opened whether or not the frame carried a word, because the frame
833
+ // that closes the call can carry none (measured: the call_id the
834
+ // added frame named is gone from the done frame).
835
+ const entry = data.output_index === undefined ? entering(item.name) : undefined;
836
+ const message = inPlaceOf(protocol, item, note, data.output_index, entry?.id);
837
+ if (entry) entry.id = message.id;
838
+ // Every word this call can be named by later is recorded, whichever
839
+ // shape its frames have: the id the guard answered for it (with the
840
+ // place in it), and the id and call_id the upstream gave it — the
841
+ // arguments frames of a call that gave an index name it by one of
842
+ // those alone, so a set of invented ids would let them through,
843
+ // describing an item the client is told was replaced by a note. The
844
+ // place it answered at is recorded too, so the copies of it that
845
+ // follow answer the same id rather than the one the upstream settles
846
+ // on after them.
847
+ if (data.output_index !== undefined) left.set(data.output_index, message.id);
848
+ for (const id of [message.id, ...names(item)]) leftIDs.add(id);
849
+ mintedFor(item, message.id);
850
+ // And the stream is inside it: its arguments frames carry no place
851
+ // of their own, and this window is what says they are its frames
852
+ // rather than an item the reply never opened.
853
+ insideID = message.id;
854
+ insideName = item.name;
855
+ for (const id of [message.id, ...names(item)]) insideWords.add(id);
644
856
  dropped++;
645
857
  const where = { item_id: message.id, output_index: data.output_index, content_index: 0 };
646
858
  const part = { type: "output_text", text: "", annotations: [] };
@@ -656,27 +868,40 @@ function snapshotStream(protocol, checkName) {
656
868
  };
657
869
  }
658
870
  if (typeof type === "string" && type.startsWith("response.function_call_arguments.")) {
659
- if (data.output_index != null && left.has(data.output_index))
660
- return { changed: true, data: null };
661
- if (leftIDs.has(data.item_id)) return { changed: true, data: null };
662
- // An item id the reply never named on an item of its own: the
663
- // arguments frames of a call it placed nowhere name the item from
664
- // the first of them on (measured: the added frame carries no id at
665
- // all, and the frames after it carry only an item_id), so while the
666
- // stream is inside that call they are its frames. They are left out
667
- // with the call — reaching the client they would describe an item
668
- // that was replaced by a note, and the collapse path refuses them —
669
- // and the id is recorded, so the frames after the first are matched
670
- // too. A frame naming an item the reply did name belongs to that
671
- // item and is left alone.
672
- const entry = nowhere[open];
673
- if (
674
- data.output_index == null &&
675
- entry &&
676
- !entry.settled &&
677
- !seen.has(data.item_id)
678
- ) {
679
- if (typeof data.item_id === "string" && data.item_id) leftIDs.add(data.item_id);
871
+ // Whose frame this is, by what the frame itself says: the place it
872
+ // gives is a fact about the reply and is read first, whoever holds
873
+ // it; a frame that gives none belongs to the item the stream is
874
+ // inside; and one that arrives outside any item is read by its word.
875
+ // The answer is the same whichever order the calls arrived in,
876
+ // because no step of it subtracts one set of words from another.
877
+ const place = data.output_index;
878
+ const word = identity(data.item_id);
879
+ let holder = "kept";
880
+ if (place != null && left.has(place) && !keptPlaces.has(place)) holder = "dropped";
881
+ // Two items stated one index: the place cannot say whose the frame
882
+ // is, and the word it names is what is left.
883
+ else if (place != null && word !== undefined && keptWords.has(word)) holder = "kept";
884
+ else if (place != null && left.has(place)) holder = "dropped";
885
+ else if (place != null && keptPlaces.has(place)) holder = "kept";
886
+ else if (place == null && insideID !== undefined)
887
+ // A word the reply named on some other item is that item's frame,
888
+ // interleaved with this call's own: the call the stream is inside
889
+ // answers for the words it was named by, and for a word the reply
890
+ // names nowhere else (measured: the added frame carries no id, and
891
+ // the frames after it name one).
892
+ holder =
893
+ word !== undefined && seen.has(word) && !insideWords.has(word) ? "kept" : "dropped";
894
+ else if (place == null && insideKept) holder = "kept";
895
+ else if (word !== undefined && leftIDs.has(word)) holder = "dropped";
896
+ if (holder === "dropped") {
897
+ if (word !== undefined) {
898
+ leftIDs.add(word);
899
+ // The id names the call the stream is inside, so a copy of that
900
+ // call which later carries it answers the id the client was
901
+ // handed (placeOf).
902
+ const answers = (place != null ? left.get(place) : undefined) ?? insideID;
903
+ if (answers !== undefined) minted.set(word, answers);
904
+ }
680
905
  return { changed: true, data: null };
681
906
  }
682
907
  }
@@ -736,7 +961,10 @@ async function readBounded(response) {
736
961
  if (!reader) return "";
737
962
  const parts = [];
738
963
  let bytes = 0;
739
- let failure;
964
+ // Named for the read it ends, not `failure`: that word is this module's
965
+ // error constructor, and a local of the same name reads as if the guard's
966
+ // own failures were being built here.
967
+ let readFailure;
740
968
  try {
741
969
  while (true) {
742
970
  const { done, value } = await reader.read();
@@ -747,7 +975,7 @@ async function readBounded(response) {
747
975
  parts.push(value);
748
976
  }
749
977
  } catch (error) {
750
- failure = error;
978
+ readFailure = error;
751
979
  }
752
980
  // Whatever ended the read — the limit, a malformed body, or the end of it —
753
981
  // this body is never read again, so the stream is cancelled and the reader
@@ -762,12 +990,12 @@ async function readBounded(response) {
762
990
  try {
763
991
  await reader.cancel();
764
992
  } catch (error) {
765
- if (!failure) throw error;
766
- if (failure instanceof Error && !("cause" in failure)) failure.cause = error;
993
+ if (!readFailure) throw error;
994
+ if (readFailure instanceof Error && !("cause" in readFailure)) readFailure.cause = error;
767
995
  } finally {
768
996
  reader.releaseLock();
769
997
  }
770
- if (failure) throw failure;
998
+ if (readFailure) throw readFailure;
771
999
  const all = new Uint8Array(bytes);
772
1000
  let offset = 0;
773
1001
  for (const part of parts) {
@@ -1125,7 +1353,12 @@ function guardedStream(response, protocol, checkName, endCutShort) {
1125
1353
  )
1126
1354
  completed = true;
1127
1355
  if (result || extra.length) {
1128
- const own = !result ? "" : changed ? rewrite(frame, result) : frame.raw;
1356
+ // A frame the walk left as it came is forwarded as it came: only
1357
+ // its own bytes carry what a rewrite would drop — the event name
1358
+ // it was sent under, and any field of the frame the guard does
1359
+ // not read.
1360
+ let own = "";
1361
+ if (result) own = changed ? rewrite(frame, result) : frame.raw;
1129
1362
  const added = extra.map((value) => encodeEvent(value, value.type)).join("");
1130
1363
  controller.enqueue(encoder.encode(own + added));
1131
1364
  return;