open-tag 0.0.5 → 0.1.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/README.md +9 -2
- package/THIRD_PARTY_NOTICES +743 -0
- package/dist/cli/index.mjs +77 -28
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/pi-extensions/web-tools.mjs +1082 -0
- package/dist/cli/pi-extensions/web-tools.mjs.map +1 -0
- package/dist/index.d.mts +10207 -947
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/pi-extensions/web-tools.mjs +1082 -0
- package/dist/pi-extensions/web-tools.mjs.map +1 -0
- package/dist/{program-CpPCcBDJ.mjs → program-CXdTyo53.mjs} +38979 -20615
- package/dist/program-CXdTyo53.mjs.map +1 -0
- package/dist/rolldown-runtime-B4iAMlE-.mjs +35 -0
- package/package.json +7 -5
- package/dist/program-CpPCcBDJ.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-tools.mjs","names":[],"sources":["../../src/pi-extensions/web-tools.ts"],"sourcesContent":["/**\n * OpenTag web tools Pi extension (trusted built artifact).\n *\n * Loaded explicitly with `pi -e <this file>` for actual execution only; `--no-extensions` stays\n * set so implicit discovery never loads it. The extension registers `web_search`/`web_fetch`,\n * forwards validated calls over the per-execution endpoint named by the nonsecret\n * OPENTAG_WEB_TOOLS_SOCKET descriptor, and saves bounded extracted content under the Session\n * workspace `.opentag/web/<stable-tool-id>/`.\n *\n * This module is intentionally self-contained (Node builtins only, no package imports): it runs\n * inside the Pi process and must never resolve workspace dependencies. The wire authority is\n * `@opentag/shared` web-tools; the gateway and Server re-validate everything authoritatively.\n *\n * Wire/artifact identity: the wire toolCallId is derived deterministically (UUID shape) from the\n * per-execution endpoint descriptor plus the actual Pi runtime tool call ID. Same-execution\n * retries reuse it (stable idempotency); a distinct execution can never collide with or\n * overwrite another execution's artifacts. Failures throw bounded redacted errors so Pi records\n * toolResult.isError=true; a normal return is never used for failed operations.\n */\n\nimport { createHash } from \"node:crypto\";\nimport { constants } from \"node:fs\";\nimport { type FileHandle, lstat, mkdir, open, readdir, realpath, rename, rm, writeFile } from \"node:fs/promises\";\nimport http from \"node:http\";\nimport { join, sep } from \"node:path\";\n\n/** Named export so Client tests can pin these against the shared wire authority. */\nexport const WEB_EXTENSION_LIMITS = {\n requestMaxBytes: 16 * 1024,\n searchResponseMaxBytes: 1024 * 1024,\n fetchResponseMaxBytes: 3 * 1024 * 1024,\n previewPageMaxBytes: 12 * 1024,\n toolResultMaxBytes: 48 * 1024,\n pageBodyMaxBytes: 1024 * 1024,\n queryMaxCodepoints: 400,\n searchLimitMax: 10,\n domainsMax: 10,\n fetchUrlsMax: 3,\n urlMaxBytes: 4 * 1024,\n /** End-to-end operation budgets, equal to the shared hard caps; the chain only decreases them. */\n searchTimeoutMs: 15_000,\n fetchTimeoutMs: 45_000,\n protocolVersion: 1,\n /** Remaining-budget hop header (hop metadata, never part of the semantic digest). */\n remainingHeader: \"x-web-remaining-ms\",\n} as const;\n\nconst TRUNCATION_MARKER = \"…[truncated]\";\nconst ENDPOINT_ENV = \"OPENTAG_WEB_TOOLS_SOCKET\";\n/** UUID-shaped stable id derivation namespace; version/variant bits are set below. */\nconst TOOL_ID_NAMESPACE = \"opentag.web.tool.v1\";\n/** Exact UUID-v5 shape produced by `deriveWebToolIdentity`; the only id ever used as a directory. */\nconst TOOL_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n/** The deadline controller aborts with this exact reason so callers keep distinct timeout wording. */\nconst WEB_CALL_TIMED_OUT = \"The web call timed out\";\n\n/** Plain JSON Schema (no TypeBox dependency); the gateway re-validates with the shared Zod authority. */\nexport const WEB_SEARCH_PARAMETERS = {\n type: \"object\",\n properties: {\n query: {\n type: \"string\",\n minLength: 1,\n maxLength: WEB_EXTENSION_LIMITS.queryMaxCodepoints,\n description: \"Search query (1–400 characters), kept verbatim including case.\",\n },\n limit: {\n type: \"integer\",\n minimum: 1,\n maximum: WEB_EXTENSION_LIMITS.searchLimitMax,\n default: 5,\n description: \"Maximum number of sources to return (default 5).\",\n },\n domains: {\n type: \"array\",\n items: { type: \"string\" },\n maxItems: WEB_EXTENSION_LIMITS.domainsMax,\n description: \"Optional public domain names to scope the search (max 10).\",\n },\n timeRange: {\n type: \"string\",\n enum: [\"day\", \"week\", \"month\", \"year\"],\n description: \"Optional recency window for results.\",\n },\n language: {\n type: \"string\",\n description: \"Optional language preference (e.g. en, zh-CN, portuguese); not a strict filter.\",\n },\n depth: {\n type: \"string\",\n enum: [\"basic\", \"advanced\"],\n default: \"basic\",\n description: \"Search depth; advanced costs more and must be chosen explicitly.\",\n },\n },\n required: [\"query\"],\n additionalProperties: false,\n} as const;\n\nexport const WEB_FETCH_PARAMETERS = {\n type: \"object\",\n properties: {\n urls: {\n type: \"array\",\n items: { type: \"string\" },\n minItems: 1,\n maxItems: WEB_EXTENSION_LIMITS.fetchUrlsMax,\n description: \"1–3 public http(s) URLs to extract page content from.\",\n },\n depth: {\n type: \"string\",\n enum: [\"basic\", \"advanced\"],\n default: \"basic\",\n description: \"Extraction depth; advanced handles complex pages and must be chosen explicitly.\",\n },\n },\n required: [\"urls\"],\n additionalProperties: false,\n} as const;\n\n/* --------------------------- stable tool identity --------------------------- */\n\n/**\n * Deterministic UUID-shaped tool identity: SHA-256 over the derivation namespace, the\n * per-execution endpoint descriptor, and the actual Pi runtime tool call ID, formatted with\n * version-5/variant bits. Same endpoint + same call ID ⇒ same id (stable retry); a different\n * execution endpoint ⇒ a different id (no cross-execution artifact collision).\n */\nexport function deriveWebToolIdentity(endpointDescriptor: string, runtimeToolCallId: string): string {\n if (typeof runtimeToolCallId !== \"string\" || runtimeToolCallId.length === 0) {\n throw new Error(\"The runtime tool call id is missing\");\n }\n const hash = createHash(\"sha256\")\n .update(TOOL_ID_NAMESPACE, \"utf8\")\n .update(\"\", \"utf8\")\n .update(endpointDescriptor, \"utf8\")\n .update(\"\", \"utf8\")\n .update(runtimeToolCallId, \"utf8\")\n .digest();\n hash[6] = ((hash[6] ?? 0) & 0x0f) | 0x50;\n hash[8] = ((hash[8] ?? 0) & 0x3f) | 0x80;\n const hex = hash.subarray(0, 16).toString(\"hex\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\n/* ------------------------------ byte-safe text ------------------------------ */\n\n/** Truncate to a UTF-8 byte budget on codepoint boundaries, appending a marker when it fits. */\nexport function truncateToByteBudget(text: string, budgetBytes: number): { text: string; truncated: boolean } {\n const bytes = Buffer.byteLength(text, \"utf8\");\n if (bytes <= budgetBytes) return { text, truncated: false };\n if (budgetBytes <= 0) return { text: \"\", truncated: true };\n const markerBytes = Buffer.byteLength(TRUNCATION_MARKER, \"utf8\");\n const buffer = Buffer.from(text, \"utf8\");\n if (budgetBytes < markerBytes) {\n // The marker itself does not fit: cut content only, still codepoint-safe and bounded.\n let cut = budgetBytes;\n while (cut > 0 && ((buffer[cut] ?? 0) & 0xc0) === 0x80) cut -= 1;\n return { text: buffer.subarray(0, cut).toString(\"utf8\"), truncated: true };\n }\n let cut = budgetBytes - markerBytes;\n while (cut > 0 && ((buffer[cut] ?? 0) & 0xc0) === 0x80) cut -= 1;\n return { text: `${buffer.subarray(0, cut).toString(\"utf8\")}${TRUNCATION_MARKER}`, truncated: true };\n}\n\n/* ------------------------------ socket client ------------------------------ */\n\ninterface GatewayReply {\n status: number;\n body: Buffer;\n}\n\n/**\n * Distinguish the operation deadline from a caller abort while keeping the established wording:\n * `The web call was aborted` (never leaks a caller-provided reason), `The web call timed out` when\n * the end-to-end deadline fired. Everything downstream fails closed on either.\n */\nfunction webCallAbortError(signal?: AbortSignal): Error {\n const reason = signal?.reason;\n if (reason instanceof Error && reason.message === WEB_CALL_TIMED_OUT) return new Error(WEB_CALL_TIMED_OUT);\n return new Error(\"The web call was aborted\");\n}\n\n/**\n * End-to-end operation deadline (original 15s/45s budget): mirrors the caller abort signal and\n * additionally aborts when the remaining budget expires. The returned signal stays live through\n * the HTTP hop AND every artifact write/publication, so filesystem work can never outlive the\n * budget; callers must keep checking it after each await and dispose it in a `finally`.\n */\nfunction startOperationDeadline(\n timeoutMs: number,\n externalSignal?: AbortSignal,\n): { signal: AbortSignal; deadlineAtMs: number; dispose(): void } {\n const deadlineAtMs = Date.now() + timeoutMs;\n const controller = new AbortController();\n const onExternalAbort = () => controller.abort(externalSignal?.reason);\n if (externalSignal?.aborted) controller.abort(externalSignal.reason);\n else externalSignal?.addEventListener(\"abort\", onExternalAbort, { once: true });\n const timer = setTimeout(() => controller.abort(new Error(WEB_CALL_TIMED_OUT)), timeoutMs);\n timer.unref?.();\n return {\n signal: controller.signal,\n deadlineAtMs,\n dispose() {\n clearTimeout(timer);\n externalSignal?.removeEventListener(\"abort\", onExternalAbort);\n },\n };\n}\n\nfunction postToGateway(\n socketPath: string,\n path: string,\n payload: unknown,\n options: { deadlineAtMs: number; signal?: AbortSignal; maxResponseBytes: number },\n): Promise<GatewayReply> {\n return new Promise((resolvePromise, rejectPromise) => {\n const fail = (message: string) => {\n finish(new Error(message));\n };\n const remainingAtDispatch = options.deadlineAtMs - Date.now();\n if (options.signal?.aborted) {\n rejectPromise(webCallAbortError(options.signal));\n return;\n }\n if (remainingAtDispatch <= 0) {\n rejectPromise(new Error(\"The web call budget was exhausted before dispatch\"));\n return;\n }\n const body = Buffer.from(JSON.stringify(payload), \"utf8\");\n if (body.byteLength > WEB_EXTENSION_LIMITS.requestMaxBytes) {\n rejectPromise(new Error(\"The web request exceeds the 16 KiB request bound\"));\n return;\n }\n let settled = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let request: http.ClientRequest;\n const onAbort = () => {\n finish(webCallAbortError(options.signal));\n request.destroy();\n };\n const finish = (error?: Error, reply?: GatewayReply) => {\n if (settled) return;\n settled = true;\n if (timer) clearTimeout(timer);\n options.signal?.removeEventListener(\"abort\", onAbort);\n if (error) rejectPromise(error);\n else if (reply) resolvePromise(reply);\n };\n request = http.request(\n {\n socketPath,\n path,\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n \"content-length\": String(body.byteLength),\n accept: \"application/json\",\n // Hop metadata only: the remaining end-to-end budget, recomputed per hop.\n [WEB_EXTENSION_LIMITS.remainingHeader]: String(remainingAtDispatch),\n },\n },\n (response) => {\n const chunks: Buffer[] = [];\n let total = 0;\n response.on(\"data\", (chunk: Buffer) => {\n total += chunk.byteLength;\n if (total > options.maxResponseBytes) {\n fail(\"The web gateway response exceeds the bound\");\n request.destroy();\n return;\n }\n chunks.push(chunk);\n });\n response.on(\"end\", () =>\n finish(undefined, { status: response.statusCode ?? 500, body: Buffer.concat(chunks) }),\n );\n response.on(\"error\", () => fail(\"The web gateway response failed\"));\n },\n );\n timer = setTimeout(() => {\n fail(\"The web call timed out\");\n request.destroy();\n }, remainingAtDispatch);\n timer.unref?.();\n request.on(\"error\", (error) => {\n const code = (error as NodeJS.ErrnoException).code;\n if (options.signal?.aborted) fail(webCallAbortError(options.signal).message);\n else if (settled) return;\n else fail(`The web gateway is unavailable: ${code ?? \"unreachable\"}`);\n });\n options.signal?.addEventListener(\"abort\", onAbort, { once: true });\n request.end(body);\n });\n}\n\n/* ------------------------------ artifact store ------------------------------ */\n\nexport interface SavedArtifact {\n readonly relativePath: string;\n readonly bytes: number;\n readonly sha256: string;\n readonly artifactTruncated: boolean;\n}\n\n/**\n * Per-attempt bookkeeping for exactly the filesystem entries this attempt created. Cleanup may\n * remove only entries registered here — anything else on disk belongs to a prior or concurrent\n * attempt and is never touched.\n */\ninterface ArtifactAttemptObserver {\n /** Called before a temp file is written, with its base name inside the attempt directory. */\n onTempFile?(name: string): void;\n /** Called immediately after an attempt file is atomically published (rename completed). */\n onPublished?(name: string): void;\n}\n\n/** Create-if-missing without following symlinks, then prove the result is a real directory. */\nasync function ensureOwnedDirectory(path: string): Promise<void> {\n try {\n await mkdir(path, { mode: 0o700 });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n }\n await assertRealDirectory(path);\n}\n\nasync function assertRealDirectory(path: string): Promise<void> {\n const stat = await lstat(path);\n if (stat.isSymbolicLink() || !stat.isDirectory()) throw new Error(\"The web artifact path is not a real directory\");\n}\n\n/**\n * Open (or reuse) the artifact directory for one stable tool call id. Every parent component is\n * validated BEFORE anything is created through it, so a hostile symlink can never cause writes\n * outside the Session store; a second page under the same id reuses the verified directory.\n */\nasync function openArtifactDirectory(cwd: string, toolCallId: string, signal?: AbortSignal): Promise<string> {\n if (!TOOL_ID_PATTERN.test(toolCallId)) {\n throw new Error(\"Unsafe web artifact tool id\");\n }\n signal?.throwIfAborted();\n const root = await realpath(cwd);\n // Validate each component before creating through it (never mkdir -p across an unchecked link).\n await ensureOwnedDirectory(join(root, \".opentag\"));\n await ensureOwnedDirectory(join(root, \".opentag\", \"web\"));\n const baseReal = await realpath(join(root, \".opentag\", \"web\"));\n if (!baseReal.startsWith(`${root}${sep}`)) throw new Error(\"The web artifact store escapes the workspace\");\n const directory = join(baseReal, toolCallId);\n await ensureOwnedDirectory(directory);\n const resolvedDirectory = await realpath(directory);\n if (!resolvedDirectory.startsWith(`${baseReal}${sep}`)) {\n throw new Error(\"The web artifact directory escapes the Session store\");\n }\n signal?.throwIfAborted();\n return resolvedDirectory;\n}\n\n/**\n * Save one page: bounded content, temp file + atomic rename, post-write regular-file proof.\n * The operation deadline stays authoritative after every await (including after publication), so\n * an aborted/timed-out attempt never reports a fabricated success. The observer is notified of the\n * temp name before the first write and of the published name right after the rename, which is the\n * only ownership evidence later cleanup is allowed to trust.\n * Throws on any failure — callers must mark the artifact unsaved and never fabricate a path.\n */\nexport async function saveWebArtifact(input: {\n cwd: string;\n toolCallId: string;\n fileName: string;\n content: string;\n signal?: AbortSignal;\n observer?: ArtifactAttemptObserver;\n}): Promise<SavedArtifact> {\n if (!/^[a-z0-9][a-z0-9.-]{0,63}$/.test(input.fileName)) throw new Error(\"Unsafe web artifact file name\");\n input.signal?.throwIfAborted();\n const directory = await openArtifactDirectory(input.cwd, input.toolCallId, input.signal);\n const truncated = truncateToByteBudget(input.content, WEB_EXTENSION_LIMITS.pageBodyMaxBytes);\n const bytes = Buffer.byteLength(truncated.text, \"utf8\");\n const sha256 = createHash(\"sha256\").update(truncated.text, \"utf8\").digest(\"hex\");\n const finalPath = join(directory, input.fileName);\n // An identical finalized page is immutable content: reuse it instead of rewriting, so a retry\n // can never replace bytes that the existing finalized index already references. Oversize or\n // unsafe entries are a bounded validation failure, never an unbounded read.\n const existing = await readBoundedRegularFile(finalPath, WEB_EXTENSION_LIMITS.pageBodyMaxBytes, input.signal);\n if (existing.kind === \"oversize\" || existing.kind === \"unsafe\") {\n throw new Error(\"The existing web artifact is not a bounded regular file\");\n }\n if (existing.kind === \"regular\" && existing.bytes.length === bytes && sha256Hex(existing.bytes) === sha256) {\n return {\n relativePath: [\".opentag\", \"web\", input.toolCallId, input.fileName].join(\"/\"),\n bytes,\n sha256,\n artifactTruncated: truncated.truncated,\n };\n }\n const tempName = `.${input.fileName}.${randomSuffix()}.tmp`;\n const tempPath = join(directory, tempName);\n input.observer?.onTempFile?.(tempName);\n try {\n // The operation signal is part of the actual write: an abort can stop the write itself, and the\n // post-await checks below still catch an abort that lands after completion.\n await writeFile(tempPath, truncated.text, {\n encoding: \"utf8\",\n mode: 0o600,\n flag: \"wx\",\n signal: input.signal,\n });\n input.signal?.throwIfAborted();\n await rename(tempPath, finalPath);\n input.observer?.onPublished?.(input.fileName);\n input.signal?.throwIfAborted();\n const stat = await lstat(finalPath);\n if (!stat.isFile() || stat.isSymbolicLink()) throw new Error(\"The web artifact failed verification\");\n input.signal?.throwIfAborted();\n } catch (error) {\n await rm(tempPath, { force: true }).catch(() => undefined);\n throw error instanceof Error ? error : new Error(\"The web artifact write failed\");\n }\n return {\n relativePath: [\".opentag\", \"web\", input.toolCallId, input.fileName].join(\"/\"),\n bytes,\n sha256,\n artifactTruncated: truncated.truncated,\n };\n}\n\nfunction randomSuffix(): string {\n return createHash(\"sha256\").update(`${Date.now()}:${Math.random()}`).digest(\"hex\").slice(0, 16);\n}\n\ninterface FetchMetadataPage {\n index: number;\n url: string;\n file: string;\n bytes: number;\n sha256: string;\n artifactTruncated: boolean;\n upstreamTruncated: boolean | null;\n finalUrl: string | null;\n sourceFetchedAt: string | null;\n completeness: \"unknown\";\n}\n\ninterface FetchMetadata {\n version: 1;\n toolCallId: string;\n requestId: string;\n retrievedAt: string;\n effectiveDepth: string;\n pages: FetchMetadataPage[];\n failures: Array<{ url: string; code: string; retryable: boolean }>;\n}\n\nfunction sha256Hex(data: Uint8Array): string {\n return createHash(\"sha256\").update(data).digest(\"hex\");\n}\n\n/**\n * Read cap for the attempt index: ample for three URLs plus failure records (URLs are bounded at\n * 4 KiB each), far below any allocation risk. Page content gets its own 1 MiB cap.\n */\nconst METADATA_READ_MAX_BYTES = 64 * 1024;\n/** Fixed read chunk: the loop stops as soon as the cap is crossed, never trusting fstat size. */\nconst READ_CHUNK_BYTES = 64 * 1024;\n\ntype BoundedReadResult =\n | { readonly kind: \"absent\" }\n | { readonly kind: \"regular\"; readonly bytes: Buffer }\n | { readonly kind: \"unsafe\" }\n | { readonly kind: \"oversize\" };\n\n/** Read-only, non-following, non-blocking open flags (POSIX bits fall back to 0 where absent). */\nfunction boundedReadFlags(): number {\n const noFollow = (constants.O_NOFOLLOW as number | undefined) ?? 0;\n const nonBlocking = (constants.O_NONBLOCK as number | undefined) ?? 0;\n return constants.O_RDONLY | noFollow | nonBlocking;\n}\n\n/**\n * Bounded, symlink-safe read of a workspace-controlled entry. The handle is opened with\n * O_NOFOLLOW and O_NONBLOCK so a symlinked entry can never be followed and a FIFO can never block\n * the open waiting for a writer; fstat must prove a regular file, and the read proceeds in fixed\n * chunks until the cap is crossed — a file growing between fstat and read therefore returns\n * `oversize` instead of allocating past the cap. Callers decide whether a non-regular result is a\n * fall-through (cleanup) or a bounded unsaved failure (writes).\n */\nasync function readBoundedRegularFile(\n path: string,\n maxBytes: number,\n signal?: AbortSignal,\n): Promise<BoundedReadResult> {\n signal?.throwIfAborted();\n let handle: FileHandle;\n try {\n // O_NONBLOCK makes an open of a FIFO return immediately so fstat can reject the non-regular\n // entry instead of blocking for a writer before the deadline and the type check run; it has\n // no effect on regular-file reads.\n handle = await open(path, boundedReadFlags());\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") return { kind: \"absent\" };\n if (code === \"ELOOP\" || code === \"EMLINK\") return { kind: \"unsafe\" };\n throw error;\n }\n try {\n const stat = await handle.stat();\n if (!stat.isFile()) return { kind: \"unsafe\" };\n if (stat.size > maxBytes) return { kind: \"oversize\" };\n const chunks: Buffer[] = [];\n let total = 0;\n while (total <= maxBytes) {\n signal?.throwIfAborted();\n const chunk = Buffer.allocUnsafe(Math.min(READ_CHUNK_BYTES, maxBytes + 1 - total));\n const { bytesRead } = await handle.read(chunk, 0, chunk.length, total);\n if (bytesRead === 0) break;\n total += bytesRead;\n if (total > maxBytes) return { kind: \"oversize\" };\n chunks.push(chunk.subarray(0, bytesRead));\n }\n signal?.throwIfAborted();\n return { kind: \"regular\", bytes: Buffer.concat(chunks, total) };\n } finally {\n await handle.close().catch(() => undefined);\n }\n}\n\n/** Files a finalized index references; malformed/absent metadata references nothing. */\nfunction finalizedPagesFromBytes(bytes: Buffer): Set<string> {\n try {\n const parsed = JSON.parse(bytes.toString(\"utf8\")) as FetchMetadata;\n if (!parsed || !Array.isArray(parsed.pages)) return new Set();\n return new Set(parsed.pages.map((page) => page.file).filter((file) => typeof file === \"string\"));\n } catch {\n return new Set();\n }\n}\n\nasync function finalizedFileSet(directory: string): Promise<Set<string>> {\n const existing = await readBoundedRegularFile(join(directory, \"metadata.json\"), METADATA_READ_MAX_BYTES);\n if (existing.kind !== \"regular\") return new Set();\n return finalizedPagesFromBytes(existing.bytes);\n}\n\n/**\n * Restore a prior finalized index atomically, and only over a regular file inside the validated\n * attempt directory. Best-effort: a restore failure never masks the original operation error.\n */\nasync function restoreMetadataBytes(directory: string, content: Buffer): Promise<void> {\n const target = join(directory, \"metadata.json\");\n const stat = await lstat(target).catch(() => undefined);\n if (!stat || stat.isSymbolicLink() || !stat.isFile()) return;\n const tempPath = join(directory, `.metadata.restore.${randomSuffix()}.tmp`);\n try {\n await writeFile(tempPath, content, { mode: 0o600, flag: \"wx\" });\n await rename(tempPath, target);\n } catch {\n await rm(tempPath, { force: true }).catch(() => undefined);\n }\n}\n\n/**\n * Which page names a surviving index protects. When this attempt published its own index and no\n * prior index is restored, its pages are its own to remove; otherwise pages referenced by the\n * surviving (prior or foreign) index must never be deleted.\n */\nasync function protectionSet(\n directory: string,\n input: { publishedMetadata?: boolean; priorMetadata?: Buffer },\n): Promise<Set<string>> {\n if (input.publishedMetadata === true && input.priorMetadata !== undefined) {\n return finalizedPagesFromBytes(input.priorMetadata);\n }\n if (input.publishedMetadata === true) return new Set();\n return finalizedFileSet(directory);\n}\n\n/**\n * Decide the fate of the index this attempt published: put the prior finalized bytes back when\n * they are provably the ones we replaced, withdraw a fresh index of our own, and never clobber\n * an index that another attempt published after ours.\n */\nasync function settlePublishedMetadata(\n directory: string,\n input: { priorMetadata?: Buffer; publishedMetadataDigest?: string },\n): Promise<void> {\n const digest = input.publishedMetadataDigest;\n if (digest === undefined) {\n await removeOwnedEntry(directory, \"metadata.json\");\n return;\n }\n const current = await readBoundedRegularFile(join(directory, \"metadata.json\"), METADATA_READ_MAX_BYTES);\n if (current.kind !== \"regular\" || sha256Hex(current.bytes) !== digest) return;\n if (input.priorMetadata !== undefined) {\n await restoreMetadataBytes(directory, input.priorMetadata);\n return;\n }\n await removeOwnedEntry(directory, \"metadata.json\");\n}\n\n/** A relative entry name that can only address one file directly inside the attempt directory. */\nfunction isSafeEntryName(name: string): boolean {\n return name.length > 0 && name !== \".\" && name !== \"..\" && !name.includes(\"/\") && !name.includes(\"\\\\\");\n}\n\n/**\n * Resolve the attempt directory for cleanup without ever traversing a symlink: every component\n * (`.opentag`, `web`, the stable id) must be a real directory. A symlinked parent means this is\n * not the Session store this execution opened, so cleanup does nothing at all.\n */\nasync function resolveCleanupDirectory(cwd: string, toolCallId: string): Promise<string | undefined> {\n if (!TOOL_ID_PATTERN.test(toolCallId)) return undefined;\n let root: string;\n try {\n root = await realpath(cwd);\n } catch {\n return undefined;\n }\n const directory = join(root, \".opentag\", \"web\", toolCallId);\n for (const component of [join(root, \".opentag\"), join(root, \".opentag\", \"web\"), directory]) {\n try {\n const stat = await lstat(component);\n if (stat.isSymbolicLink() || !stat.isDirectory()) return undefined;\n } catch {\n return undefined;\n }\n }\n try {\n const resolved = await realpath(directory);\n if (!resolved.startsWith(`${root}${sep}`)) return undefined;\n return resolved;\n } catch {\n return undefined;\n }\n}\n\n/** Remove one explicitly owned entry, and only when it is still a regular file (never a link). */\nasync function removeOwnedEntry(directory: string, name: string): Promise<void> {\n const target = join(directory, name);\n try {\n const stat = await lstat(target);\n if (stat.isSymbolicLink() || !stat.isFile()) return;\n } catch {\n return;\n }\n await rm(target, { force: true }).catch(() => undefined);\n}\n\n/**\n * Aborted/failed attempt cleanup: remove ONLY the entries this attempt explicitly registered —\n * the temp file names it reported before writing and the page files it published — and never a\n * wildcard `*.tmp` sweep. Anything else in the directory (another attempt's temp/publication\n * files, earlier successful artifacts, and pages protected by a surviving index) is preserved.\n * When this attempt published an index over a prior finalized one, the prior bytes are restored\n * unless another attempt has since published its own (proven by digest); a fresh index with no\n * prior is withdrawn. The whole path is validated first, so a symlinked intermediate component\n * cannot redirect the deletion outside the Session store.\n */\nexport async function cleanupWebArtifacts(input: {\n cwd: string;\n toolCallId: string;\n preExisting: ReadonlySet<string>;\n writtenThisAttempt: ReadonlySet<string>;\n /** Temp base names this attempt created before writing; the only temp names cleanup may remove. */\n tempFiles?: ReadonlySet<string>;\n /** True when this attempt published metadata.json: its index and pages are this attempt's. */\n publishedMetadata?: boolean;\n /** Finalized index bytes this attempt replaced; restored when the attempt is withdrawn. */\n priorMetadata?: Buffer;\n /** sha256 of the index bytes this attempt published, used to prove the on-disk index is ours. */\n publishedMetadataDigest?: string;\n}): Promise<void> {\n const directory = await resolveCleanupDirectory(input.cwd, input.toolCallId);\n if (!directory) return;\n // Resolve protected page names before removing anything; a restored prior index is the survivor.\n const finalized = await protectionSet(directory, input);\n const owned = new Set<string>();\n for (const name of input.tempFiles ?? []) {\n if (isSafeEntryName(name)) owned.add(name);\n }\n for (const name of input.writtenThisAttempt) {\n if (!isSafeEntryName(name)) continue;\n if (input.preExisting.has(name)) continue;\n // A page a surviving finalized index references is not ours to delete.\n if (finalized.has(name)) continue;\n owned.add(name);\n }\n if (input.publishedMetadata) await settlePublishedMetadata(directory, input);\n await Promise.all([...owned].map((name) => removeOwnedEntry(directory, name)));\n}\n\n/**\n * Publish the attempt index. Like page writes, every await is followed by a deadline check:\n * cancellation while the temp file is being written must never rename metadata into place, and an\n * abort right after the rename is still surfaced to the caller. The caller then withdraws its own\n * index and restores the prior finalized bytes it captured before this rename.\n */\nasync function writeFetchMetadata(\n directory: string,\n serialized: string,\n signal?: AbortSignal,\n observer?: ArtifactAttemptObserver,\n): Promise<void> {\n const finalPath = join(directory, \"metadata.json\");\n const tempName = `.metadata.json.${randomSuffix()}.tmp`;\n const tempPath = join(directory, tempName);\n signal?.throwIfAborted();\n observer?.onTempFile?.(tempName);\n try {\n // The operation signal is part of the actual write; the post-await checks still catch an\n // abort that lands after the write completes.\n await writeFile(tempPath, serialized, {\n encoding: \"utf8\",\n mode: 0o600,\n flag: \"wx\",\n signal,\n });\n signal?.throwIfAborted();\n await rename(tempPath, finalPath);\n observer?.onPublished?.(\"metadata.json\");\n signal?.throwIfAborted();\n const stat = await lstat(finalPath);\n if (!stat.isFile() || stat.isSymbolicLink()) throw new Error(\"The web metadata failed verification\");\n signal?.throwIfAborted();\n } catch (error) {\n await rm(tempPath, { force: true }).catch(() => undefined);\n throw error instanceof Error ? error : new Error(\"The web metadata write failed\");\n }\n}\n\n/* ------------------------------- formatting ------------------------------- */\n\nfunction searchOutput(result: Record<string, unknown>): { text: string; truncated: boolean } {\n const results = result.results as Record<string, unknown>[];\n const lines: string[] = [\n `web_search ok — ${results.length} source(s); requestId ${String(result.requestId)}; retrievedAt ${String(\n result.retrievedAt,\n )}; depth ${String(result.effectiveDepth)}.`,\n \"Snippets are provider search snippets for source selection, not page content.\",\n ];\n results.forEach((item, index) => {\n const published = typeof item.publishedAt === \"string\" ? `; published ${item.publishedAt}` : \"\";\n lines.push(\n `\\n[${index + 1}] ${typeof item.title === \"string\" && item.title ? item.title : \"(untitled)\"}`,\n `URL: ${String(item.url)}${published}`,\n String(item.snippet ?? \"\"),\n );\n });\n return truncateToByteBudget(lines.join(\"\\n\"), WEB_EXTENSION_LIMITS.toolResultMaxBytes);\n}\n\ninterface FetchItemView {\n status: string;\n url: string;\n finalUrl: string | null;\n content?: string;\n sourceFetchedAt?: string | null;\n upstreamTruncated?: boolean | null;\n previewTruncated?: boolean;\n artifactTruncated?: boolean;\n code?: string;\n retryable?: boolean;\n}\n\ninterface FetchOutput {\n text: string;\n artifacts: SavedArtifact[];\n artifactWriteFailures: number;\n metadataWritten: boolean;\n}\n\n/** Details artifact records returned to Pi: same combined truncation flag as the metadata. */\nfunction artifactDetails(\n artifacts: SavedArtifact[],\n): Array<{ path: string; bytes: number; sha256: string; artifactTruncated: boolean }> {\n return artifacts.map((artifact) => ({\n path: artifact.relativePath,\n bytes: artifact.bytes,\n sha256: artifact.sha256,\n artifactTruncated: artifact.artifactTruncated,\n }));\n}\n\n/** Every requested URL failed: the batch is an operation failure, not a partial success. */\nfunction allFailedError(items: FetchItemView[]): Error {\n const codes = items.map((item) => `${item.url}: ${String(item.code)}${item.retryable ? \" (retryable)\" : \"\"}`);\n return new Error(`web_fetch failed for every requested URL — ${truncateToByteBudget(codes.join(\"; \"), 512).text}`);\n}\n\n/** One page's report section: metadata lines, flags, and the bounded preview. */\nfunction pageSection(index: number, item: FetchItemView, content: string, saved: SavedArtifact | undefined): string[] {\n const preview = truncateToByteBudget(content, WEB_EXTENSION_LIMITS.previewPageMaxBytes);\n const flags: string[] = [\"completeness unknown (extracted content, not raw HTML)\"];\n if (preview.truncated || item.previewTruncated === true) flags.push(\"preview truncated\");\n if (saved?.artifactTruncated || item.artifactTruncated === true) flags.push(\"stored content truncated\");\n if (item.upstreamTruncated === true) flags.push(\"upstream truncated\");\n if (item.upstreamTruncated === null || item.upstreamTruncated === undefined)\n flags.push(\"upstream truncation unknown\");\n return [\n `\\n[${index + 1}] ${item.url} — ${item.status}`,\n `finalUrl: ${item.finalUrl ?? \"unknown\"}; sourceFetchedAt: ${item.sourceFetchedAt ?? \"unknown\"}`,\n saved\n ? `saved: ${saved.relativePath} (${saved.bytes} bytes, sha256:${saved.sha256})`\n : \"saved: none (artifact write failed; content exists only in this preview)\",\n `flags: ${flags.join(\"; \")}`,\n \"preview:\",\n preview.text,\n ];\n}\n\n/** The attempt index, including the combined upstream + local truncation flag per page. */\nfunction buildFetchMetadata(\n result: Record<string, unknown>,\n toolCallId: string,\n pages: Array<{ index: number; item: FetchItemView; artifact: SavedArtifact }>,\n results: FetchItemView[],\n): FetchMetadata {\n return {\n version: 1,\n toolCallId,\n requestId: String(result.requestId),\n retrievedAt: String(result.retrievedAt),\n effectiveDepth: String(result.effectiveDepth),\n pages: pages.map((page) => ({\n index: page.index,\n url: page.item.url,\n file: page.artifact.relativePath.split(\"/\").pop() ?? \"\",\n bytes: page.artifact.bytes,\n sha256: page.artifact.sha256,\n artifactTruncated: page.artifact.artifactTruncated,\n upstreamTruncated: page.item.upstreamTruncated ?? null,\n finalUrl: page.item.finalUrl ?? null,\n sourceFetchedAt: page.item.sourceFetchedAt ?? null,\n completeness: \"unknown\",\n })),\n failures: results\n .filter((item) => item.status === \"failed\")\n .map((item) => ({ url: item.url, code: String(item.code), retryable: item.retryable === true })),\n };\n}\n\n/** Save one non-failed page with the combined truncation flag; abort always propagates. */\nasync function saveOnePage(input: {\n cwd: string;\n toolCallId: string;\n index: number;\n item: FetchItemView;\n content: string;\n signal: AbortSignal;\n observer: ArtifactAttemptObserver;\n}): Promise<{ saved?: SavedArtifact; writeFailed: boolean }> {\n try {\n const saved = await saveWebArtifact({\n cwd: input.cwd,\n toolCallId: input.toolCallId,\n fileName: `page-${input.index}.md`,\n content: input.content,\n signal: input.signal,\n observer: input.observer,\n });\n // The reported record carries the combined upstream + local truncation flag, matching the\n // metadata index; neither source of truncation may silently disappear from the details.\n return {\n saved: { ...saved, artifactTruncated: saved.artifactTruncated || input.item.artifactTruncated === true },\n writeFailed: false,\n };\n } catch (error) {\n if (input.signal.aborted) throw error;\n return { writeFailed: true };\n }\n}\n\n/**\n * Explicit per-attempt ownership ledger: only entries registered here (temp names before their\n * first write, published names after their rename, and the index this attempt published) may be\n * removed by cleanup — no wildcard tmp sweeps.\n */\ninterface AttemptLedger {\n readonly tempFiles: Set<string>;\n readonly writtenThisAttempt: Set<string>;\n readonly observer: ArtifactAttemptObserver;\n metadataPublished: boolean;\n /** sha256 of the index bytes this attempt renamed into place; undefined until then. */\n publishedMetadataDigest: string | undefined;\n /** Index bytes present immediately before this attempt's rename; restored on cancellation. */\n priorMetadata: Buffer | undefined;\n}\n\nfunction createAttemptLedger(): AttemptLedger {\n const tempFiles = new Set<string>();\n const writtenThisAttempt = new Set<string>();\n return {\n tempFiles,\n writtenThisAttempt,\n metadataPublished: false,\n publishedMetadataDigest: undefined,\n priorMetadata: undefined,\n observer: {\n onTempFile: (name) => {\n tempFiles.add(name);\n },\n onPublished: (name) => {\n writtenThisAttempt.add(name);\n },\n },\n };\n}\n\nfunction fetchHeader(result: Record<string, unknown>, results: FetchItemView[]): string {\n return `web_fetch ${String(result.status)} — ${results.length} URL result(s); requestId ${String(\n result.requestId,\n )}; retrievedAt ${String(result.retrievedAt)}; depth ${String(result.effectiveDepth)}.`;\n}\n\n/** Open the attempt store up front so pre-existing files are known before any write. */\nasync function openAttemptStore(\n cwd: string,\n toolCallId: string,\n signal: AbortSignal,\n): Promise<{ directory: string; preExisting: Set<string> }> {\n const directory = await openArtifactDirectory(cwd, toolCallId, signal);\n const preExisting = new Set(await readdir(directory).catch(() => [] as string[]));\n return { directory, preExisting };\n}\n\nfunction failedPageSection(index: number, item: FetchItemView): string {\n return `\\n[${index + 1}] ${item.url} — FAILED: ${String(item.code)}${item.retryable ? \" (retryable with a new call)\" : \"\"}`;\n}\n\n/**\n * Save every non-failed page for this attempt and build its report sections. A genuine write\n * failure is counted and reported; an abort always propagates so the caller never sees success.\n */\nasync function saveResultPages(\n results: FetchItemView[],\n toolCallId: string,\n cwd: string,\n signal: AbortSignal,\n ledger: AttemptLedger,\n): Promise<{\n artifacts: SavedArtifact[];\n pages: Array<{ index: number; item: FetchItemView; artifact: SavedArtifact }>;\n sections: string[];\n artifactWriteFailures: number;\n}> {\n const artifacts: SavedArtifact[] = [];\n const pages: Array<{ index: number; item: FetchItemView; artifact: SavedArtifact }> = [];\n const sections: string[] = [];\n let artifactWriteFailures = 0;\n for (const [index, item] of results.entries()) {\n signal.throwIfAborted();\n if (item.status === \"failed\") {\n sections.push(failedPageSection(index, item));\n continue;\n }\n const content = typeof item.content === \"string\" ? item.content : \"\";\n const { saved, writeFailed } = await saveOnePage({\n cwd,\n toolCallId,\n index,\n item,\n content,\n signal,\n observer: ledger.observer,\n });\n if (writeFailed) artifactWriteFailures += 1;\n if (saved) {\n artifacts.push(saved);\n pages.push({ index, item, artifact: saved });\n }\n sections.push(...pageSection(index, item, content, saved));\n }\n return { artifacts, pages, sections, artifactWriteFailures };\n}\n\n/**\n * Publish the attempt index. A cancellation during the temp write is rethrown (never a degraded\n * success); only a genuine non-abort write failure returns `failed: true`.\n */\nasync function publishAttemptMetadata(\n directory: string,\n result: Record<string, unknown>,\n toolCallId: string,\n pages: Array<{ index: number; item: FetchItemView; artifact: SavedArtifact }>,\n results: FetchItemView[],\n signal: AbortSignal,\n ledger: AttemptLedger,\n): Promise<{ written: boolean; failed: boolean }> {\n const metadata = buildFetchMetadata(result, toolCallId, pages, results);\n const serialized = `${JSON.stringify(metadata, undefined, 2)}\\n`;\n const digest = sha256Hex(Buffer.from(serialized, \"utf8\"));\n // Capture the index this rename replaces so a cancelled retry can put the prior index back. An\n // unreadable (unsafe/oversize) existing index fails the attempt before any rename: we never\n // overwrite an index that could not have been preserved.\n const prior = await readBoundedRegularFile(join(directory, \"metadata.json\"), METADATA_READ_MAX_BYTES, signal);\n if (prior.kind === \"oversize\" || prior.kind === \"unsafe\") {\n throw new Error(\"The existing web metadata is not a bounded regular file\");\n }\n ledger.priorMetadata = prior.kind === \"regular\" ? prior.bytes : undefined;\n try {\n await writeFetchMetadata(directory, serialized, signal, {\n onTempFile: (name) => {\n ledger.tempFiles.add(name);\n },\n onPublished: () => {\n ledger.metadataPublished = true;\n ledger.publishedMetadataDigest = digest;\n },\n });\n return { written: true, failed: false };\n } catch (error) {\n if (signal.aborted) throw error;\n return { written: false, failed: true };\n }\n}\n\nasync function cleanupAttempt(\n cwd: string,\n toolCallId: string,\n preExisting: ReadonlySet<string>,\n ledger: AttemptLedger,\n): Promise<void> {\n await cleanupWebArtifacts({\n cwd,\n toolCallId,\n preExisting,\n writtenThisAttempt: ledger.writtenThisAttempt,\n tempFiles: ledger.tempFiles,\n publishedMetadata: ledger.metadataPublished,\n priorMetadata: ledger.priorMetadata,\n publishedMetadataDigest: ledger.publishedMetadataDigest,\n });\n}\n\n/** One final truncation covers framing AND suffix: the total never exceeds the tool bound. */\nfunction assembleFetchText(sections: string[], artifactWriteFailures: number): string {\n const suffix =\n artifactWriteFailures > 0\n ? `\\n${artifactWriteFailures} artifact write(s) failed; no fabricated paths are reported.`\n : \"\";\n return truncateToByteBudget(`${sections.join(\"\\n\")}${suffix}`, WEB_EXTENSION_LIMITS.toolResultMaxBytes).text;\n}\n\nasync function fetchOutput(\n result: Record<string, unknown>,\n toolCallId: string,\n cwd: string,\n signal: AbortSignal,\n): Promise<FetchOutput> {\n const results = result.results as FetchItemView[];\n const ledger = createAttemptLedger();\n const sections: string[] = [fetchHeader(result, results)];\n let directory: string | undefined;\n let preExisting = new Set<string>();\n try {\n signal.throwIfAborted();\n if (results.some((item) => item.status !== \"failed\")) {\n const store = await openAttemptStore(cwd, toolCallId, signal);\n directory = store.directory;\n preExisting = store.preExisting;\n }\n const pageOutcome = await saveResultPages(results, toolCallId, cwd, signal, ledger);\n sections.push(...pageOutcome.sections);\n signal.throwIfAborted();\n let artifactWriteFailures = pageOutcome.artifactWriteFailures;\n let metadataWritten = false;\n if (directory && pageOutcome.pages.length > 0) {\n const published = await publishAttemptMetadata(\n directory,\n result,\n toolCallId,\n pageOutcome.pages,\n results,\n signal,\n ledger,\n );\n metadataWritten = published.written;\n if (published.written) sections.push(`\\nmetadata: ${\".opentag\"}/web/${toolCallId}/metadata.json`);\n else if (published.failed) {\n artifactWriteFailures += 1;\n sections.push(\"\\nmetadata: write failed (page files above remain saved)\");\n }\n }\n // Last check before reporting success: an abort that landed during the final publication still\n // fails the tool result and lets cleanup withdraw this attempt's metadata/pages.\n signal.throwIfAborted();\n return {\n text: assembleFetchText(sections, artifactWriteFailures),\n artifacts: pageOutcome.artifacts,\n artifactWriteFailures,\n metadataWritten,\n };\n } catch (error) {\n await cleanupAttempt(cwd, toolCallId, preExisting, ledger);\n throw error;\n }\n}\n\n/* ------------------------------ tool execution ----------------------------- */\n\nfunction errorEnvelope(body: Buffer): { code: string; retryable: boolean } | undefined {\n try {\n const parsed = JSON.parse(body.toString(\"utf8\")) as { error?: { code?: unknown; retryable?: unknown } };\n if (typeof parsed?.error?.code !== \"string\") return undefined;\n return { code: parsed.error.code, retryable: parsed.error.retryable === true };\n } catch {\n return undefined;\n }\n}\n\nfunction boundedString(value: unknown, maxBytes: number): value is string {\n return typeof value === \"string\" && Buffer.byteLength(value, \"utf8\") <= maxBytes;\n}\n\nconst INVALID_WIRE_PAYLOAD = \"The web gateway returned an invalid payload\";\n\nfunction invalidWirePayload(): Error {\n return new Error(INVALID_WIRE_PAYLOAD);\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction requireBoundedString(value: unknown, maxBytes: number): string {\n if (!boundedString(value, maxBytes)) throw invalidWirePayload();\n return value;\n}\n\nfunction requireNonEmptyBoundedString(value: unknown, maxBytes: number): string {\n const text = requireBoundedString(value, maxBytes);\n if (text.length === 0) throw invalidWirePayload();\n return text;\n}\n\n/** Shared envelope: request id, retrieval time, depth, and the results array shape. */\nfunction assertWireEnvelope(record: Record<string, unknown>): void {\n requireNonEmptyBoundedString(record.requestId, 128);\n requireBoundedString(record.retrievedAt, 64);\n if (record.effectiveDepth !== \"basic\" && record.effectiveDepth !== \"advanced\") throw invalidWirePayload();\n if (!Array.isArray(record.results)) throw invalidWirePayload();\n}\n\nfunction assertSearchWireItem(value: unknown): void {\n if (!isPlainRecord(value)) throw invalidWirePayload();\n requireNonEmptyBoundedString(value.sourceId, 128);\n requireBoundedString(value.title, 2048);\n requireNonEmptyBoundedString(value.url, WEB_EXTENSION_LIMITS.urlMaxBytes);\n requireBoundedString(value.snippet, 8192);\n if (value.publishedAt !== undefined && !boundedString(value.publishedAt, 128)) throw invalidWirePayload();\n}\n\nfunction assertFailedFetchWireItem(item: Record<string, unknown>): void {\n requireNonEmptyBoundedString(item.code, 64);\n if (typeof item.retryable !== \"boolean\") throw invalidWirePayload();\n}\n\nfunction assertContentFetchWireItem(item: Record<string, unknown>): void {\n if (item.status !== \"ok\" && item.status !== \"partial\") throw invalidWirePayload();\n if (item.contentKind !== \"extracted\" || item.completeness !== \"unknown\") throw invalidWirePayload();\n requireBoundedString(item.content, WEB_EXTENSION_LIMITS.pageBodyMaxBytes);\n if (item.finalUrl !== null && !boundedString(item.finalUrl, WEB_EXTENSION_LIMITS.urlMaxBytes)) {\n throw invalidWirePayload();\n }\n if (item.sourceFetchedAt !== null && !boundedString(item.sourceFetchedAt, 64)) throw invalidWirePayload();\n if (item.upstreamTruncated !== null && typeof item.upstreamTruncated !== \"boolean\") throw invalidWirePayload();\n if (typeof item.previewTruncated !== \"boolean\" || typeof item.artifactTruncated !== \"boolean\") {\n throw invalidWirePayload();\n }\n}\n\nfunction assertFetchWireItem(value: unknown): void {\n if (!isPlainRecord(value)) throw invalidWirePayload();\n requireNonEmptyBoundedString(value.url, WEB_EXTENSION_LIMITS.urlMaxBytes);\n if (value.status === \"failed\") {\n assertFailedFetchWireItem(value);\n return;\n }\n assertContentFetchWireItem(value);\n}\n\nfunction assertSearchWireResult(record: Record<string, unknown>, results: unknown[]): void {\n if (record.status !== \"ok\") throw invalidWirePayload();\n if (results.length > 25) throw invalidWirePayload();\n for (const item of results) assertSearchWireItem(item);\n}\n\nfunction assertFetchWireResult(record: Record<string, unknown>, results: unknown[]): void {\n if (![\"ok\", \"partial\", \"failed\"].includes(String(record.status))) throw invalidWirePayload();\n if (results.length > WEB_EXTENSION_LIMITS.fetchUrlsMax) throw invalidWirePayload();\n for (const item of results) assertFetchWireItem(item);\n}\n\n/**\n * Defensive wire validation (the trusted gateway/client hold the authoritative shared schemas).\n * Enough shape and bounds are checked here to prevent mislabeled or synthetic successes.\n */\nfunction assertWireResult(value: unknown, kind: \"search\" | \"fetch\"): Record<string, unknown> {\n if (!isPlainRecord(value)) throw invalidWirePayload();\n assertWireEnvelope(value);\n const results = value.results as unknown[];\n if (kind === \"search\") assertSearchWireResult(value, results);\n else assertFetchWireResult(value, results);\n return value;\n}\n\nfunction normalizeUrl(raw: string): string | undefined {\n try {\n const url = new URL(raw);\n url.hash = \"\";\n if ((url.protocol === \"http:\" && url.port === \"80\") || (url.protocol === \"https:\" && url.port === \"443\")) {\n url.port = \"\";\n }\n return url.toString();\n } catch {\n return undefined;\n }\n}\n\n/**\n * Results must correspond exactly to the requested URLs after documented normalization/dedup\n * (fragment removed, host case/default ports normalized): every item matches one unclaimed\n * request, none are missing, and duplicates never borrow another URL's content.\n */\nfunction requestedUrlsMatch(urls: string[], results: FetchItemView[]): void {\n const expected = new Map<string, number>();\n for (const url of urls) {\n const normalized = normalizeUrl(url);\n if (normalized) expected.set(normalized, (expected.get(normalized) ?? 0) + 1);\n }\n const seen = new Set<string>();\n for (const item of results) {\n const normalized = normalizeUrl(item.url);\n if (!normalized || !expected.has(normalized) || seen.has(normalized)) {\n throw new Error(\"The web gateway returned a result for an unrequested or duplicated URL\");\n }\n seen.add(normalized);\n }\n if (seen.size !== expected.size) {\n throw new Error(\"The web gateway omitted a requested URL from the results\");\n }\n}\n\ntype GatewayOperation = \"web_search\" | \"web_fetch\";\n\n/** Validate the HTTP status and parse a successful JSON reply; failures stay bounded/redacted. */\nfunction requireWebSuccessReply(operation: GatewayOperation, reply: GatewayReply): Record<string, unknown> {\n if (reply.status !== 200) {\n const failure = errorEnvelope(reply.body);\n throw new Error(\n `${operation} failed: ${failure?.code ?? `http_${reply.status}`}${failure?.retryable ? \" (retryable)\" : \"\"}`,\n );\n }\n return JSON.parse(reply.body.toString(\"utf8\")) as Record<string, unknown>;\n}\n\ninterface SearchToolParams {\n query: string;\n limit?: number;\n domains?: string[];\n timeRange?: string;\n language?: string;\n depth?: string;\n}\n\n/** Optional search fields are included only when the caller supplied them. */\nfunction searchRequestBody(toolCallId: string, params: SearchToolParams): Record<string, unknown> {\n return {\n protocolVersion: WEB_EXTENSION_LIMITS.protocolVersion,\n toolCallId,\n query: params.query,\n ...(params.limit !== undefined ? { limit: params.limit } : {}),\n ...(params.domains ? { domains: params.domains } : {}),\n ...(params.timeRange ? { timeRange: params.timeRange } : {}),\n ...(params.language ? { language: params.language } : {}),\n depth: params.depth === \"advanced\" ? \"advanced\" : \"basic\",\n };\n}\n\ninterface ExtensionTool {\n name: string;\n label: string;\n description: string;\n parameters: unknown;\n execute(\n toolCallId: string,\n params: never,\n signal: AbortSignal | undefined,\n onUpdate: unknown,\n ctx: { cwd: string },\n ): Promise<{ content: { type: \"text\"; text: string }[]; details: unknown }>;\n}\n\nexport default function openTagWebTools(pi: { registerTool(tool: ExtensionTool): void }): void {\n const socketPath = process.env[ENDPOINT_ENV];\n // No endpoint descriptor: the trusted gateway is absent, so the tools must not register.\n if (!socketPath) return;\n\n pi.registerTool({\n name: \"web_search\",\n label: \"Web Search\",\n description:\n \"Search the public web through OpenTag's managed provider. Returns bounded source snippets for selection; \" +\n \"use web_fetch on chosen URLs for page content. depth=advanced costs more and must be chosen explicitly. \" +\n \"No automatic retries; a failed call may be retried as a new explicit call when marked retryable.\",\n parameters: WEB_SEARCH_PARAMETERS,\n async execute(runtimeToolCallId, rawParams, signal, _onUpdate, _ctx) {\n const params = rawParams as SearchToolParams;\n const toolCallId = deriveWebToolIdentity(socketPath, runtimeToolCallId);\n // The original 15s budget stays authoritative for the HTTP hop AND all formatting: the\n // deadline signal fires even if the gateway reply is already in flight.\n const deadline = startOperationDeadline(WEB_EXTENSION_LIMITS.searchTimeoutMs, signal);\n try {\n const reply = await postToGateway(socketPath, \"/web/search\", searchRequestBody(toolCallId, params), {\n deadlineAtMs: deadline.deadlineAtMs,\n signal: deadline.signal,\n maxResponseBytes: WEB_EXTENSION_LIMITS.searchResponseMaxBytes,\n });\n deadline.signal.throwIfAborted();\n const result = assertWireResult(requireWebSuccessReply(\"web_search\", reply), \"search\");\n const output = searchOutput(result);\n deadline.signal.throwIfAborted();\n return {\n content: [{ type: \"text\" as const, text: output.text }],\n details: {\n requestId: String(result.requestId),\n status: \"ok\",\n sourceCount: (result.results as unknown[]).length,\n truncated: output.truncated,\n },\n };\n } finally {\n deadline.dispose();\n }\n },\n });\n\n pi.registerTool({\n name: \"web_fetch\",\n label: \"Web Fetch\",\n description:\n \"Extract page content for 1–3 public http(s) URLs through OpenTag's managed provider. Content is the \" +\n \"provider's extraction (not raw HTML; completeness unknown). Full pages are saved under the Session \" +\n \"workspace .opentag/web/ with sha256 metadata; previews are bounded. HTTP 200 can still contain per-URL \" +\n \"failures — read every item. depth=advanced costs more and must be chosen explicitly.\",\n parameters: WEB_FETCH_PARAMETERS,\n async execute(runtimeToolCallId, rawParams, signal, _onUpdate, ctx) {\n const params = rawParams as { urls: string[]; depth?: string };\n const toolCallId = deriveWebToolIdentity(socketPath, runtimeToolCallId);\n // The original 45s budget covers the HTTP hop AND every artifact write/publication: the\n // deadline signal remains live after the reply and is the only signal fetchOutput trusts.\n const deadline = startOperationDeadline(WEB_EXTENSION_LIMITS.fetchTimeoutMs, signal);\n try {\n const reply = await postToGateway(\n socketPath,\n \"/web/fetch\",\n {\n protocolVersion: WEB_EXTENSION_LIMITS.protocolVersion,\n toolCallId,\n urls: params.urls,\n depth: params.depth === \"advanced\" ? \"advanced\" : \"basic\",\n },\n {\n deadlineAtMs: deadline.deadlineAtMs,\n signal: deadline.signal,\n maxResponseBytes: WEB_EXTENSION_LIMITS.fetchResponseMaxBytes,\n },\n );\n deadline.signal.throwIfAborted();\n const result = assertWireResult(requireWebSuccessReply(\"web_fetch\", reply), \"fetch\");\n const items = result.results as FetchItemView[];\n requestedUrlsMatch(params.urls, items);\n if (items.length === 0 || items.every((item) => item.status === \"failed\")) {\n // An all-failed batch is an operation failure: Pi must record toolResult.isError=true.\n throw allFailedError(items);\n }\n const output = await fetchOutput(result, toolCallId, ctx.cwd, deadline.signal);\n // A late cancellation/deadline must never resolve a successful tool result.\n deadline.signal.throwIfAborted();\n return {\n content: [{ type: \"text\" as const, text: output.text }],\n details: {\n requestId: String(result.requestId),\n status: String(result.status),\n artifacts: artifactDetails(output.artifacts),\n artifactWriteFailures: output.artifactWriteFailures,\n metadataWritten: output.metadataWritten,\n },\n };\n } finally {\n deadline.dispose();\n }\n },\n });\n}\n"],"mappings":"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"}
|