@tangle-network/agent-runtime 0.113.1 → 0.115.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/{activation-UZ1JenNv.js → activation-BNQQO-bc.js} +3 -3
- package/dist/{activation-UZ1JenNv.js.map → activation-BNQQO-bc.js.map} +1 -1
- package/dist/agent.d.ts +18 -7
- package/dist/agent.js +123 -64
- package/dist/agent.js.map +1 -1
- package/dist/candidate-execution/index.js +4 -4
- package/dist/{candidate-execution-Co3sRC_7.js → candidate-execution-CfpJrd3o.js} +4 -4
- package/dist/{candidate-execution-Co3sRC_7.js.map → candidate-execution-CfpJrd3o.js.map} +1 -1
- package/dist/{environment-provider-BsHpW4Dp.js → environment-provider-CCaEhA-l.js} +38 -4
- package/dist/environment-provider-CCaEhA-l.js.map +1 -0
- package/dist/{environment-provider-DG-cSx89.d.ts → environment-provider-Cygthiy3.d.ts} +40 -1
- package/dist/environment-provider.d.ts +1 -1
- package/dist/environment-provider.js +1 -1
- package/dist/{improvement-cycle-C3CrNfcy.js → improvement-cycle-CBLOgfsQ.js} +4 -4
- package/dist/{improvement-cycle-C3CrNfcy.js.map → improvement-cycle-CBLOgfsQ.js.map} +1 -1
- package/dist/{index-Cr7QlEUL.d.ts → index-B_vcAAle.d.ts} +6 -6
- package/dist/{index-zMAqz53v.d.ts → index-BuzkRsOn.d.ts} +244 -9
- package/dist/{index-B6WclhZI.d.ts → index-I97xACXk.d.ts} +3 -3
- package/dist/index.d.ts +8 -8
- package/dist/index.js +13 -13
- package/dist/intelligence.d.ts +3 -3
- package/dist/intelligence.js +6 -6
- package/dist/kernel.d.ts +4 -4
- package/dist/kernel.js +7 -7
- package/dist/{knowledge-GAJFl_gz.js → knowledge-DcIKJaPd.js} +5 -5
- package/dist/{knowledge-GAJFl_gz.js.map → knowledge-DcIKJaPd.js.map} +1 -1
- package/dist/knowledge.d.ts +1 -1
- package/dist/knowledge.js +1 -1
- package/dist/{local-harness-Ce1emWJE.d.ts → local-harness-BnB1E9nM.d.ts} +82 -2
- package/dist/{loop-runner-bin-DvLdFo38.js → loop-runner-bin-C2GIqSUn.js} +3 -3
- package/dist/{loop-runner-bin-DvLdFo38.js.map → loop-runner-bin-C2GIqSUn.js.map} +1 -1
- package/dist/{loop-runner-bin-Cba5tsdb.d.ts → loop-runner-bin-DgAXgQCI.d.ts} +3 -3
- package/dist/loop-runner-bin.d.ts +1 -1
- package/dist/loop-runner-bin.js +1 -1
- package/dist/mcp/bin.js +2 -2
- package/dist/mcp/index.d.ts +2 -2
- package/dist/mcp/index.js +5 -5
- package/dist/{openai-tools-DftmzKLc.js → openai-tools-BWAEIHkZ.js} +2 -2
- package/dist/{openai-tools-DftmzKLc.js.map → openai-tools-BWAEIHkZ.js.map} +1 -1
- package/dist/{otel-export-D34IQW1C.js → otel-export-CPZTSADj.js} +16 -7
- package/dist/otel-export-CPZTSADj.js.map +1 -0
- package/dist/{prepare-BKVdOn0w.js → prepare-BHQBb02e.js} +2 -2
- package/dist/{prepare-BKVdOn0w.js.map → prepare-BHQBb02e.js.map} +1 -1
- package/dist/primeintellect/index.d.ts +1 -1
- package/dist/{protected-model-port-BtldAE9j.js → protected-model-port-DqAH1Z2M.js} +2 -2
- package/dist/{protected-model-port-BtldAE9j.js.map → protected-model-port-DqAH1Z2M.js.map} +1 -1
- package/dist/{redact-xZvPu9qe.d.ts → redact-BRaHUFMf.d.ts} +2 -2
- package/dist/{runtime-DlSPFm9o.js → runtime-C_g_SsXg.js} +8 -5
- package/dist/runtime-C_g_SsXg.js.map +1 -0
- package/dist/{spawn-journal-DLhnom3e.js → spawn-journal-DCPbicXB.js} +2 -1
- package/dist/{spawn-journal-DLhnom3e.js.map → spawn-journal-DCPbicXB.js.map} +1 -1
- package/dist/{structural-rollout-R_ZqFaB5.js → structural-rollout-Cqm1dKa7.js} +3 -3
- package/dist/{structural-rollout-R_ZqFaB5.js.map → structural-rollout-Cqm1dKa7.js.map} +1 -1
- package/dist/{supervise-BDvAFOTM.js → supervise-BcCHhLrz.js} +807 -35
- package/dist/supervise-BcCHhLrz.js.map +1 -0
- package/dist/{supervisor-DS278ICs.js → supervisor-CspDspod.js} +250 -22
- package/dist/supervisor-CspDspod.js.map +1 -0
- package/dist/testing.js +9 -9
- package/dist/{trace-propagation-CJJC7SVB.js → trace-propagation-B-pL7xn_.js} +2 -2
- package/dist/{trace-propagation-CJJC7SVB.js.map → trace-propagation-B-pL7xn_.js.map} +1 -1
- package/dist/{workspace-archive-CYYxZyA7.js → workspace-archive-DXzJq7WP.js} +2 -2
- package/dist/{workspace-archive-CYYxZyA7.js.map → workspace-archive-DXzJq7WP.js.map} +1 -1
- package/package.json +1 -1
- package/dist/environment-provider-BsHpW4Dp.js.map +0 -1
- package/dist/otel-export-D34IQW1C.js.map +0 -1
- package/dist/runtime-DlSPFm9o.js.map +0 -1
- package/dist/supervise-BDvAFOTM.js.map +0 -1
- package/dist/supervisor-DS278ICs.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { c as RuntimeRunStateError, u as ValidationError } from "./errors-DEAvWQPy.js";
|
|
2
2
|
import { n as addTokenUsage, p as zeroTokenUsage } from "./util-Cc9g9Y-o.js";
|
|
3
|
-
import { a as contentAddress, c as replaySpawnTree, o as materializeTreeView, s as pendingWaits } from "./spawn-journal-
|
|
3
|
+
import { a as contentAddress, c as replaySpawnTree, o as materializeTreeView, s as pendingWaits } from "./spawn-journal-DCPbicXB.js";
|
|
4
4
|
import { i as notifyRuntimeHookEvent } from "./runtime-hooks-C7iJOWm3.js";
|
|
5
5
|
import { estimateCost, isModelPriced } from "@tangle-network/agent-eval";
|
|
6
6
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -1917,14 +1917,7 @@ function splitReasoning(rawContent, fieldReasoning) {
|
|
|
1917
1917
|
* `routerChatWithUsage`. `tool_choice: 'auto'` lets the model decide; the driver loops on the result.
|
|
1918
1918
|
*/
|
|
1919
1919
|
async function routerChatWithTools(cfg, messages, tools, opts) {
|
|
1920
|
-
const body =
|
|
1921
|
-
model: cfg.model,
|
|
1922
|
-
messages,
|
|
1923
|
-
tools,
|
|
1924
|
-
tool_choice: opts?.toolChoice ?? "auto",
|
|
1925
|
-
temperature: opts?.temperature ?? .3,
|
|
1926
|
-
...opts?.maxTokens ? { max_tokens: opts.maxTokens } : {}
|
|
1927
|
-
};
|
|
1920
|
+
const body = toolCompletionBody(cfg, messages, tools, opts);
|
|
1928
1921
|
const data = cfg.complete ? await cfg.complete(body) : await (async () => {
|
|
1929
1922
|
const res = await fetch(`${cfg.routerBaseUrl.replace(/\/$/, "")}/chat/completions`, {
|
|
1930
1923
|
method: "POST",
|
|
@@ -1944,12 +1937,7 @@ async function routerChatWithTools(cfg, messages, tools, opts) {
|
|
|
1944
1937
|
name: tc.function?.name ?? "",
|
|
1945
1938
|
arguments: tc.function?.arguments ?? "{}"
|
|
1946
1939
|
}));
|
|
1947
|
-
const
|
|
1948
|
-
const usage = u && typeof u.prompt_tokens === "number" && typeof u.completion_tokens === "number" ? {
|
|
1949
|
-
input: u.prompt_tokens,
|
|
1950
|
-
output: u.completion_tokens
|
|
1951
|
-
} : void 0;
|
|
1952
|
-
const costUsd = usage && isModelPriced(cfg.model) ? estimateCost(usage.input, usage.output, cfg.model) : void 0;
|
|
1940
|
+
const { usage, costUsd } = meterTurn(data.usage, cfg.model);
|
|
1953
1941
|
return {
|
|
1954
1942
|
content: msg?.content ?? null,
|
|
1955
1943
|
toolCalls,
|
|
@@ -1957,6 +1945,222 @@ async function routerChatWithTools(cfg, messages, tools, opts) {
|
|
|
1957
1945
|
...costUsd !== void 0 ? { costUsd } : {}
|
|
1958
1946
|
};
|
|
1959
1947
|
}
|
|
1948
|
+
/** The OpenAI-shape request body both tool-calling transports send. One builder so the streamed
|
|
1949
|
+
* turn cannot drift from the buffered one on model, temperature, tool_choice, or the ceiling. */
|
|
1950
|
+
function toolCompletionBody(cfg, messages, tools, opts) {
|
|
1951
|
+
return {
|
|
1952
|
+
model: cfg.model,
|
|
1953
|
+
messages,
|
|
1954
|
+
tools,
|
|
1955
|
+
tool_choice: opts?.toolChoice ?? "auto",
|
|
1956
|
+
temperature: opts?.temperature ?? .3,
|
|
1957
|
+
...opts?.maxTokens ? { max_tokens: opts.maxTokens } : {}
|
|
1958
|
+
};
|
|
1959
|
+
}
|
|
1960
|
+
/**
|
|
1961
|
+
* REAL usage → the metered pair, or `undefined` when the provider reported none. Never a
|
|
1962
|
+
* fabricated 0: a phantom 0 reads as a free call to the conserved budget pool, which would then
|
|
1963
|
+
* over-spend. Shared by the buffered and streamed transports so both meter identically.
|
|
1964
|
+
*/
|
|
1965
|
+
function meterTurn(raw, model) {
|
|
1966
|
+
const usage = raw && typeof raw.prompt_tokens === "number" && typeof raw.completion_tokens === "number" ? {
|
|
1967
|
+
input: raw.prompt_tokens,
|
|
1968
|
+
output: raw.completion_tokens
|
|
1969
|
+
} : void 0;
|
|
1970
|
+
if (!usage) return {};
|
|
1971
|
+
const localEstimate = isModelPriced(model) ? estimateCost(usage.input, usage.output, model) : void 0;
|
|
1972
|
+
const cache = readPromptCache(raw?.prompt_cache);
|
|
1973
|
+
const costUsd = localEstimate !== void 0 && cache?.readSavingsUsd !== void 0 ? Math.max(0, localEstimate - cache.readSavingsUsd) : localEstimate;
|
|
1974
|
+
return {
|
|
1975
|
+
usage,
|
|
1976
|
+
...costUsd !== void 0 ? { costUsd } : {},
|
|
1977
|
+
...cache ? { cache } : {}
|
|
1978
|
+
};
|
|
1979
|
+
}
|
|
1980
|
+
function readPromptCache(raw) {
|
|
1981
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
1982
|
+
const o = raw;
|
|
1983
|
+
const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
1984
|
+
const readTokens = num(o.read_tokens);
|
|
1985
|
+
const writeTokens = num(o.write_tokens);
|
|
1986
|
+
if (readTokens === void 0 && writeTokens === void 0) return void 0;
|
|
1987
|
+
const savings = num(o.read_savings_usd);
|
|
1988
|
+
return {
|
|
1989
|
+
readTokens: readTokens ?? 0,
|
|
1990
|
+
writeTokens: writeTokens ?? 0,
|
|
1991
|
+
...savings !== void 0 ? { readSavingsUsd: savings } : {},
|
|
1992
|
+
...typeof o.status === "string" ? { status: o.status } : {}
|
|
1993
|
+
};
|
|
1994
|
+
}
|
|
1995
|
+
/**
|
|
1996
|
+
* The SAME completion as `routerChatWithTools`, taken over SSE (`stream: true`) and reassembled
|
|
1997
|
+
* into the identical `RouterChatToolsResult`. Opt in with `RouterConfig.stream` — the buffered
|
|
1998
|
+
* function is untouched and stays the default for every existing caller.
|
|
1999
|
+
*
|
|
2000
|
+
* What it buys: a buffered POST holds one connection idle for the whole completion, and that idle
|
|
2001
|
+
* window is what an intermediary gateway kills (524/503). Streaming puts bytes on the wire from the
|
|
2002
|
+
* first generated token, so the connection is only idle through prefill.
|
|
2003
|
+
*
|
|
2004
|
+
* Usage accounting is preserved exactly: `stream_options.include_usage` asks the provider for a
|
|
2005
|
+
* terminal usage chunk, and those tokens run through the same `meterTurn` the buffered path uses.
|
|
2006
|
+
*
|
|
2007
|
+
* When NO chunk reported usage, `usage`/`costUsd` stay undefined (never a fabricated 0) AND
|
|
2008
|
+
* `usageUnknown: true` is set. A stream that finishes with no usage chunk means the
|
|
2009
|
+
* `include_usage` request was not honored upstream, and returning a quiet `undefined` for it is
|
|
2010
|
+
* indistinguishable from a free turn — the marker is what lets a metering caller record an UNKNOWN
|
|
2011
|
+
* turn instead. Streaming raises the odds of this (one dropped terminal frame is enough), which is
|
|
2012
|
+
* why the streamed transport says so explicitly and the buffered one has no equivalent claim to make.
|
|
2013
|
+
*/
|
|
2014
|
+
async function streamRouterChatWithTools(cfg, messages, tools, opts) {
|
|
2015
|
+
if (cfg.complete) throw new ValidationError("streamRouterChatWithTools: RouterConfig.complete is a BUFFERED transport (it returns one parsed completion body) and cannot serve a stream. Drop `stream` to use the injected transport, or drop `complete` to stream from the router.");
|
|
2016
|
+
const body = {
|
|
2017
|
+
...toolCompletionBody(cfg, messages, tools, opts),
|
|
2018
|
+
stream: true,
|
|
2019
|
+
stream_options: { include_usage: true }
|
|
2020
|
+
};
|
|
2021
|
+
const res = await fetch(`${cfg.routerBaseUrl.replace(/\/$/, "")}/chat/completions`, {
|
|
2022
|
+
method: "POST",
|
|
2023
|
+
headers: {
|
|
2024
|
+
"content-type": "application/json",
|
|
2025
|
+
authorization: `Bearer ${cfg.routerKey}`,
|
|
2026
|
+
accept: "text/event-stream"
|
|
2027
|
+
},
|
|
2028
|
+
body: JSON.stringify(body),
|
|
2029
|
+
...opts?.signal ? { signal: opts.signal } : {}
|
|
2030
|
+
});
|
|
2031
|
+
if (!res.ok) throw new Error(`router ${res.status}: ${(await res.text()).slice(0, 200)}`);
|
|
2032
|
+
if (!res.body) throw new ValidationError(`router ${res.status}: streamed completion returned no response body to read`);
|
|
2033
|
+
let content = "";
|
|
2034
|
+
let sawContent = false;
|
|
2035
|
+
let fieldReasoning = "";
|
|
2036
|
+
let finishReason;
|
|
2037
|
+
let rawUsage;
|
|
2038
|
+
const calls = /* @__PURE__ */ new Map();
|
|
2039
|
+
let lastCallIndex = -1;
|
|
2040
|
+
for await (const chunk of readChatCompletionChunks(res.body)) {
|
|
2041
|
+
if (chunk.error) throw new ValidationError(`router stream error: ${chunk.error.message ?? chunk.error.type ?? "unknown"}`);
|
|
2042
|
+
if (chunk.usage) rawUsage = chunk.usage;
|
|
2043
|
+
const choice = chunk.choices?.[0];
|
|
2044
|
+
if (!choice) continue;
|
|
2045
|
+
if (typeof choice.finish_reason === "string") finishReason = choice.finish_reason;
|
|
2046
|
+
const delta = choice.delta;
|
|
2047
|
+
if (!delta) continue;
|
|
2048
|
+
if (typeof delta.content === "string") {
|
|
2049
|
+
content += delta.content;
|
|
2050
|
+
sawContent = true;
|
|
2051
|
+
}
|
|
2052
|
+
const reasoningDelta = delta.reasoning_content ?? delta.reasoning;
|
|
2053
|
+
if (typeof reasoningDelta === "string") fieldReasoning += reasoningDelta;
|
|
2054
|
+
for (const tc of delta.tool_calls ?? []) {
|
|
2055
|
+
const index = typeof tc.index === "number" ? tc.index : tc.id && !idAlreadyOpen(calls, tc.id) ? lastCallIndex + 1 : Math.max(lastCallIndex, 0);
|
|
2056
|
+
lastCallIndex = Math.max(lastCallIndex, index);
|
|
2057
|
+
const acc = calls.get(index) ?? { arguments: "" };
|
|
2058
|
+
if (tc.id) acc.id = tc.id;
|
|
2059
|
+
if (tc.function?.name) acc.name = tc.function.name;
|
|
2060
|
+
if (tc.function?.arguments) acc.arguments += tc.function.arguments;
|
|
2061
|
+
calls.set(index, acc);
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
const split = splitReasoning(content, fieldReasoning.length > 0 ? fieldReasoning : void 0);
|
|
2065
|
+
const toolCalls = [...calls.entries()].sort((a, b) => a[0] - b[0]).map(([index, call]) => ({
|
|
2066
|
+
id: call.id ?? `call_${index}`,
|
|
2067
|
+
name: call.name ?? "",
|
|
2068
|
+
arguments: call.arguments || "{}"
|
|
2069
|
+
}));
|
|
2070
|
+
const { usage, costUsd } = meterTurn(rawUsage, cfg.model);
|
|
2071
|
+
return {
|
|
2072
|
+
content: sawContent ? split.content : null,
|
|
2073
|
+
toolCalls,
|
|
2074
|
+
...split.reasoning ? { reasoning: split.reasoning } : {},
|
|
2075
|
+
...finishReason !== void 0 ? { finishReason } : {},
|
|
2076
|
+
...usage ? { usage } : {},
|
|
2077
|
+
...costUsd !== void 0 ? { costUsd } : {},
|
|
2078
|
+
...usage ? {} : { usageUnknown: true }
|
|
2079
|
+
};
|
|
2080
|
+
}
|
|
2081
|
+
function idAlreadyOpen(calls, id) {
|
|
2082
|
+
for (const call of calls.values()) if (call.id === id) return true;
|
|
2083
|
+
return false;
|
|
2084
|
+
}
|
|
2085
|
+
/**
|
|
2086
|
+
* Decode an SSE body into chat-completion chunks. Frames are blank-line separated; `data:` lines
|
|
2087
|
+
* concatenate; `:` comments are keepalives; `[DONE]` ends the stream. The unterminated tail is
|
|
2088
|
+
* parsed too — an upstream that dies mid-response routinely sends a last frame with no trailing
|
|
2089
|
+
* blank line, and dropping it would turn a real upstream error into a silent empty turn.
|
|
2090
|
+
*
|
|
2091
|
+
* All three SSE line terminators (CRLF, LF, lone CR) are normalized, and the boundary scan runs
|
|
2092
|
+
* over the ACCUMULATED buffer — never over a single read in isolation, because a read may split a
|
|
2093
|
+
* CRLF. See `normalizeSseLineEndings`; the split is exercised byte-by-byte by the tests.
|
|
2094
|
+
*
|
|
2095
|
+
* The generator OWNS the body for its lifetime and releases it on every exit. Stopping early —
|
|
2096
|
+
* at `[DONE]`, or because the consumer threw — leaves the HTTP response only partly read, so the
|
|
2097
|
+
* body is CANCELLED rather than merely unlocked: releasing the lock alone leaves the connection
|
|
2098
|
+
* held open until the socket times out, which under a connection pool starves later turns.
|
|
2099
|
+
*/
|
|
2100
|
+
async function* readChatCompletionChunks(body) {
|
|
2101
|
+
const reader = body.getReader();
|
|
2102
|
+
const decoder = new TextDecoder();
|
|
2103
|
+
let buf = "";
|
|
2104
|
+
let drained = false;
|
|
2105
|
+
try {
|
|
2106
|
+
for (;;) {
|
|
2107
|
+
const { done, value } = await reader.read();
|
|
2108
|
+
buf = normalizeSseLineEndings(done ? buf : buf + decoder.decode(value, { stream: true }), !done);
|
|
2109
|
+
let sep = buf.indexOf("\n\n");
|
|
2110
|
+
while (sep !== -1) {
|
|
2111
|
+
const frame = parseChunkFrame(buf.slice(0, sep));
|
|
2112
|
+
buf = buf.slice(sep + 2);
|
|
2113
|
+
if (frame === "done") return;
|
|
2114
|
+
if (frame) yield frame;
|
|
2115
|
+
sep = buf.indexOf("\n\n");
|
|
2116
|
+
}
|
|
2117
|
+
if (done) break;
|
|
2118
|
+
}
|
|
2119
|
+
drained = true;
|
|
2120
|
+
const tail = parseChunkFrame(buf);
|
|
2121
|
+
if (tail && tail !== "done") yield tail;
|
|
2122
|
+
} finally {
|
|
2123
|
+
if (!drained) await reader.cancel().catch(() => void 0);
|
|
2124
|
+
reader.releaseLock();
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
/**
|
|
2128
|
+
* Normalize an SSE body's line terminators to `\n`. The SSE grammar accepts CRLF, LF, AND a lone
|
|
2129
|
+
* CR, so a `\r`-separated body is legal — and left unnormalized it decodes to zero frames, which
|
|
2130
|
+
* this transport would report as a turn with no content, no tool calls, and `usageUnknown: true`
|
|
2131
|
+
* and no error at all. Losing a whole turn in silence is the one outcome it may not produce.
|
|
2132
|
+
*
|
|
2133
|
+
* `holdTrailingCr` keeps an ambiguous FINAL `\r` out of the conversion while more bytes may still
|
|
2134
|
+
* arrive: rewriting it to `\n` before the next byte is seen turns a read-split CRLF into a FALSE
|
|
2135
|
+
* frame boundary, splitting one frame into two unparseable halves. At EOF nothing can follow it,
|
|
2136
|
+
* so it is converted. Re-running this over the accumulated buffer is idempotent.
|
|
2137
|
+
*/
|
|
2138
|
+
function normalizeSseLineEndings(buf, holdTrailingCr) {
|
|
2139
|
+
if (holdTrailingCr && buf.endsWith("\r")) return `${buf.slice(0, -1).replace(/\r\n?/g, "\n")}\r`;
|
|
2140
|
+
return buf.replace(/\r\n?/g, "\n");
|
|
2141
|
+
}
|
|
2142
|
+
/** One SSE frame → a chunk, `'done'`, or undefined for a comment/keepalive/unparseable payload. */
|
|
2143
|
+
function parseChunkFrame(frame) {
|
|
2144
|
+
const dataLines = [];
|
|
2145
|
+
for (const line of frame.split("\n")) {
|
|
2146
|
+
if (!line || line.startsWith(":")) continue;
|
|
2147
|
+
if (line.startsWith("data:")) dataLines.push(line.slice(5).trimStart());
|
|
2148
|
+
}
|
|
2149
|
+
if (dataLines.length === 0) return void 0;
|
|
2150
|
+
const data = dataLines.join("\n");
|
|
2151
|
+
if (data === "[DONE]") return "done";
|
|
2152
|
+
try {
|
|
2153
|
+
return JSON.parse(data);
|
|
2154
|
+
} catch {
|
|
2155
|
+
return;
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
/** Pick the tool-calling transport the config asked for. `routerChatWithTools` itself never
|
|
2159
|
+
* streams; this is the one place `RouterConfig.stream` is honored, so every router-client entry
|
|
2160
|
+
* point (`routerBrain`, `routerToolLoop`) obeys the same switch. */
|
|
2161
|
+
function chatWithTools(cfg, messages, tools, opts) {
|
|
2162
|
+
return cfg.stream === true ? streamRouterChatWithTools(cfg, messages, tools, opts) : routerChatWithTools(cfg, messages, tools, opts);
|
|
2163
|
+
}
|
|
1960
2164
|
/**
|
|
1961
2165
|
* The tool-using router backend: a real agentic loop OVER the Tangle router (which
|
|
1962
2166
|
* supports tool-calling), off-box — no sandbox. Each turn is one router completion
|
|
@@ -1970,7 +2174,7 @@ async function routerChatWithTools(cfg, messages, tools, opts) {
|
|
|
1970
2174
|
*/
|
|
1971
2175
|
async function routerToolLoop(cfg, system, user, tools, execute, opts) {
|
|
1972
2176
|
return runBrainLoop({
|
|
1973
|
-
chat: (messages, toolSpecs) =>
|
|
2177
|
+
chat: (messages, toolSpecs) => chatWithTools(cfg, messages, toolSpecs, {
|
|
1974
2178
|
...opts?.temperature !== void 0 ? { temperature: opts.temperature } : {},
|
|
1975
2179
|
...opts?.maxTokens ? { maxTokens: opts.maxTokens } : {},
|
|
1976
2180
|
...opts?.signal ? { signal: opts.signal } : {}
|
|
@@ -1992,10 +2196,13 @@ async function routerToolLoop(cfg, system, user, tools, execute, opts) {
|
|
|
1992
2196
|
* tool-calling. The driver's spawn/observe/steer/await/stop turns become real router tool-calls.
|
|
1993
2197
|
* The turnkey production brain — tests script a mock `ToolLoopChat`; production passes
|
|
1994
2198
|
* `routerBrain(cfg)`. No message translation: the loop already speaks the router's OpenAI shape.
|
|
2199
|
+
*
|
|
2200
|
+
* Transport follows `cfg.stream`: buffered by default, SSE when the caller opts in. A supervisor
|
|
2201
|
+
* turn is the longest completion in the system, so it is the call site streaming exists for.
|
|
1995
2202
|
*/
|
|
1996
2203
|
function routerBrain(cfg, opts = {}) {
|
|
1997
2204
|
const temperature = opts.temperature ?? .4;
|
|
1998
|
-
return (messages, tools) =>
|
|
2205
|
+
return (messages, tools) => chatWithTools(cfg, messages, tools, {
|
|
1999
2206
|
temperature,
|
|
2000
2207
|
toolChoice: "auto",
|
|
2001
2208
|
...cfg.maxTokens !== void 0 ? { maxTokens: cfg.maxTokens } : {}
|
|
@@ -2041,10 +2248,12 @@ function readWorkerProgress(scope, executor, now, stallAfterMs = DEFAULT_STALL_A
|
|
|
2041
2248
|
stallAfterMs,
|
|
2042
2249
|
turns: executor?.turns ?? scope.turns,
|
|
2043
2250
|
tokens: scope.tokens,
|
|
2251
|
+
...scope.tokensKnown === false ? { tokensKnown: false } : {},
|
|
2044
2252
|
usd: scope.usd,
|
|
2045
2253
|
...scope.usdKnown === false ? { usdKnown: false } : {},
|
|
2046
2254
|
pendingMessages: executor?.pendingMessages ?? 0,
|
|
2047
2255
|
recentActivity: executor?.recentActivity ?? [],
|
|
2256
|
+
...executor?.derived?.length ? { derived: executor.derived } : {},
|
|
2048
2257
|
...note ? { note } : {}
|
|
2049
2258
|
};
|
|
2050
2259
|
}
|
|
@@ -2645,6 +2854,7 @@ function createScope(args) {
|
|
|
2645
2854
|
lastActivityAt: child.lastActivityAt,
|
|
2646
2855
|
turns: child.spent.iterations,
|
|
2647
2856
|
tokens: child.spent.tokens,
|
|
2857
|
+
...child.spent.tokensKnown === false ? { tokensKnown: false } : {},
|
|
2648
2858
|
usd: child.spent.usd,
|
|
2649
2859
|
...child.spent.usdKnown === false ? { usdKnown: false } : {}
|
|
2650
2860
|
}, fromExecutor, opts.now ?? now(), opts.stallAfterMs ?? 18e4);
|
|
@@ -3040,6 +3250,7 @@ function clampSpend(spend, budget) {
|
|
|
3040
3250
|
output: Math.floor(spend.tokens.output * ratio)
|
|
3041
3251
|
} : spend.tokens,
|
|
3042
3252
|
usd: budget.maxUsd === void 0 ? spend.usd : Math.min(spend.usd, budget.maxUsd),
|
|
3253
|
+
...spend.tokensKnown === false ? { tokensKnown: false } : {},
|
|
3043
3254
|
...spend.usdKnown === false ? { usdKnown: false } : {},
|
|
3044
3255
|
ms: spend.ms
|
|
3045
3256
|
};
|
|
@@ -3263,6 +3474,7 @@ function sumSpend(settled) {
|
|
|
3263
3474
|
total.tokens.input += ev.spent.tokens.input;
|
|
3264
3475
|
total.tokens.output += ev.spent.tokens.output;
|
|
3265
3476
|
total.usd += ev.spent.usd;
|
|
3477
|
+
if (ev.spent.tokensKnown === false) total.tokensKnown = false;
|
|
3266
3478
|
if (ev.spent.usdKnown === false) total.usdKnown = false;
|
|
3267
3479
|
total.ms += ev.spent.ms;
|
|
3268
3480
|
}
|
|
@@ -3287,13 +3499,17 @@ function sumMetered(events) {
|
|
|
3287
3499
|
total.tokens.input += ev.spend.tokens.input;
|
|
3288
3500
|
total.tokens.output += ev.spend.tokens.output;
|
|
3289
3501
|
total.usd += ev.spend.usd;
|
|
3502
|
+
if (ev.spend.tokensKnown === false) total.tokensKnown = false;
|
|
3290
3503
|
if (ev.spend.usdKnown === false) total.usdKnown = false;
|
|
3291
3504
|
total.ms += ev.spend.ms;
|
|
3292
3505
|
}
|
|
3293
3506
|
return total;
|
|
3294
3507
|
}
|
|
3508
|
+
/** An all-zero spend that carries an UNKNOWN marker counts as non-zero: a sub-driver whose turns
|
|
3509
|
+
* went unmeasured did real work, and dropping its `metered` event here would re-hide upstream the
|
|
3510
|
+
* very turn the driver refused to skip. */
|
|
3295
3511
|
function isNonZeroSpend(s) {
|
|
3296
|
-
return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0;
|
|
3512
|
+
return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0 || s.tokensKnown === false || s.usdKnown === false;
|
|
3297
3513
|
}
|
|
3298
3514
|
/** A spend, or `undefined` when it is all-zero — so `metered()` returns undefined for a driver
|
|
3299
3515
|
* whose sub-tree did no inference (and the parent journals no empty `metered` event). */
|
|
@@ -3365,6 +3581,10 @@ function isAgent(value) {
|
|
|
3365
3581
|
* unknown reconcile) so a child can never refund twice. If dollar cost is unknowable under a
|
|
3366
3582
|
* dollar limit, reconciliation closes the known token/iteration work, marks the dollar channel
|
|
3367
3583
|
* unusable, and refuses later reservations; inventing a numeric dollar total would be worse.
|
|
3584
|
+
* If a TOKEN count is unknowable (a provider that reported no usage for work that ran), the pool
|
|
3585
|
+
* records the work, debits what it knows, and marks `readout().tokensKnown` false — the balance is
|
|
3586
|
+
* then a ceiling, not a measurement. It does not close admission the way the dollar channel does:
|
|
3587
|
+
* tokens are always capped, so one unreported turn must not end the run.
|
|
3368
3588
|
*
|
|
3369
3589
|
* @experimental
|
|
3370
3590
|
*/
|
|
@@ -3427,6 +3647,7 @@ function createBudgetPool(root, now = Date.now) {
|
|
|
3427
3647
|
let freeTokens = root.maxTokens;
|
|
3428
3648
|
let reservedTokens = 0;
|
|
3429
3649
|
let committedTokens = 0;
|
|
3650
|
+
let tokensTainted = false;
|
|
3430
3651
|
const usdCapped = root.maxUsd !== void 0;
|
|
3431
3652
|
let freeUsd = root.maxUsd ?? 0;
|
|
3432
3653
|
let reservedUsd = 0;
|
|
@@ -3493,6 +3714,7 @@ function createBudgetPool(root, now = Date.now) {
|
|
|
3493
3714
|
if (spent.iterations > rIterations) throw new Error(`budget pool: ticket ${ticket.id} spent ${spent.iterations} iterations > reserved ${rIterations}`);
|
|
3494
3715
|
if (usdCapped && spent.usd > rUsd) throw new Error(`budget pool: ticket ${ticket.id} spent $${spent.usd} > reserved $${rUsd}`);
|
|
3495
3716
|
open.delete(ticket.id);
|
|
3717
|
+
if (spent.tokensKnown === false) tokensTainted = true;
|
|
3496
3718
|
reservedTokens -= rTokens;
|
|
3497
3719
|
committedTokens += spentTokens;
|
|
3498
3720
|
freeTokens += rTokens - spentTokens;
|
|
@@ -3511,6 +3733,7 @@ function createBudgetPool(root, now = Date.now) {
|
|
|
3511
3733
|
}
|
|
3512
3734
|
function observe(spend) {
|
|
3513
3735
|
if (usdCapped && spend.usdKnown === false) throw new Error("budget pool: cannot observe unknown dollar cost under a dollar-capped budget");
|
|
3736
|
+
if (spend.tokensKnown === false) tokensTainted = true;
|
|
3514
3737
|
const tokens = totalTokens(spend.tokens);
|
|
3515
3738
|
freeTokens -= tokens;
|
|
3516
3739
|
committedTokens += tokens;
|
|
@@ -3522,6 +3745,7 @@ function createBudgetPool(root, now = Date.now) {
|
|
|
3522
3745
|
function readout() {
|
|
3523
3746
|
return {
|
|
3524
3747
|
tokensLeft: freeTokens,
|
|
3748
|
+
tokensKnown: !tokensTainted,
|
|
3525
3749
|
usdLeft: usdCapped ? usdTainted ? 0 : freeUsd : 0,
|
|
3526
3750
|
usdCapped,
|
|
3527
3751
|
deadlineMs: absoluteDeadlineMs,
|
|
@@ -4012,6 +4236,7 @@ function accumulate(a, b) {
|
|
|
4012
4236
|
a.tokens.input += b.tokens.input;
|
|
4013
4237
|
a.tokens.output += b.tokens.output;
|
|
4014
4238
|
a.usd += b.usd;
|
|
4239
|
+
if (b.tokensKnown === false) a.tokensKnown = false;
|
|
4015
4240
|
if (b.usdKnown === false) a.usdKnown = false;
|
|
4016
4241
|
a.ms += b.ms;
|
|
4017
4242
|
}
|
|
@@ -4025,17 +4250,20 @@ function addSpend(a, b) {
|
|
|
4025
4250
|
output: a.tokens.output + b.tokens.output
|
|
4026
4251
|
},
|
|
4027
4252
|
usd: a.usd + b.usd,
|
|
4253
|
+
...a.tokensKnown === false || b.tokensKnown === false ? { tokensKnown: false } : {},
|
|
4028
4254
|
...a.usdKnown === false || b.usdKnown === false ? { usdKnown: false } : {},
|
|
4029
4255
|
ms: a.ms + b.ms
|
|
4030
4256
|
};
|
|
4031
4257
|
}
|
|
4032
4258
|
/** True when any driver metered inference this run (so the winner carries a `spentBreakdown`).
|
|
4033
4259
|
* Checks every channel `addSpend` sums — including `ms` — so the gate stays consistent with the
|
|
4034
|
-
* total even though the coordination driver currently stamps `ms: 0`.
|
|
4260
|
+
* total even though the coordination driver currently stamps `ms: 0`. An all-zero spend that
|
|
4261
|
+
* carries an UNKNOWN marker is non-empty: work happened and went unmeasured, which is exactly the
|
|
4262
|
+
* fact a breakdown must not hide by looking like no work at all. */
|
|
4035
4263
|
function isNonEmptySpend(s) {
|
|
4036
|
-
return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0;
|
|
4264
|
+
return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0 || s.tokensKnown === false || s.usdKnown === false;
|
|
4037
4265
|
}
|
|
4038
4266
|
//#endregion
|
|
4039
|
-
export {
|
|
4267
|
+
export { captureWorktreeDiff as A, routerChatWithUsage as C, runSettledCommand as D, runBrainLoop as E, runLocalHarness as F, CodexExecutionDiagnosticError as I, removeWorktree as M, harnessInvocation as N, runWorktreeChecks as O, parseCodexTokenUsage as P, routerChatWithTools as S, streamRouterChatWithTools as T, waitUntil as _, runFinalizer as a, readWorkerProgress as b, spendFromUsageEvents as c, settledToIteration as d, createWaitProbes as f, validateWaitSpec as g, timerAt as h, pickBestDelivered as i, createWorktree as j, runWorktreeHarness as k, withDriverExecutor as l, pollFor as m, bestDelivered as n, runTree as o, isWaitOutcome as p, collectDelivered as r, createBudgetPool as s, createSupervisor as t, createScope as u, DEFAULT_STALL_AFTER_MS as v, routerToolLoop as w, routerBrain as x, createActivityLog as y };
|
|
4040
4268
|
|
|
4041
|
-
//# sourceMappingURL=supervisor-
|
|
4269
|
+
//# sourceMappingURL=supervisor-CspDspod.js.map
|