{"version":3,"sources":["../src/netFetch.ts"],"sourcesContent":["// hostFetch — the app-facing side of the §5.11 parent-fetch proxy. The app calls\n// `hostFetch(url, init)`; the HOST performs the fetch with its real origin, but\n// only after validating `url` against your manifest's\n// `requests.\"net:fetch\".hosts` ∩ the user's consented hosts, blocking SSRF\n// targets, omitting immediately.run credentials, refusing redirects, and bounding\n// the response size. No raw network handle ever crosses the boundary (§8.10) —\n// only the serialized response.\n\nimport { protocolRequest } from './sandboxUtils';\nimport { throwOnRefusal } from './protocolRefusal';\nimport { protocolStream } from './protocolStream';\nimport { SCHEMES } from './protocolSchemes';\nimport { PROTOCOL_FETCH } from './generated/protocol';\n\n/** Request options for {@link hostFetch}: method, headers, and a string body. */\nexport interface HostFetchInit {\n  method?: string;\n  headers?: Record<string, string>;\n  /** Request body for non-GET/HEAD methods (string). */\n  body?: string;\n  /** R3-861: `'bytes'` returns the exact response bytes in `bodyBytes` (an image,\n   *  a PDF — never text-decoded). Default `'text'`, unchanged. A bytes response\n   *  over the host's size cap is REFUSED `too-large`, never truncated. */\n  responseType?: 'text' | 'bytes';\n}\n\n/** The serialized response from {@link hostFetch} (no live stream crosses the boundary). */\nexport interface HostFetchResponse {\n  status: number;\n  statusText: string;\n  headers: Record<string, string>;\n  /** The text body — `''` when the request asked for bytes. */\n  body: string;\n  /** True if the body hit the host's size cap and was truncated (text mode only —\n   *  bytes mode refuses `too-large` instead). */\n  truncated: boolean;\n  /** R3-861: the exact response bytes when the request said `responseType: 'bytes'`\n   *  (transferred, zero-copy). */\n  bodyBytes?: Uint8Array;\n}\n\n/**\n * Fetch through the host's parent-fetch proxy (§5.11). Requires the `net:fetch`\n * capability with `url`'s origin in your effective allowlist (manifest ∩ the\n * user's consent) — both are arranged at load via the consent screen.\n *\n * A reachable server's reply (including a non-2xx status) RESOLVES — inspect\n * `.status`. A gate/SSRF/transport failure REJECTS with an {@link Error} carrying\n * a machine `.code`: `forbidden` (outside the allowlist), `blocked` (SSRF target),\n * `invalid` (bad url/scheme), `redirect` (the host refuses to follow redirects),\n * `too-large`, or `network`.\n */\nexport const hostFetch = async (url: string, init: HostFetchInit = {}): Promise<HostFetchResponse> => {\n  const res = (await protocolRequest(SCHEMES[PROTOCOL_FETCH], 'fetch', [\n    {\n      url,\n      method: init.method,\n      headers: init.headers,\n      body: init.body,\n      ...(init.responseType ? { responseType: init.responseType } : {}),\n    },\n  ])) as { ok: true; data: HostFetchResponse } | { ok: false; code?: string; message?: string } | undefined;\n  throwOnRefusal(res, 'hostFetch failed');\n  return res.data;\n};\n\n/** One streamed slice of the response body (a chunk as it arrives from the\n *  host). Concatenate `.chunk` across the stream to rebuild the body. */\nexport interface HostFetchStreamEvent {\n  chunk: string;\n}\n\n/** The terminal value of a {@link hostFetchStream} run — the response metadata,\n *  delivered once after the last body chunk. There is no `body` field: the body\n *  arrived as the stream of {@link HostFetchStreamEvent}s. */\nexport interface HostFetchStreamResult {\n  status: number;\n  statusText: string;\n  headers: Record<string, string>;\n  /** True if the stream hit the host's cumulative byte cap and was truncated. */\n  truncated: boolean;\n  /** Total decoded body bytes delivered. */\n  bytes: number;\n}\n\n/**\n * Stream a response through the host's parent-fetch proxy (§5.11 streaming;\n * `LLM_AND_AGENTS_SPEC §2.2`) — the streaming counterpart of {@link hostFetch},\n * for SSE / LLM token streaming. Same `net:fetch` gate (manifest ∩ consent),\n * same credential-less rule and per-hop SSRF re-check; the response body is\n * pumped as a sequence of {@link HostFetchStreamEvent} chunks instead of a single\n * buffered reply.\n *\n * ```ts\n * let body = '';\n * for await (const { chunk } of hostFetchStream(url, { method: 'POST', body })) {\n *   body += chunk;            // e.g. parse SSE / token deltas as they arrive\n * }\n * ```\n *\n * The generator **returns** a {@link HostFetchStreamResult} (status + headers +\n * `truncated`/`bytes`) when the body completes. A gate/SSRF/transport failure —\n * or one of the host's stream bounds — **throws** a `StreamError` (from\n * `./protocolStream`) carrying a\n * machine `.code`: `forbidden` (outside the allowlist), `blocked` (SSRF target),\n * `invalid` (bad url/scheme), `redirect` (the host refuses to follow redirects),\n * `idle-timeout` / `total-timeout` (the host's stream bounds), `byte-cap`, or\n * `network`. The stream is bounded by the host's idle/total timeouts and a\n * cumulative byte cap, so it cannot be used as an unbounded transfer channel.\n *\n * Requires the host to implement the `protocol-fetch` streaming emitter; until\n * that lands a streaming request fails with `not-streamable` and callers should\n * fall back to {@link hostFetch} (`LLM_AND_AGENTS_SPEC §2.2`, roadmap P3-71).\n */\nexport function hostFetchStream(\n  url: string,\n  init: HostFetchInit = {},\n): AsyncGenerator<HostFetchStreamEvent, HostFetchStreamResult, void> {\n  return protocolStream<HostFetchStreamEvent, HostFetchStreamResult>(PROTOCOL_FETCH, 'fetchStream', [\n    { url, method: init.method, headers: init.headers, body: init.body },\n  ]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA,0BAAgC;AAChC,6BAA+B;AAC/B,4BAA+B;AAC/B,6BAAwB;AACxB,sBAA+B;AAwCxB,MAAM,YAAY,OAAO,KAAa,OAAsB,CAAC,MAAkC;AACpG,QAAM,MAAO,UAAM,qCAAgB,+BAAQ,8BAAc,GAAG,SAAS;AAAA,IACnE;AAAA,MACE;AAAA,MACA,QAAQ,KAAK;AAAA,MACb,SAAS,KAAK;AAAA,MACd,MAAM,KAAK;AAAA,MACX,GAAI,KAAK,eAAe,EAAE,cAAc,KAAK,aAAa,IAAI,CAAC;AAAA,IACjE;AAAA,EACF,CAAC;AACD,6CAAe,KAAK,kBAAkB;AACtC,SAAO,IAAI;AACb;AAkDO,SAAS,gBACd,KACA,OAAsB,CAAC,GAC4C;AACnE,aAAO,sCAA4D,gCAAgB,eAAe;AAAA,IAChG,EAAE,KAAK,QAAQ,KAAK,QAAQ,SAAS,KAAK,SAAS,MAAM,KAAK,KAAK;AAAA,EACrE,CAAC;AACH;","names":[]}