llm-relay 0.62.0 → 0.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/accounting-store-io.js +0 -33
- package/dist/accounting-store-io.js.map +1 -1
- package/dist/accounting-store-schema.js +0 -65
- package/dist/accounting-store-schema.js.map +1 -1
- package/dist/accounting-store.js +0 -195
- package/dist/accounting-store.js.map +1 -1
- package/dist/accounting.js +0 -72
- package/dist/accounting.js.map +1 -1
- package/dist/anthropic.js +0 -18
- package/dist/anthropic.js.map +1 -1
- package/dist/authEnv.js +0 -78
- package/dist/authEnv.js.map +1 -1
- package/dist/availability-snapshot.js +0 -58
- package/dist/availability-snapshot.js.map +1 -1
- package/dist/availability.js +0 -162
- package/dist/availability.js.map +1 -1
- package/dist/backend.js +1 -416
- package/dist/backend.js.map +1 -1
- package/dist/benchmarks.js +1 -63
- package/dist/benchmarks.js.map +1 -1
- package/dist/breaker-persistence.js +0 -59
- package/dist/breaker-persistence.js.map +1 -1
- package/dist/candidates.js +0 -64
- package/dist/candidates.js.map +1 -1
- package/dist/catalog.js +1 -140
- package/dist/catalog.js.map +1 -1
- package/dist/circuit-breaker.js +1 -90
- package/dist/circuit-breaker.js.map +1 -1
- package/dist/claude-hook.js +0 -81
- package/dist/claude-hook.js.map +1 -1
- package/dist/cli.d.ts +36 -0
- package/dist/cli.js +73 -730
- package/dist/cli.js.map +1 -1
- package/dist/config-edit.js +0 -11
- package/dist/config-edit.js.map +1 -1
- package/dist/config.d.ts +21 -0
- package/dist/config.js +22 -372
- package/dist/config.js.map +1 -1
- package/dist/configured-limits.js +0 -58
- package/dist/configured-limits.js.map +1 -1
- package/dist/context-limits.js +0 -97
- package/dist/context-limits.js.map +1 -1
- package/dist/control-authorization.js +0 -36
- package/dist/control-authorization.js.map +1 -1
- package/dist/cooldown-clear.js +0 -6
- package/dist/cooldown-clear.js.map +1 -1
- package/dist/credential-fleet.js +0 -5
- package/dist/credential-fleet.js.map +1 -1
- package/dist/credential-select.js +4 -30
- package/dist/credential-select.js.map +1 -1
- package/dist/dashboard-auth.js +0 -29
- package/dist/dashboard-auth.js.map +1 -1
- package/dist/dashboard-contract.js +0 -46
- package/dist/dashboard-contract.js.map +1 -1
- package/dist/dashboard-routes.js +0 -32
- package/dist/dashboard-routes.js.map +1 -1
- package/dist/dashboard-snapshot.js +0 -154
- package/dist/dashboard-snapshot.js.map +1 -1
- package/dist/dashboard-static.js +0 -22
- package/dist/dashboard-static.js.map +1 -1
- package/dist/delegate-gate/cast-necessity.js +1 -22
- package/dist/delegate-gate/cast-necessity.js.map +1 -1
- package/dist/delegate-gate/cli.js +0 -6
- package/dist/delegate-gate/cli.js.map +1 -1
- package/dist/delegate-gate/diff-parser.js +0 -32
- package/dist/delegate-gate/diff-parser.js.map +1 -1
- package/dist/delegate-gate/fix.js +0 -10
- package/dist/delegate-gate/fix.js.map +1 -1
- package/dist/delegate-gate/gate.js +0 -12
- package/dist/delegate-gate/gate.js.map +1 -1
- package/dist/delegate-gate/minimality.js +0 -38
- package/dist/delegate-gate/minimality.js.map +1 -1
- package/dist/delegate-gate/post-image.js +1 -13
- package/dist/delegate-gate/post-image.js.map +1 -1
- package/dist/delegate-gate/shared-state.js +0 -23
- package/dist/delegate-gate/shared-state.js.map +1 -1
- package/dist/delegate-gate/test-assertions.js +1 -54
- package/dist/delegate-gate/test-assertions.js.map +1 -1
- package/dist/delegate-gate/types.js +0 -11
- package/dist/delegate-gate/types.js.map +1 -1
- package/dist/dialect-stream.js +9 -37
- package/dist/dialect-stream.js.map +1 -1
- package/dist/dispatch-exhaustion-persistence.js +0 -35
- package/dist/dispatch-exhaustion-persistence.js.map +1 -1
- package/dist/dispatch.js +0 -269
- package/dist/dispatch.js.map +1 -1
- package/dist/documents.js +0 -92
- package/dist/documents.js.map +1 -1
- package/dist/dotenv.js +0 -22
- package/dist/dotenv.js.map +1 -1
- package/dist/dynamic-pools.js +0 -93
- package/dist/dynamic-pools.js.map +1 -1
- package/dist/emitSse.js +0 -64
- package/dist/emitSse.js.map +1 -1
- package/dist/executable-lookup.js +0 -59
- package/dist/executable-lookup.js.map +1 -1
- package/dist/hard-cap.js +1 -32
- package/dist/hard-cap.js.map +1 -1
- package/dist/host-routing.js +0 -40
- package/dist/host-routing.js.map +1 -1
- package/dist/installed-hosts.js +0 -71
- package/dist/installed-hosts.js.map +1 -1
- package/dist/json-shape.js +0 -19
- package/dist/json-shape.js.map +1 -1
- package/dist/kernel/contracts.js +0 -26
- package/dist/kernel/contracts.js.map +1 -1
- package/dist/kernel/request-lifecycle.js +0 -4
- package/dist/kernel/request-lifecycle.js.map +1 -1
- package/dist/key-checker.js +0 -115
- package/dist/key-checker.js.map +1 -1
- package/dist/key-import.js +0 -20
- package/dist/key-import.js.map +1 -1
- package/dist/keys-cli.js +0 -15
- package/dist/keys-cli.js.map +1 -1
- package/dist/keystore.js +2 -51
- package/dist/keystore.js.map +1 -1
- package/dist/lane-cadence.js +1 -51
- package/dist/lane-cadence.js.map +1 -1
- package/dist/lane-manifest.js +0 -54
- package/dist/lane-manifest.js.map +1 -1
- package/dist/lane-probe.js +0 -36
- package/dist/lane-probe.js.map +1 -1
- package/dist/lane-quota-probe.js +0 -61
- package/dist/lane-quota-probe.js.map +1 -1
- package/dist/log.js +0 -17
- package/dist/log.js.map +1 -1
- package/dist/mcp/lane-runner.d.ts +114 -0
- package/dist/mcp/lane-runner.js +186 -0
- package/dist/mcp/lane-runner.js.map +1 -0
- package/dist/mcp/protocol.d.ts +98 -0
- package/dist/mcp/protocol.js +67 -0
- package/dist/mcp/protocol.js.map +1 -0
- package/dist/mcp/server.d.ts +78 -0
- package/dist/mcp/server.js +360 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/metadata.js +0 -73
- package/dist/metadata.js.map +1 -1
- package/dist/network-block.js +0 -45
- package/dist/network-block.js.map +1 -1
- package/dist/offload.js +0 -21
- package/dist/offload.js.map +1 -1
- package/dist/onboarding.js +0 -36
- package/dist/onboarding.js.map +1 -1
- package/dist/openai-dialect.js +0 -32
- package/dist/openai-dialect.js.map +1 -1
- package/dist/openai-request.js +0 -236
- package/dist/openai-request.js.map +1 -1
- package/dist/os-keyring.js +0 -28
- package/dist/os-keyring.js.map +1 -1
- package/dist/ping/cadence.js +3 -90
- package/dist/ping/cadence.js.map +1 -1
- package/dist/ping/metrics.js +0 -91
- package/dist/ping/metrics.js.map +1 -1
- package/dist/ping/ping.js +0 -17
- package/dist/ping/ping.js.map +1 -1
- package/dist/ping/probe-cache.js +2 -53
- package/dist/ping/probe-cache.js.map +1 -1
- package/dist/ping/quota.js +0 -17
- package/dist/ping/quota.js.map +1 -1
- package/dist/ping/runtime-telemetry.js +1 -18
- package/dist/ping/runtime-telemetry.js.map +1 -1
- package/dist/pool-health.js +0 -49
- package/dist/pool-health.js.map +1 -1
- package/dist/presets.js +0 -4
- package/dist/presets.js.map +1 -1
- package/dist/process-safety-net.js +0 -36
- package/dist/process-safety-net.js.map +1 -1
- package/dist/quota-demotion.js +1 -35
- package/dist/quota-demotion.js.map +1 -1
- package/dist/quota-observation.js +0 -21
- package/dist/quota-observation.js.map +1 -1
- package/dist/rate-limits.js +1 -94
- package/dist/rate-limits.js.map +1 -1
- package/dist/refusal-interpretation.js +2 -327
- package/dist/refusal-interpretation.js.map +1 -1
- package/dist/registry.js +0 -11
- package/dist/registry.js.map +1 -1
- package/dist/repair.js +1 -119
- package/dist/repair.js.map +1 -1
- package/dist/request-log.js +0 -2
- package/dist/request-log.js.map +1 -1
- package/dist/reshaper.js +0 -85
- package/dist/reshaper.js.map +1 -1
- package/dist/resolved-attempt.js +0 -3
- package/dist/resolved-attempt.js.map +1 -1
- package/dist/responses-request.js +0 -187
- package/dist/responses-request.js.map +1 -1
- package/dist/routes/admin.js +0 -38
- package/dist/routes/admin.js.map +1 -1
- package/dist/secret-file-acl.js +0 -26
- package/dist/secret-file-acl.js.map +1 -1
- package/dist/self-update.js +0 -118
- package/dist/self-update.js.map +1 -1
- package/dist/server.js +15 -952
- package/dist/server.js.map +1 -1
- package/dist/session-pin.js +0 -9
- package/dist/session-pin.js.map +1 -1
- package/dist/setup-claude.js +0 -9
- package/dist/setup-claude.js.map +1 -1
- package/dist/spend-headroom.js +0 -20
- package/dist/spend-headroom.js.map +1 -1
- package/dist/sse-frames.js +0 -31
- package/dist/sse-frames.js.map +1 -1
- package/dist/sse.js +1 -19
- package/dist/sse.js.map +1 -1
- package/dist/state-paths.js +0 -16
- package/dist/state-paths.js.map +1 -1
- package/dist/stream-commit.js +1 -27
- package/dist/stream-commit.js.map +1 -1
- package/dist/target-facts.js +2 -130
- package/dist/target-facts.js.map +1 -1
- package/dist/telemetry.js +0 -8
- package/dist/telemetry.js.map +1 -1
- package/dist/think-tags.js +1 -38
- package/dist/think-tags.js.map +1 -1
- package/dist/tier-data.js +0 -29
- package/dist/tier-data.js.map +1 -1
- package/dist/tool-dialects.js +1 -103
- package/dist/tool-dialects.js.map +1 -1
- package/dist/tool-use-ids.js +0 -51
- package/dist/tool-use-ids.js.map +1 -1
- package/dist/usage-observer.js +0 -52
- package/dist/usage-observer.js.map +1 -1
- package/dist/validator.js +1 -25
- package/dist/validator.js.map +1 -1
- package/dist/winenv.js +0 -63
- package/dist/winenv.js.map +1 -1
- package/dist/write-behind.js +0 -12
- package/dist/write-behind.js.map +1 -1
- package/package.json +2 -2
- package/skills/llm-relay/SKILL.md +26 -1
package/dist/tier-data.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tier-data.js","sourceRoot":"","sources":["../src/tier-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"tier-data.js","sourceRoot":"","sources":["../src/tier-data.ts"],"names":[],"mappings":"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"}
|
package/dist/tool-dialects.js
CHANGED
|
@@ -1,69 +1,14 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Recovering tool calls a HOST failed to parse.
|
|
3
|
-
*
|
|
4
|
-
* A model emits tool calls in its own dialect; the serving host is supposed to parse that into the
|
|
5
|
-
* OpenAI `message.tool_calls` field. NIM and OpenRouter do. Some free hosts do not, and hand back
|
|
6
|
-
* the raw envelope as ordinary assistant TEXT. Before this module the consequence was silent and
|
|
7
|
-
* total: no `tool_calls` ⇒ `stop_reason: end_turn` ⇒ zero `tool_use` blocks ⇒ the validator finds
|
|
8
|
-
* nothing malformed and PASSES ⇒ repair never engages ⇒ the client receives markup it treats as a
|
|
9
|
-
* final answer. Measured 2026-08-08: a task wrote its output correctly and then emitted
|
|
10
|
-
* `</|DSML|parameter></|DSML|invoke></|DSML|tool_calls>` as its entire visible response.
|
|
11
|
-
* See docs/tool-call-dialect-leak.md.
|
|
12
|
-
*
|
|
13
|
-
* ⚠ This is PARSING, not inference — the same side of the repair boundary as fixing args that
|
|
14
|
-
* violate a schema. The envelope set is CLOSED (same reasoning as the effort vocabulary in
|
|
15
|
-
* sync-tiers.mjs and the alias list in authEnv.ts): prose that merely mentions a tool name is not a
|
|
16
|
-
* tool call, and promoting it to one would be fabricating intent, which is the one thing the repair
|
|
17
|
-
* path must never do. A dialect we do not recognize yields `detected` at most, never a guess.
|
|
18
|
-
*/
|
|
19
|
-
/**
|
|
20
|
-
* The relay-owned error code for a dialect-rescue destructive refusal, on every surface that can
|
|
21
|
-
* carry one: the buffered error bodies, the mid-stream SSE `error` event, and the commit probe's
|
|
22
|
-
* classifier.
|
|
23
|
-
*
|
|
24
|
-
* It has ONE job beyond naming the failure: the probe must read it as RELAY-authored, not
|
|
25
|
-
* upstream. An in-band error is provenance `upstream` by default and therefore retriable, which
|
|
26
|
-
* would let a streamed pre-commit refusal reroll onto the next candidate while the buffered lanes
|
|
27
|
-
* treat the same refusal as terminal — two paths, one policy. See
|
|
28
|
-
* docs/dialect-rescue-destructive-refusal-2026-08-24.md §3.
|
|
29
|
-
*/
|
|
30
1
|
export const DIALECT_REFUSED_DESTRUCTIVE_CODE = "tool_dialect_refused_destructive";
|
|
31
|
-
/** A fresh, un-refused signal for one wrapped stream. */
|
|
32
2
|
export function dialectRefusalSignal() {
|
|
33
3
|
return { refused: false };
|
|
34
4
|
}
|
|
35
|
-
/**
|
|
36
|
-
* Render the refused tool names for an error message, bounded.
|
|
37
|
-
*
|
|
38
|
-
* The names are model-authored: an envelope's `name="…"` attribute, admitted only because it
|
|
39
|
-
* MATCHED the operator's list — and a prefix pattern (`git_*`) admits arbitrary text after the
|
|
40
|
-
* prefix. So the wording is bounded here rather than at each of the four seams, the same reasoning
|
|
41
|
-
* as `stream-commit.ts` `boundedError`. One definition, because a message that is truncated on
|
|
42
|
-
* three lanes and unbounded on the fourth is the asymmetry this whole change exists to remove.
|
|
43
|
-
*/
|
|
44
5
|
export function describeRefused(refused) {
|
|
45
6
|
const shown = refused.slice(0, 5).map((name) => (name.length > 64 ? `${name.slice(0, 64)}…` : name));
|
|
46
7
|
const extra = refused.length - shown.length;
|
|
47
8
|
return extra > 0 ? `${shown.join(", ")}, +${extra} more` : shown.join(", ");
|
|
48
9
|
}
|
|
49
|
-
/**
|
|
50
|
-
* Marker substrings that mean "a tool-call envelope is present". Detection is deliberately broader
|
|
51
|
-
* than parsing: a truncated envelope must still be recognized as one, because returning its
|
|
52
|
-
* fragment as an answer is what made this failure read as "the job died".
|
|
53
|
-
*/
|
|
54
|
-
/**
|
|
55
|
-
* ⚠ Markers deliberately omit the `<` / `</` prefix so a CLOSING tag matches too. The truncated
|
|
56
|
-
* tail of a stream is mostly closing tags — one of the two measured failure bodies was exactly
|
|
57
|
-
* `</|DSML|parameter></|DSML|invoke></|DSML|tool_calls>`, which an open-tag-only marker set
|
|
58
|
-
* reports as ordinary prose. That is the single case where detection matters most, because there
|
|
59
|
-
* is no call left to recover and the alternative is handing the fragment back as an answer.
|
|
60
|
-
*/
|
|
61
10
|
const DIALECT_MARKERS = [
|
|
62
11
|
{ dialect: "deepseek", markers: ["|tool▁calls▁begin|", "|tool▁call▁begin|", "|tool▁sep|", "|tool▁call▁end|", "|tool▁calls▁end|"] },
|
|
63
|
-
// The ASCII-pipe token family (Kimi-K2 and friends) is a DIFFERENT grammar from the fullwidth
|
|
64
|
-
// DeepSeek form above, not a spelling variant: section wrappers, the name riding in a
|
|
65
|
-
// `functions.NAME:IDX` id token, and an argument-begin separator. Observed in production on the
|
|
66
|
-
// same free pool (fork-validated in freellmapi's rescue). Adoption review §1.8.
|
|
67
12
|
{
|
|
68
13
|
dialect: "kimi",
|
|
69
14
|
markers: [
|
|
@@ -88,18 +33,6 @@ export function detectDialect(text) {
|
|
|
88
33
|
}
|
|
89
34
|
const ALL_MARKERS = DIALECT_MARKERS.flatMap((d) => d.markers);
|
|
90
35
|
const LONGEST_MARKER = Math.max(...ALL_MARKERS.map((m) => m.length));
|
|
91
|
-
/**
|
|
92
|
-
* How much of a partial text stream is safe to forward, and whether a marker has landed.
|
|
93
|
-
*
|
|
94
|
-
* The streaming-needle problem: a marker arrives split across SSE deltas, so forwarding each delta
|
|
95
|
-
* as it comes would emit the first half of an envelope before we know it is one. `safeLen` is
|
|
96
|
-
* everything except the longest trailing run that could still GROW into a marker, so the caller can
|
|
97
|
-
* stream normally while never emitting into an envelope it is about to capture.
|
|
98
|
-
*
|
|
99
|
-
* ⚠ The holdback is bounded by the longest marker, so ordinary prose containing `<` streams with at
|
|
100
|
-
* most that many characters of lag — it does not degrade into buffering the whole response, which
|
|
101
|
-
* would trade this bug for a latency regression on every tool-bearing request.
|
|
102
|
-
*/
|
|
103
36
|
export function scanForMarker(text) {
|
|
104
37
|
if (ALL_MARKERS.some((m) => text.includes(m)))
|
|
105
38
|
return { safeLen: text.length, hit: true };
|
|
@@ -111,14 +44,6 @@ export function scanForMarker(text) {
|
|
|
111
44
|
}
|
|
112
45
|
return { safeLen: text.length, hit: false };
|
|
113
46
|
}
|
|
114
|
-
/**
|
|
115
|
-
* Index at which a marker begins, for splitting emitted prose from a captured envelope.
|
|
116
|
-
*
|
|
117
|
-
* ⚠ Backs up over the tag opener. Markers deliberately omit the `<` / `</` prefix so a CLOSING tag
|
|
118
|
-
* matches, which means a `|DSML|tool_calls` hit points one character PAST the real start. Capturing
|
|
119
|
-
* from there left the `<` behind as prose and handed `stripEnvelopes` a tag it no longer recognized,
|
|
120
|
-
* so the envelope's outer wrapper survived into the recovered text.
|
|
121
|
-
*/
|
|
122
47
|
export function markerStart(text) {
|
|
123
48
|
let best = -1;
|
|
124
49
|
for (const m of ALL_MARKERS) {
|
|
@@ -134,7 +59,6 @@ export function markerStart(text) {
|
|
|
134
59
|
return best - 1;
|
|
135
60
|
return best;
|
|
136
61
|
}
|
|
137
|
-
/** Coerce a stringly-typed dialect parameter using the tool's declared schema. */
|
|
138
62
|
function coerce(raw, schema) {
|
|
139
63
|
const t = Array.isArray(schema?.type) ? schema?.type[0] : schema?.type;
|
|
140
64
|
const s = raw.trim();
|
|
@@ -157,12 +81,9 @@ function coerce(raw, schema) {
|
|
|
157
81
|
case "string":
|
|
158
82
|
return raw;
|
|
159
83
|
default:
|
|
160
|
-
// No declared type: keep the literal. Guessing a shape here would silently rewrite an
|
|
161
|
-
// argument the model meant as text.
|
|
162
84
|
return raw;
|
|
163
85
|
}
|
|
164
86
|
}
|
|
165
|
-
/** `{"name": "x", "arguments": {...}}` — the JSON payload shape shared by several dialects. */
|
|
166
87
|
function fromJsonPayload(raw) {
|
|
167
88
|
let j;
|
|
168
89
|
try {
|
|
@@ -190,7 +111,6 @@ function fromJsonPayload(raw) {
|
|
|
190
111
|
return null;
|
|
191
112
|
return { name, input: args };
|
|
192
113
|
}
|
|
193
|
-
/** `<invoke name="x"><parameter name="p">v</parameter></invoke>`, with or without DSML markers. */
|
|
194
114
|
function fromInvokeForm(text, schemas) {
|
|
195
115
|
const calls = [];
|
|
196
116
|
const invoke = /<(?:|DSML|)?invoke\s+name="([^"]+)"\s*>([\s\S]*?)<\/(?:|DSML|)?invoke>/g;
|
|
@@ -212,7 +132,6 @@ function fromInvokeForm(text, schemas) {
|
|
|
212
132
|
}
|
|
213
133
|
return calls;
|
|
214
134
|
}
|
|
215
|
-
/** DeepSeek native: `…begin|>function<|tool▁sep|>NAME\n```json\n{…}\n``` `. */
|
|
216
135
|
function fromDeepSeekForm(text) {
|
|
217
136
|
const calls = [];
|
|
218
137
|
const re = /<|tool▁sep|>([^\n<]+)\n+(?:```(?:json)?\n)?([\s\S]*?)(?:\n?```)?\s*<|tool▁call▁end|>/g;
|
|
@@ -227,14 +146,13 @@ function fromDeepSeekForm(text) {
|
|
|
227
146
|
if (typeof parsed === "object" && parsed !== null)
|
|
228
147
|
input = parsed;
|
|
229
148
|
}
|
|
230
|
-
catch {
|
|
149
|
+
catch {
|
|
231
150
|
return [];
|
|
232
151
|
}
|
|
233
152
|
calls.push({ name: name.trim(), input });
|
|
234
153
|
}
|
|
235
154
|
return calls;
|
|
236
155
|
}
|
|
237
|
-
/** Kimi ASCII token blocks: `<|tool_call_begin|>functions.NAME:0<|tool_call_argument_begin|>{…}<|tool_call_end|>`. */
|
|
238
156
|
function fromKimiTokenForm(text) {
|
|
239
157
|
const calls = [];
|
|
240
158
|
const re = /<\|tool_call_begin\|>\s*([\s\S]*?)\s*<\|tool_call_argument_begin\|>\s*([\s\S]*?)\s*<\|tool_call_end\|>/g;
|
|
@@ -243,10 +161,6 @@ function fromKimiTokenForm(text) {
|
|
|
243
161
|
const payload = m[2];
|
|
244
162
|
if (!idToken || payload === undefined)
|
|
245
163
|
continue;
|
|
246
|
-
// The function name rides in the id token as `functions.NAME:IDX`. Some models degrade it to
|
|
247
|
-
// an opaque id (observed upstream: `chatcmpl-tool-<hex>`), which leaves no way to know WHICH
|
|
248
|
-
// tool was meant — unparseable, so the whole recovery fails clean to `detected` rather than
|
|
249
|
-
// guessing a target.
|
|
250
164
|
const nameMatch = /^functions\.([A-Za-z0-9_.-]+):\d+$/.exec(idToken.trim());
|
|
251
165
|
if (!nameMatch)
|
|
252
166
|
return [];
|
|
@@ -262,7 +176,6 @@ function fromKimiTokenForm(text) {
|
|
|
262
176
|
}
|
|
263
177
|
return calls;
|
|
264
178
|
}
|
|
265
|
-
/** `<tool_call>{json}</tool_call>` (Hermes/Qwen) and `<function=NAME>{json}</function>`. */
|
|
266
179
|
function fromTaggedJsonForms(text) {
|
|
267
180
|
const calls = [];
|
|
268
181
|
for (const m of text.matchAll(/<tool_call>([\s\S]*?)<\/tool_call>/g)) {
|
|
@@ -290,7 +203,6 @@ function fromTaggedJsonForms(text) {
|
|
|
290
203
|
}
|
|
291
204
|
return calls;
|
|
292
205
|
}
|
|
293
|
-
/** Everything the envelope occupied, so the surviving prose can stay a text block. */
|
|
294
206
|
function stripEnvelopes(text) {
|
|
295
207
|
return text
|
|
296
208
|
.replace(/<(?:|DSML|)?function_calls>[\s\S]*?<\/(?:|DSML|)?function_calls>/g, "")
|
|
@@ -304,20 +216,6 @@ function stripEnvelopes(text) {
|
|
|
304
216
|
.replace(/<\/?think>/g, "")
|
|
305
217
|
.trim();
|
|
306
218
|
}
|
|
307
|
-
/**
|
|
308
|
-
* Recover tool calls from a text block a host failed to parse.
|
|
309
|
-
*
|
|
310
|
-
* `schemas` supplies declared parameter types for the dialects that carry parameters as strings;
|
|
311
|
-
* without it those values stay strings and the validator reports the type error, which is the
|
|
312
|
-
* correct visible failure rather than a silent coercion.
|
|
313
|
-
*
|
|
314
|
-
* `isDestructive` is the operator's configured refusal set (`destructiveMatcher`), and it is
|
|
315
|
-
* REQUIRED on purpose. There are four rescue commit points — buffered and streamed, on each of the
|
|
316
|
-
* Anthropic-translated and direct-Chat lanes — and an optional parameter would let a fifth be
|
|
317
|
-
* added that silently omits the policy, which is the exact failure this call site exists to close.
|
|
318
|
-
* Making it required puts the decision in ONE place and lets the compiler enumerate the callers.
|
|
319
|
-
* Still parsing, not judgment: a set-membership test on a name the operator wrote down.
|
|
320
|
-
*/
|
|
321
219
|
export function recoverToolCalls(text, schemas, isDestructive) {
|
|
322
220
|
const dialect = detectDialect(text);
|
|
323
221
|
if (!dialect)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-dialects.js","sourceRoot":"","sources":["../src/tool-dialects.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tool-dialects.js","sourceRoot":"","sources":["../src/tool-dialects.ts"],"names":[],"mappings":"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"}
|
package/dist/tool-use-ids.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { isRecord } from "./json-shape.js";
|
|
2
2
|
import { BufferedSseFrames, sseEventFields } from "./sse-frames.js";
|
|
3
|
-
/** How deep a conversation is walked for ids. A `tool_result`'s own content is the only nesting. */
|
|
4
3
|
const MAX_BLOCK_DEPTH = 2;
|
|
5
4
|
function collectFromBlocks(blocks, into, depth) {
|
|
6
5
|
if (!Array.isArray(blocks) || depth > MAX_BLOCK_DEPTH)
|
|
@@ -15,19 +14,10 @@ function collectFromBlocks(blocks, into, depth) {
|
|
|
15
14
|
if (typeof block.tool_use_id === "string" && block.tool_use_id.length > 0) {
|
|
16
15
|
into.add(block.tool_use_id);
|
|
17
16
|
}
|
|
18
|
-
// A `tool_result`'s content carries text/image blocks only, but walking it costs one
|
|
19
|
-
// bounded pass and means an id can never hide from the uniqueness check.
|
|
20
17
|
collectFromBlocks(block.content, into, depth + 1);
|
|
21
18
|
}
|
|
22
19
|
}
|
|
23
20
|
}
|
|
24
|
-
/**
|
|
25
|
-
* Every `tool_use.id` and `tool_result.tool_use_id` already present in the REQUEST conversation.
|
|
26
|
-
*
|
|
27
|
-
* Pure: it reads the already-parsed request body the caller holds, so it adds no disk read and no
|
|
28
|
-
* network call to the request path. Compute it lazily — a response with no tool call at all never
|
|
29
|
-
* needs it.
|
|
30
|
-
*/
|
|
31
21
|
export function knownToolUseIds(reqJson) {
|
|
32
22
|
const out = new Set();
|
|
33
23
|
if (!isRecord(reqJson))
|
|
@@ -42,13 +32,6 @@ export function knownToolUseIds(reqJson) {
|
|
|
42
32
|
}
|
|
43
33
|
return out;
|
|
44
34
|
}
|
|
45
|
-
/**
|
|
46
|
-
* `id` when nothing has claimed it, else the smallest free `<id>_relay<k>`.
|
|
47
|
-
*
|
|
48
|
-
* Pure and deterministic — it does not record the answer. `ToolUseIdMinter` is what marks a
|
|
49
|
-
* result as taken, because "is this id free?" and "claim this id" are different decisions and a
|
|
50
|
-
* caller inspecting one candidate must not mutate the set.
|
|
51
|
-
*/
|
|
52
35
|
export function uniqueToolUseId(id, taken) {
|
|
53
36
|
if (!taken.has(id))
|
|
54
37
|
return id;
|
|
@@ -58,19 +41,12 @@ export function uniqueToolUseId(id, taken) {
|
|
|
58
41
|
return candidate;
|
|
59
42
|
}
|
|
60
43
|
}
|
|
61
|
-
/**
|
|
62
|
-
* Per-RESPONSE minting state: the conversation's ids plus everything this response has emitted.
|
|
63
|
-
*
|
|
64
|
-
* Claiming each emitted id — the unchanged ones too — is what makes two identical ids inside ONE
|
|
65
|
-
* response diverge, which is the shape kimi produces when a turn calls the same tool twice.
|
|
66
|
-
*/
|
|
67
44
|
export class ToolUseIdMinter {
|
|
68
45
|
taken;
|
|
69
46
|
count = 0;
|
|
70
47
|
constructor(taken) {
|
|
71
48
|
this.taken = new Set(taken);
|
|
72
49
|
}
|
|
73
|
-
/** The id this response should carry for `id`, claiming it against later blocks. */
|
|
74
50
|
mint(id) {
|
|
75
51
|
const next = uniqueToolUseId(id, this.taken);
|
|
76
52
|
this.taken.add(next);
|
|
@@ -78,17 +54,10 @@ export class ToolUseIdMinter {
|
|
|
78
54
|
this.count += 1;
|
|
79
55
|
return next;
|
|
80
56
|
}
|
|
81
|
-
/** How many ids this response had to change. A count, never an id. */
|
|
82
57
|
get rewritten() {
|
|
83
58
|
return this.count;
|
|
84
59
|
}
|
|
85
60
|
}
|
|
86
|
-
/**
|
|
87
|
-
* Rewrite the `tool_use` ids of one buffered Anthropic content array.
|
|
88
|
-
*
|
|
89
|
-
* Non-`tool_use` blocks are returned untouched and by reference: this is an identifier fix, not a
|
|
90
|
-
* re-serialization of the model's answer.
|
|
91
|
-
*/
|
|
92
61
|
export function rewriteToolUseIds(content, taken) {
|
|
93
62
|
const blocks = Array.isArray(content) ? content : [];
|
|
94
63
|
const minter = new ToolUseIdMinter(taken);
|
|
@@ -105,7 +74,6 @@ export function rewriteToolUseIds(content, taken) {
|
|
|
105
74
|
const rewritten = minter.rewritten;
|
|
106
75
|
return { content: rewritten > 0 ? out : blocks, rewritten, map };
|
|
107
76
|
}
|
|
108
|
-
/** Re-serialize one event, replacing its `data:` payload and keeping every other line in place. */
|
|
109
77
|
function replaceEventData(block, data) {
|
|
110
78
|
const eol = block.includes("\r\n") ? "\r\n" : "\n";
|
|
111
79
|
const lines = block.split(/\r?\n/);
|
|
@@ -116,7 +84,6 @@ function replaceEventData(block, data) {
|
|
|
116
84
|
out.push(line);
|
|
117
85
|
continue;
|
|
118
86
|
}
|
|
119
|
-
// Multi-line data folds into the one line we emit at the first data line's position.
|
|
120
87
|
if (emitted)
|
|
121
88
|
continue;
|
|
122
89
|
out.push(`data: ${data}`);
|
|
@@ -124,19 +91,6 @@ function replaceEventData(block, data) {
|
|
|
124
91
|
}
|
|
125
92
|
return out.join(eol);
|
|
126
93
|
}
|
|
127
|
-
/**
|
|
128
|
-
* Wrap a translated Anthropic SSE stream so a colliding `tool_use` id is minted afresh.
|
|
129
|
-
*
|
|
130
|
-
* Applied AFTER dialect recovery so a recovered call is covered too, and BEFORE anything that
|
|
131
|
-
* watches for the first `tool_use` — validation, repair and `guardReshaped`'s structural
|
|
132
|
-
* conservation check all see the final ids, which is what makes the repaired message the same
|
|
133
|
-
* message the client received.
|
|
134
|
-
*
|
|
135
|
-
* Every event this does not rewrite is forwarded byte-identical, including a truncated tail and an
|
|
136
|
-
* event whose data is not JSON: any doubt releases the original bytes. `takenIds` is a thunk so a
|
|
137
|
-
* response with no tool call never pays for walking the conversation, and `onRewrite` reports the
|
|
138
|
-
* running count for the metadata-only log field (headers are long gone by then).
|
|
139
|
-
*/
|
|
140
94
|
export function rewriteToolUseIdsInStream(upstream, takenIds, onRewrite) {
|
|
141
95
|
const decoder = new TextDecoder();
|
|
142
96
|
const encoder = new TextEncoder();
|
|
@@ -150,8 +104,6 @@ export function rewriteToolUseIdsInStream(upstream, takenIds, onRewrite) {
|
|
|
150
104
|
};
|
|
151
105
|
const reader = upstream.getReader();
|
|
152
106
|
const rewriteOne = (block) => {
|
|
153
|
-
// Cheap gate: a tool_use content_block_start always spells the type literally, so ordinary
|
|
154
|
-
// text deltas cost a substring scan rather than a JSON parse.
|
|
155
107
|
if (!block.includes("tool_use"))
|
|
156
108
|
return null;
|
|
157
109
|
const dataLines = sseEventFields(block).dataLines.map((line) => line.trim());
|
|
@@ -192,12 +144,9 @@ export function rewriteToolUseIdsInStream(upstream, takenIds, onRewrite) {
|
|
|
192
144
|
}
|
|
193
145
|
frames.append(decoder.decode());
|
|
194
146
|
processFrames();
|
|
195
|
-
// A truncated final event is outside this seam; preserve it verbatim.
|
|
196
147
|
push(frames.takeRemainder());
|
|
197
148
|
}
|
|
198
149
|
catch (e) {
|
|
199
|
-
// Release what is held before reporting: an id fix must never turn a transport failure
|
|
200
|
-
// into deleted content. Same contract as `stripThinkTagsInStream`.
|
|
201
150
|
frames.append(decoder.decode());
|
|
202
151
|
processFrames();
|
|
203
152
|
push(frames.takeRemainder());
|
package/dist/tool-use-ids.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-use-ids.js","sourceRoot":"","sources":["../src/tool-use-ids.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"tool-use-ids.js","sourceRoot":"","sources":["../src/tool-use-ids.ts"],"names":[],"mappings":"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"}
|
package/dist/usage-observer.js
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Observe provider-reported usage and relay-estimated model output without
|
|
3
|
-
* changing the response path. In particular, this module never changes (or
|
|
4
|
-
* buffers) a response chunk: it only keeps bounded parsing state alongside
|
|
5
|
-
* the stream.
|
|
6
|
-
*/
|
|
7
1
|
import { estimateTokensFromCharacters } from "./metadata.js";
|
|
8
2
|
export function createUsageAccumulator() {
|
|
9
3
|
return {
|
|
@@ -35,7 +29,6 @@ function recordField(accumulator, field, value) {
|
|
|
35
29
|
accumulator[field] = value;
|
|
36
30
|
}
|
|
37
31
|
catch {
|
|
38
|
-
// A malformed/hostile accumulator must not affect pass-through.
|
|
39
32
|
}
|
|
40
33
|
}
|
|
41
34
|
function asRecord(value) {
|
|
@@ -50,7 +43,6 @@ function publishOutputEstimate(accumulator, state) {
|
|
|
50
43
|
: estimateTokensFromCharacters(state.characters);
|
|
51
44
|
}
|
|
52
45
|
catch {
|
|
53
|
-
// A malformed/hostile accumulator must not affect pass-through.
|
|
54
46
|
}
|
|
55
47
|
}
|
|
56
48
|
function isBase64DataUrl(value) {
|
|
@@ -89,8 +81,6 @@ function argumentCharacters(value) {
|
|
|
89
81
|
return sanitized.removedBinary ? sanitized.characters : value.length;
|
|
90
82
|
}
|
|
91
83
|
catch {
|
|
92
|
-
// Malformed JSON cannot be semantically inspected for escaped binary
|
|
93
|
-
// fields. Unknown is safer than a knowingly inflated raw-length total.
|
|
94
84
|
return undefined;
|
|
95
85
|
}
|
|
96
86
|
}
|
|
@@ -116,7 +106,6 @@ function taintOutputEstimate(accumulator, state) {
|
|
|
116
106
|
accumulator.estimatedOutputTokens = undefined;
|
|
117
107
|
}
|
|
118
108
|
catch {
|
|
119
|
-
// A malformed/hostile accumulator must not affect pass-through.
|
|
120
109
|
}
|
|
121
110
|
}
|
|
122
111
|
function recordOutputText(accumulator, state, value) {
|
|
@@ -124,11 +113,6 @@ function recordOutputText(accumulator, state, value) {
|
|
|
124
113
|
return;
|
|
125
114
|
recordOutputCharacters(accumulator, state, value.length);
|
|
126
115
|
}
|
|
127
|
-
/**
|
|
128
|
-
* A streamed logical string may be split before `data:...;base64,` becomes
|
|
129
|
-
* recognizable. Count partial text immediately, but retain only enough state
|
|
130
|
-
* to roll it back if later fragments prove the whole string is binary.
|
|
131
|
-
*/
|
|
132
116
|
function appendStreamedOutputText(accumulator, state, key, value) {
|
|
133
117
|
if (state.overflow || typeof value !== "string" || value.length === 0)
|
|
134
118
|
return;
|
|
@@ -235,8 +219,6 @@ function appendToolArguments(accumulator, state, key, value) {
|
|
|
235
219
|
}
|
|
236
220
|
state.pendingArguments.set(key, (prior ?? "") + value);
|
|
237
221
|
state.pendingArgumentCharacters = nextSize;
|
|
238
|
-
// Until the complete JSON can be sanitized, a partial tool argument could be
|
|
239
|
-
// base64. Hide the whole estimate rather than publish a knowingly partial total.
|
|
240
222
|
publishOutputEstimate(accumulator, state);
|
|
241
223
|
}
|
|
242
224
|
function flushToolArguments(accumulator, state, key) {
|
|
@@ -289,8 +271,6 @@ function inspectAnthropicOutput(root, accumulator, state) {
|
|
|
289
271
|
}
|
|
290
272
|
const key = streamArgumentKey("anthropic", root.index, "unknown");
|
|
291
273
|
if (root.type === "content_block_start") {
|
|
292
|
-
// Anthropic streams seed tool-use blocks with protocol-owned `input: {}`;
|
|
293
|
-
// the authored argument JSON arrives in input_json_delta frames.
|
|
294
274
|
const block = asRecord(root.content_block);
|
|
295
275
|
if (block?.type === "text") {
|
|
296
276
|
appendStreamedOutputText(accumulator, state, `${key}:text`, block.text);
|
|
@@ -346,8 +326,6 @@ function inspectOpenAiMessage(value, accumulator, state, streamKeyPrefix) {
|
|
|
346
326
|
}
|
|
347
327
|
}
|
|
348
328
|
}
|
|
349
|
-
// Providers use these as alternate spellings. Prefer the explicit Chat field
|
|
350
|
-
// so a host emitting both aliases cannot double the same reasoning text.
|
|
351
329
|
const reasoningContent = typeof message.reasoning_content === "string"
|
|
352
330
|
&& message.reasoning_content.length > 0
|
|
353
331
|
? message.reasoning_content
|
|
@@ -421,9 +399,6 @@ function inspectUsageRecord(usage, protocol, accumulator) {
|
|
|
421
399
|
const outputName = protocol === "openai-chat" ? "completion_tokens" : "output_tokens";
|
|
422
400
|
recordField(accumulator, "inputTokens", usage[inputName]);
|
|
423
401
|
recordField(accumulator, "outputTokens", usage[outputName]);
|
|
424
|
-
// Keep completionTokens as a compatibility alias, while outputTokens is the
|
|
425
|
-
// canonical provider-reported field. Each assignment is isolated so a
|
|
426
|
-
// consumer's accessor cannot suppress the other fact.
|
|
427
402
|
if (validCompletionTokens(usage[outputName])) {
|
|
428
403
|
recordField(accumulator, "completionTokens", usage[outputName]);
|
|
429
404
|
}
|
|
@@ -432,7 +407,6 @@ function inspectUsageRecord(usage, protocol, accumulator) {
|
|
|
432
407
|
recordField(accumulator, "cacheReadInputTokens", usage.cache_read_input_tokens);
|
|
433
408
|
return;
|
|
434
409
|
}
|
|
435
|
-
// openai-chat: cached tokens ride in prompt_tokens_details.
|
|
436
410
|
const details = asRecord(usage.prompt_tokens_details);
|
|
437
411
|
if (details)
|
|
438
412
|
recordField(accumulator, "cachedInputTokens", details.cached_tokens);
|
|
@@ -456,7 +430,6 @@ function inspectJson(value, protocol, accumulator, estimate, streamed = false) {
|
|
|
456
430
|
}
|
|
457
431
|
}
|
|
458
432
|
catch {
|
|
459
|
-
// An observer must never make provider bytes fail a request.
|
|
460
433
|
}
|
|
461
434
|
}
|
|
462
435
|
function newSseState() {
|
|
@@ -487,19 +460,15 @@ function inspectSseFrame(state, protocol, accumulator, estimate) {
|
|
|
487
460
|
}
|
|
488
461
|
}
|
|
489
462
|
catch {
|
|
490
|
-
// A reported-usage observer failure is isolated from pass-through.
|
|
491
463
|
}
|
|
492
464
|
}
|
|
493
465
|
}
|
|
494
466
|
}
|
|
495
467
|
else {
|
|
496
|
-
// The only non-Anthropic protocol is openai-chat: every frame is inspected.
|
|
497
468
|
inspectJson(value, protocol, accumulator, estimate, true);
|
|
498
469
|
}
|
|
499
470
|
}
|
|
500
471
|
catch {
|
|
501
|
-
// A skipped frame might have carried content. Preserve traffic, but do not
|
|
502
|
-
// leave a partial estimate looking complete.
|
|
503
472
|
taintOutputEstimate(accumulator, estimate);
|
|
504
473
|
}
|
|
505
474
|
}
|
|
@@ -515,8 +484,6 @@ function processSseLine(line, terminatorBytes, state, protocol, accumulator, est
|
|
|
515
484
|
resetSse(state);
|
|
516
485
|
return;
|
|
517
486
|
}
|
|
518
|
-
// Count all frame bytes, including fields we do not interpret. This keeps
|
|
519
|
-
// an attacker from evading the bound with a very large unknown field.
|
|
520
487
|
state.size += new TextEncoder().encode(line).byteLength + terminatorBytes;
|
|
521
488
|
if (state.size > MAX_SSE_FRAME) {
|
|
522
489
|
state.overflow = true;
|
|
@@ -547,8 +514,6 @@ function makeSseObserver(protocol, accumulator, estimate) {
|
|
|
547
514
|
const code = text.charCodeAt(i);
|
|
548
515
|
if (code !== 10 && code !== 13)
|
|
549
516
|
continue;
|
|
550
|
-
// CRLF may be split over two provider chunks. Keep a trailing CR
|
|
551
|
-
// until LF arrives so it cannot dispatch a false blank frame.
|
|
552
517
|
if (code === 13 && i === text.length - 1 && !final)
|
|
553
518
|
break;
|
|
554
519
|
index = i;
|
|
@@ -557,16 +522,12 @@ function makeSseObserver(protocol, accumulator, estimate) {
|
|
|
557
522
|
}
|
|
558
523
|
if (index < 0) {
|
|
559
524
|
if (discardUntilBreak) {
|
|
560
|
-
// An oversized line can end in CR whose LF arrives in the next chunk.
|
|
561
|
-
// Retain one byte so the frame separator remains intact.
|
|
562
525
|
text = text.endsWith("\r") && !final ? "\r" : "";
|
|
563
526
|
if (final)
|
|
564
527
|
discardUntilBreak = false;
|
|
565
528
|
return;
|
|
566
529
|
}
|
|
567
530
|
if (state.size + new TextEncoder().encode(text).byteLength > MAX_SSE_FRAME) {
|
|
568
|
-
// Do not retain an unterminated oversized line. The next blank
|
|
569
|
-
// line will reset the poisoned frame and permit recovery.
|
|
570
531
|
state.overflow = true;
|
|
571
532
|
text = "";
|
|
572
533
|
discardUntilBreak = true;
|
|
@@ -594,8 +555,6 @@ function makeSseObserver(protocol, accumulator, estimate) {
|
|
|
594
555
|
text = "";
|
|
595
556
|
}
|
|
596
557
|
if (final) {
|
|
597
|
-
// A final line without the customary trailing blank line is still a
|
|
598
|
-
// useful terminal event in practice.
|
|
599
558
|
inspectSseFrame(state, protocol, accumulator, estimate);
|
|
600
559
|
resetSse(state);
|
|
601
560
|
}
|
|
@@ -603,9 +562,6 @@ function makeSseObserver(protocol, accumulator, estimate) {
|
|
|
603
562
|
return {
|
|
604
563
|
push(bytes) {
|
|
605
564
|
try {
|
|
606
|
-
// Keep decoded input bounded even when a provider hands us a very
|
|
607
|
-
// large chunk. TextDecoder's streaming state safely handles a UTF-8
|
|
608
|
-
// code point split between these slices.
|
|
609
565
|
for (let offset = 0; offset < bytes.byteLength; offset += MAX_DECODE_SLICE) {
|
|
610
566
|
const end = Math.min(offset + MAX_DECODE_SLICE, bytes.byteLength);
|
|
611
567
|
const slice = bytes.subarray(offset, end);
|
|
@@ -679,10 +635,6 @@ function makeJsonObserver(protocol, accumulator, estimate) {
|
|
|
679
635
|
},
|
|
680
636
|
};
|
|
681
637
|
}
|
|
682
|
-
/**
|
|
683
|
-
* Return a response whose body is byte-for-byte the input body while usage is
|
|
684
|
-
* observed synchronously as each chunk passes through.
|
|
685
|
-
*/
|
|
686
638
|
export function observeUsage(response, protocol, accumulator, options = {}) {
|
|
687
639
|
if (!response.body)
|
|
688
640
|
return response;
|
|
@@ -698,13 +650,10 @@ export function observeUsage(response, protocol, accumulator, options = {}) {
|
|
|
698
650
|
: makeJsonObserver(protocol, accumulator, estimate);
|
|
699
651
|
const transform = new TransformStream({
|
|
700
652
|
transform(chunk, controller) {
|
|
701
|
-
// Observer failures are isolated; errors from the upstream body are
|
|
702
|
-
// owned by pipeThrough and therefore propagate unchanged.
|
|
703
653
|
try {
|
|
704
654
|
observer.push(chunk);
|
|
705
655
|
}
|
|
706
656
|
catch {
|
|
707
|
-
// A user-supplied accumulator/property must never break pass-through.
|
|
708
657
|
}
|
|
709
658
|
controller.enqueue(chunk);
|
|
710
659
|
},
|
|
@@ -713,7 +662,6 @@ export function observeUsage(response, protocol, accumulator, options = {}) {
|
|
|
713
662
|
observer.finish();
|
|
714
663
|
}
|
|
715
664
|
catch {
|
|
716
|
-
// See transform(): observation is strictly best effort.
|
|
717
665
|
}
|
|
718
666
|
},
|
|
719
667
|
});
|