{"version":3,"file":"encoding.mjs","names":[],"sources":["../../src/batteries/encoding/index.ts"],"sourcesContent":["/**\n * Opt-in serialization battery: make the ADK primitives round-trip through `@nhtio/encoder`.\n *\n * @module @nhtio/adk/batteries/encoding\n *\n * @remarks\n * The ADK primitives already carry the `@nhtio/encoder` custom-class contract — they implement the\n * `Symbol.for('@nhtio/encoder:toEncoded' | ':fromEncoded')` methods with **zero dependency** on the\n * encoder. That half works whether or not `@nhtio/encoder` is installed. This battery wires up the\n * *other* half — the part that genuinely needs the encoder — and nothing else imports it:\n *\n * 1. `registerAdkEncodables()` — tells the decoder how to map each `custom:<ClassName>` wire tag back to\n *    its constructor. Call it **once, before your first `decode()`**. Without it, `encode()` still works\n *    but `decode()` throws on every ADK primitive.\n * 2. Auto-registers the **in-memory** and **fetch** reader resolvers (they carry no live binding, so they\n *    need nothing from you). Durable-store resolvers — flydrive `Disk`, OPFS root — you register yourself\n *    with {@link registerSpoolReaderResolver} / {@link registerMediaReaderResolver}, because only you hold\n *    the live binding the serialised locator cannot carry.\n *\n * ::: what this battery does NOT do\n * It serialises the **conversation graph**, not your storage. It does not persist anything — it turns a\n * live object tree into a string and back. It does not conjure live bindings: a reader handle decodes to\n * a working reader only if you registered a resolver for its tag. It does not make closures portable: a\n * {@link @nhtio/adk!Tool} handler serialises by source text only (see {@link @nhtio/adk!Tool}). And it\n * cannot serialise a {@link @nhtio/adk!TurnGate} — a live pending Promise has no serialised form.\n * :::\n *\n * @example Register once at startup, then encode/decode freely.\n * ```typescript\n * import { encode, decode } from '@nhtio/encoder'\n * import { registerAdkEncodables } from '@nhtio/adk/batteries/encoding'\n *\n * registerAdkEncodables()\n *\n * const wire = encode(message)            // a Message with nested Identity / Tokenizable / Media\n * const restored = decode<Message>(wire)  // instanceof Message, nested primitives intact\n * ```\n *\n * @example Durable-store media/artifacts need a resolver carrying the live binding.\n * ```typescript\n * import { Disk } from 'flydrive'\n * import { FlydriveSpoolReader } from '@nhtio/adk/batteries/storage/flydrive'\n * import { registerAdkEncodables, registerSpoolReaderResolver } from '@nhtio/adk/batteries/encoding'\n *\n * registerAdkEncodables()\n * const disk = new Disk(myDriver)\n * registerSpoolReaderResolver('spool:flydrive', (locator) => {\n *   const { key, streamThresholdBytes } = locator as { key: string; streamThresholdBytes?: number }\n *   return new FlydriveSpoolReader(disk, key, { streamThresholdBytes })\n * })\n * ```\n */\n\nimport { registerClass } from '@nhtio/encoder'\nimport {\n  InMemorySpoolReader,\n  SPOOL_READER_TAG_IN_MEMORY,\n} from '@nhtio/adk/batteries/storage/in_memory'\nimport {\n  registerMediaReaderResolver,\n  registerSpoolReaderResolver,\n  inMemoryMediaReader,\n  fromFetch,\n  decodeBase64,\n  MEDIA_READER_TAG_IN_MEMORY,\n  MEDIA_READER_TAG_FETCH,\n} from '@nhtio/adk/common'\nimport {\n  Tokenizable,\n  Registry,\n  Identity,\n  Memory,\n  Message,\n  Retrievable,\n  Thought,\n  ToolCall,\n  Tool,\n  ArtifactTool,\n  ToolRegistry,\n  SpooledArtifact,\n  SpooledJsonArtifact,\n  SpooledMarkdownArtifact,\n  Media,\n} from '@nhtio/adk'\nimport type { LocatorValue } from '@nhtio/adk/common'\n\n/**\n * Re-export the resolver-registration functions so consumers wire durable-store bindings from one place.\n */\nexport {\n  registerMediaReaderResolver,\n  registerSpoolReaderResolver,\n  resolveMediaReader,\n  resolveSpoolReader,\n} from '@nhtio/adk/common'\nexport type {\n  ReaderDescriptor,\n  LocatorValue,\n  MediaReaderResolver,\n  SpoolReaderResolver,\n} from '@nhtio/adk/common'\n\n/**\n * Every ADK primitive that opts in to the `@nhtio/encoder` custom-class contract.\n *\n * @remarks\n * `TurnGate` is intentionally absent — it wraps a live pending Promise and `AbortController` that cannot\n * survive serialisation. Each entry must expose a static `[DECODE_METHOD]`, which `registerClass`\n * requires.\n */\nconst ENCODABLE_CLASSES = [\n  Tokenizable,\n  Registry,\n  Identity,\n  Memory,\n  Message,\n  Retrievable,\n  Thought,\n  ToolCall,\n  Tool,\n  ArtifactTool,\n  ToolRegistry,\n  SpooledArtifact,\n  SpooledJsonArtifact,\n  SpooledMarkdownArtifact,\n  Media,\n] as const\n\nlet autoResolversRegistered = false\n\n/**\n * Auto-register the resolvers that need no live binding: in-memory (bytes inlined in the locator) and\n * fetch (URL re-issued on read). Idempotent.\n */\nconst registerBindingFreeResolvers = (): void => {\n  if (autoResolversRegistered) return\n  autoResolversRegistered = true\n\n  registerMediaReaderResolver(MEDIA_READER_TAG_IN_MEMORY, (locator: LocatorValue) => {\n    const { bytesBase64 } = locator as { bytesBase64: string }\n    return inMemoryMediaReader(decodeBase64(bytesBase64))\n  })\n\n  registerMediaReaderResolver(MEDIA_READER_TAG_FETCH, (locator: LocatorValue) => {\n    const { url, init } = locator as unknown as { url: string; init?: RequestInit }\n    return fromFetch(url, init)\n  })\n\n  registerSpoolReaderResolver(SPOOL_READER_TAG_IN_MEMORY, (locator: LocatorValue) => {\n    const { content } = locator as { content: string }\n    return new InMemorySpoolReader(content)\n  })\n}\n\n/**\n * Register every ADK primitive with the `@nhtio/encoder` decoder, and auto-register the binding-free\n * reader resolvers (in-memory, fetch).\n *\n * @remarks\n * Idempotent — safe to call more than once (re-registering a class is a no-op overwrite). Call it once at\n * application startup, before the first `decode()`. Encoding never needs this; only decoding does,\n * because the decoder must map a `custom:<ClassName>` tag back to a constructor.\n *\n * Durable-store reader resolvers (flydrive, OPFS) are NOT registered here — they need the live `Disk` /\n * OPFS root only you hold. Register them yourself with {@link registerSpoolReaderResolver}.\n */\nexport const registerAdkEncodables = (): void => {\n  for (const ctor of ENCODABLE_CLASSES) {\n    // The ADK primitives implement the contract via raw `Symbol.for()` keys (zero-dep \"Option B\"), so\n    // their static `[DECODE_METHOD]` is keyed by a symbol nominally distinct from the encoder's own\n    // `unique symbol` — identical at runtime (`Symbol.for('@nhtio/encoder:fromEncoded')`), but not\n    // structurally assignable to `DecodableConstructor` at the type level. The cast bridges that gap.\n    registerClass(ctor as unknown as Parameters<typeof registerClass>[0])\n  }\n  registerBindingFreeResolvers()\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8GA,IAAM,oBAAoB;CACxB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,IAAI,0BAA0B;;;;;AAM9B,IAAM,qCAA2C;CAC/C,IAAI,yBAAyB;CAC7B,0BAA0B;CAE1B,4BAA4B,6BAA6B,YAA0B;EACjF,MAAM,EAAE,gBAAgB;EACxB,OAAO,oBAAoB,aAAa,WAAW,CAAC;CACtD,CAAC;CAED,4BAA4B,yBAAyB,YAA0B;EAC7E,MAAM,EAAE,KAAK,SAAS;EACtB,OAAO,UAAU,KAAK,IAAI;CAC5B,CAAC;CAED,4BAA4B,6BAA6B,YAA0B;EACjF,MAAM,EAAE,YAAY;EACpB,OAAO,IAAI,oBAAoB,OAAO;CACxC,CAAC;AACH;;;;;;;;;;;;;AAcA,IAAa,8BAAoC;CAC/C,KAAK,MAAM,QAAQ,mBAKjB,cAAc,IAAsD;CAEtE,6BAA6B;AAC/B"}