{"version":3,"file":"toon-BSTQnHed.mjs","names":["#options","#resolveParsed","#hasParsed","#parsed","#loadToonModule","#formatToonError"],"sources":["../src/batteries/artifacts/toon/exceptions.ts","../src/batteries/artifacts/toon/index.ts"],"sourcesContent":["/**\n * Battery-scoped exceptions for the TOON artifact battery.\n *\n * @remarks\n * Internal sibling of the `@nhtio/adk/batteries/artifacts/toon` entry — re-exported from the\n * battery's own barrel per the battery-scoped-exceptions rule. These are the typed errors the\n * implementor-facing API throws; the agent-facing forge catches them and renders readable\n * failure strings the model can act on.\n */\n\nimport { createException } from '@nhtio/adk/factories'\n\n/**\n * Thrown when the TOON peer dependency cannot be loaded.\n *\n * @remarks\n * The @toon-format/toon package is an optional peer. If it is not installed, methods requiring\n * it throw this exception with installation instructions. The message template includes the package\n * name, a description of its purpose, the underlying error, and the exact install command, so the\n * consumer sees a complete, actionable message regardless of call site.\n */\nexport const E_TOON_PEER_MISSING = createException<[string]>(\n  'E_TOON_PEER_MISSING',\n  'the toon battery could not load its peer dependency \"@toon-format/toon\" (needed for TOON format artifact queries): %s — install it (pnpm add @toon-format/toon)',\n  'E_TOON_PEER_MISSING',\n  500\n)\n\n/**\n * Thrown when TOON decoding fails.\n *\n * @remarks\n * Wraps the underlying `ToonDecodeError` from the `@toon-format/toon` package, surfacing its\n * `line` and `source` properties so the model can self-correct.\n */\nexport const E_TOON_DECODE_FAILED = createException<[string]>(\n  'E_TOON_DECODE_FAILED',\n  'TOON decode error: %s',\n  'E_TOON_DECODE_FAILED',\n  422\n)\n","/**\n * TOON artifact battery — structured queries over TOON format artifacts.\n *\n * @module @nhtio/adk/batteries/artifacts/toon\n *\n * @remarks\n * Adds {@link SpooledToonArtifact}, a {@link @nhtio/adk!SpooledArtifact} specialisation for\n * structured TOON queries. TOON encodes the JSON data model in a compact text format; parsing\n * produces the same values as decoding JSON, so queries use {@link https://github.com/JSONPath-Plus/JSONPath JSONPath-Plus} for path navigation, exactly as {@link @nhtio/adk!SpooledJsonArtifact} does.\n *\n * Requires the optional peer `@toon-format/toon` (version `^4.1.1`). If it is not installed,\n * methods requiring it throw {@link E_TOON_PEER_MISSING} with installation instructions.\n *\n * Export note: {@link registerArtifactEncodables} must be called before any attempt to\n * `decode()` a spooled TOON artifact. Call it once at startup:\n *\n * ```ts\n * import { registerArtifactEncodables } from '@nhtio/adk/batteries/artifacts'\n * await registerArtifactEncodables()\n * ```\n */\n\nimport { v6 as uuidv6 } from 'uuid'\nimport { JSONPath } from 'jsonpath-plus'\nimport { validator } from '@nhtio/validation'\nimport { resolveSpoolReader } from '@nhtio/adk/common'\nimport { isInstanceOf, isObject } from '@nhtio/adk/guards'\nimport { E_TOON_PEER_MISSING, E_TOON_DECODE_FAILED } from './exceptions'\nimport {\n  ArtifactTool,\n  SpooledJsonArtifact,\n  Tool,\n  ToolRegistry,\n  ReaderDescriptor,\n} from '@nhtio/adk/common'\n\n// Well-known @nhtio/encoder contract keys, resolved through the global symbol registry.\n// These are identical to the symbols core uses, with no import edge on the optional peer.\nconst ENCODE_METHOD: unique symbol = Symbol.for('@nhtio/encoder:toEncoded')\nconst DECODE_METHOD: unique symbol = Symbol.for('@nhtio/encoder:fromEncoded')\n\n/**\n * Lazily-loaded TOON module, cached as a module-level promise.\n *\n * @remarks\n * Shared across all instances of SpooledToonArtifact. Once the promise resolves or rejects,\n * the same promise is reused for all subsequent calls, ensuring the in-flight promise and\n * any error state are preserved globally.\n */\nlet toonModulePromise: Promise<Record<string, unknown>> | undefined\nimport {\n  SpooledArtifact,\n  collectArtifactCompatibleIds,\n  resolveArtifactById,\n  defaultSerialise,\n} from '@nhtio/adk/spooled_artifact'\nimport type { SpoolReader, ToolMethodDescriptor, DispatchContext } from '@nhtio/adk/types'\n\n/** Snapshot payload for the encoder contract; the encoder treats it as opaque. */\ntype AdkEncodableSnapshot = unknown\n\n/**\n * TOON decode options.\n *\n * @remarks\n * Passed to the `@toon-format/toon` `decode` function.\n */\nexport interface ToonDecodeOptions {\n  /** The indent size used in the TOON encoding (default: 2). */\n  indentSize?: number\n  /** When `true`, reject non-standard TOON (default: `true`). */\n  strict?: boolean\n}\n\n/**\n * A {@link @nhtio/adk!SpooledArtifact} specialisation that adds TOON-aware read operations.\n *\n * @remarks\n * Construct with an optional `options` object to control decoding. When omitted, the TOON is\n * decoded with `strict: true` (default). Once decoded (on first access), the parsed value is\n * cached for the lifetime of the instance.\n *\n * All TOON methods are async, consistent with {@link @nhtio/adk!SpooledArtifact}.\n *\n * Path-based methods (`toon_get`, `toon_filter`, `toon_pluck`) use\n * [JSONPath-Plus](https://github.com/JSONPath-Plus/JSONPath) expressions. Full JSONPath syntax\n * is supported, including recursive descent (`..`), filter expressions (`[?(@.age > 18)]`),\n * and union selectors.\n */\nexport class SpooledToonArtifact extends SpooledArtifact {\n  #parsed: unknown\n  #hasParsed: boolean = false\n  #options: ToonDecodeOptions | undefined\n\n  /**\n   * @param reader - The backing store to read from.\n   * @param options - Optional TOON decode options.\n   */\n  constructor(reader: SpoolReader, options?: ToonDecodeOptions) {\n    super(reader)\n    this.#options = options\n  }\n\n  /**\n   * Returns `true` if `value` is a {@link SpooledToonArtifact} instance.\n   *\n   * @remarks\n   * Uses the cross-realm-safe {@link @nhtio/adk!isInstanceOf} guard: `instanceof` first, then\n   * `Symbol.hasInstance`, then a `constructor.name` fallback. Matches the pattern used by every\n   * other class guard in the ADK; safe against the dual-module-copy case where two distinct\n   * `SpooledToonArtifact` classes coexist in the same realm.\n   *\n   * @param value - The value to test.\n   * @returns `true` when `value` is a {@link SpooledToonArtifact} instance.\n   */\n  public static isSpooledToonArtifact(value: unknown): value is SpooledToonArtifact {\n    return isInstanceOf(value, 'SpooledToonArtifact', SpooledToonArtifact)\n  }\n\n  /**\n   * The TOON-specific artifact-query descriptors this class adds on top of the base set.\n   *\n   * @remarks\n   * Lists `artifact_toon_type`, `artifact_toon_keys`, `artifact_toon_length`,\n   * `artifact_toon_get`, `artifact_toon_filter`, `artifact_toon_slice`, `artifact_toon_pluck`.\n   * The base seven descriptors (`artifact_head`, etc.) are NOT included here — they are\n   * forged separately by {@link SpooledToonArtifact.forgeTools}, which calls\n   * `SpooledArtifact.forgeTools(ctx)` to produce the base-narrowed tools and then registers\n   * its own TOON tools on the result. Downstream consumers building custom subclasses\n   * should follow the same pattern: own only your own descriptors; override `forgeTools` to\n   * compose with the base output.\n   */\n  public static toolMethods: ReadonlyArray<ToolMethodDescriptor> = Object.freeze([\n    {\n      name: 'artifact_toon_type',\n      method: 'toon_type',\n      description: 'Return the format of a TOON artifact produced earlier in this turn.',\n      argsSchema: validator.object({}),\n    },\n    {\n      name: 'artifact_toon_keys',\n      method: 'toon_keys',\n      description: 'Return the top-level keys of a TOON artifact produced earlier in this turn.',\n      argsSchema: validator.object({}),\n    },\n    {\n      name: 'artifact_toon_length',\n      method: 'toon_length',\n      description:\n        'Return the element count of a TOON artifact produced earlier in this turn (1 if the root is not an array).',\n      argsSchema: validator.object({}),\n    },\n    {\n      name: 'artifact_toon_get',\n      method: 'toon_get',\n      description:\n        'Evaluate a JSONPath expression against a TOON artifact produced earlier in this turn.',\n      argsSchema: validator.object({\n        path: validator.string().required().description(\"JSONPath expression, e.g. '$.user.name'.\"),\n      }),\n    },\n    {\n      name: 'artifact_toon_filter',\n      method: 'toon_filter',\n      description:\n        'Return elements of a TOON artifact (produced earlier in this turn) matched by a JSONPath filter.',\n      argsSchema: validator.object({\n        path: validator\n          .string()\n          .required()\n          .description(\"JSONPath filter expression, e.g. '$[?(@.age>18)]'.\"),\n      }),\n    },\n    {\n      name: 'artifact_toon_slice',\n      method: 'toon_slice',\n      description:\n        'Return a slice of elements by index range from a TOON artifact produced earlier in this turn.',\n      argsSchema: validator.object({\n        start: validator\n          .number()\n          .integer()\n          .min(0)\n          .optional()\n          .description('Start index (inclusive).'),\n        end: validator.number().integer().min(0).optional().description('End index (exclusive).'),\n      }),\n    },\n    {\n      name: 'artifact_toon_pluck',\n      method: 'toon_pluck',\n      description:\n        'Return all values matched by a JSONPath expression across a TOON artifact produced earlier in this turn.',\n      argsSchema: validator.object({\n        path: validator.string().required().description(\"JSONPath expression, e.g. '$..name'.\"),\n      }),\n    },\n  ])\n\n  /**\n   * Forges base-class tools plus TOON-specific tools narrowed to {@link SpooledToonArtifact}.\n   *\n   * @remarks\n   * Standard subclass extension pattern: call `SpooledArtifact.forgeTools(ctx)` to produce\n   * the base seven `artifact_*` tools narrowed to any `SpooledArtifact` in the turn, then\n   * register one `ArtifactTool` per TOON-specific descriptor narrowed to TOON artifacts.\n   * Downstream consumers building their own subclasses should follow the same shape.\n   */\n  public static override forgeTools(ctx: DispatchContext): ToolRegistry {\n    const registry = SpooledArtifact.forgeTools(ctx)\n    const requires = SpooledToonArtifact\n    const compatibleIds = collectArtifactCompatibleIds(ctx, requires)\n    if (compatibleIds.length === 0) return registry\n\n    for (const descriptor of this.toolMethods) {\n      const callIdSchema = validator\n        .string()\n        .valid(...compatibleIds)\n        .required()\n        .description('ToolCall id of the artifact to query.')\n\n      const argsSchema = (\n        descriptor.argsSchema ?? validator.object<Record<string, never>>({})\n      ).append({\n        callId: callIdSchema,\n      })\n\n      const tool = new ArtifactTool({\n        name: descriptor.name,\n        description: descriptor.description,\n        inputSchema: argsSchema,\n        ephemeral: true,\n        onCollision: 'replace',\n        handler: async (rawArgs, ctxInner) => {\n          const args = rawArgs as Record<string, unknown> & { callId: string }\n          const resolved = resolveArtifactById(ctxInner, args.callId, requires)\n          if (!resolved) return `Error: no artifact with id ${args.callId} in this turn`\n          const artifact = resolved.artifact\n          const methodArgs: unknown[] = []\n          if (\n            descriptor.method === 'toon_get' ||\n            descriptor.method === 'toon_filter' ||\n            descriptor.method === 'toon_pluck'\n          ) {\n            methodArgs.push(args.path as string)\n          } else if (descriptor.method === 'toon_slice') {\n            methodArgs.push(args.start as number | undefined, args.end as number | undefined)\n          }\n          const fn = (artifact as unknown as Record<string, (...a: unknown[]) => unknown>)[\n            descriptor.method\n          ]\n          if (typeof fn !== 'function') {\n            return `Error: artifact has no method ${descriptor.method}`\n          }\n          const result = await Promise.resolve(fn.apply(artifact, methodArgs))\n          const serialise = descriptor.serialise ?? defaultSerialise\n          return serialise(result)\n        },\n      })\n      registry.register(tool)\n    }\n    return registry\n  }\n\n  /**\n   * Parses and caches the TOON content.\n   *\n   * @remarks\n   * On first access, decodes the TOON source and caches the parsed value. Subsequent calls\n   * return the cached value without re-parsing.\n   *\n   * @returns The parsed TOON value.\n   */\n  async #resolveParsed(): Promise<unknown> {\n    if (this.#hasParsed) {\n      return this.#parsed\n    }\n    const toonModule = await this.#loadToonModule()\n    const text = await this.asString()\n    try {\n      const decode = toonModule.decode as (text: string, opts: Record<string, unknown>) => unknown\n      this.#parsed = decode(text, {\n        indentSize: this.#options?.indentSize ?? 2,\n        strict: this.#options?.strict ?? true,\n      })\n    } catch (err) {\n      const message = this.#formatToonError(err)\n      throw new E_TOON_DECODE_FAILED([message])\n    }\n    this.#hasParsed = true\n    return this.#parsed\n  }\n\n  /**\n   * Formats an error from the TOON decoder, including line number and source.\n   */\n  #formatToonError(err: unknown): string {\n    if (isObject(err) && (err as Record<string, unknown>).name === 'ToonDecodeError') {\n      const toonErr = err as { line?: number; source?: string; message?: string }\n      const line = toonErr.line ?? '?'\n      const source = toonErr.source ?? '(no source)'\n      return `line ${line}: ${source}`\n    }\n    return String((err as Record<string, unknown>).message ?? err)\n  }\n\n  /**\n   * Loads the TOON module lazily, with error handling.\n   *\n   * @remarks\n   * Uses the module-level `toonModulePromise` to ensure the in-flight promise and error state\n   * are shared across all instances of SpooledToonArtifact.\n   */\n  async #loadToonModule() {\n    toonModulePromise ??= import('@toon-format/toon').catch((err: unknown) => {\n      throw new E_TOON_PEER_MISSING([String(err)])\n    })\n    return (await toonModulePromise) as Record<string, unknown>\n  }\n\n  /**\n   * Returns the format of a TOON artifact.\n   *\n   * @remarks\n   * Reports only the format name. The delimiter is deliberately not reported: the TOON decoder\n   * exposes no delimiter information, and every method of inferring one from the source proved\n   * unreliable for some class of strict-valid document. Four distinct approaches were attempted,\n   * each defeated by its own edge case:\n   * - Lexical scanning for the delimiter character failed on unquoted pipes inside values\n   * - Anchored header regex requiring a word key failed on quoted keys and keyless root arrays\n   * - Widened regex accepting quoted keys failed on escaped quotes within the key (e.g., \"a\\\"b\")\n   * - Byte-exact round-trip re-encoding failed on non-canonical formatting (indentation, line\n   *   endings, trailing newlines)\n   *\n   * Delimiter inference is no longer attempted: nothing in the model's workflow needs it, and\n   * the TOON decoder is the source of truth for all document metadata.\n   *\n   * This result is cached after the first parse and returned identically on every call.\n   *\n   * @returns An object with `format: 'toon'`.\n   */\n  async toon_type(): Promise<{ format: string }> {\n    await this.#resolveParsed()\n    return { format: 'toon' }\n  }\n\n  /**\n   * Returns the top-level keys of the parsed TOON content.\n   *\n   * @remarks\n   * - If the root is an object, returns its keys.\n   * - If the root is not a plain object (e.g. an array or scalar), returns `undefined`.\n   *\n   * @returns Array of key strings, or `undefined` when the root is not an object.\n   */\n  async toon_keys(): Promise<string[] | undefined> {\n    const value = await this.#resolveParsed()\n    if (isObject(value)) {\n      return Object.keys(value as object)\n    }\n    return undefined\n  }\n\n  /**\n   * Returns the element count of the parsed TOON content.\n   *\n   * @remarks\n   * - If the root is an array, returns the array length.\n   * - Otherwise, returns `1` (the root is a single element).\n   *\n   * @returns The element count.\n   */\n  async toon_length(): Promise<number> {\n    const value = await this.#resolveParsed()\n    if (Array.isArray(value)) {\n      return value.length\n    }\n    return 1\n  }\n\n  /**\n   * Evaluates a JSONPath expression against the parsed TOON content.\n   *\n   * @remarks\n   * Uses [JSONPath-Plus](https://github.com/JSONPath-Plus/JSONPath). Full JSONPath syntax is\n   * supported: recursive descent (`$..*`), filter expressions (`$[?(@.age > 18)]`), union\n   * selectors, and more.\n   *\n   * @param path - A JSONPath expression (e.g. `'$.user.address.city'`, `'$..name'`).\n   * @returns Array of matched values. Empty array when no matches are found.\n   */\n  async toon_get(path: string): Promise<unknown[]> {\n    const value = await this.#resolveParsed()\n    return JSONPath({ path, json: value as object })\n  }\n\n  /**\n   * Returns elements matched by a JSONPath filter expression.\n   *\n   * @remarks\n   * Evaluates `path` against the root value and returns it in an array if matched.\n   *\n   * @param path - A JSONPath expression (e.g. `'$[?(@.status === \"active\")]'`).\n   * @returns Array of matching elements (at most one element, the root if matched).\n   */\n  async toon_filter(path: string): Promise<unknown[]> {\n    const value = await this.#resolveParsed()\n    const matches = JSONPath({ path, json: value as object })\n    return Array.isArray(matches) && matches.length > 0 ? [value] : []\n  }\n\n  /**\n   * Returns a slice of the parsed content by index range.\n   *\n   * @remarks\n   * - If the root is an array, behaves like `Array.prototype.slice`.\n   * - If the root is not an array, returns the entire root in an array.\n   *\n   * @param start - Start index (inclusive). Defaults to `0`.\n   * @param end - End index (exclusive). Defaults to the element count.\n   * @returns Array of sliced elements.\n   */\n  async toon_slice(start?: number, end?: number): Promise<unknown[]> {\n    const value = await this.#resolveParsed()\n    if (Array.isArray(value)) {\n      return value.slice(start, end)\n    }\n    return [value]\n  }\n\n  /**\n   * Returns all values matched by a JSONPath expression.\n   *\n   * @remarks\n   * Convenience over {@link SpooledToonArtifact.toon_get} with an identical signature — use\n   * whichever name better communicates intent at the call site. `toon_pluck` reads well for\n   * extracting a single field; `toon_get` reads well for structured queries.\n   *\n   * @param path - A JSONPath expression (e.g. `'$..name'`).\n   * @returns Array of matched values.\n   */\n  async toon_pluck(path: string): Promise<unknown[]> {\n    return this.toon_get(path)\n  }\n\n  /**\n   * Serialise this SpooledToonArtifact into an `@nhtio/encoder` snapshot — the reader **handle** plus\n   * the decode `options`.\n   *\n   * @remarks\n   * Overrides {@link SpooledArtifact.[ENCODE_METHOD]} to carry the constructor's `options` (the\n   * parsed-value cache is derived and not encoded). Round-trips via\n   * {@link SpooledToonArtifact.[DECODE_METHOD]}.\n   *\n   * @returns A snapshot consumed by {@link SpooledToonArtifact.[DECODE_METHOD]}.\n   */\n  [ENCODE_METHOD](): AdkEncodableSnapshot {\n    return { reader: this.readerDescriptor(), options: this.#options }\n  }\n\n  /**\n   * Reconstruct a {@link SpooledToonArtifact} from a {@link SpooledToonArtifact.[ENCODE_METHOD]}\n   * snapshot.\n   *\n   * @param data - The snapshot produced by {@link SpooledToonArtifact.[ENCODE_METHOD]}.\n   * @returns A fresh {@link SpooledToonArtifact}} backed by a freshly-resolved reader.\n   */\n  static [DECODE_METHOD](data: AdkEncodableSnapshot): SpooledToonArtifact {\n    const snapshot = data as {\n      reader: ReaderDescriptor\n      options?: ToonDecodeOptions\n    }\n    return new SpooledToonArtifact(resolveSpoolReader(snapshot.reader), snapshot.options)\n  }\n}\n\n/**\n * Converter tool: TOON to JSON.\n *\n * @remarks\n * Accepts either inline TOON text or a reference to an artifact produced earlier in this turn.\n * Converts to JSON and returns a new {@link @nhtio/adk!SpooledJsonArtifact}.\n *\n * This is a plain {@link @nhtio/adk!Tool}, not an {@link @nhtio/adk!ArtifactTool} — the handler\n * returns the new artifact directly, allowing the forge to discover and query it on the next\n * iteration without explicit wiring.\n */\nexport const toonToJsonTool: Tool = new Tool({\n  name: 'toon_to_json',\n  description: 'Convert inline TOON text or a TOON artifact to JSON.',\n  inputSchema: validator.object({\n    text: validator\n      .string()\n      .optional()\n      .allow('')\n      .description('Inline TOON text. Provide this or call_id, not both.'),\n    call_id: validator\n      .string()\n      .optional()\n      .allow('')\n      .description('ToolCall id of a TOON artifact produced earlier in this turn.'),\n  }),\n  artifactConstructor: () => SpooledJsonArtifact,\n  handler: async (rawArgs: unknown, ctx: DispatchContext) => {\n    const args = rawArgs as { text?: string; call_id?: string }\n    const text = args.text ?? ''\n    const callId = args.call_id ?? ''\n\n    const hasText = text.length > 0\n    const hasCallId = callId.length > 0\n\n    if (!hasText && !hasCallId) {\n      return 'Error: provide either text or call_id, not neither'\n    }\n    if (hasText && hasCallId) {\n      return 'Error: provide either text or call_id, not both'\n    }\n\n    let toonContent: string\n    if (hasCallId) {\n      const resolved = resolveArtifactById(ctx, callId, SpooledToonArtifact)\n      if (!resolved) {\n        return `Error: no TOON artifact with id ${callId} in this turn`\n      }\n      toonContent = await resolved.artifact.asString()\n    } else {\n      toonContent = text\n    }\n\n    const toonModule = await import('@toon-format/toon').catch((err: unknown) => {\n      throw new E_TOON_PEER_MISSING([String(err)])\n    })\n\n    let parsed: unknown\n    try {\n      const decode = toonModule.decode as (text: string, opts: Record<string, unknown>) => unknown\n      parsed = decode(toonContent, { indentSize: 2, strict: true })\n    } catch (err) {\n      const message =\n        isObject(err) && (err as Record<string, unknown>).name === 'ToonDecodeError'\n          ? `line ${(err as { line?: number }).line ?? '?'}: ${(err as { source?: string }).source ?? '(no source)'}`\n          : String((err as Record<string, unknown>).message ?? err)\n      return `Error: failed to decode TOON: ${message}`\n    }\n\n    const json = JSON.stringify(parsed, null, 2)\n    const sourceId = hasCallId ? callId : uuidv6()\n    const reader = await ctx.storeRetrievableBytes(`${ctx.id}:toon_to_json:${sourceId}`, json)\n    return new SpooledJsonArtifact(reader)\n  },\n})\n\n/**\n * Converter tool: JSON to TOON.\n *\n * @remarks\n * Accepts either inline JSON text or a reference to a JSON artifact produced earlier in this turn.\n * Converts to TOON and returns a new {@link SpooledToonArtifact}.\n *\n * This is a plain {@link @nhtio/adk!Tool}, not an {@link @nhtio/adk!ArtifactTool} — the handler\n * returns the new artifact directly, allowing the forge to discover and query it on the next\n * iteration without explicit wiring.\n */\nexport const jsonToToonTool: Tool = new Tool({\n  name: 'json_to_toon',\n  description: 'Convert inline JSON text or a JSON artifact to TOON.',\n  inputSchema: validator.object({\n    text: validator\n      .string()\n      .optional()\n      .allow('')\n      .description('Inline JSON text. Provide this or call_id, not both.'),\n    call_id: validator\n      .string()\n      .optional()\n      .allow('')\n      .description('ToolCall id of a JSON artifact produced earlier in this turn.'),\n  }),\n  artifactConstructor: () => SpooledToonArtifact,\n  handler: async (rawArgs: unknown, ctx: DispatchContext) => {\n    const args = rawArgs as { text?: string; call_id?: string }\n    const text = args.text ?? ''\n    const callId = args.call_id ?? ''\n\n    const hasText = text.length > 0\n    const hasCallId = callId.length > 0\n\n    if (!hasText && !hasCallId) {\n      return 'Error: provide either text or call_id, not neither'\n    }\n    if (hasText && hasCallId) {\n      return 'Error: provide either text or call_id, not both'\n    }\n\n    let jsonContent: string\n    if (hasCallId) {\n      const resolved = resolveArtifactById(ctx, callId, SpooledJsonArtifact)\n      if (!resolved) {\n        return `Error: no JSON artifact with id ${callId} in this turn`\n      }\n      jsonContent = await resolved.artifact.asString()\n    } else {\n      jsonContent = text\n    }\n\n    let parsed: unknown\n    try {\n      parsed = JSON.parse(jsonContent)\n    } catch (err) {\n      return `Error: failed to parse JSON: ${String((err as Record<string, unknown>).message ?? err)}`\n    }\n\n    const toonModule = await import('@toon-format/toon').catch((err: unknown) => {\n      throw new E_TOON_PEER_MISSING([String(err)])\n    })\n\n    let toon: string\n    try {\n      const encode = toonModule.encode as (val: unknown, opts: Record<string, unknown>) => string\n      toon = encode(parsed, { indentSize: 2 })\n    } catch (err) {\n      return `Error: failed to encode TOON: ${String((err as Record<string, unknown>).message ?? err)}`\n    }\n\n    const sourceId = hasCallId ? callId : uuidv6()\n    const reader = await ctx.storeRetrievableBytes(`${ctx.id}:json_to_toon:${sourceId}`, toon)\n    return new SpooledToonArtifact(reader)\n  },\n})\n\n/**\n * Exports for the public barrel.\n */\nexport { E_TOON_PEER_MISSING, E_TOON_DECODE_FAILED } from './exceptions'\n"],"mappings":"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"}