UNPKG

@tanstack/ai-sandbox

Version:

Provider-agnostic sandbox layer for TanStack AI — run harness adapters inside isolated sandboxes (defineSandbox, defineWorkspace, withSandbox) with a uniform SandboxHandle, workspace bootstrap, policy, and resumable lifecycle.

102 lines (101 loc) 2.95 kB
import { EventType, withTanstackMetadata } from "@tanstack/ai"; //#region src/bridge-events.ts /** * Helpers that let a harness adapter surface custom events emitted by BRIDGED * tools (via {@link ToolBridgeCoreOptions.emitCustomEvent}) on its live output * stream. * * The bridge runs out-of-band from the harness's own event stream, so a bridged * tool's progress/console events have no path to the client on their own. An * adapter creates a {@link BridgeEventChannel}, hands its `emitCustomEvent` to * the bridge provisioner, and {@link mergeChunkStreams | merges} the channel's * stream into its translated output — so events interleave live while the agent * runs (e.g. code mode's `code_mode:console` logs during a long execution). */ /** Create a channel whose emitted events become CUSTOM {@link StreamChunk}s. */ function createBridgeEventChannel(meta) { const buffer = []; let notify = null; let closed = false; async function* stream() { for (;;) { const next = buffer.shift(); if (next !== void 0) { yield next; continue; } if (closed) return; await new Promise((resolve) => { notify = resolve; }); notify = null; } } return { emitCustomEvent(eventName, value) { if (closed) return; buffer.push(withTanstackMetadata({ type: EventType.CUSTOM, name: eventName, value, timestamp: Date.now() }, { model: meta.model, ...meta.threadId !== void 0 ? { threadId: meta.threadId } : {}, ...meta.runId !== void 0 ? { runId: meta.runId } : {} })); notify?.(); }, close() { closed = true; notify?.(); }, stream: stream() }; } /** * Merge a `side` chunk stream into a `base` chunk stream, yielding from whichever * settles first. Terminates when `base` ends (the run is over), then releases the * side iterator — so a never-ending channel (until closed) doesn't hang the merge. */ async function* mergeChunkStreams(base, side) { const baseIt = base[Symbol.asyncIterator](); const sideIt = side[Symbol.asyncIterator](); let baseNext = baseIt.next().then((r) => ({ from: "base", r })); let sideNext = sideIt.next().then((r) => ({ from: "side", r })); let sideLive = true; try { for (;;) { const winner = await Promise.race(sideLive ? [baseNext, sideNext] : [baseNext]); if (winner.from === "base") { if (winner.r.done) return; yield winner.r.value; baseNext = baseIt.next().then((r) => ({ from: "base", r })); } else if (winner.r.done) sideLive = false; else { yield winner.r.value; sideNext = sideIt.next().then((r) => ({ from: "side", r })); } } } finally { const baseReturn = baseIt.return?.(void 0); if (baseReturn) baseReturn.catch(() => {}); const sideReturn = sideIt.return?.(void 0); if (sideReturn) sideReturn.catch(() => {}); } } //#endregion export { createBridgeEventChannel, mergeChunkStreams }; //# sourceMappingURL=bridge-events.js.map