zidane 6.4.0 → 6.4.1
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/{acp-DzUgjzNa.js → acp-CH5tOdRb.js} +6 -6
- package/dist/{acp-DzUgjzNa.js.map → acp-CH5tOdRb.js.map} +1 -1
- package/dist/acp-cli.js +6 -6
- package/dist/acp.d.ts +1 -1
- package/dist/acp.js +1 -1
- package/dist/{agent-COHX5SpM.js → agent-CjhcxKp7.js} +22 -12
- package/dist/{agent-COHX5SpM.js.map → agent-CjhcxKp7.js.map} +1 -1
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +1 -1
- package/dist/{anthropic-Bw39RApb.js → anthropic-BAuvWmqU.js} +5 -5
- package/dist/{anthropic-Bw39RApb.js.map → anthropic-BAuvWmqU.js.map} +1 -1
- package/dist/{auth-XS60l6Q5.js → auth-C1N0jfb5.js} +5 -5
- package/dist/{auth-XS60l6Q5.js.map → auth-C1N0jfb5.js.map} +1 -1
- package/dist/chat/pure.d.ts +3 -3
- package/dist/chat.d.ts +4 -4
- package/dist/chat.js +4 -4
- package/dist/{context-transport-Nb9CVjLJ.js → context-transport-C5Qq5MXb.js} +3 -3
- package/dist/{context-transport-Nb9CVjLJ.js.map → context-transport-C5Qq5MXb.js.map} +1 -1
- package/dist/contexts/daytona.d.ts +1 -1
- package/dist/contexts/e2b.d.ts +1 -1
- package/dist/{errors-DJUxZg9b.js → errors-BwBxu3zD.js} +27 -2
- package/dist/errors-BwBxu3zD.js.map +1 -0
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +1 -1
- package/dist/extensions.d.ts +1 -1
- package/dist/extensions.js +1 -1
- package/dist/{fetch-url-_5WKWpbQ.js → fetch-url-B7aSg6Bp.js} +2 -2
- package/dist/{fetch-url-_5WKWpbQ.js.map → fetch-url-B7aSg6Bp.js.map} +1 -1
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +3 -3
- package/dist/{index-1Dr4Yqw1.d.ts → index-LxAV7xSk.d.ts} +24 -2
- package/dist/index-LxAV7xSk.d.ts.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +13 -13
- package/dist/{login-CPU73khP.js → login-cfxezMQT.js} +3 -3
- package/dist/{login-CPU73khP.js.map → login-cfxezMQT.js.map} +1 -1
- package/dist/{mcp-BNg6hUeX.js → mcp-BFlrDmGB.js} +4 -4
- package/dist/{mcp-BNg6hUeX.js.map → mcp-BFlrDmGB.js.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{messages-DIxD2UCr.js → messages-XfLqem2d.js} +2 -2
- package/dist/{messages-DIxD2UCr.js.map → messages-XfLqem2d.js.map} +1 -1
- package/dist/{oauth-CuFPhsHY.js → oauth-bkEOybqm.js} +2 -2
- package/dist/{oauth-CuFPhsHY.js.map → oauth-bkEOybqm.js.map} +1 -1
- package/dist/{openai-compat-BTbSyQbm.js → openai-compat-70-QNsmd.js} +2 -2
- package/dist/{openai-compat-BTbSyQbm.js.map → openai-compat-70-QNsmd.js.map} +1 -1
- package/dist/output/stream-json.d.ts +1 -1
- package/dist/output/terminal.d.ts +1 -1
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +1 -1
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/anthropic.js +1 -1
- package/dist/providers/arcee.d.ts +1 -1
- package/dist/providers/arcee.js +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/baseten.js +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/cerebras.js +1 -1
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/local.js +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai-compat.js +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openai.js +4 -4
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/openrouter.js +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers/xai.js +1 -1
- package/dist/{providers-BsH1MnhC.js → providers-DQFY2gl-.js} +4 -4
- package/dist/{providers-BsH1MnhC.js.map → providers-DQFY2gl-.js.map} +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +5 -5
- package/dist/{resolve-BPTA1RpX.js → resolve-CZJxds85.js} +2 -2
- package/dist/{resolve-BPTA1RpX.js.map → resolve-CZJxds85.js.map} +1 -1
- package/dist/restate.d.ts +1 -1
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session/sqlite.js +1 -1
- package/dist/{session-DY2LWai4.js → session-SOpMv7kl.js} +2 -2
- package/dist/{session-DY2LWai4.js.map → session-SOpMv7kl.js.map} +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/{skills-C7eIjWYm.js → skills-Dd6XCi4s.js} +3 -3
- package/dist/{skills-C7eIjWYm.js.map → skills-Dd6XCi4s.js.map} +1 -1
- package/dist/skills.d.ts +1 -1
- package/dist/skills.js +2 -2
- package/dist/{stdio-DfWu1rNB.js → stdio-CnDuGcCi.js} +2 -2
- package/dist/{stdio-DfWu1rNB.js.map → stdio-CnDuGcCi.js.map} +1 -1
- package/dist/{stdio-loader-mfN5JYns.js → stdio-loader-CknZi5f-.js} +2 -2
- package/dist/{stdio-loader-mfN5JYns.js.map → stdio-loader-CknZi5f-.js.map} +1 -1
- package/dist/{timeout-8vSIRjzl.d.ts → timeout-DEaqslLO.d.ts} +25 -2
- package/dist/timeout-DEaqslLO.d.ts.map +1 -0
- package/dist/{tool-formatters-BaHq343O.d.ts → tool-formatters-Bto7FF6V.d.ts} +2 -2
- package/dist/{tool-formatters-BaHq343O.d.ts.map → tool-formatters-Bto7FF6V.d.ts.map} +1 -1
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/fetch-url.js +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/tools/web-search.js +2 -2
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +1 -1
- package/dist/{transcript-anchors-BquAWoGN.d.ts → transcript-anchors-BTu5SjfP.d.ts} +2 -2
- package/dist/{transcript-anchors-BquAWoGN.d.ts.map → transcript-anchors-BTu5SjfP.d.ts.map} +1 -1
- package/dist/{transcript-anchors-CWzlwIOr.js → transcript-anchors-BtKt8Nwx.js} +7 -7
- package/dist/{transcript-anchors-CWzlwIOr.js.map → transcript-anchors-BtKt8Nwx.js.map} +1 -1
- package/dist/tui.d.ts +3 -3
- package/dist/tui.js +10 -10
- package/dist/{turn-operations-OQlIx13T.d.ts → turn-operations-BYYMryiI.d.ts} +3 -3
- package/dist/{turn-operations-OQlIx13T.d.ts.map → turn-operations-BYYMryiI.d.ts.map} +1 -1
- package/dist/types-BIarq1qE.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -1
- package/dist/{xai-DnZfHvTz.js → xai-BapwnBgs.js} +3 -3
- package/dist/{xai-DnZfHvTz.js.map → xai-BapwnBgs.js.map} +1 -1
- package/package.json +1 -1
- package/dist/errors-DJUxZg9b.js.map +0 -1
- package/dist/index-1Dr4Yqw1.d.ts.map +0 -1
- package/dist/timeout-8vSIRjzl.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-url-_5WKWpbQ.js","names":["dns","request","httpsRequest","httpRequest"],"sources":["../src/tools/_html.ts","../src/tools/fetch-url.ts"],"sourcesContent":["/**\n * Shared HTML → plain-text helpers used by the web-egress tools (`web_search`\n * snippet extraction, `fetch_url` body decoding).\n *\n * Best-effort by contract: a real HTML parser would be more correct but the\n * payload is always model context, never user-facing rendered output, so a\n * tag-stripper plus a curated entity table covers the practical cases\n * (Wikipedia, GitHub READMEs, MDN docs, Stack Overflow answers). When a tool\n * needs richer parsing it should reach for an actual parser rather than\n * extending these helpers ad-hoc.\n */\n\n/**\n * Curated named-entity table. Kept narrow on purpose — the long tail\n * (`Å`, `×`, mathematical refs, …) is out of scope because the\n * model handles unknown entities fine when they pass through verbatim. Add\n * entries when an entity is observed mangling real output, not speculatively.\n *\n * Keys are stored without the leading `&` / trailing `;` and matched\n * case-insensitively by the decoder.\n */\nconst NAMED_ENTITIES: Record<string, string> = {\n amp: '&',\n apos: '\\'',\n bull: '•',\n copy: '\\u00A9',\n ensp: ' ',\n emsp: ' ',\n gt: '>',\n hellip: '\\u2026',\n laquo: '\\u00AB',\n ldquo: '\\u201C',\n lsquo: '\\u2018',\n lt: '<',\n mdash: '\\u2014',\n middot: '\\u00B7',\n nbsp: ' ',\n ndash: '\\u2013',\n quot: '\"',\n raquo: '\\u00BB',\n rdquo: '\\u201D',\n reg: '\\u00AE',\n rsquo: '\\u2019',\n thinsp: ' ',\n trade: '\\u2122',\n}\n\n/**\n * `String.fromCodePoint` raises on out-of-range / non-finite inputs; the\n * agent's untrusted-payload assumption means we can't let a single broken\n * numeric reference crash the whole decode. Empty string on rejection is\n * the safest outcome (matches what most browsers do for invalid refs).\n */\nfunction safeFromCodePoint(cp: number): string {\n if (!Number.isFinite(cp) || cp < 0 || cp > 0x10FFFF)\n return ''\n try {\n return String.fromCodePoint(cp)\n }\n catch {\n return ''\n }\n}\n\n/**\n * Decode numeric character references (`&#x...;` / `&#NNN;`) and the curated\n * subset of named entities in {@link NAMED_ENTITIES}. Unknown named entities\n * are left as-is so the model can detect them rather than seeing a silently\n * corrupted token.\n *\n * `&` is decoded LAST so a double-escaped string like `&lt;` resolves\n * to `<` (not `<`) — that's the spec-correct single-pass decode shape.\n */\nexport function decodeHtmlEntities(text: string): string {\n let s = text\n .replace(/&#x([0-9a-f]+);/gi, (_, h) => safeFromCodePoint(Number.parseInt(h, 16)))\n .replace(/&#(\\d+);/g, (_, n) => safeFromCodePoint(Number.parseInt(n, 10)))\n s = s.replace(/&([a-z][a-z0-9]*);/gi, (full, name) => {\n const key = name.toLowerCase()\n // Defer `amp` so `&lt;` doesn't double-decode in a single pass.\n if (key === 'amp')\n return full\n const replacement = NAMED_ENTITIES[key]\n return replacement ?? full\n })\n return s.replace(/&/gi, '&')\n}\n\n/**\n * Strip `<script>`, `<style>`, `<noscript>`, comments, and every other tag.\n * Block-level openers (`<br>`, `<p>`, `<div>`, `<li>`, `<tr>`, `<h1-6>`) are\n * replaced with newlines so the resulting plaintext preserves readable\n * structure. Other tags collapse to spaces.\n */\nexport function stripHtml(html: string): string {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, ' ')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, ' ')\n .replace(/<noscript[\\s\\S]*?<\\/noscript>/gi, ' ')\n .replace(/<!--[\\s\\S]*?-->/g, ' ')\n .replace(/<(?:br|p|div|li|tr|h[1-6])[^>]*>/gi, '\\n')\n .replace(/<[^>]+>/g, ' ')\n}\n\n/**\n * Inline variant — strip tags + decode entities + collapse all whitespace\n * (including line breaks) into single spaces. For short snippets where\n * structural breaks would be visual noise (search-result titles, link text).\n */\nexport function stripHtmlInline(html: string): string {\n return decodeHtmlEntities(stripHtml(html))\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\n/**\n * Block variant — strip tags + decode entities, preserve newlines emitted\n * by block-level openers, then collapse inline whitespace per-line and drop\n * empty lines. For full-page reductions where structure aids reading.\n */\nexport function stripHtmlBlock(html: string): string {\n return decodeHtmlEntities(stripHtml(html))\n .split('\\n')\n .map(line => line.replace(/[ \\t]+/g, ' ').trim())\n .filter(line => line.length > 0)\n .join('\\n')\n}\n","import type { RequestOptions as HttpRequestOptions } from 'node:http'\nimport type { RequestOptions as HttpsRequestOptions } from 'node:https'\nimport type { ToolContext, ToolDef } from './types'\nimport { Buffer } from 'node:buffer'\nimport { promises as dns } from 'node:dns'\nimport { request as httpRequest } from 'node:http'\nimport { request as httpsRequest } from 'node:https'\nimport { isIP } from 'node:net'\nimport { errorMessage } from '../errors'\nimport { stripHtmlBlock } from './_html'\n\n/**\n * Fetch a URL and return its text content.\n *\n * Companion to `web_search` (when registered) — after the model finds a\n * promising result it fetches the page to read the body in full. HTML is\n * reduced to plain text because the model rarely benefits from the markup\n * and we want to keep the payload inside the per-turn output budget.\n *\n * Restrictions:\n * - Only http(s) URLs. Anything else (file://, data:, ftp:) is rejected so\n * the tool can't be coerced into reading local files or arbitrary\n * resources.\n * - SSRF guard with TOCTOU defense: the host is DNS-resolved against the\n * loopback / link-local / private / reserved blocklist BEFORE the\n * request, and the resolved IP is then PINNED at the connection layer\n * via `node:http(s).request({ host: <resolved IP>, headers: { Host: ... },\n * servername: ... })`. This closes the classic DNS-rebinding gap where a\n * `globalThis.fetch` re-resolves the hostname and connects to a freshly\n * minted private IP. TLS verification still runs against the original\n * hostname via the explicit `servername` option.\n *\n * Note: the more idiomatic Node fix (an `undici.Agent` with a custom\n * `connect.lookup`) is silently a no-op on Bun ≤1.3 — both\n * `undici.Agent` and `node:https.Agent.lookup` are stubbed out. The\n * explicit `host: IP` + `Host:` header dance is the only mechanism that\n * actually pins on the current Bun runtime.\n * - Redirects are followed manually (up to `MAX_REDIRECTS`) and each hop\n * is re-validated, so a public hostname that 302s into a metadata\n * endpoint is rejected at the redirect step.\n * - 15s hard timeout per request.\n * - Output capped at `max_bytes` (default 200 KiB) — long pages are\n * truncated with an explicit marker. The pinned reader also stops\n * buffering past 2× the cap so a multi-MB body can't OOM us.\n */\n\nconst DEFAULT_MAX_BYTES = 200 * 1024\nconst HARD_MAX_BYTES = 1024 * 1024\nconst HTTP_TIMEOUT_MS = 15_000\nconst MAX_REDIRECTS = 5\n\n/**\n * IPv4 ranges that the SSRF guard refuses to fetch from. Covers the cloud\n * metadata services (AWS/Azure 169.254.169.254, GCP routes through the same\n * link-local block) plus every RFC1918 / loopback / reserved block a model\n * could be prompt-injected into hitting.\n */\nfunction isBlockedIPv4(ip: string): boolean {\n const parts = ip.split('.').map(Number)\n if (parts.length !== 4 || parts.some(p => !Number.isInteger(p) || p < 0 || p > 255))\n return true\n const [a, b] = parts as [number, number, number, number]\n if (a === 0)\n return true // 0.0.0.0/8 unspecified / \"this network\"\n if (a === 10)\n return true // 10/8 private\n if (a === 127)\n return true // loopback\n if (a === 169 && b === 254)\n return true // link-local + cloud metadata\n if (a === 172 && b >= 16 && b <= 31)\n return true // 172.16/12 private\n if (a === 192 && b === 168)\n return true // 192.168/16 private\n if (a === 192 && b === 0 && parts[2] === 0)\n return true // 192.0.0/24 IETF\n if (a === 198 && (b === 18 || b === 19))\n return true // 198.18/15 benchmark\n if (a >= 224)\n return true // multicast + reserved (224/4, 240/4)\n return false\n}\n\nfunction isBlockedIPv6(ip: string): boolean {\n const lower = ip.toLowerCase()\n // Normalise IPv4-mapped (::ffff:1.2.3.4) — recurse on the v4 tail. URL\n // parsing tends to canonicalise the dotted form into the hex pair\n // `::ffff:a9fe:a9fe`, so accept both shapes.\n const v4MappedDotted = lower.match(/^::ffff:([0-9.]+)$/)\n if (v4MappedDotted)\n return isBlockedIPv4(v4MappedDotted[1]!)\n const v4MappedHex = lower.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/)\n if (v4MappedHex) {\n const hi = Number.parseInt(v4MappedHex[1]!, 16)\n const lo = Number.parseInt(v4MappedHex[2]!, 16)\n const ipv4 = `${(hi >> 8) & 0xFF}.${hi & 0xFF}.${(lo >> 8) & 0xFF}.${lo & 0xFF}`\n return isBlockedIPv4(ipv4)\n }\n if (lower === '::' || lower === '::1')\n return true\n // fc00::/7 unique-local (covers fc.. and fd..)\n if (/^f[cd][0-9a-f]{0,2}:/.test(lower))\n return true\n // fe80::/10 link-local\n if (/^fe[89ab][0-9a-f]?:/.test(lower))\n return true\n // ff00::/8 multicast\n if (lower.startsWith('ff'))\n return true\n return false\n}\n\nexport function isBlockedAddress(ip: string): boolean {\n const family = isIP(ip)\n if (family === 4)\n return isBlockedIPv4(ip)\n if (family === 6)\n return isBlockedIPv6(ip)\n return true // not a recognised IP literal — refuse\n}\n\n/**\n * Normalize a hostname for allowlist comparison: lowercase, strip a trailing\n * dot (FQDN root) and IPv6 brackets, drop a leading dot on allowlist entries.\n */\nfunction normalizeHost(host: string): string {\n let h = host.trim().toLowerCase()\n if (h.startsWith('[') && h.endsWith(']'))\n h = h.slice(1, -1)\n if (h.endsWith('.'))\n h = h.slice(0, -1)\n if (h.startsWith('.'))\n h = h.slice(1)\n return h\n}\n\n/**\n * Site identity for cross-host redirect detection: the lowercased hostname\n * with a leading `www.` stripped, so `example.com` ⇄ `www.example.com` and a\n * path-only redirect aren't flagged as cross-host. Returns `null` for an\n * unparseable URL (caller skips the note rather than guessing).\n */\nfunction sameSiteHost(url: string): string | null {\n try {\n return new URL(url).hostname.toLowerCase().replace(/^www\\./, '')\n }\n catch {\n return null\n }\n}\n\n/**\n * Host-suffix allowlist check. An empty / undefined list means \"no allowlist\"\n * → every host passes (the SSRF blocklist is the only gate). Otherwise the\n * host must equal, or be a subdomain of, one of the entries. Subdomain match\n * is on a dot boundary so `example.com` matches `docs.example.com` but not\n * `notexample.com`.\n */\nexport function isHostAllowed(hostname: string, allowHosts: readonly string[] | undefined): boolean {\n if (!allowHosts || allowHosts.length === 0)\n return true\n const host = normalizeHost(hostname)\n for (const entry of allowHosts) {\n const allowed = normalizeHost(entry)\n if (allowed.length === 0)\n continue\n if (host === allowed || host.endsWith(`.${allowed}`))\n return true\n }\n return false\n}\n\n/**\n * Resolve `hostname` and refuse if any answer falls in a blocked range.\n *\n * Returns the first allowed `{ address, family }` so the caller can pin\n * the connection to it. Throws on any blocked answer so a multi-record\n * response with one private record fails closed.\n */\nasync function resolveAndCheck(hostname: string, allowHosts?: readonly string[]): Promise<{ address: string, family: 4 | 6 }> {\n // Egress allowlist (when configured) gates BEFORE DNS so a non-approved\n // host never even triggers a lookup. Enforced here so every redirect hop\n // is re-checked, not just the initial URL.\n if (!isHostAllowed(hostname, allowHosts))\n throw new Error(`refused: ${hostname} is not in the configured egress allowlist`)\n // `URL.hostname` keeps brackets around IPv6 literals (`[::1]`) — strip them\n // before the IP check so `isIP` recognises the literal and `dns.lookup`\n // doesn't try to resolve `[::1]` as a name.\n const bare = hostname.startsWith('[') && hostname.endsWith(']')\n ? hostname.slice(1, -1)\n : hostname\n const family = isIP(bare)\n if (family === 4 || family === 6) {\n if (isBlockedAddress(bare))\n throw new Error(`refused: ${bare} is in a blocked range`)\n return { address: bare, family }\n }\n const answers = await dns.lookup(bare, { all: true, verbatim: true })\n if (answers.length === 0)\n throw new Error(`DNS lookup returned no records for ${bare}`)\n for (const { address } of answers) {\n if (isBlockedAddress(address))\n throw new Error(`refused: ${bare} resolves to blocked address ${address}`)\n }\n const first = answers[0]!\n const fam = first.family === 6 ? 6 : 4\n return { address: first.address, family: fam }\n}\n\n/**\n * Pinned HTTP/HTTPS request. Connects to `pinnedIp` directly (bypassing\n * Node/Bun's own DNS resolution) while preserving the `Host:` header and\n * (for HTTPS) TLS SNI on the URL's original hostname. This is the\n * concrete defense against DNS rebinding — see the file-level docstring.\n *\n * Reads up to `maxBufferBytes` of body and then forcibly closes the\n * connection. The body is decoded as UTF-8; binary responses degrade to\n * mojibake but the tool's contract is text payloads anyway.\n */\ninterface PinnedResponse {\n status: number\n url: string\n contentType: string\n body: string\n location: string | null\n /**\n * True when the body was cut at `maxBufferBytes` before the server\n * finished sending. Optional so test seams that stub `requestImpl`\n * don't have to populate it; absent means \"not truncated\".\n */\n truncated?: boolean\n}\n\nfunction buildHostHeader(url: URL): string {\n if (!url.port)\n return url.hostname\n const defaultPort = url.protocol === 'https:' ? '443' : '80'\n return url.port === defaultPort ? url.hostname : `${url.hostname}:${url.port}`\n}\n\nexport function pinnedRequest(\n url: URL,\n pinnedIp: string,\n family: 4 | 6,\n headers: Record<string, string>,\n signal: AbortSignal,\n maxBufferBytes: number,\n): Promise<PinnedResponse> {\n return new Promise((resolve, reject) => {\n const isHttps = url.protocol === 'https:'\n const port = url.port\n ? Number.parseInt(url.port, 10)\n : (isHttps ? 443 : 80)\n\n // `host` is the literal IP, `servername` is the original hostname so\n // TLS cert verification still matches the cert's SAN/CN against the\n // user-visible name. Node uses `servername` for `checkServerIdentity`\n // automatically when set.\n const options: HttpRequestOptions & HttpsRequestOptions = {\n host: pinnedIp,\n port,\n path: `${url.pathname}${url.search}`,\n method: 'GET',\n family,\n headers: { ...headers, Host: buildHostHeader(url) },\n ...(isHttps ? { servername: url.hostname } : {}),\n }\n\n const request = isHttps ? httpsRequest : httpRequest\n const chunks: Buffer[] = []\n let received = 0\n // Single-shot settle guard. Bun's `node:http` is quirky around\n // `req.destroy(err)` — passing an error doesn't reliably surface as an\n // `error` event the way it does on Node. We track the settle state\n // ourselves and call `resolve` / `reject` directly from the abort /\n // timeout paths so the promise terminates even when the underlying\n // socket teardown is silent.\n let settled = false\n // Forward-declared so the `finish` / abort / timeout handlers can\n // close over `req` before it's assigned below. The request object is\n // wired into them via mutable refs because the handlers must be set\n // up *before* `request(options, callback)` fires its first event.\n let req: ReturnType<typeof httpRequest> | undefined\n let timer: ReturnType<typeof setTimeout> | undefined\n\n const teardown = (): void => {\n try { req?.destroy() }\n catch { /* tear-down best effort */ }\n }\n\n const finish = (settler: () => void): void => {\n if (settled)\n return\n settled = true\n if (timer)\n clearTimeout(timer)\n signal.removeEventListener('abort', onSignalAbort)\n settler()\n }\n\n function onSignalAbort(): void {\n finish(() => reject(new Error('request aborted')))\n teardown()\n }\n\n // Pre-aborted signal — short-circuit so we don't even open a socket.\n // (This is the realistic shape when a user hits Ctrl+C while the agent\n // is already shutting down: the signal is `.aborted` by the time the\n // tool function runs.)\n if (signal.aborted) {\n reject(new Error('request aborted'))\n return\n }\n\n timer = setTimeout(() => {\n finish(() => reject(new Error(`request timed out after ${HTTP_TIMEOUT_MS}ms`)))\n teardown()\n }, HTTP_TIMEOUT_MS)\n\n signal.addEventListener('abort', onSignalAbort, { once: true })\n\n req = request(options, (res) => {\n const status = res.statusCode ?? 0\n const headersOut = res.headers\n const contentType = (headersOut['content-type'] ?? '').toString().toLowerCase()\n const location = typeof headersOut.location === 'string' ? headersOut.location : null\n\n // Short-circuit on redirect responses — body is irrelevant.\n if (status >= 300 && status < 400 && location) {\n res.resume() // drain so the socket can be released\n finish(() => resolve({ status, url: url.toString(), contentType, body: '', location }))\n return\n }\n\n let bodyTruncated = false\n res.on('data', (chunk: Buffer) => {\n if (received >= maxBufferBytes) {\n // Stop accumulating; tear the socket down so the server stops\n // sending. We still resolve cleanly from `end` (or `close` if\n // the server doesn't finish flushing before the kill lands).\n bodyTruncated = true\n teardown()\n return\n }\n // Keep only what fits in the budget — a chunk straddling the cap\n // would otherwise overshoot it by up to one chunk. A partial slice\n // means the body was cut, so flag it and stop the transfer.\n const room = maxBufferBytes - received\n if (chunk.length > room) {\n chunks.push(chunk.subarray(0, room))\n received += room\n bodyTruncated = true\n teardown()\n return\n }\n received += chunk.length\n chunks.push(chunk)\n })\n res.on('end', () => {\n const body = Buffer.concat(chunks).toString('utf-8')\n finish(() => resolve({ status, url: url.toString(), contentType, body, location: null, truncated: bodyTruncated }))\n })\n res.on('close', () => {\n // Socket torn down before `end` (e.g. when our maxBufferBytes\n // trigger destroyed the request). Resolve with whatever we\n // buffered so far — `truncated` tells the caller the body is cut.\n if (!settled) {\n const body = Buffer.concat(chunks).toString('utf-8')\n finish(() => resolve({ status, url: url.toString(), contentType, body, location: null, truncated: bodyTruncated }))\n }\n })\n res.on('error', err => finish(() => reject(err)))\n })\n\n req.on('error', (err: Error) => finish(() => reject(err)))\n req.end()\n })\n}\n\n/**\n * Walk up to {@link MAX_REDIRECTS} hops, validating the target host of each\n * 3xx response against the SSRF blocklist before following.\n *\n * `deps` is an internal seam for tests — production callers use the\n * defaults. Both knobs are intentionally opaque: hosts that want to\n * customize the blocklist should use a higher-level mechanism (a future\n * `behavior.fetchUrlAllowHosts` setting) rather than reaching into this.\n */\nexport interface FetchSsrfDeps {\n /** Resolve + validate a hostname. Defaults to {@link resolveAndCheck}. */\n resolver?: (hostname: string) => Promise<{ address: string, family: 4 | 6 }>\n /** Execute a single pinned HTTP request. Defaults to {@link pinnedRequest}. */\n requestImpl?: typeof pinnedRequest\n}\n\nexport async function fetchWithSsrfGuard(\n startUrl: URL,\n headers: Record<string, string>,\n signal: AbortSignal,\n maxBufferBytes: number,\n deps: FetchSsrfDeps = {},\n allowHosts?: readonly string[],\n): Promise<PinnedResponse> {\n // Default resolver binds the egress allowlist; a test-injected resolver\n // owns its own policy.\n const resolver = deps.resolver ?? ((hostname: string) => resolveAndCheck(hostname, allowHosts))\n const requestImpl = deps.requestImpl ?? pinnedRequest\n let current = startUrl\n for (let hop = 0; hop <= MAX_REDIRECTS; hop++) {\n const { address, family } = await resolver(current.hostname)\n const res = await requestImpl(current, address, family, headers, signal, maxBufferBytes)\n if (!res.location)\n return res\n if (hop === MAX_REDIRECTS)\n throw new Error(`too many redirects (>${MAX_REDIRECTS})`)\n let next: URL\n try {\n next = new URL(res.location, current)\n }\n catch {\n throw new Error(`invalid redirect target: ${res.location}`)\n }\n if (next.protocol !== 'http:' && next.protocol !== 'https:')\n throw new Error(`refused redirect to non-http(s) target: ${next.protocol}`)\n current = next\n }\n throw new Error('redirect loop exited unexpectedly')\n}\n\n/**\n * Opt-in, process-local response cache for `fetch_url`. Disabled unless\n * `behavior.fetchUrlCacheTtlMs` is set (> 0). Bounded by entry count and total\n * cached bytes; eviction is oldest-first (insertion order via `Map`, which is\n * a good-enough LRU because we delete-then-set on hit to refresh recency).\n *\n * Keyed on `<url>\\u0000<cap>\\u0000<egress-policy>` so two calls with different\n * `max_bytes` (hence different truncation) don't alias, and — because the\n * cache is module-global — an agent with a permissive `fetchUrlAllowHosts`\n * can't populate entries that a co-resident agent with a stricter allowlist\n * would then be served. Only successful text responses are stored — the\n * caller gates on status before writing.\n */\nconst FETCH_CACHE_MAX_ENTRIES = 64\nconst FETCH_CACHE_MAX_BYTES = 16 * 1024 * 1024 // 16 MiB total\n\ninterface FetchCacheEntry {\n /** Fully-formatted tool output (header + body) ready to return verbatim. */\n output: string\n expiresAt: number\n bytes: number\n}\n\nconst fetchCache = new Map<string, FetchCacheEntry>()\nlet fetchCacheBytes = 0\n\nfunction cacheKey(url: string, cap: number, allowHosts?: readonly string[]): string {\n // Normalize + sort so equivalent policies (`['A.com', 'b.com']` vs\n // `['b.com', 'a.com.']`) share entries; an absent/empty allowlist keys\n // as the empty policy.\n const policy = (allowHosts ?? []).map(normalizeHost).sort().join(',')\n return `${url}\\u0000${cap}\\u0000${policy}`\n}\n\nfunction cacheGet(key: string): string | null {\n const entry = fetchCache.get(key)\n if (!entry)\n return null\n if (Date.now() >= entry.expiresAt) {\n fetchCache.delete(key)\n fetchCacheBytes -= entry.bytes\n return null\n }\n // Refresh recency: re-insert at the tail of the Map's iteration order.\n fetchCache.delete(key)\n fetchCache.set(key, entry)\n return entry.output\n}\n\nfunction cacheSet(key: string, output: string, ttlMs: number): void {\n const bytes = Buffer.byteLength(output, 'utf-8')\n // A single oversize payload that can't fit under the byte cap is simply not\n // cached (still returned to the caller) rather than evicting everything.\n if (bytes > FETCH_CACHE_MAX_BYTES)\n return\n // Drop any stale entry for this key first so the byte accounting stays exact.\n const prev = fetchCache.get(key)\n if (prev) {\n fetchCache.delete(key)\n fetchCacheBytes -= prev.bytes\n }\n fetchCache.set(key, { output, expiresAt: Date.now() + ttlMs, bytes })\n fetchCacheBytes += bytes\n // Evict oldest-first until both caps are satisfied.\n while (fetchCache.size > FETCH_CACHE_MAX_ENTRIES || fetchCacheBytes > FETCH_CACHE_MAX_BYTES) {\n const oldest = fetchCache.keys().next().value\n if (oldest === undefined)\n break\n const victim = fetchCache.get(oldest)!\n fetchCache.delete(oldest)\n fetchCacheBytes -= victim.bytes\n }\n}\n\n/** Test seam — drop all cached entries. */\nexport function clearFetchUrlCache(): void {\n fetchCache.clear()\n fetchCacheBytes = 0\n}\n\n/**\n * Test seam — exercise the cache get/set/eviction in isolation without\n * standing up the full SSRF + HTTP path. Not part of the public tool surface;\n * `execute` uses the module-private functions directly.\n */\nexport const __fetchCacheTestApi = {\n key: cacheKey,\n get: cacheGet,\n set: cacheSet,\n size: () => fetchCache.size,\n bytes: () => fetchCacheBytes,\n}\n\nexport const fetchUrl: ToolDef = {\n isConcurrencySafe: true,\n spec: {\n name: 'fetch_url',\n description: 'Fetch a public URL and return its body as plain text. HTML is reduced to text (scripts, styles, tags stripped; entities decoded). If a `web_search` tool is also available, use it first when you do not already know the exact URL. Only http(s) URLs are accepted; loopback, link-local, private, and cloud-metadata addresses are refused. Output is capped (default 200 KiB).',\n inputSchema: {\n type: 'object',\n properties: {\n url: {\n type: 'string',\n description: 'HTTP or HTTPS URL to fetch.',\n },\n max_bytes: {\n type: 'number',\n description: `Truncate the response body beyond this many bytes. Default: ${DEFAULT_MAX_BYTES}. Hard cap: ${HARD_MAX_BYTES}.`,\n },\n },\n required: ['url'],\n },\n },\n async execute({ url, max_bytes }, ctx: ToolContext) {\n const raw = typeof url === 'string' ? url.trim() : ''\n if (!raw)\n return 'fetch_url error: `url` is required'\n let parsed: URL\n try {\n parsed = new URL(raw)\n }\n catch {\n return `fetch_url error: invalid URL: ${raw}`\n }\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')\n return `fetch_url error: only http(s) URLs are allowed (got ${parsed.protocol})`\n\n const cap = Math.min(\n typeof max_bytes === 'number' && max_bytes > 0 ? Math.floor(max_bytes) : DEFAULT_MAX_BYTES,\n HARD_MAX_BYTES,\n )\n // Buffer up to 2× the cap so HTML overhead doesn't starve the visible\n // text budget — a 200 KiB cap on rendered text can legitimately need to\n // read ~400 KiB of raw HTML before stripping. Hard ceiling is still\n // governed by `HARD_MAX_BYTES * 2` which is comfortably below OOM.\n const bufferCap = Math.min(cap * 2, HARD_MAX_BYTES * 2)\n\n // Opt-in response cache (off unless `behavior.fetchUrlCacheTtlMs > 0`).\n // The egress allowlist is part of the key AND re-checked before serving\n // a hit — the cache is module-global, so neither a policy change within\n // an agent nor a co-resident agent's entries may bypass this agent's\n // allowlist.\n const allowHosts = ctx.behavior?.fetchUrlAllowHosts\n const ttlMs = ctx.behavior?.fetchUrlCacheTtlMs\n const cacheEnabled = typeof ttlMs === 'number' && ttlMs > 0\n const key = cacheKey(parsed.toString(), cap, allowHosts)\n if (cacheEnabled && isHostAllowed(parsed.hostname, allowHosts)) {\n const hit = cacheGet(key)\n if (hit !== null)\n return hit\n }\n\n try {\n const res = await fetchWithSsrfGuard(\n parsed,\n {\n 'user-agent': 'Mozilla/5.0 (compatible; zidane/1.0)',\n 'accept': 'text/html,text/plain,application/json;q=0.9,*/*;q=0.5',\n 'accept-encoding': 'identity', // no gzip — we don't decompress\n },\n ctx.signal,\n bufferCap,\n {},\n allowHosts,\n )\n const body = res.body\n const isHtml = res.contentType.includes('html') || /^\\s*<!doctype html|^\\s*<html/i.test(body)\n const text = isHtml ? stripHtmlBlock(body) : body\n const truncated = text.length > cap\n // Two distinct truncation reasons: the rendered text overflowing `cap`,\n // and the raw transfer being cut at the buffer ceiling (`res.truncated`)\n // — the latter can leave the rendered text under `cap` (HTML overhead\n // stripped away), so it needs its own marker or the model would treat\n // a partial body as complete.\n const out = truncated\n ? `${text.slice(0, cap)}\\n\\n[truncated at ${cap} bytes; full length ${text.length}]`\n : res.truncated\n ? `${text}\\n\\n[response truncated: body exceeded the ${bufferCap}-byte transfer buffer]`\n : text\n // `res.url` reflects the FINAL hop after redirects; flag a cross-host\n // landing so the model knows the content came from a different origin\n // than it asked for (open-redirect awareness). Same-host path changes\n // and www add/strip are treated as the same site and not flagged.\n const finalHost = sameSiteHost(res.url)\n const requestedHost = sameSiteHost(parsed.toString())\n const redirectNote = finalHost && requestedHost && finalHost !== requestedHost\n ? `Note: ${parsed.hostname} redirected to a different host (${new URL(res.url).hostname}).\\n`\n : ''\n const header = `URL: ${res.url}\\nStatus: ${res.status}\\nContent-Type: ${res.contentType || '(none)'}\\n${redirectNote}\\n`\n const formatted = header + out\n // Cache only successful, complete text responses — never errors /\n // redirects / transfer-truncated bodies (a retry may get the rest).\n if (cacheEnabled && res.status >= 200 && res.status < 300 && res.truncated !== true)\n cacheSet(key, formatted, ttlMs)\n return formatted\n }\n catch (err) {\n return `fetch_url error: ${errorMessage(err)}`\n }\n },\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"fetch-url-B7aSg6Bp.js","names":["dns","request","httpsRequest","httpRequest"],"sources":["../src/tools/_html.ts","../src/tools/fetch-url.ts"],"sourcesContent":["/**\n * Shared HTML → plain-text helpers used by the web-egress tools (`web_search`\n * snippet extraction, `fetch_url` body decoding).\n *\n * Best-effort by contract: a real HTML parser would be more correct but the\n * payload is always model context, never user-facing rendered output, so a\n * tag-stripper plus a curated entity table covers the practical cases\n * (Wikipedia, GitHub READMEs, MDN docs, Stack Overflow answers). When a tool\n * needs richer parsing it should reach for an actual parser rather than\n * extending these helpers ad-hoc.\n */\n\n/**\n * Curated named-entity table. Kept narrow on purpose — the long tail\n * (`Å`, `×`, mathematical refs, …) is out of scope because the\n * model handles unknown entities fine when they pass through verbatim. Add\n * entries when an entity is observed mangling real output, not speculatively.\n *\n * Keys are stored without the leading `&` / trailing `;` and matched\n * case-insensitively by the decoder.\n */\nconst NAMED_ENTITIES: Record<string, string> = {\n amp: '&',\n apos: '\\'',\n bull: '•',\n copy: '\\u00A9',\n ensp: ' ',\n emsp: ' ',\n gt: '>',\n hellip: '\\u2026',\n laquo: '\\u00AB',\n ldquo: '\\u201C',\n lsquo: '\\u2018',\n lt: '<',\n mdash: '\\u2014',\n middot: '\\u00B7',\n nbsp: ' ',\n ndash: '\\u2013',\n quot: '\"',\n raquo: '\\u00BB',\n rdquo: '\\u201D',\n reg: '\\u00AE',\n rsquo: '\\u2019',\n thinsp: ' ',\n trade: '\\u2122',\n}\n\n/**\n * `String.fromCodePoint` raises on out-of-range / non-finite inputs; the\n * agent's untrusted-payload assumption means we can't let a single broken\n * numeric reference crash the whole decode. Empty string on rejection is\n * the safest outcome (matches what most browsers do for invalid refs).\n */\nfunction safeFromCodePoint(cp: number): string {\n if (!Number.isFinite(cp) || cp < 0 || cp > 0x10FFFF)\n return ''\n try {\n return String.fromCodePoint(cp)\n }\n catch {\n return ''\n }\n}\n\n/**\n * Decode numeric character references (`&#x...;` / `&#NNN;`) and the curated\n * subset of named entities in {@link NAMED_ENTITIES}. Unknown named entities\n * are left as-is so the model can detect them rather than seeing a silently\n * corrupted token.\n *\n * `&` is decoded LAST so a double-escaped string like `&lt;` resolves\n * to `<` (not `<`) — that's the spec-correct single-pass decode shape.\n */\nexport function decodeHtmlEntities(text: string): string {\n let s = text\n .replace(/&#x([0-9a-f]+);/gi, (_, h) => safeFromCodePoint(Number.parseInt(h, 16)))\n .replace(/&#(\\d+);/g, (_, n) => safeFromCodePoint(Number.parseInt(n, 10)))\n s = s.replace(/&([a-z][a-z0-9]*);/gi, (full, name) => {\n const key = name.toLowerCase()\n // Defer `amp` so `&lt;` doesn't double-decode in a single pass.\n if (key === 'amp')\n return full\n const replacement = NAMED_ENTITIES[key]\n return replacement ?? full\n })\n return s.replace(/&/gi, '&')\n}\n\n/**\n * Strip `<script>`, `<style>`, `<noscript>`, comments, and every other tag.\n * Block-level openers (`<br>`, `<p>`, `<div>`, `<li>`, `<tr>`, `<h1-6>`) are\n * replaced with newlines so the resulting plaintext preserves readable\n * structure. Other tags collapse to spaces.\n */\nexport function stripHtml(html: string): string {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, ' ')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, ' ')\n .replace(/<noscript[\\s\\S]*?<\\/noscript>/gi, ' ')\n .replace(/<!--[\\s\\S]*?-->/g, ' ')\n .replace(/<(?:br|p|div|li|tr|h[1-6])[^>]*>/gi, '\\n')\n .replace(/<[^>]+>/g, ' ')\n}\n\n/**\n * Inline variant — strip tags + decode entities + collapse all whitespace\n * (including line breaks) into single spaces. For short snippets where\n * structural breaks would be visual noise (search-result titles, link text).\n */\nexport function stripHtmlInline(html: string): string {\n return decodeHtmlEntities(stripHtml(html))\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\n/**\n * Block variant — strip tags + decode entities, preserve newlines emitted\n * by block-level openers, then collapse inline whitespace per-line and drop\n * empty lines. For full-page reductions where structure aids reading.\n */\nexport function stripHtmlBlock(html: string): string {\n return decodeHtmlEntities(stripHtml(html))\n .split('\\n')\n .map(line => line.replace(/[ \\t]+/g, ' ').trim())\n .filter(line => line.length > 0)\n .join('\\n')\n}\n","import type { RequestOptions as HttpRequestOptions } from 'node:http'\nimport type { RequestOptions as HttpsRequestOptions } from 'node:https'\nimport type { ToolContext, ToolDef } from './types'\nimport { Buffer } from 'node:buffer'\nimport { promises as dns } from 'node:dns'\nimport { request as httpRequest } from 'node:http'\nimport { request as httpsRequest } from 'node:https'\nimport { isIP } from 'node:net'\nimport { errorMessage } from '../errors'\nimport { stripHtmlBlock } from './_html'\n\n/**\n * Fetch a URL and return its text content.\n *\n * Companion to `web_search` (when registered) — after the model finds a\n * promising result it fetches the page to read the body in full. HTML is\n * reduced to plain text because the model rarely benefits from the markup\n * and we want to keep the payload inside the per-turn output budget.\n *\n * Restrictions:\n * - Only http(s) URLs. Anything else (file://, data:, ftp:) is rejected so\n * the tool can't be coerced into reading local files or arbitrary\n * resources.\n * - SSRF guard with TOCTOU defense: the host is DNS-resolved against the\n * loopback / link-local / private / reserved blocklist BEFORE the\n * request, and the resolved IP is then PINNED at the connection layer\n * via `node:http(s).request({ host: <resolved IP>, headers: { Host: ... },\n * servername: ... })`. This closes the classic DNS-rebinding gap where a\n * `globalThis.fetch` re-resolves the hostname and connects to a freshly\n * minted private IP. TLS verification still runs against the original\n * hostname via the explicit `servername` option.\n *\n * Note: the more idiomatic Node fix (an `undici.Agent` with a custom\n * `connect.lookup`) is silently a no-op on Bun ≤1.3 — both\n * `undici.Agent` and `node:https.Agent.lookup` are stubbed out. The\n * explicit `host: IP` + `Host:` header dance is the only mechanism that\n * actually pins on the current Bun runtime.\n * - Redirects are followed manually (up to `MAX_REDIRECTS`) and each hop\n * is re-validated, so a public hostname that 302s into a metadata\n * endpoint is rejected at the redirect step.\n * - 15s hard timeout per request.\n * - Output capped at `max_bytes` (default 200 KiB) — long pages are\n * truncated with an explicit marker. The pinned reader also stops\n * buffering past 2× the cap so a multi-MB body can't OOM us.\n */\n\nconst DEFAULT_MAX_BYTES = 200 * 1024\nconst HARD_MAX_BYTES = 1024 * 1024\nconst HTTP_TIMEOUT_MS = 15_000\nconst MAX_REDIRECTS = 5\n\n/**\n * IPv4 ranges that the SSRF guard refuses to fetch from. Covers the cloud\n * metadata services (AWS/Azure 169.254.169.254, GCP routes through the same\n * link-local block) plus every RFC1918 / loopback / reserved block a model\n * could be prompt-injected into hitting.\n */\nfunction isBlockedIPv4(ip: string): boolean {\n const parts = ip.split('.').map(Number)\n if (parts.length !== 4 || parts.some(p => !Number.isInteger(p) || p < 0 || p > 255))\n return true\n const [a, b] = parts as [number, number, number, number]\n if (a === 0)\n return true // 0.0.0.0/8 unspecified / \"this network\"\n if (a === 10)\n return true // 10/8 private\n if (a === 127)\n return true // loopback\n if (a === 169 && b === 254)\n return true // link-local + cloud metadata\n if (a === 172 && b >= 16 && b <= 31)\n return true // 172.16/12 private\n if (a === 192 && b === 168)\n return true // 192.168/16 private\n if (a === 192 && b === 0 && parts[2] === 0)\n return true // 192.0.0/24 IETF\n if (a === 198 && (b === 18 || b === 19))\n return true // 198.18/15 benchmark\n if (a >= 224)\n return true // multicast + reserved (224/4, 240/4)\n return false\n}\n\nfunction isBlockedIPv6(ip: string): boolean {\n const lower = ip.toLowerCase()\n // Normalise IPv4-mapped (::ffff:1.2.3.4) — recurse on the v4 tail. URL\n // parsing tends to canonicalise the dotted form into the hex pair\n // `::ffff:a9fe:a9fe`, so accept both shapes.\n const v4MappedDotted = lower.match(/^::ffff:([0-9.]+)$/)\n if (v4MappedDotted)\n return isBlockedIPv4(v4MappedDotted[1]!)\n const v4MappedHex = lower.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/)\n if (v4MappedHex) {\n const hi = Number.parseInt(v4MappedHex[1]!, 16)\n const lo = Number.parseInt(v4MappedHex[2]!, 16)\n const ipv4 = `${(hi >> 8) & 0xFF}.${hi & 0xFF}.${(lo >> 8) & 0xFF}.${lo & 0xFF}`\n return isBlockedIPv4(ipv4)\n }\n if (lower === '::' || lower === '::1')\n return true\n // fc00::/7 unique-local (covers fc.. and fd..)\n if (/^f[cd][0-9a-f]{0,2}:/.test(lower))\n return true\n // fe80::/10 link-local\n if (/^fe[89ab][0-9a-f]?:/.test(lower))\n return true\n // ff00::/8 multicast\n if (lower.startsWith('ff'))\n return true\n return false\n}\n\nexport function isBlockedAddress(ip: string): boolean {\n const family = isIP(ip)\n if (family === 4)\n return isBlockedIPv4(ip)\n if (family === 6)\n return isBlockedIPv6(ip)\n return true // not a recognised IP literal — refuse\n}\n\n/**\n * Normalize a hostname for allowlist comparison: lowercase, strip a trailing\n * dot (FQDN root) and IPv6 brackets, drop a leading dot on allowlist entries.\n */\nfunction normalizeHost(host: string): string {\n let h = host.trim().toLowerCase()\n if (h.startsWith('[') && h.endsWith(']'))\n h = h.slice(1, -1)\n if (h.endsWith('.'))\n h = h.slice(0, -1)\n if (h.startsWith('.'))\n h = h.slice(1)\n return h\n}\n\n/**\n * Site identity for cross-host redirect detection: the lowercased hostname\n * with a leading `www.` stripped, so `example.com` ⇄ `www.example.com` and a\n * path-only redirect aren't flagged as cross-host. Returns `null` for an\n * unparseable URL (caller skips the note rather than guessing).\n */\nfunction sameSiteHost(url: string): string | null {\n try {\n return new URL(url).hostname.toLowerCase().replace(/^www\\./, '')\n }\n catch {\n return null\n }\n}\n\n/**\n * Host-suffix allowlist check. An empty / undefined list means \"no allowlist\"\n * → every host passes (the SSRF blocklist is the only gate). Otherwise the\n * host must equal, or be a subdomain of, one of the entries. Subdomain match\n * is on a dot boundary so `example.com` matches `docs.example.com` but not\n * `notexample.com`.\n */\nexport function isHostAllowed(hostname: string, allowHosts: readonly string[] | undefined): boolean {\n if (!allowHosts || allowHosts.length === 0)\n return true\n const host = normalizeHost(hostname)\n for (const entry of allowHosts) {\n const allowed = normalizeHost(entry)\n if (allowed.length === 0)\n continue\n if (host === allowed || host.endsWith(`.${allowed}`))\n return true\n }\n return false\n}\n\n/**\n * Resolve `hostname` and refuse if any answer falls in a blocked range.\n *\n * Returns the first allowed `{ address, family }` so the caller can pin\n * the connection to it. Throws on any blocked answer so a multi-record\n * response with one private record fails closed.\n */\nasync function resolveAndCheck(hostname: string, allowHosts?: readonly string[]): Promise<{ address: string, family: 4 | 6 }> {\n // Egress allowlist (when configured) gates BEFORE DNS so a non-approved\n // host never even triggers a lookup. Enforced here so every redirect hop\n // is re-checked, not just the initial URL.\n if (!isHostAllowed(hostname, allowHosts))\n throw new Error(`refused: ${hostname} is not in the configured egress allowlist`)\n // `URL.hostname` keeps brackets around IPv6 literals (`[::1]`) — strip them\n // before the IP check so `isIP` recognises the literal and `dns.lookup`\n // doesn't try to resolve `[::1]` as a name.\n const bare = hostname.startsWith('[') && hostname.endsWith(']')\n ? hostname.slice(1, -1)\n : hostname\n const family = isIP(bare)\n if (family === 4 || family === 6) {\n if (isBlockedAddress(bare))\n throw new Error(`refused: ${bare} is in a blocked range`)\n return { address: bare, family }\n }\n const answers = await dns.lookup(bare, { all: true, verbatim: true })\n if (answers.length === 0)\n throw new Error(`DNS lookup returned no records for ${bare}`)\n for (const { address } of answers) {\n if (isBlockedAddress(address))\n throw new Error(`refused: ${bare} resolves to blocked address ${address}`)\n }\n const first = answers[0]!\n const fam = first.family === 6 ? 6 : 4\n return { address: first.address, family: fam }\n}\n\n/**\n * Pinned HTTP/HTTPS request. Connects to `pinnedIp` directly (bypassing\n * Node/Bun's own DNS resolution) while preserving the `Host:` header and\n * (for HTTPS) TLS SNI on the URL's original hostname. This is the\n * concrete defense against DNS rebinding — see the file-level docstring.\n *\n * Reads up to `maxBufferBytes` of body and then forcibly closes the\n * connection. The body is decoded as UTF-8; binary responses degrade to\n * mojibake but the tool's contract is text payloads anyway.\n */\ninterface PinnedResponse {\n status: number\n url: string\n contentType: string\n body: string\n location: string | null\n /**\n * True when the body was cut at `maxBufferBytes` before the server\n * finished sending. Optional so test seams that stub `requestImpl`\n * don't have to populate it; absent means \"not truncated\".\n */\n truncated?: boolean\n}\n\nfunction buildHostHeader(url: URL): string {\n if (!url.port)\n return url.hostname\n const defaultPort = url.protocol === 'https:' ? '443' : '80'\n return url.port === defaultPort ? url.hostname : `${url.hostname}:${url.port}`\n}\n\nexport function pinnedRequest(\n url: URL,\n pinnedIp: string,\n family: 4 | 6,\n headers: Record<string, string>,\n signal: AbortSignal,\n maxBufferBytes: number,\n): Promise<PinnedResponse> {\n return new Promise((resolve, reject) => {\n const isHttps = url.protocol === 'https:'\n const port = url.port\n ? Number.parseInt(url.port, 10)\n : (isHttps ? 443 : 80)\n\n // `host` is the literal IP, `servername` is the original hostname so\n // TLS cert verification still matches the cert's SAN/CN against the\n // user-visible name. Node uses `servername` for `checkServerIdentity`\n // automatically when set.\n const options: HttpRequestOptions & HttpsRequestOptions = {\n host: pinnedIp,\n port,\n path: `${url.pathname}${url.search}`,\n method: 'GET',\n family,\n headers: { ...headers, Host: buildHostHeader(url) },\n ...(isHttps ? { servername: url.hostname } : {}),\n }\n\n const request = isHttps ? httpsRequest : httpRequest\n const chunks: Buffer[] = []\n let received = 0\n // Single-shot settle guard. Bun's `node:http` is quirky around\n // `req.destroy(err)` — passing an error doesn't reliably surface as an\n // `error` event the way it does on Node. We track the settle state\n // ourselves and call `resolve` / `reject` directly from the abort /\n // timeout paths so the promise terminates even when the underlying\n // socket teardown is silent.\n let settled = false\n // Forward-declared so the `finish` / abort / timeout handlers can\n // close over `req` before it's assigned below. The request object is\n // wired into them via mutable refs because the handlers must be set\n // up *before* `request(options, callback)` fires its first event.\n let req: ReturnType<typeof httpRequest> | undefined\n let timer: ReturnType<typeof setTimeout> | undefined\n\n const teardown = (): void => {\n try { req?.destroy() }\n catch { /* tear-down best effort */ }\n }\n\n const finish = (settler: () => void): void => {\n if (settled)\n return\n settled = true\n if (timer)\n clearTimeout(timer)\n signal.removeEventListener('abort', onSignalAbort)\n settler()\n }\n\n function onSignalAbort(): void {\n finish(() => reject(new Error('request aborted')))\n teardown()\n }\n\n // Pre-aborted signal — short-circuit so we don't even open a socket.\n // (This is the realistic shape when a user hits Ctrl+C while the agent\n // is already shutting down: the signal is `.aborted` by the time the\n // tool function runs.)\n if (signal.aborted) {\n reject(new Error('request aborted'))\n return\n }\n\n timer = setTimeout(() => {\n finish(() => reject(new Error(`request timed out after ${HTTP_TIMEOUT_MS}ms`)))\n teardown()\n }, HTTP_TIMEOUT_MS)\n\n signal.addEventListener('abort', onSignalAbort, { once: true })\n\n req = request(options, (res) => {\n const status = res.statusCode ?? 0\n const headersOut = res.headers\n const contentType = (headersOut['content-type'] ?? '').toString().toLowerCase()\n const location = typeof headersOut.location === 'string' ? headersOut.location : null\n\n // Short-circuit on redirect responses — body is irrelevant.\n if (status >= 300 && status < 400 && location) {\n res.resume() // drain so the socket can be released\n finish(() => resolve({ status, url: url.toString(), contentType, body: '', location }))\n return\n }\n\n let bodyTruncated = false\n res.on('data', (chunk: Buffer) => {\n if (received >= maxBufferBytes) {\n // Stop accumulating; tear the socket down so the server stops\n // sending. We still resolve cleanly from `end` (or `close` if\n // the server doesn't finish flushing before the kill lands).\n bodyTruncated = true\n teardown()\n return\n }\n // Keep only what fits in the budget — a chunk straddling the cap\n // would otherwise overshoot it by up to one chunk. A partial slice\n // means the body was cut, so flag it and stop the transfer.\n const room = maxBufferBytes - received\n if (chunk.length > room) {\n chunks.push(chunk.subarray(0, room))\n received += room\n bodyTruncated = true\n teardown()\n return\n }\n received += chunk.length\n chunks.push(chunk)\n })\n res.on('end', () => {\n const body = Buffer.concat(chunks).toString('utf-8')\n finish(() => resolve({ status, url: url.toString(), contentType, body, location: null, truncated: bodyTruncated }))\n })\n res.on('close', () => {\n // Socket torn down before `end` (e.g. when our maxBufferBytes\n // trigger destroyed the request). Resolve with whatever we\n // buffered so far — `truncated` tells the caller the body is cut.\n if (!settled) {\n const body = Buffer.concat(chunks).toString('utf-8')\n finish(() => resolve({ status, url: url.toString(), contentType, body, location: null, truncated: bodyTruncated }))\n }\n })\n res.on('error', err => finish(() => reject(err)))\n })\n\n req.on('error', (err: Error) => finish(() => reject(err)))\n req.end()\n })\n}\n\n/**\n * Walk up to {@link MAX_REDIRECTS} hops, validating the target host of each\n * 3xx response against the SSRF blocklist before following.\n *\n * `deps` is an internal seam for tests — production callers use the\n * defaults. Both knobs are intentionally opaque: hosts that want to\n * customize the blocklist should use a higher-level mechanism (a future\n * `behavior.fetchUrlAllowHosts` setting) rather than reaching into this.\n */\nexport interface FetchSsrfDeps {\n /** Resolve + validate a hostname. Defaults to {@link resolveAndCheck}. */\n resolver?: (hostname: string) => Promise<{ address: string, family: 4 | 6 }>\n /** Execute a single pinned HTTP request. Defaults to {@link pinnedRequest}. */\n requestImpl?: typeof pinnedRequest\n}\n\nexport async function fetchWithSsrfGuard(\n startUrl: URL,\n headers: Record<string, string>,\n signal: AbortSignal,\n maxBufferBytes: number,\n deps: FetchSsrfDeps = {},\n allowHosts?: readonly string[],\n): Promise<PinnedResponse> {\n // Default resolver binds the egress allowlist; a test-injected resolver\n // owns its own policy.\n const resolver = deps.resolver ?? ((hostname: string) => resolveAndCheck(hostname, allowHosts))\n const requestImpl = deps.requestImpl ?? pinnedRequest\n let current = startUrl\n for (let hop = 0; hop <= MAX_REDIRECTS; hop++) {\n const { address, family } = await resolver(current.hostname)\n const res = await requestImpl(current, address, family, headers, signal, maxBufferBytes)\n if (!res.location)\n return res\n if (hop === MAX_REDIRECTS)\n throw new Error(`too many redirects (>${MAX_REDIRECTS})`)\n let next: URL\n try {\n next = new URL(res.location, current)\n }\n catch {\n throw new Error(`invalid redirect target: ${res.location}`)\n }\n if (next.protocol !== 'http:' && next.protocol !== 'https:')\n throw new Error(`refused redirect to non-http(s) target: ${next.protocol}`)\n current = next\n }\n throw new Error('redirect loop exited unexpectedly')\n}\n\n/**\n * Opt-in, process-local response cache for `fetch_url`. Disabled unless\n * `behavior.fetchUrlCacheTtlMs` is set (> 0). Bounded by entry count and total\n * cached bytes; eviction is oldest-first (insertion order via `Map`, which is\n * a good-enough LRU because we delete-then-set on hit to refresh recency).\n *\n * Keyed on `<url>\\u0000<cap>\\u0000<egress-policy>` so two calls with different\n * `max_bytes` (hence different truncation) don't alias, and — because the\n * cache is module-global — an agent with a permissive `fetchUrlAllowHosts`\n * can't populate entries that a co-resident agent with a stricter allowlist\n * would then be served. Only successful text responses are stored — the\n * caller gates on status before writing.\n */\nconst FETCH_CACHE_MAX_ENTRIES = 64\nconst FETCH_CACHE_MAX_BYTES = 16 * 1024 * 1024 // 16 MiB total\n\ninterface FetchCacheEntry {\n /** Fully-formatted tool output (header + body) ready to return verbatim. */\n output: string\n expiresAt: number\n bytes: number\n}\n\nconst fetchCache = new Map<string, FetchCacheEntry>()\nlet fetchCacheBytes = 0\n\nfunction cacheKey(url: string, cap: number, allowHosts?: readonly string[]): string {\n // Normalize + sort so equivalent policies (`['A.com', 'b.com']` vs\n // `['b.com', 'a.com.']`) share entries; an absent/empty allowlist keys\n // as the empty policy.\n const policy = (allowHosts ?? []).map(normalizeHost).sort().join(',')\n return `${url}\\u0000${cap}\\u0000${policy}`\n}\n\nfunction cacheGet(key: string): string | null {\n const entry = fetchCache.get(key)\n if (!entry)\n return null\n if (Date.now() >= entry.expiresAt) {\n fetchCache.delete(key)\n fetchCacheBytes -= entry.bytes\n return null\n }\n // Refresh recency: re-insert at the tail of the Map's iteration order.\n fetchCache.delete(key)\n fetchCache.set(key, entry)\n return entry.output\n}\n\nfunction cacheSet(key: string, output: string, ttlMs: number): void {\n const bytes = Buffer.byteLength(output, 'utf-8')\n // A single oversize payload that can't fit under the byte cap is simply not\n // cached (still returned to the caller) rather than evicting everything.\n if (bytes > FETCH_CACHE_MAX_BYTES)\n return\n // Drop any stale entry for this key first so the byte accounting stays exact.\n const prev = fetchCache.get(key)\n if (prev) {\n fetchCache.delete(key)\n fetchCacheBytes -= prev.bytes\n }\n fetchCache.set(key, { output, expiresAt: Date.now() + ttlMs, bytes })\n fetchCacheBytes += bytes\n // Evict oldest-first until both caps are satisfied.\n while (fetchCache.size > FETCH_CACHE_MAX_ENTRIES || fetchCacheBytes > FETCH_CACHE_MAX_BYTES) {\n const oldest = fetchCache.keys().next().value\n if (oldest === undefined)\n break\n const victim = fetchCache.get(oldest)!\n fetchCache.delete(oldest)\n fetchCacheBytes -= victim.bytes\n }\n}\n\n/** Test seam — drop all cached entries. */\nexport function clearFetchUrlCache(): void {\n fetchCache.clear()\n fetchCacheBytes = 0\n}\n\n/**\n * Test seam — exercise the cache get/set/eviction in isolation without\n * standing up the full SSRF + HTTP path. Not part of the public tool surface;\n * `execute` uses the module-private functions directly.\n */\nexport const __fetchCacheTestApi = {\n key: cacheKey,\n get: cacheGet,\n set: cacheSet,\n size: () => fetchCache.size,\n bytes: () => fetchCacheBytes,\n}\n\nexport const fetchUrl: ToolDef = {\n isConcurrencySafe: true,\n spec: {\n name: 'fetch_url',\n description: 'Fetch a public URL and return its body as plain text. HTML is reduced to text (scripts, styles, tags stripped; entities decoded). If a `web_search` tool is also available, use it first when you do not already know the exact URL. Only http(s) URLs are accepted; loopback, link-local, private, and cloud-metadata addresses are refused. Output is capped (default 200 KiB).',\n inputSchema: {\n type: 'object',\n properties: {\n url: {\n type: 'string',\n description: 'HTTP or HTTPS URL to fetch.',\n },\n max_bytes: {\n type: 'number',\n description: `Truncate the response body beyond this many bytes. Default: ${DEFAULT_MAX_BYTES}. Hard cap: ${HARD_MAX_BYTES}.`,\n },\n },\n required: ['url'],\n },\n },\n async execute({ url, max_bytes }, ctx: ToolContext) {\n const raw = typeof url === 'string' ? url.trim() : ''\n if (!raw)\n return 'fetch_url error: `url` is required'\n let parsed: URL\n try {\n parsed = new URL(raw)\n }\n catch {\n return `fetch_url error: invalid URL: ${raw}`\n }\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')\n return `fetch_url error: only http(s) URLs are allowed (got ${parsed.protocol})`\n\n const cap = Math.min(\n typeof max_bytes === 'number' && max_bytes > 0 ? Math.floor(max_bytes) : DEFAULT_MAX_BYTES,\n HARD_MAX_BYTES,\n )\n // Buffer up to 2× the cap so HTML overhead doesn't starve the visible\n // text budget — a 200 KiB cap on rendered text can legitimately need to\n // read ~400 KiB of raw HTML before stripping. Hard ceiling is still\n // governed by `HARD_MAX_BYTES * 2` which is comfortably below OOM.\n const bufferCap = Math.min(cap * 2, HARD_MAX_BYTES * 2)\n\n // Opt-in response cache (off unless `behavior.fetchUrlCacheTtlMs > 0`).\n // The egress allowlist is part of the key AND re-checked before serving\n // a hit — the cache is module-global, so neither a policy change within\n // an agent nor a co-resident agent's entries may bypass this agent's\n // allowlist.\n const allowHosts = ctx.behavior?.fetchUrlAllowHosts\n const ttlMs = ctx.behavior?.fetchUrlCacheTtlMs\n const cacheEnabled = typeof ttlMs === 'number' && ttlMs > 0\n const key = cacheKey(parsed.toString(), cap, allowHosts)\n if (cacheEnabled && isHostAllowed(parsed.hostname, allowHosts)) {\n const hit = cacheGet(key)\n if (hit !== null)\n return hit\n }\n\n try {\n const res = await fetchWithSsrfGuard(\n parsed,\n {\n 'user-agent': 'Mozilla/5.0 (compatible; zidane/1.0)',\n 'accept': 'text/html,text/plain,application/json;q=0.9,*/*;q=0.5',\n 'accept-encoding': 'identity', // no gzip — we don't decompress\n },\n ctx.signal,\n bufferCap,\n {},\n allowHosts,\n )\n const body = res.body\n const isHtml = res.contentType.includes('html') || /^\\s*<!doctype html|^\\s*<html/i.test(body)\n const text = isHtml ? stripHtmlBlock(body) : body\n const truncated = text.length > cap\n // Two distinct truncation reasons: the rendered text overflowing `cap`,\n // and the raw transfer being cut at the buffer ceiling (`res.truncated`)\n // — the latter can leave the rendered text under `cap` (HTML overhead\n // stripped away), so it needs its own marker or the model would treat\n // a partial body as complete.\n const out = truncated\n ? `${text.slice(0, cap)}\\n\\n[truncated at ${cap} bytes; full length ${text.length}]`\n : res.truncated\n ? `${text}\\n\\n[response truncated: body exceeded the ${bufferCap}-byte transfer buffer]`\n : text\n // `res.url` reflects the FINAL hop after redirects; flag a cross-host\n // landing so the model knows the content came from a different origin\n // than it asked for (open-redirect awareness). Same-host path changes\n // and www add/strip are treated as the same site and not flagged.\n const finalHost = sameSiteHost(res.url)\n const requestedHost = sameSiteHost(parsed.toString())\n const redirectNote = finalHost && requestedHost && finalHost !== requestedHost\n ? `Note: ${parsed.hostname} redirected to a different host (${new URL(res.url).hostname}).\\n`\n : ''\n const header = `URL: ${res.url}\\nStatus: ${res.status}\\nContent-Type: ${res.contentType || '(none)'}\\n${redirectNote}\\n`\n const formatted = header + out\n // Cache only successful, complete text responses — never errors /\n // redirects / transfer-truncated bodies (a retry may get the rest).\n if (cacheEnabled && res.status >= 200 && res.status < 300 && res.truncated !== true)\n cacheSet(key, formatted, ttlMs)\n return formatted\n }\n catch (err) {\n return `fetch_url error: ${errorMessage(err)}`\n }\n },\n}\n"],"mappings":"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"}
|
package/dist/headless.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $n as HeadlessResult, Jn as FormattedHeadlessTurnEvent, Qn as HeadlessOutputFormat, Xn as HeadlessEvent, Yn as HeadlessErrorInfo, Zn as HeadlessOptions, ar as exitCodeForHeadlessResult, cr as formattedHeadlessTurnEventToJsonl, dr as providerTranscriptFormatForProvider, er as HeadlessStatus, fr as runHeadless, ir as ProviderTranscriptFormat, lr as headlessEventToJsonl, mr as transcriptToProviderMessages, nr as OpenAIChatContentPart, or as formatHeadlessResult, pr as transcriptToOpenAIMessages, qn as FormattedHeadlessResult, rr as OpenAIChatMessage, sr as formatHeadlessTurnEvent, tr as HeadlessUsage, ur as installHeadlessEventAdapter } from "./index-
|
|
1
|
+
import { $n as HeadlessResult, Jn as FormattedHeadlessTurnEvent, Qn as HeadlessOutputFormat, Xn as HeadlessEvent, Yn as HeadlessErrorInfo, Zn as HeadlessOptions, ar as exitCodeForHeadlessResult, cr as formattedHeadlessTurnEventToJsonl, dr as providerTranscriptFormatForProvider, er as HeadlessStatus, fr as runHeadless, ir as ProviderTranscriptFormat, lr as headlessEventToJsonl, mr as transcriptToProviderMessages, nr as OpenAIChatContentPart, or as formatHeadlessResult, pr as transcriptToOpenAIMessages, qn as FormattedHeadlessResult, rr as OpenAIChatMessage, sr as formatHeadlessTurnEvent, tr as HeadlessUsage, ur as installHeadlessEventAdapter } from "./index-LxAV7xSk.js";
|
|
2
2
|
export { FormattedHeadlessResult, FormattedHeadlessTurnEvent, HeadlessErrorInfo, HeadlessEvent, HeadlessOptions, HeadlessOutputFormat, HeadlessResult, HeadlessStatus, HeadlessUsage, OpenAIChatContentPart, OpenAIChatMessage, ProviderTranscriptFormat, exitCodeForHeadlessResult, formatHeadlessResult, formatHeadlessTurnEvent, formattedHeadlessTurnEventToJsonl, headlessEventToJsonl, installHeadlessEventAdapter, providerTranscriptFormatForProvider, runHeadless, transcriptToOpenAIMessages, transcriptToProviderMessages };
|
package/dist/headless.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { a as isWireTurn, i as documentBlockMarker, l as toolResultToText, n as assertResolvedMediaBlock } from "./types-BIarq1qE.js";
|
|
2
|
-
import { ht as basic_default, t as createAgent } from "./agent-
|
|
2
|
+
import { ht as basic_default, t as createAgent } from "./agent-CjhcxKp7.js";
|
|
3
3
|
import { r as createProcessContext } from "./contexts-DWVt7MvF.js";
|
|
4
4
|
import { t as audioFormatFromMediaType } from "./media-sniff-UKijEaiQ.js";
|
|
5
|
-
import { c as ensureToolResultPairing, g as toAnthropic } from "./messages-
|
|
6
|
-
import { d as createMemoryStore, t as createSession } from "./session-
|
|
5
|
+
import { c as ensureToolResultPairing, g as toAnthropic } from "./messages-XfLqem2d.js";
|
|
6
|
+
import { d as createMemoryStore, t as createSession } from "./session-SOpMv7kl.js";
|
|
7
7
|
import { t as createRunSummaryCollector } from "./run-summary-BU_NgwUT.js";
|
|
8
8
|
//#region src/headless.ts
|
|
9
9
|
function exitCodeForHeadlessResult(result) {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
* You lose TypeScript inference on `ctx` this way; pair with
|
|
43
43
|
* `/// <reference types="zidane" />` or an `import type { ExtensionContext } from 'zidane'`
|
|
44
44
|
* (type-only imports don't need a runtime resolution) to recover it.
|
|
45
|
-
*/import {
|
|
45
|
+
*/import { u as ClassifiedError } from "./timeout-DEaqslLO.js";
|
|
46
46
|
import { c as ExecutionContext, d as ListFilesEntry, f as ListFilesOptions, g as TaskExitInfo, l as ExecutionHandle, s as ExecResult } from "./types-M2DauK2D.js";
|
|
47
47
|
import { Hookable } from "hookable";
|
|
48
48
|
import { ReactNode } from "react";
|
|
@@ -3359,6 +3359,28 @@ interface AgentBehavior {
|
|
|
3359
3359
|
* surfaced as `'length'`) is absorbed by auto-compaction on the resumed turn.
|
|
3360
3360
|
*/
|
|
3361
3361
|
resumeOnMaxTokens?: boolean;
|
|
3362
|
+
/**
|
|
3363
|
+
* Cap on consecutive `resumeOnMaxTokens` resumes. Every resume re-bills the
|
|
3364
|
+
* full input context and can re-spend the entire output budget, so the cap
|
|
3365
|
+
* is what turns a stuck model from an unbounded burn into a bounded one.
|
|
3366
|
+
* When consecutive length resumes reach it, the loop exits cleanly with the
|
|
3367
|
+
* last turn's `TurnUsage.finishReason` still `'length'` so consumers can
|
|
3368
|
+
* surface the truncation from `stats`.
|
|
3369
|
+
*
|
|
3370
|
+
* Only bounds turns that produced visible text: a resumed turn with no text
|
|
3371
|
+
* and no tool calls (a pure reasoning spin that restarts from scratch every
|
|
3372
|
+
* resume) is bounded separately and much tighter — after 2 consecutive such
|
|
3373
|
+
* turns the run fails with {@link AgentLengthResumeExhaustedError} instead
|
|
3374
|
+
* of silently completing.
|
|
3375
|
+
*
|
|
3376
|
+
* NOTE: semantics match {@link AgentBehavior.maxConsecutiveRefusalResumes} —
|
|
3377
|
+
* `0` DISABLES the recovery entirely (a truncated turn ends the run
|
|
3378
|
+
* immediately, as if `resumeOnMaxTokens` were off); unlimited resumes are
|
|
3379
|
+
* never offered.
|
|
3380
|
+
*
|
|
3381
|
+
* Default: `8`.
|
|
3382
|
+
*/
|
|
3383
|
+
maxConsecutiveLengthResumes?: number;
|
|
3362
3384
|
/**
|
|
3363
3385
|
* Persist completed turns to the session store **incrementally**, after
|
|
3364
3386
|
* every `turn:after` / `tool-results:after`. This is session-turn
|
|
@@ -17775,4 +17797,4 @@ declare function definePreset(config: Preset): Preset;
|
|
|
17775
17797
|
declare function composePresets(...presets: Preset[]): Preset;
|
|
17776
17798
|
//#endregion
|
|
17777
17799
|
export { SessionArtifactDirMigration as $, ExtensionConfigSurface as $a, CompletionItem as $c, hashMessages as $d, cerebras as $f, remintDuplicateToolCallIds as $h, ExtensionCatalogPartition as $i, isInteractionTool as $l, hashContent as $m, HeadlessResult as $n, ExtensionToolResultTransform as $o, McpToolHookContext as $p, createRunSummaryCollector as $r, resolveSkills as $s, EvalRunUsage as $t, restoreChainFallbacks as $u, Meter as A, inlineLoadedExtension as Aa, BUILTIN_THEMES as Ac, shouldAutoCompact as Ad, applyOAICacheBreakpoints as Af, FileMapStoreOptions as Ah, CompactionSlice as Ai, ConfirmQuestion as Al, ToolHookContext as Am, efficiencyMetricValues as An, ExtensionModal as Ao, readOAuthCredentials as Ap, createSkillsReadTool as Ar, SpawnToolOptions as As, resolveExtensionDocsDir as At, DEFAULT_PERSIST_EXCLUDE_TOOLS as Au, McpCredentialStore as B, ExtensionConfigActionItem as Ba, resolveChipColor as Bc, ContextItem as Bd, toOAIMessages as Bf, SYNTHETIC_TOOL_RESULT_PLACEHOLDER as Bh, defaultRepeatGuardTracked as Bi, PlanRequest as Bl, ToolResultVideoContent as Bm, functionalityMetric as Bn, ExtensionSessionStateSurface as Bo, AutoCompactConfig as Bp, grep as Br, matchModelEntries as Bs, EvalAgentStats as Bt, Owner as Bu, SuccessfulMutationOutcome as C, removeInstalledExtension as Ca, installAllowedToolsGate as Cc, piIdOf as Cd, OAIMessage as Cf, resumeChainRunIds as Ch, buildCompactPrompt as Ci, commandBlockToEvent as Cl, SpawnHookContext as Cm, clearRegisteredEvals as Cn, ExtensionFooterHint as Co, resolvePromptCacheKey as Cp, ToolSearchToolOptions as Cr, createExtensionHostRuntimeBridge as Cs, ZIDANE_SKILL_EDIT_SKILL_NAME as Ct, AgentAccent as Cu, Counter as D, ExtensionScanEntry as Da, SkillActivationState as Dc, AutoCompactInput as Dd, OpenAICompatParams as Df, createRemoteStore as Dh, buildUpToCompactPrompt as Di, parseCommandInvocation as Dl, ThinkingDecay as Dm, createReusableExecutionContext as Dn, ExtensionKeybindingDispatchContext as Do, credentialsFromParams as Dp, SkillsRunScriptToolOptions as Dr, SpawnTaskInput as Ds, extensionDirForCwd as Dt, BUILTIN_AGENTS as Du, resolveModelTargets as E, ENTRY_CANDIDATES as Ea, DeactivationReason as Ec, AutoCompactDecision as Ed, OpenAICompatHttpError as Ef, RemoteStoreOptions as Eh, buildTailCompactPrompt as Ei, executeCommand as El, StreamHookContext as Em, createEvalRunReporter as En, ExtensionKeybinding as Eo, credentialsFilePath as Ep, createSkillsUseTool as Er, ExtensionSubagentDef as Es, editSessionMetadata as Et, BUILD_AGENT as Eu, createMetricsHooks as F, discoverAndBuildExtensionRegistry as Fa, SyntaxTokenStyle as Fc, ContextCategory as Fd, consumeSSE as Ff, PairingDropKind as Fh, summaryToTurn as Fi, InteractionsProvider as Fl, ToolResultDocumentRefContent as Fm, fileExistsOneOf as Fn, ExtensionPromptTransform as Fo, AgentBehavior as Fp, readFile as Fr, createSpawnTool as Fs, EvalAgent as Ft, EditDiffDisplay as Fu, OAuthCallbackHandle as G, ExtensionConfigChangeBehavior as Ga, createPathAutocompleteProvider as Gc, CacheBreakInfo as Gd, mapCodexStopReason as Gf, detectTurnInterruption as Gh, AgentHookMap as Gi, QuestionPayload as Gl, toolResultToText as Gm, runEvalCase as Gn, ExtensionToolCatalogTransform as Go, CompactHookContext as Gp, RunSummaryBudget as Gr, SkillValidationResult as Gs, EvalDefinitionContext as Gt, StreamEvent as Gu, McpOAuthProviderOptions as H, ExtensionConfigActionStatus as Ha, InlineAutocompleteContext as Hc, ContextSnapshot as Hd, userMessage as Hf, ToWireMessagesOptions as Hh, stableStringify as Hi, PlanStep as Hl, TurnUsage as Hm, normalizeMetric as Hn, ExtensionSource as Ho, ChildRunStats as Hp, edit as Hr, writeSkillToDisk as Hs, EvalCaseOptions as Ht, Screen as Hu, LoginMcpServerOptions as I, ExtensionPromptBridge as Ia, Theme as Ic, ContextCategoryId as Id, formatTools as If, PairingRepair as Ih, truncateHeadForPtlRetry as Ii, PRESENT_PLAN_TOOL as Il, ToolResultImageContent as Im, finalizeEvalMetrics as In, ExtensionProviderMiddleware as Io, AgentClock as Ip, multiEdit as Ir, isSpawnTool as Is, EvalAgentMcpServers as It, EditHunk as Iu, startOAuthCallback as J, ExtensionConfigItemBase as Ja, tokenEndingAtCursor as Jc, CacheDimensionName as Jd, LocalParams as Jf, ensureToolResultPairing as Jh, McpConnector as Ji, QuestionType as Jl, ToolMap as Jm, FormattedHeadlessTurnEvent as Jn, ExtensionToolRenderer as Jo, DedupHasher as Jp, RunSummaryCollectorOptions as Jr, parseAllowedToolPattern as Js, EvalRunMetricAggregate as Jt, buildRunModel as Ju, OAuthCallbackOptions as K, ExtensionConfigEnumItem as Ka, patchInlineGhost as Kc, CacheBreakLoggerOptions as Kd, openai as Kf, dropInvalidToolPairings as Kh, AgentHooks as Ki, QuestionRequest as Kl, ToolContext as Km, statusCompleted as Kn, ExtensionToolPolicy as Ko, CompactStartHookContext as Kp, RunSummaryByModel as Kr, isToolAllowedByUnion as Ks, EvalMetric as Kt, ToolCallDisplay as Ku, LoginMcpServerResult as L, ExtensionPromptDelivery as La, ThemeColors as Lc, ContextExactCounts as Ld, mapOAIFinishReason as Lf, PairingRepairMode as Lh, GenerateStructuredDataOptions as Li, PendingInteractionEntry as Ll, ToolResultImageRefContent as Lm, formatEvalCaseSummary as Ln, ExtensionRegistry as Lo, AgentRunOptions as Lp, listFiles as Lr, SpawnModelEntry as Ls, EvalAgentRunOptions as Lt, EditOutcome as Lu, MetricsHookSet as M, parseExtensionScanPathEnv as Ma, ChipColorMap as Mc, ContextBreakdownContributionContext as Md, assistantMessage as Mf, EnsureToolResultPairingOptions as Mh, anchorPreviewFor as Mi, InteractionRequest as Ml, ToolResultAudioContent as Mm, fileContains as Mn, ExtensionOrderedContribution as Mo, resolveOAuthApiKey as Mp, CreateShellToolOptions as Mr, SubagentDef as Ms, withZidaneSkillEditSkill as Mt, accentColor as Mu, MetricsHooksOptions as N, scanExtensionDirectories as Na, DEFAULT_THEME as Nc, ContextBreakdownContributor as Nd, buildAssistantContent as Nf, ORPHANED_TOOL_RESULT_MARKER as Nh, sliceForCompaction as Ni, InteractionResponse as Nl, ToolResultContent as Nm, fileContentQuality as Nn, ExtensionPaths as No, writeOAuthCredentials as Np, createShellTool as Nr, SubagentOverride as Ns, CreateEvalAgentOptions as Nt, resolveAgentId as Nu, Histogram as O, defaultExtensionScanPaths as Oa, SkillActivationStateOptions as Oc, OUTPUT_RESERVE_TOKENS as Od, OpenAICompatStreamError as Of, createMemoryStore as Oh, ANCHOR_PREVIEW_MAX_CHARS as Oi, ASK_USER_TOOL as Ol, ThinkingLevel as Om, defineEval as On, ExtensionManifest as Oo, extractRuntimeCredentials as Op, createSkillsRunScriptTool as Or, SpawnTaskOutcome as Os, extensionScaffold as Ot, DEFAULT_AGENT_ID as Ou, UpDownCounter as P, DiscoverAndBuildExtensionRegistryOptions as Pa, SyntaxStyles as Pc, ContextBreakdownOptions as Pd, classifyOpenAICompatError as Pf, PairingDrop as Ph, stripImagesFromTurns as Pi, InteractionsActions as Pl, ToolResultDocumentContent as Pm, fileExists as Pn, ExtensionPermission as Po, OAuthProviderInterface as Pp, shell as Pr, SubagentRegistry as Ps, EFFICIENCY_METRICS as Pt, singleAgentRegistry as Pu, PersistOutcome as Q, ExtensionConfigStringItem as Qa, CompletionContext as Qc, fnv1a32 as Qd, CerebrasParams as Qf, reasoningWireAction as Qh, BuildExtensionRegistryOptions as Qi, createInteractionTools as Ql, getReadState as Qm, HeadlessOutputFormat as Qn, ExtensionToolResultPatch as Qo, McpServerConfig as Qp, RunSummaryValidation as Qr, validateSkillName as Qs, EvalRunSummaryCase as Qt, providerKeySetChanged as Qu, loginMcpServer as R, createExtensionPromptBridge as Ra, ThemeSelect as Rc, ContextExtensionRow as Rd, openAICompatPromptMessage as Rf, ReasoningWireAction as Rh, generateStructuredData as Ri, PlanDecision as Rl, ToolResultMetadata as Rm, formatEvalRunSummary as Rn, ExtensionRunTransform as Ro, AgentStats as Rp, InteractionToolOptions as Rr, createModelSearchTool as Rs, EvalAgentRunResult as Rt, EditOutcomeKind as Ru, MutationValidationResult as S, readExtensionInstallProvenance as Sa, IMPLICITLY_ALLOWED_SKILL_TOOLS as Sc, openrouterDescriptor as Sd, ASSISTANT_TOOL_CALLS_TAG as Sf, loadSession as Sh, TRAILER as Si, CommandStepFn as Sl, SessionTurn as Sm, buildTrajectory as Sn, ExtensionDefinition as So, derivePromptCacheKey as Sp, LazyToolEntry as Sr, ExtensionWorkspaceSurface as Ss, ZIDANE_EXTENSION_SKILL_NAME as St, jsonSink as Su, normalizeProviders as T, registerExtensionHostModules as Ta, ActiveSkill as Tc, AUTO_COMPACT_MIN_GROWTH_FRACTION as Td, OpenAICompatAuthHeader as Tf, withExtensionSession as Th, buildFullCompactPrompt as Ti, commandSteps as Tl, StepThinkingDecay as Tm, createEvalAgent as Tn, ExtensionGuiSurface as To, ResolveOAuthApiKeyOptions as Tp, SkillsUseToolOptions as Tr, ChildAgent as Ts, buildZidaneSkillEditSkill as Tt, AgentRegistry as Tu, createMemoryMcpCredentialStore as U, ExtensionConfigBooleanItem as Ua, InlineAutocompleteProvider as Uc, ContextUsageSplit as Ud, OpenAIParams as Uf, TurnInterruptionState as Uh, localReliabilityBehavior as Ui, Question as Ul, toolOutputBudgetByteLength as Um, registerEvalTests as Un, ExtensionStorageScope as Uo, CompactEndHookContext as Up, RunSummary as Ur, writeSkillsToDisk as Us, EvalCaseResult as Ut, SessionMeta as Uu, McpOAuthProvider as V, ExtensionConfigActionResult as Va, resolveTheme as Vc, ContextMcpGroup as Vd, toolResultsMessage as Vf, TOOL_USE_INTERRUPTED_MARKER as Vh, normalizeShellCommand as Vi, PlanResponse as Vl, TurnFinishReason as Vm, llmJudge as Vn, ExtensionSkillPolicy as Vo, CacheTtl as Vp, glob as Vr, defineSkill as Vs, EvalArtifacts as Vt, Picked as Vu, hasAuthorizationHeader as W, ExtensionConfigCategory as Wa, InlineGhost as Wc, buildContextBreakdown as Wd, classifyOpenAIError as Wf, autoDetectAndConvert as Wh, Agent as Wi, QuestionChoice as Wl, toolOutputByteLength as Wm, relativeArtifactPath as Wn, ExtensionStorageSurface as Wo, CompactFunction as Wp, RunSummaryBlock as Wr, SkillValidationIssue as Ws, EvalDefinition as Wt, Settings as Wu, PERSISTENCE_PREVIEW_BYTES as X, ExtensionConfigNumberItem as Xa, useCompletion as Xc, CacheDriftInfo as Xd, CursorParams as Xf, fromAnthropic as Xh, McpToolWrap as Xi, TextQuestion as Xl, ReadStateEntry as Xm, HeadlessEvent as Xn, ExtensionToolRendererRegistration as Xo, GeometricThinkingDecay as Xp, RunSummaryRepeatGuard as Xr, validateResourcePathReal as Xs, EvalRunReporterOptions as Xt, chainsEqual as Xu, PERSISTED_STUB_PREFIX as Y, ExtensionConfigModelItem as Ya, CompletionState as Yc, CacheDimensionSnapshot as Yd, local as Yf, filterUnresolvedToolUses as Yh, McpToolMeta as Yi, SelectQuestion as Yl, HydrateReadStateOptions as Ym, HeadlessErrorInfo as Yn, ExtensionToolRendererEntry as Yo, DedupToolConfig as Yp, RunSummaryError as Yr, validateResourcePath as Ys, EvalRunReporter as Yt, chainPositionOf as Yu, PersistInput as Z, ExtensionConfigScope as Za, ActiveTrigger as Zc, diffCacheDimensions as Zd, cursor as Zf, fromOpenAI as Zh, createAgent as Zi, buildResumedToolResultsTurn as Zl, ReadStateMap as Zm, HeadlessOptions as Zn, ExtensionToolResultMetadataRegistration as Zo, JsonValue as Zp, RunSummaryTokens as Zr, validateSkillForWrite as Zs, EvalRunSummary as Zt, filterChainFallbacks as Zu, statsByModel as _, InstalledExtensionTargetOptions as _a, getDefaultScanPaths as _c, localDescriptor as _d, sanitizeToolSchema as _f, wrapDiscoveredMcpTools as _g, createSession as _h, BASE_INSTRUCTIONS as _i, CommandOutputBlock as _l, RunHookMap as _m, TrajectoryStep as _n, ExtensionContext as _o, resolveAnthropicAuthShape as _p, waitTask as _r, ExtensionModelEntry as _s, EXTENSION_EDIT_METADATA_KEY as _t, LoggingHooksOptions as _u, basicTools as a, mergeContributions as aa, SkillInstallCandidate as ac, detectAuth as ad, StreamCallbacks as af, McpConnection as ag, SkillConfig as ah, RecentFile as ai, findActiveTrigger as al, ProgressNudgeMessage as am, EvalWorkspaceFile as an, clampExtensionConfigNumber as ao, AnthropicBedrockConfig as ap, exitCodeForHeadlessResult as ar, ExtensionBannerOptions as as, resolveLegacySessionDir as at, InferSchemaOutput as au, MutationValidationHooksOptions as b, listExtensionCandidates as ba, parseSkillFile as bc, modelsForDescriptor as bd, normalizeOpenRouterModelId as bf, demoteStaleRunningStatus as bh, CompactPromptOptions as bi, CommandStep as bl, SessionHookContext as bm, buildEvalRunSummary as bn, ExtensionCustomRenderer as bo, PromptCacheKeyContext as bp, TailTruncateOptions as br, ExtensionSessionsSurface as bs, SKILL_EDIT_METADATA_KEY as bt, createLogger as bu, StartSpan as c, ExtensionInstallCandidate as ca, SkillInstallOptions as cc, ModelInfo as cd, ToolCall as cf, connectMcpServers as cg, SkillSource as ch, selectFilesFromSession as ci, resolveCompletionSubmit as cl, PromptImagePart as cm, LlmJudgeOptions as cn, isValidExtensionConfigValue as co, AnthropicThinkingPlan as cp, formattedHeadlessTurnEventToJsonl as cr, ExtensionHudStatus as cs, resolveSessionDir as ct, isStandardSchema as cu, TracingHooksOptions as d, ExtensionInstallIo as da, installSkillFromSpec as dc, anthropicDescriptor as dd, TurnResult as df, expandEnvPlaceholders as dg, LoadSessionOptions as dh, CompactOptions as di, CommandArgSuggestion as dl, PromptVideoPart as dm, MetricSpec as dn, sanitizeExtensionConfigActionPatch as do, applyAnthropicCacheBreakpoints as dp, providerTranscriptFormatForProvider as dr, ExtensionNotifyOptions as ds, SHELL_CASCADE_CANCEL_MESSAGE as dt, ConsoleSinkOptions as du, buildExtensionPreset as ea, InterpolateShellCommandsOptions as ec, sanitizeModelChain as ed, installCacheBreakLogger as ef, sanitizeHistoryForProvider as eg, hydrateReadStateFromSession as eh, BYTES_PER_TOKEN as ei, CompletionProvider as el, McpToolSchema as em, EvalScore as en, ExtensionConfigValue as eo, BasetenParams as ep, HeadlessStatus as er, ExtensionToolVerdict as es, buildPersistedStub as et, makeRequestInteraction as eu, createTracingHooks as f, ExtensionInstallProvenance as fa, listSkillCandidates as fc, cerebrasDescriptor as fd, XaiParams as ff, expandMcpConfigEnv as fg, Session as fh, CompactResult as fi, CommandAttachment as fl, RepeatGuardConfig as fm, MetricSpecMap as fn, toWireConfigSchema as fo, buildAnthropicClientOptions as fp, runHeadless as fr, ExtensionNotifyTone as fs, TOOL_USE_CANCELLED_MESSAGE as ft, LogLevel as fu, formatTokenUsage as g, InstallExtensionFromSpecOptions as ga, discoverSkills as gc, getModelInfo as gd, SchemaSanitizeResult as gf, resultToString as gg, SessionStore as gh, CompactPromptTooLongError as gi, CommandInvocation as gl, RetryConfig as gm, Trajectory as gn, ExtensionCapability as go, planAnthropicThinking as gp, WAIT_TASK_TIMED_OUT_PREFIX as gr, ExtensionHostRuntimeImpl as gs, renderStaleReadNotice as gt, LoggingHookSet as gu, flattenTurns as h, ExtensionInstallSource as ha, SourcedScanPath as hc, getContextWindow as hd, SchemaSanitizeProfile as hf, normalizeMcpServers as hg, SessionRun as hh, CompactInvalidInputError as hi, CommandInteraction as hl, ResolveContentRefBlock as hm, ReusableExecutionContext as hn, ExtensionBehaviorTransform as ho, defaultAnthropicMaxTokens as hp, writeFile as hr, ExtensionHostRuntimeBridge as hs, collectNewlyStaleReadPaths as ht, Logger as hu, _default as i, filterExtensionRegistry as ia, SkillCandidateDetail as ic, ProviderKey as id, ProviderCapabilities as if, ConnectMcpServersOptions as ig, ShellInterpolationApproval as ih, PostCompactRestoreOptions as ii, collectReferences as il, ProgressNudgeKind as im, EvalVariantSummary as in, WireExtensionConfigSchema as io, arcee as ip, ProviderTranscriptFormat as ir, TuiContributions as is, resolveLegacyPersistDir as it, useInteractionsQueue as iu, MetricAttributes as j, isExtensionDefinition as ja, ChipColor as jc, ContextBreakdown as jd, applyOAIToolCacheBreakpoint as jf, createFileMapStore as jh, SummaryToTurnInput as ji, CreateInteractionToolsOptions as jl, ToolOutcome as jm, emitEfficiencyMetrics as jn, ExtensionModalNode as jo, registerCustomOAuthProvider as jp, shellKill as jr, SpawnToolState as js, withZidaneExtensionSkill as jt, PLAN_AGENT as ju, InstrumentOptions as k, discoverExtensions as ka, createSkillActivationState as kc, effectiveContextWindow as kd, TOOL_RESULTS_TAG as kf, FileMapAdapter as kh, CompactScope as ki, AnswerValue as kl, ToolBatchExecutor as km, defineMetrics as kn, ExtensionMcpConfigTransform as ko, isOAuthAccessToken as kp, SkillsReadToolOptions as kr, SpawnTool as ks, readSkillEditMarker as kt, DEFAULT_BUDGET_EXCLUDE_TOOLS as ku, TracingConventions as l, ExtensionInstallError as la, SkillInstallResult as lc, ModelOption as ld, ToolResult as lf, createStderrLineEmitter as lg, SkillsConfig as lh, selectRecentFiles as li, Command as ll, PromptPart as lm, MetricDirection as ln, resolveExtensionConfig as lo, EXTENDED_CACHE_TTL_BETA as lp, headlessEventToJsonl as lr, ExtensionHudWriter as ls, resolveTasksDir as lt, resolveSchemaInput as lu, TokenUsageLike as m, ExtensionInstallScope as ma, readSkillProvenance as mc, enabledModelOptions as md, SchemaSanitizeOptions as mf, normalizeMcpBlocks as mg, SessionMetaRow as mh, compactConversation as mi, CommandGenerateDataOptions as ml, ResolveContentRef as mm, RegisterEvalTestsOptions as mn, ExtensionApiVersion as mo, credentialIdentity as mp, transcriptToProviderMessages as mr, createExtensionHud as ms, applyCompactSummaryCutoff as mt, LogSink as mu, composePresets as n, composeExtensionPreset as na, stripShellInterpolations as nc, AuthMethod as nd, HistoryRepair as nf, toOpenAI as ng, readStateKey as nh, utf8ByteLength as ni, CompletionSubmitContext as nl, ModelTargetSpec as nm, EvalScorerContext as nn, StoredExtensionConfigItem as no, planBasetenReasoning as np, OpenAIChatContentPart as nr, GuiContributions as ns, maybePersistToolResult as nt, serializeInteractionResponse as nu, GEN_AI_ATTRIBUTES as o, partitionExtensionCatalog as oa, SkillInstallError as oc, BUILTIN_PROVIDERS as od, StreamOptions as of, McpExecutionBinding as og, SkillDiagnostic as oh, buildPostCompactAttachments as oi, mergeReferences as ol, PromptAudioPart as om, EvalWorkspaceOptions as on, coerceExtensionConfigInput as oo, AnthropicContextManagement as op, formatHeadlessResult as or, ExtensionHud as os, resolveMcpWarningsDir as ot, SchemaInput as ou, ModelUsage as p, ExtensionInstallResult as pa, parseSkillInstallSpec as pc, credKeyOf as pd, xai as pf, nonOwningMcpView as pg, SessionData as ph, CompactStartEvent as pi, CommandExecution as pl, RepeatGuardToolMatcher as pm, MetricStats as pn, EMPTY_EXTENSION_REGISTRY as po, classifyAnthropicError as pp, transcriptToOpenAIMessages as pr, ExtensionStatusOptions as ps, TOOL_USE_SKIPPED_MESSAGE as pt, LogRecord as pu, OAuthCallbackResult as q, ExtensionConfigItem as qa, resolveInlineSuggestion as qc, CacheDimensionDiff as qd, toPiMessages as qf, ensureEndsWithUserMessage as qh, AgentOptions as qi, QuestionResponse as ql, ToolDef as qm, FormattedHeadlessResult as qn, ExtensionToolRenderProps as qo, DEFAULT_AGENT_CLOCK as qp, RunSummaryCollector as qr, matchesAllowedTool as qs, EvalMetricError as qt, ModelChainEntry as qu, definePreset as r, disposeExtensionRegistry as ra, SKILL_PROVENANCE_FILE as rc, ProviderAuth as rd, Provider as rf, toWireMessages as rg, resolveReadStateMap as rh, PostCompactAttachments as ri, applyInsert as rl, OAuthRefreshHookContext as rm, EvalTestRunner as rn, WireExtensionConfigItem as ro, ArceeParams as rp, OpenAIChatMessage as rr, LoadedExtension as rs, migrateSessionArtifactDirs as rt, useInteractionsActions as ru, Span as s, ExtensionFetchResult as sa, SkillInstallErrorCode as sc, CustomField as sd, TokenCountPayload as sf, attachStderrWarnPump as sg, SkillResource as sh, selectFilesFromReadState as si, mergeSuggestions as sl, PromptDocumentPart as sm, EvalWorkspaceSnapshot as sn, isValidExtensionConfigItem as so, AnthropicParams as sp, formatHeadlessTurnEvent as sr, ExtensionHudState as ss, resolvePersistDir as st, StandardSchemaLike as su, Preset as t, buildExtensionRegistry as ta, interpolateShellCommands as tc, toggleChainEntry as td, snapshotCacheDimensions as tf, toAnthropic as tg, hydrateReadStateFromSessionAsync as th, estimateTokens as ti, CompletionReference as tl, ModelTarget as tm, EvalScorer as tn, ExtensionConfigValueItem as to, baseten as tp, HeadlessUsage as tr, ExtensionTuiSurface as ts, cleanupPersistedSession as tt, pendingInteractionsFromTurns as tu, TracingHookSet as u, ExtensionInstallErrorCode as ua, SkillInstallSpec as uc, ProviderDescriptor as ud, ToolSpec as uf, deriveMcpToolMeta as ug, CreateSessionOptions as uh, CompactEndEvent as ui, CommandAgentResult as ul, PromptTextPart as um, MetricEmitter as un, resolveExtensionConfigValue as uo, anthropic as up, installHeadlessEventAdapter as ur, ExtensionNotification as us, INTERRUPT_MESSAGE_FOR_TOOL_USE as ut, zodToJsonSchema as uu, MutationValidationBatch as v, fetchExtensionSource as va, inferSource as vc, modelOptionsFor as vd, sanitizeToolSpecs as vf, currentExtensionSession as vh, CompactDirection as vi, CommandSpawnResult as vl, SessionContentBlock as vm, TrajectoryStepKind as vn, ExtensionContextTransform as vo, resolveAnthropicBetas as vp, ValidationResult as vr, ExtensionRuntimeSurface as vs, EditSessionTarget as vt, RingBufferSink as vu, createMutationValidationHooks as w, updateInstalledExtension as wa, ActivationVia as wc, restoreModelOptions as wd, OAITool as wf, setExtensionSessionState as wh, buildFromCompactPrompt as wi, commandBlocksToContent as wl, StaleReadPolicy as wm, computeEvalTagScores as wn, ExtensionGuiPanel as wo, OAuthParams as wp, createToolSearchTool as wr, createWorkspaceSurface as ws, buildZidaneExtensionSkill as wt, AgentProfile as wu, MutationValidationMutation as x, parseExtensionInstallSource as xa, buildCatalog as xc, openaiDescriptor as xd, openrouter as xf, getExtensionSessionState as xh, NO_TOOLS_PREAMBLE as xi, CommandStepContext as xl, SessionMessage as xm, buildRegisteredEvals as xn, ExtensionCustomRendererEntry as xo, PromptCacheKeyOption as xp, tailTruncate as xr, ExtensionWorkspacePair as xs, SkillEditMarker as xt, createLoggingHooks as xu, MutationValidationFailure as y, installExtensionFromSpec as ya, parseFrontmatter as yc, modelSupportsReasoning as yd, OpenRouterParams as yf, deleteExtensionSessionState as yh, CompactPromptBuilder as yi, CommandSpawnTask as yl, SessionEndStatus as ym, artifactPath as yn, ExtensionContributions as yo, resolveAnthropicMaxOutput as yp, validateToolArgs as yr, ExtensionSessionSummary as ys, ExtensionEditMarker as yt, consoleSink as yu, McpCredentialEntry as z, defineExtension as za, ThemeSurfaces as zc, ContextExtensionSection as zd, openaiCompat as zf, RemintResult as zh, defaultRepeatGuardNormalize as zi, PlanPayload as zl, ToolResultTextContent as zm, formatTrajectoryLine as zn, ExtensionRuntimeEnv as zo, AutoCompact as zp, createInteractionTool as zr, formatModelLine as zs, EvalAgentRunStats as zt, EditPayload as zu };
|
|
17778
|
-
//# sourceMappingURL=index-
|
|
17800
|
+
//# sourceMappingURL=index-LxAV7xSk.d.ts.map
|