@tangle-network/agent-app 0.27.2 → 0.28.0

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.
Files changed (98) hide show
  1. package/dist/DesignCanvas-AOIVN3SJ.js +12 -0
  2. package/dist/DesignCanvasEditor-TN7CWUJW.js +13 -0
  3. package/dist/{TimelineEditor-JDG54OBP.js → TimelineEditor-Z7C6BFP5.js} +2 -2
  4. package/dist/{apply-Cp8c3K9D.d.ts → apply-Gk4raT2j.d.ts} +3 -1
  5. package/dist/{chunk-AFDROJ64.js → chunk-2QI7XV2T.js} +1 -1
  6. package/dist/chunk-2QI7XV2T.js.map +1 -0
  7. package/dist/{chunk-LUE4HO5C.js → chunk-4IQMTTNE.js} +10 -10
  8. package/dist/chunk-4IQMTTNE.js.map +1 -0
  9. package/dist/{chunk-HABVCJJT.js → chunk-7I37CO5D.js} +271 -404
  10. package/dist/chunk-7I37CO5D.js.map +1 -0
  11. package/dist/{chunk-5RT6KY4G.js → chunk-GQRL6VNU.js} +1 -1
  12. package/dist/chunk-GQRL6VNU.js.map +1 -0
  13. package/dist/{chunk-SCG5JJ4C.js → chunk-HFC4BTWJ.js} +32 -4
  14. package/dist/chunk-HFC4BTWJ.js.map +1 -0
  15. package/dist/{chunk-7QCIYDGC.js → chunk-IOREXWLM.js} +2 -2
  16. package/dist/chunk-J7JNV5BH.js +14 -0
  17. package/dist/chunk-J7JNV5BH.js.map +1 -0
  18. package/dist/{chunk-6UOE5CTA.js → chunk-JT6HHO5P.js} +5 -10
  19. package/dist/chunk-JT6HHO5P.js.map +1 -0
  20. package/dist/{chunk-JZAJE3JL.js → chunk-M2EBUDZ7.js} +19 -5
  21. package/dist/chunk-M2EBUDZ7.js.map +1 -0
  22. package/dist/{chunk-RJ2ZHK5D.js → chunk-S6WS2B32.js} +132 -7
  23. package/dist/chunk-S6WS2B32.js.map +1 -0
  24. package/dist/chunk-SQV7NUEM.js +136 -0
  25. package/dist/chunk-SQV7NUEM.js.map +1 -0
  26. package/dist/chunk-UAKFCMWK.js +1 -0
  27. package/dist/chunk-UAKFCMWK.js.map +1 -0
  28. package/dist/chunk-V5XZKHCL.js +296 -0
  29. package/dist/chunk-V5XZKHCL.js.map +1 -0
  30. package/dist/chunk-XWJXZ6WE.js +604 -0
  31. package/dist/chunk-XWJXZ6WE.js.map +1 -0
  32. package/dist/chunk-Y2SZVW35.js +125 -0
  33. package/dist/chunk-Y2SZVW35.js.map +1 -0
  34. package/dist/{chunk-27LQXQSS.js → chunk-YEWTVDDT.js} +224 -767
  35. package/dist/chunk-YEWTVDDT.js.map +1 -0
  36. package/dist/{chunk-UDXMR3AD.js → chunk-YNALYIQ3.js} +56 -18
  37. package/dist/chunk-YNALYIQ3.js.map +1 -0
  38. package/dist/command-stack-CkjelfK_.d.ts +267 -0
  39. package/dist/design-canvas/drizzle.d.ts +2 -2
  40. package/dist/design-canvas/index.d.ts +7 -6
  41. package/dist/design-canvas/index.js +3 -2
  42. package/dist/design-canvas-react/engine.d.ts +349 -0
  43. package/dist/design-canvas-react/engine.js +86 -0
  44. package/dist/design-canvas-react/engine.js.map +1 -0
  45. package/dist/design-canvas-react/index.d.ts +69 -649
  46. package/dist/design-canvas-react/index.js +84 -173
  47. package/dist/design-canvas-react/index.js.map +1 -1
  48. package/dist/design-canvas-react/lazy.d.ts +6 -0
  49. package/dist/design-canvas-react/lazy.js +9 -0
  50. package/dist/design-canvas-react/lazy.js.map +1 -0
  51. package/dist/{export-presets-Dl5Aa5xj.d.ts → export-presets-DAsU5Wal.d.ts} +2 -143
  52. package/dist/flow-types-Cb_AblZs.d.ts +25 -0
  53. package/dist/index.d.ts +11 -8
  54. package/dist/index.js +21 -8
  55. package/dist/lazy-BHHqS8-j.d.ts +72 -0
  56. package/dist/missions/index.d.ts +12 -0
  57. package/dist/missions/index.js +1 -1
  58. package/dist/{model-BHLN208Z.d.ts → model-BBwyOuWG.d.ts} +3 -1
  59. package/dist/operations-DLIvvD00.d.ts +144 -0
  60. package/dist/platform/index.js +1 -1
  61. package/dist/runtime/index.d.ts +33 -33
  62. package/dist/runtime/index.js +1 -1
  63. package/dist/sequences/index.d.ts +1 -1
  64. package/dist/sequences/index.js +2 -1
  65. package/dist/sequences-react/index.d.ts +15 -1
  66. package/dist/sequences-react/index.js +2 -2
  67. package/dist/{store-CUStmtdH.d.ts → store-CFyxgTlD.d.ts} +1 -1
  68. package/dist/theme/index.d.ts +80 -0
  69. package/dist/theme/index.js +14 -0
  70. package/dist/theme/index.js.map +1 -0
  71. package/dist/theme/tailwind-preset.d.ts +65 -0
  72. package/dist/theme/tailwind-preset.js +33 -0
  73. package/dist/theme/tailwind-preset.js.map +1 -0
  74. package/dist/theme/tokens.css +95 -0
  75. package/dist/trace/index.d.ts +2 -18
  76. package/dist/trace/index.js +2 -2
  77. package/dist/web/index.d.ts +25 -2
  78. package/dist/web/index.js +3 -1
  79. package/dist/web-react/index.d.ts +71 -31
  80. package/dist/web-react/index.js +625 -486
  81. package/dist/web-react/index.js.map +1 -1
  82. package/package.json +28 -3
  83. package/dist/DesignCanvas-73IUJJUP.js +0 -10
  84. package/dist/DesignCanvasEditor-6F2UTOSW.js +0 -9
  85. package/dist/chunk-27LQXQSS.js.map +0 -1
  86. package/dist/chunk-5RT6KY4G.js.map +0 -1
  87. package/dist/chunk-6UOE5CTA.js.map +0 -1
  88. package/dist/chunk-AFDROJ64.js.map +0 -1
  89. package/dist/chunk-HABVCJJT.js.map +0 -1
  90. package/dist/chunk-JZAJE3JL.js.map +0 -1
  91. package/dist/chunk-LUE4HO5C.js.map +0 -1
  92. package/dist/chunk-RJ2ZHK5D.js.map +0 -1
  93. package/dist/chunk-SCG5JJ4C.js.map +0 -1
  94. package/dist/chunk-UDXMR3AD.js.map +0 -1
  95. /package/dist/{DesignCanvas-73IUJJUP.js.map → DesignCanvas-AOIVN3SJ.js.map} +0 -0
  96. /package/dist/{DesignCanvasEditor-6F2UTOSW.js.map → DesignCanvasEditor-TN7CWUJW.js.map} +0 -0
  97. /package/dist/{TimelineEditor-JDG54OBP.js.map → TimelineEditor-Z7C6BFP5.js.map} +0 -0
  98. /package/dist/{chunk-7QCIYDGC.js.map → chunk-IOREXWLM.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/design-canvas/model.ts","../src/design-canvas/validate.ts","../src/design-canvas/apply.ts","../src/design-canvas/export-presets.ts"],"sourcesContent":["/**\n * Design-canvas scene model — the product-agnostic document behind the visual\n * asset editor. A document is an ordered list of pages; a page is an ordered\n * list of elements (index order IS z-order, bottom→top); every element is a\n * typed node with a closed attribute vocabulary. The whole document\n * serializes as one JSON value and persists atomically with a revision\n * counter (see ./store) — every canvas action is a durable operation against\n * this schema, which is what makes the canvas automatable: agents and\n * external data sources mutate the same document the editor renders.\n *\n * Units are CSS pixels throughout; `settings.dpi` carries the print\n * conversion factor for bleed/trim-aware exports. Angles are degrees.\n * Nothing here touches Konva, React, or a database.\n */\n\nexport const SCENE_SCHEMA_VERSION = 1\n\nexport interface SceneDocument {\n schemaVersion: typeof SCENE_SCHEMA_VERSION\n title: string\n pages: ScenePage[]\n settings: SceneSettings\n metadata: Record<string, unknown>\n}\n\nexport interface SceneSettings {\n /** Print conversion factor for mm↔px math at export; 96 = CSS default. */\n dpi: number\n}\n\n/** Per-side bleed extents in px, drawn OUTSIDE the page bounds. Trim = the\n * page rect itself; exports may include or exclude the bleed area. */\nexport interface PageBleed {\n top: number\n right: number\n bottom: number\n left: number\n}\n\n/** Saved ruler guides, in page coordinates. */\nexport interface PageGuides {\n vertical: number[]\n horizontal: number[]\n}\n\nexport interface ScenePage {\n id: string\n name: string\n width: number\n height: number\n /** Page background fill (color string); elements paint over it. */\n background: string\n bleed: PageBleed | null\n guides: PageGuides\n elements: SceneElement[]\n}\n\n// ---------------------------------------------------------------------------\n// Elements\n// ---------------------------------------------------------------------------\n\n/** Attributes every element carries. `x`/`y` are the element's top-left in\n * page coordinates (for `group`, children are relative to the group origin).\n * `slot` names a template binding point — `apply_data` targets it. */\nexport interface SceneElementBase {\n id: string\n name: string\n x: number\n y: number\n /** Degrees, clockwise, about the element's top-left origin (Konva default). */\n rotation: number\n /** 0..1 */\n opacity: number\n locked: boolean\n visible: boolean\n slot?: string\n}\n\nexport interface RectElement extends SceneElementBase {\n kind: 'rect'\n width: number\n height: number\n fill: string\n stroke?: string\n strokeWidth?: number\n cornerRadius?: number\n}\n\nexport interface EllipseElement extends SceneElementBase {\n kind: 'ellipse'\n width: number\n height: number\n fill: string\n stroke?: string\n strokeWidth?: number\n}\n\nexport interface LineElement extends SceneElementBase {\n kind: 'line'\n /** Flat [x0, y0, x1, y1, ...] relative to (x, y); ≥ 2 points. */\n points: number[]\n stroke: string\n strokeWidth: number\n dash?: number[]\n}\n\nexport interface TextElement extends SceneElementBase {\n kind: 'text'\n text: string\n /** Wrap width; height derives from content. */\n width: number\n fontFamily: string\n fontSize: number\n fontStyle: 'normal' | 'bold' | 'italic' | 'bold italic'\n fill: string\n align: 'left' | 'center' | 'right'\n lineHeight: number\n letterSpacing: number\n}\n\nexport interface ImageElement extends SceneElementBase {\n kind: 'image'\n width: number\n height: number\n /** http(s) or rooted /api/ path — same boundary rule as sequences media. */\n src: string\n /** How the source maps into the frame; 'fill' stretches, 'cover' crops. */\n fit: 'fill' | 'cover' | 'contain'\n}\n\n/** Video placed on a canvas renders and exports as its poster frame — motion\n * belongs to the sequences surface; this keeps video assets placeable in\n * static layouts (e.g. a thumbnail mock) without a playback engine. */\nexport interface VideoElement extends SceneElementBase {\n kind: 'video'\n width: number\n height: number\n src: string\n posterSrc?: string\n}\n\nexport interface GroupElement extends SceneElementBase {\n kind: 'group'\n children: SceneElement[]\n}\n\nexport type SceneElement =\n | RectElement\n | EllipseElement\n | LineElement\n | TextElement\n | ImageElement\n | VideoElement\n | GroupElement\n\nexport type SceneElementKind = SceneElement['kind']\n\nexport const SCENE_ELEMENT_KINDS: readonly SceneElementKind[] = [\n 'rect', 'ellipse', 'line', 'text', 'image', 'video', 'group',\n] as const\n\n// ---------------------------------------------------------------------------\n// Geometry\n// ---------------------------------------------------------------------------\n\nexport interface Bounds {\n x: number\n y: number\n width: number\n height: number\n}\n\n/** Unrotated local extent of an element (line/group derive from content). */\nexport function elementExtent(element: SceneElement): { width: number; height: number } {\n switch (element.kind) {\n case 'rect':\n case 'ellipse':\n case 'image':\n case 'video':\n return { width: element.width, height: element.height }\n case 'text':\n // Height derives from content at render time; the model exposes a\n // deterministic estimate so layout math (snapping, describe) never\n // depends on a canvas context: lines × fontSize × lineHeight.\n return {\n width: element.width,\n height: estimateTextHeight(element),\n }\n case 'line': {\n let maxX = 0\n let maxY = 0\n for (let i = 0; i < element.points.length; i += 2) {\n maxX = Math.max(maxX, Math.abs(element.points[i]!))\n maxY = Math.max(maxY, Math.abs(element.points[i + 1]!))\n }\n return { width: maxX, height: maxY }\n }\n case 'group': {\n // minX/minY track the negative-space corner: a rotated child whose AABB\n // extends left/above the group origin contributes negative values, so\n // they must seed from the first child, not 0 (0 would clip them).\n let minX = 0, minY = 0, maxX = 0, maxY = 0\n for (const child of element.children) {\n const aabb = elementAabb(child)\n minX = Math.min(minX, aabb.x)\n minY = Math.min(minY, aabb.y)\n maxX = Math.max(maxX, aabb.x + aabb.width)\n maxY = Math.max(maxY, aabb.y + aabb.height)\n }\n return { width: maxX - minX, height: maxY - minY }\n }\n }\n}\n\nexport function estimateTextHeight(element: Pick<TextElement, 'text' | 'fontSize' | 'lineHeight'>): number {\n const lines = element.text.length === 0 ? 1 : element.text.split('\\n').length\n return lines * element.fontSize * element.lineHeight\n}\n\n/** Axis-aligned bounding box in the parent's coordinate space, accounting for\n * rotation about the element's top-left origin. */\nexport function elementAabb(element: SceneElement): Bounds {\n const { width, height } = elementExtent(element)\n if (element.rotation % 360 === 0) {\n return { x: element.x, y: element.y, width, height }\n }\n const rad = (element.rotation * Math.PI) / 180\n const cos = Math.cos(rad)\n const sin = Math.sin(rad)\n const corners: Array<[number, number]> = [\n [0, 0],\n [width * cos, width * sin],\n [-height * sin, height * cos],\n [width * cos - height * sin, width * sin + height * cos],\n ]\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity\n for (const [cx, cy] of corners) {\n minX = Math.min(minX, cx); minY = Math.min(minY, cy)\n maxX = Math.max(maxX, cx); maxY = Math.max(maxY, cy)\n }\n return { x: element.x + minX, y: element.y + minY, width: maxX - minX, height: maxY - minY }\n}\n\nexport function boundsIntersect(a: Bounds, b: Bounds): boolean {\n return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y\n}\n\n// ---------------------------------------------------------------------------\n// Lookup + traversal\n// ---------------------------------------------------------------------------\n\nexport function requirePage(document: SceneDocument, pageId: string): ScenePage {\n const page = document.pages.find((candidate) => candidate.id === pageId)\n if (!page) throw new Error(`page ${pageId} not found in document`)\n return page\n}\n\n/** Depth-first search across a page including group children. Returns the\n * element and the array that owns it (page.elements or a group's children),\n * so callers can splice in place. */\nexport function findElement(page: ScenePage, elementId: string): { element: SceneElement; owner: SceneElement[]; index: number } | null {\n const stack: SceneElement[][] = [page.elements]\n while (stack.length > 0) {\n const owner = stack.pop()!\n for (let index = 0; index < owner.length; index += 1) {\n const element = owner[index]!\n if (element.id === elementId) return { element, owner, index }\n if (element.kind === 'group') stack.push(element.children)\n }\n }\n return null\n}\n\nexport function requireElement(page: ScenePage, elementId: string): { element: SceneElement; owner: SceneElement[]; index: number } {\n const found = findElement(page, elementId)\n if (!found) throw new Error(`element ${elementId} not found on page ${page.id}`)\n return found\n}\n\n/** All slot names declared across the document — the template's fillable\n * surface. Duplicate slot names are a validation error (see ./validate). */\nexport function collectSlots(document: SceneDocument): Map<string, { pageId: string; elementId: string; kind: SceneElementKind }> {\n const slots = new Map<string, { pageId: string; elementId: string; kind: SceneElementKind }>()\n for (const page of document.pages) {\n const stack = [...page.elements]\n while (stack.length > 0) {\n const element = stack.pop()!\n if (element.slot) {\n if (slots.has(element.slot)) throw new Error(`duplicate slot name \"${element.slot}\"`)\n slots.set(element.slot, { pageId: page.id, elementId: element.id, kind: element.kind })\n }\n if (element.kind === 'group') stack.push(...element.children)\n }\n }\n return slots\n}\n\n// ---------------------------------------------------------------------------\n// Construction\n// ---------------------------------------------------------------------------\n\nexport interface NewPageOptions {\n name?: string\n width?: number\n height?: number\n background?: string\n}\n\nexport function createEmptyDocument(title: string, page?: NewPageOptions): SceneDocument {\n return {\n schemaVersion: SCENE_SCHEMA_VERSION,\n title,\n pages: [createPage(page ?? {}, 'page-1')],\n settings: { dpi: 96 },\n metadata: {},\n }\n}\n\nexport function createPage(options: NewPageOptions, id: string): ScenePage {\n const width = options.width ?? 1080\n const height = options.height ?? 1080\n assertPositiveFinite(width, 'page width')\n assertPositiveFinite(height, 'page height')\n return {\n id,\n name: options.name ?? 'Page',\n width,\n height,\n background: options.background ?? '#ffffff',\n bleed: null,\n guides: { vertical: [], horizontal: [] },\n elements: [],\n }\n}\n\nexport function assertPositiveFinite(value: number, label: string): void {\n if (!Number.isFinite(value) || value <= 0) throw new Error(`${label} must be a positive finite number`)\n}\n\nexport function assertFinite(value: number, label: string): void {\n if (!Number.isFinite(value)) throw new Error(`${label} must be a finite number`)\n}\n\nconst COLOR_PATTERN = /^(#[0-9a-fA-F]{3,8}|rgba?\\(\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*(,\\s*(0|1|0?\\.\\d+)\\s*)?\\)|transparent)$/\n\nexport function assertColor(value: string, label: string): void {\n if (!COLOR_PATTERN.test(value)) throw new Error(`${label} must be a hex/rgb(a) color or 'transparent', got \"${value}\"`)\n}\n\n/** Media boundary rule shared with sequences: remote http(s) or a rooted\n * /api/ path — never sandbox-local files or data: blobs. */\nexport function assertSceneMediaSrc(value: string, label: string): void {\n if (/^https?:\\/\\//i.test(value) || /^\\/api\\//.test(value)) return\n throw new Error(`${label} must be an http(s) URL or a rooted /api/ path, got \"${value}\"`)\n}\n","/**\n * Pre-write validation for scene operations. Every rule runs against a\n * `SceneDocument` snapshot BEFORE any mutation, so a rejected batch leaves no\n * partial state. Batch errors carry the shape `operation N (type): reason` —\n * precise enough for an LLM planner to repair the offending operation and\n * resubmit.\n *\n * Validation is static: a batch is checked against the document as given, so\n * an operation may not reference entities created by an earlier operation in\n * the same batch. Dispatchers that chain operations must refresh the document\n * between applications and validate per-operation.\n *\n * Slot typing contract (for apply_data bindings):\n * text elements → value is any string\n * image elements → value must pass assertSceneMediaSrc (src rewrite)\n * video elements → value must pass assertSceneMediaSrc (src rewrite)\n * rect elements → value must pass assertColor (fill recolor)\n * ellipse elements → value must pass assertColor (fill recolor)\n * line elements → value must pass assertColor (stroke recolor)\n * group elements → value must pass assertColor (fill/stroke passed down to children)\n */\n\nimport {\n assertColor,\n assertFinite,\n assertPositiveFinite,\n assertSceneMediaSrc,\n collectSlots,\n findElement,\n requireElement,\n requirePage,\n} from './model'\nimport type { SceneDocument, SceneElement, SceneElementKind } from './model'\nimport type {\n AddElementOperation,\n ApplyDataOperation,\n BindSlotOperation,\n DeleteElementOperation,\n DeletePageOperation,\n DuplicatePageOperation,\n GroupElementsOperation,\n ReorderElementOperation,\n ReorderPageOperation,\n SceneAttrsPatch,\n SceneOperation,\n SetAttrsOperation,\n SetDocumentTitleOperation,\n SetPageGuidesOperation,\n SetPagePropsOperation,\n UngroupElementOperation,\n} from './operations'\n\n// ---------------------------------------------------------------------------\n// Public entry\n// ---------------------------------------------------------------------------\n\nexport function validateSceneOperations(document: SceneDocument, operations: SceneOperation[]): void {\n operations.forEach((operation, index) => {\n try {\n validateSceneOperation(document, operation)\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n throw new Error(`operation ${index + 1} (${operation.type}): ${reason}`)\n }\n })\n}\n\nexport function validateSceneOperation(document: SceneDocument, operation: SceneOperation): void {\n switch (operation.type) {\n case 'add_element':\n return validateAddElement(document, operation)\n case 'set_attrs':\n return validateSetAttrs(document, operation)\n case 'reorder_element':\n return validateReorderElement(document, operation)\n case 'delete_element':\n return validateDeleteElement(document, operation)\n case 'group_elements':\n return validateGroupElements(document, operation)\n case 'ungroup_element':\n return validateUngroupElement(document, operation)\n case 'add_page':\n return validateAddPage(operation)\n case 'duplicate_page':\n return validateDuplicatePage(document, operation)\n case 'delete_page':\n return validateDeletePage(document, operation)\n case 'reorder_page':\n return validateReorderPage(document, operation)\n case 'set_page_props':\n return validateSetPageProps(document, operation)\n case 'set_page_guides':\n return validateSetPageGuides(document, operation)\n case 'bind_slot':\n return validateBindSlot(document, operation)\n case 'apply_data':\n return validateApplyData(document, operation)\n case 'set_document_title':\n return validateSetDocumentTitle(operation)\n default: {\n const unknown = operation as { type?: unknown }\n throw new Error(`unsupported operation type ${JSON.stringify(unknown.type)}`)\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Per-operation validators\n// ---------------------------------------------------------------------------\n\nfunction validateAddElement(document: SceneDocument, op: AddElementOperation): void {\n const page = requirePage(document, op.pageId)\n assertUniqueIdDocumentWide(document, op.element.id)\n if (op.parentGroupId !== undefined) {\n const { element: parent } = requireElement(page, op.parentGroupId)\n if (parent.kind !== 'group') {\n throw new Error(`parentGroupId \"${op.parentGroupId}\" is a ${parent.kind}, not a group`)\n }\n }\n if (op.index !== undefined) {\n const owner = op.parentGroupId\n ? (() => {\n const { element: g } = requireElement(page, op.parentGroupId)\n return (g as { kind: 'group'; children: SceneElement[] }).children\n })()\n : page.elements\n if (op.index < 0 || op.index > owner.length) {\n throw new Error(`index ${op.index} out of range (owner has ${owner.length} elements)`)\n }\n }\n validateElementAttrs(op.element.kind, op.element as unknown as SceneAttrsPatch, true)\n}\n\nfunction validateSetAttrs(document: SceneDocument, op: SetAttrsOperation): void {\n const page = requirePage(document, op.pageId)\n const { element } = requireElement(page, op.elementId)\n // A patch that ONLY unlocks is always permitted even on a locked element.\n const isUnlockOnly = Object.keys(op.attrs).length === 1 && op.attrs.locked === false\n if (element.locked && !isUnlockOnly) {\n throw new Error(`element \"${op.elementId}\" is locked; unlock it first (pass attrs: {locked: false}) before making other changes`)\n }\n validateElementAttrs(element.kind, op.attrs, false)\n}\n\nfunction validateReorderElement(document: SceneDocument, op: ReorderElementOperation): void {\n const page = requirePage(document, op.pageId)\n const { element, owner } = requireElement(page, op.elementId)\n if (element.locked) {\n throw new Error(`element \"${op.elementId}\" is locked; unlock it before reordering`)\n }\n // toIndex is the insertion point after splice-remove; owner.length-1 is valid\n // (element becomes last). owner.length itself is out-of-range post-remove.\n if (op.toIndex < 0 || op.toIndex >= owner.length) {\n throw new Error(`toIndex ${op.toIndex} out of range (owner has ${owner.length} elements)`)\n }\n}\n\nfunction validateDeleteElement(document: SceneDocument, op: DeleteElementOperation): void {\n const page = requirePage(document, op.pageId)\n const { element } = requireElement(page, op.elementId)\n if (element.locked) {\n throw new Error(`element \"${op.elementId}\" is locked; unlock it before deleting`)\n }\n}\n\nfunction validateGroupElements(document: SceneDocument, op: GroupElementsOperation): void {\n if (op.elementIds.length < 2) {\n throw new Error(`group_elements requires ≥ 2 element ids (got ${op.elementIds.length})`)\n }\n assertUniqueIdDocumentWide(document, op.groupId)\n const page = requirePage(document, op.pageId)\n // Resolve all targets and check siblings + locked\n const owners = op.elementIds.map((id) => {\n const { element, owner } = requireElement(page, id)\n if (element.locked) throw new Error(`element \"${id}\" is locked; unlock before grouping`)\n return owner\n })\n // All must share the exact same owner array reference (sibling check)\n const firstOwner = owners[0]\n for (let i = 1; i < owners.length; i++) {\n if (owners[i] !== firstOwner) {\n throw new Error(`elements are not siblings — they must all share the same parent (page root or one group)`)\n }\n }\n}\n\nfunction validateUngroupElement(document: SceneDocument, op: UngroupElementOperation): void {\n const page = requirePage(document, op.pageId)\n const { element } = requireElement(page, op.groupId)\n if (element.kind !== 'group') {\n throw new Error(`element \"${op.groupId}\" is a ${element.kind}, not a group`)\n }\n}\n\nfunction validateAddPage(op: { options?: { width?: number; height?: number; background?: string } }): void {\n const opts = op.options\n if (!opts) return\n if (opts.width !== undefined) assertPositiveFinite(opts.width, 'page width')\n if (opts.height !== undefined) assertPositiveFinite(opts.height, 'page height')\n if (opts.background !== undefined) assertColor(opts.background, 'page background')\n}\n\nfunction validateDuplicatePage(document: SceneDocument, op: DuplicatePageOperation): void {\n requirePage(document, op.sourcePageId)\n const existing = document.pages.find((p) => p.id === op.pageId)\n if (existing) throw new Error(`pageId \"${op.pageId}\" already exists in the document`)\n}\n\nfunction validateDeletePage(document: SceneDocument, op: DeletePageOperation): void {\n requirePage(document, op.pageId)\n if (document.pages.length === 1) {\n throw new Error('cannot delete the last remaining page')\n }\n}\n\nfunction validateReorderPage(document: SceneDocument, op: ReorderPageOperation): void {\n requirePage(document, op.pageId)\n if (op.toIndex < 0 || op.toIndex >= document.pages.length) {\n throw new Error(`toIndex ${op.toIndex} out of range (document has ${document.pages.length} pages)`)\n }\n}\n\nfunction validateSetPageProps(document: SceneDocument, op: SetPagePropsOperation): void {\n requirePage(document, op.pageId)\n if (op.width !== undefined) assertPositiveFinite(op.width, 'page width')\n if (op.height !== undefined) assertPositiveFinite(op.height, 'page height')\n if (op.background !== undefined) assertColor(op.background, 'page background')\n if (op.bleed != null) {\n assertNonNegativeFinite(op.bleed.top, 'bleed.top')\n assertNonNegativeFinite(op.bleed.right, 'bleed.right')\n assertNonNegativeFinite(op.bleed.bottom, 'bleed.bottom')\n assertNonNegativeFinite(op.bleed.left, 'bleed.left')\n }\n}\n\nfunction validateSetPageGuides(document: SceneDocument, op: SetPageGuidesOperation): void {\n requirePage(document, op.pageId)\n for (const pos of op.guides.vertical) {\n if (!Number.isFinite(pos)) throw new Error(`guide position ${pos} is not finite`)\n }\n for (const pos of op.guides.horizontal) {\n if (!Number.isFinite(pos)) throw new Error(`guide position ${pos} is not finite`)\n }\n}\n\nfunction validateBindSlot(document: SceneDocument, op: BindSlotOperation): void {\n const page = requirePage(document, op.pageId)\n requireElement(page, op.elementId)\n if (op.slot === null) return\n // Slot names must be unique document-wide; a re-bind of the SAME element to\n // its own current slot name is fine (idempotent) — collectSlots throws on\n // duplicates owned by DIFFERENT elements, so we check manually.\n for (const p of document.pages) {\n const stack = [...p.elements]\n while (stack.length > 0) {\n const el = stack.pop()!\n if (el.slot === op.slot && el.id !== op.elementId) {\n throw new Error(`slot \"${op.slot}\" is already bound to element \"${el.id}\" on page \"${p.id}\"`)\n }\n if (el.kind === 'group') stack.push(...el.children)\n }\n }\n}\n\nfunction validateApplyData(document: SceneDocument, op: ApplyDataOperation): void {\n // Build the slot map; collectSlots throws on internal duplicates\n const slots = collectSlots(document)\n for (const [slotName, value] of Object.entries(op.bindings)) {\n const slot = slots.get(slotName)\n if (!slot) {\n throw new Error(`slot \"${slotName}\" does not exist in the document`)\n }\n validateSlotValue(slotName, slot.kind, value)\n }\n}\n\nfunction validateSetDocumentTitle(op: SetDocumentTitleOperation): void {\n if (op.title.trim().length === 0) throw new Error('title must be non-empty')\n}\n\n// ---------------------------------------------------------------------------\n// Element attr validation\n// ---------------------------------------------------------------------------\n\n/** Attributes present on EVERY element kind (SceneElementBase minus id/kind). */\nconst BASE_ATTRS = new Set([\n 'name', 'x', 'y', 'rotation', 'opacity', 'locked', 'visible', 'slot',\n])\n\n/** Per-kind allowed attributes (beyond base). */\nconst KIND_ATTRS: Record<SceneElementKind, Set<string>> = {\n rect: new Set(['width', 'height', 'fill', 'stroke', 'strokeWidth', 'cornerRadius']),\n ellipse: new Set(['width', 'height', 'fill', 'stroke', 'strokeWidth']),\n line: new Set(['points', 'stroke', 'strokeWidth', 'dash']),\n text: new Set(['text', 'width', 'fontFamily', 'fontSize', 'fontStyle', 'fill', 'align', 'lineHeight', 'letterSpacing']),\n image: new Set(['width', 'height', 'src', 'fit']),\n video: new Set(['width', 'height', 'src', 'posterSrc']),\n group: new Set([]),\n}\n\nconst FONT_STYLES = new Set(['normal', 'bold', 'italic', 'bold italic'])\nconst ALIGN_VALUES = new Set(['left', 'center', 'right'])\nconst FIT_VALUES = new Set(['fill', 'cover', 'contain'])\n\n/**\n * Validates attribute patches (or full element attrs for add_element).\n * `isConstruction` = true when validating a new element (required fields\n * must be present); false for a partial set_attrs patch.\n */\nfunction validateElementAttrs(\n kind: SceneElementKind,\n attrs: SceneAttrsPatch,\n isConstruction: boolean,\n): void {\n const allowed = KIND_ATTRS[kind]\n\n // Foreign attribute check: reject any key that is neither base nor kind-specific.\n for (const key of Object.keys(attrs)) {\n if (key === 'id' || key === 'kind' || key === 'children') continue\n if (!BASE_ATTRS.has(key) && !allowed.has(key)) {\n throw new Error(`attribute \"${key}\" is not valid for a ${kind} element`)\n }\n }\n\n // Base attribute validation\n if (attrs.opacity !== undefined) {\n if (typeof attrs.opacity !== 'number' || !Number.isFinite(attrs.opacity) || attrs.opacity < 0 || attrs.opacity > 1) {\n throw new Error('opacity must be a number in [0, 1]')\n }\n }\n if (attrs.x !== undefined) assertFinite(attrs.x, 'x')\n if (attrs.y !== undefined) assertFinite(attrs.y, 'y')\n if (attrs.rotation !== undefined) assertFinite(attrs.rotation, 'rotation')\n\n // Kind-specific attribute validation\n switch (kind) {\n case 'rect':\n case 'ellipse':\n case 'image':\n case 'video':\n if (attrs.width !== undefined) assertPositiveFinite(attrs.width, 'width')\n if (attrs.height !== undefined) assertPositiveFinite(attrs.height, 'height')\n break\n case 'text':\n if (attrs.width !== undefined) assertPositiveFinite(attrs.width, 'width')\n if (attrs.fontSize !== undefined) assertPositiveFinite(attrs.fontSize, 'fontSize')\n if (attrs.lineHeight !== undefined) {\n if (typeof attrs.lineHeight !== 'number' || !Number.isFinite(attrs.lineHeight) || attrs.lineHeight <= 0) {\n throw new Error('lineHeight must be a positive finite number')\n }\n }\n if (attrs.fontStyle !== undefined && !FONT_STYLES.has(attrs.fontStyle)) {\n throw new Error(`fontStyle must be one of: ${[...FONT_STYLES].join(', ')}`)\n }\n if (attrs.align !== undefined && !ALIGN_VALUES.has(attrs.align)) {\n throw new Error(`align must be one of: ${[...ALIGN_VALUES].join(', ')}`)\n }\n break\n case 'line':\n if (attrs.points !== undefined) {\n if (!Array.isArray(attrs.points) || attrs.points.length < 4 || attrs.points.length % 2 !== 0) {\n throw new Error('points must be an even-length array with at least 4 numbers (2 points)')\n }\n for (let i = 0; i < attrs.points.length; i++) {\n if (!Number.isFinite(attrs.points[i])) {\n throw new Error(`points[${i}] is not finite`)\n }\n }\n }\n if (attrs.strokeWidth !== undefined) assertPositiveFinite(attrs.strokeWidth, 'strokeWidth')\n break\n case 'group':\n // Group geometry derives from children; no width/height attrs.\n break\n }\n\n // Color validation for fill/stroke\n if (attrs.fill !== undefined) assertColor(attrs.fill, 'fill')\n if (attrs.stroke !== undefined) assertColor(attrs.stroke, 'stroke')\n if (attrs.strokeWidth !== undefined && kind !== 'line') {\n assertPositiveFinite(attrs.strokeWidth, 'strokeWidth')\n }\n\n // Image/video src\n if (attrs.src !== undefined) assertSceneMediaSrc(attrs.src, 'src')\n if ((attrs as { posterSrc?: string }).posterSrc !== undefined) {\n assertSceneMediaSrc((attrs as { posterSrc: string }).posterSrc, 'posterSrc')\n }\n\n // fit\n if (attrs.fit !== undefined && !FIT_VALUES.has(attrs.fit)) {\n throw new Error(`fit must be one of: ${[...FIT_VALUES].join(', ')}`)\n }\n\n // Construction-time required-field checks\n if (isConstruction) {\n validateRequiredConstructionAttrs(kind, attrs)\n }\n}\n\nfunction validateRequiredConstructionAttrs(kind: SceneElementKind, attrs: SceneAttrsPatch): void {\n switch (kind) {\n case 'rect':\n requireAttrPresent(attrs, 'width', kind)\n requireAttrPresent(attrs, 'height', kind)\n requireAttrPresent(attrs, 'fill', kind)\n break\n case 'ellipse':\n requireAttrPresent(attrs, 'width', kind)\n requireAttrPresent(attrs, 'height', kind)\n requireAttrPresent(attrs, 'fill', kind)\n break\n case 'line':\n requireAttrPresent(attrs, 'points', kind)\n requireAttrPresent(attrs, 'stroke', kind)\n requireAttrPresent(attrs, 'strokeWidth', kind)\n break\n case 'text':\n requireAttrPresent(attrs, 'text', kind)\n requireAttrPresent(attrs, 'width', kind)\n requireAttrPresent(attrs, 'fontFamily', kind)\n requireAttrPresent(attrs, 'fontSize', kind)\n requireAttrPresent(attrs, 'fontStyle', kind)\n requireAttrPresent(attrs, 'fill', kind)\n requireAttrPresent(attrs, 'align', kind)\n requireAttrPresent(attrs, 'lineHeight', kind)\n requireAttrPresent(attrs, 'letterSpacing', kind)\n break\n case 'image':\n requireAttrPresent(attrs, 'width', kind)\n requireAttrPresent(attrs, 'height', kind)\n requireAttrPresent(attrs, 'src', kind)\n requireAttrPresent(attrs, 'fit', kind)\n break\n case 'video':\n requireAttrPresent(attrs, 'width', kind)\n requireAttrPresent(attrs, 'height', kind)\n requireAttrPresent(attrs, 'src', kind)\n break\n case 'group':\n // Groups are created empty; children are added separately.\n break\n }\n}\n\nfunction requireAttrPresent(attrs: SceneAttrsPatch, key: string, kind: SceneElementKind): void {\n if ((attrs as Record<string, unknown>)[key] === undefined) {\n throw new Error(`${key} is required when constructing a ${kind} element`)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Slot value typing (apply_data)\n// ---------------------------------------------------------------------------\n\n/**\n * Validates that a slot binding value matches the slot element's kind.\n * text → any string; image/video → media src; rect/ellipse/line/group → color.\n */\nexport function validateSlotValue(slotName: string, elementKind: SceneElementKind, value: string): void {\n switch (elementKind) {\n case 'text':\n // Any string is valid for a text slot.\n return\n case 'image':\n case 'video':\n try {\n assertSceneMediaSrc(value, `slot \"${slotName}\" value`)\n } catch (e) {\n throw new Error(\n `slot \"${slotName}\" is bound to a ${elementKind} element — value must be an http(s) URL or a rooted /api/ path (got \"${value}\")`,\n )\n }\n return\n case 'rect':\n case 'ellipse':\n case 'line':\n case 'group':\n try {\n assertColor(value, `slot \"${slotName}\" value`)\n } catch {\n throw new Error(\n `slot \"${slotName}\" is bound to a ${elementKind} element — value must be a color (hex/rgb(a)/transparent) for fill/stroke recolor (got \"${value}\")`,\n )\n }\n return\n }\n}\n\n// ---------------------------------------------------------------------------\n// Document-wide uniqueness\n// ---------------------------------------------------------------------------\n\nfunction assertUniqueIdDocumentWide(document: SceneDocument, id: string): void {\n for (const page of document.pages) {\n if (page.id === id) throw new Error(`id \"${id}\" is already used by a page`)\n const stack = [...page.elements]\n while (stack.length > 0) {\n const el = stack.pop()!\n if (el.id === id) throw new Error(`id \"${id}\" is already used by element on page \"${page.id}\"`)\n if (el.kind === 'group') stack.push(...el.children)\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal guards\n// ---------------------------------------------------------------------------\n\nfunction assertNonNegativeFinite(value: number, label: string): void {\n if (!Number.isFinite(value) || value < 0) {\n throw new Error(`${label} must be a non-negative finite number`)\n }\n}\n","/**\n * Pure application of validated scene operations to a SceneDocument.\n * `applySceneOperations` deep-clones the document then mutates the clone,\n * returning the new document and per-op results. It never touches the store,\n * a clock, or a PRNG — all id generation goes through the caller-supplied\n * `mintId` option so callers control determinism (counter in tests,\n * crypto.randomUUID in production).\n *\n * Rotation semantics for group/ungroup:\n * Group: children retain their absolute rotation. The group origin is the\n * min-x, min-y corner of the AABB union of all member elements. Children\n * are rebased so their position in group-local space equals\n * (element.x - group.x, element.y - group.y). A rotated child appears at\n * the same absolute page position after grouping because the group's additive\n * translation preserves the child's own-origin transform.\n * Ungroup: inverse — child.x += group.x, child.y += group.y; rotation stays.\n * This is consistent with Konva's transform-about-own-origin model without\n * needing a full matrix concatenation for the common case.\n *\n * `storeApplyScenePlan` layers store I/O on top: getDocument → validate →\n * apply → saveDocument(expectedRev) → recordDecision, retrying once on\n * stale-rev by refetch + revalidate + reapply.\n */\n\nimport {\n assertColor,\n assertSceneMediaSrc,\n collectSlots,\n createPage,\n elementAabb,\n requireElement,\n requirePage,\n} from './model'\nimport type {\n GroupElement,\n NewPageOptions,\n SceneDocument,\n SceneElement,\n ScenePage,\n} from './model'\nimport type {\n AddElementOperation,\n AddPageOperation,\n ApplyDataOperation,\n BindSlotOperation,\n DeleteElementOperation,\n DeletePageOperation,\n DuplicatePageOperation,\n GroupElementsOperation,\n ReorderElementOperation,\n ReorderPageOperation,\n SceneOperation,\n ScenePlan,\n SetAttrsOperation,\n SetDocumentTitleOperation,\n SetPageGuidesOperation,\n SetPagePropsOperation,\n UngroupElementOperation,\n} from './operations'\nimport type { NewSceneDecision, SceneDocumentRecord, SceneStore } from './store'\nimport { validateSceneOperations } from './validate'\n\n// ---------------------------------------------------------------------------\n// Result types\n// ---------------------------------------------------------------------------\n\nexport type SceneApplyResult =\n | { kind: 'element'; pageId: string; element: SceneElement }\n | { kind: 'page'; page: ScenePage }\n | { kind: 'document' }\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface ApplySceneOptions {\n /**\n * Provides fresh ids when duplicate_page re-mints element ids. Never called\n * by any other operation. Counter-based in tests; crypto.randomUUID in\n * production (wrapped so `mintId()` has no arguments, matching this signature).\n */\n mintId: () => string\n}\n\n// ---------------------------------------------------------------------------\n// Public: pure apply\n// ---------------------------------------------------------------------------\n\n/** Full form: returns the new document AND per-op results. `mintId` must be\n * provided when any operation in the list may be `duplicate_page` (which\n * re-mints element ids); for all other operation types it is never called. */\nexport function applySceneOperations(\n document: SceneDocument,\n operations: SceneOperation[],\n options: ApplySceneOptions,\n): { document: SceneDocument; results: SceneApplyResult[] }\n\n/** Convenience 2-arg form: returns the new document directly. Uses a\n * monotonic counter as the mintId so `duplicate_page` never collides within\n * a single call (suitable for editor-local optimistic state; server will\n * re-mint on persist). */\nexport function applySceneOperations(\n document: SceneDocument,\n operations: SceneOperation[],\n): SceneDocument\n\nexport function applySceneOperations(\n document: SceneDocument,\n operations: SceneOperation[],\n options?: ApplySceneOptions,\n): { document: SceneDocument; results: SceneApplyResult[] } | SceneDocument {\n const opts = options ?? { mintId: makeCounter() }\n const doc = deepCloneDocument(document)\n const results: SceneApplyResult[] = []\n for (const operation of operations) {\n results.push(applyOneOperation(doc, operation, opts))\n }\n return options !== undefined ? { document: doc, results } : doc\n}\n\n/** Apply a single operation to a document and return the new document.\n * Equivalent to `applySceneOperations(doc, [op])` but returns `SceneDocument`\n * directly — the common case in editor commands and tests. */\nexport function applySceneOperation(\n document: SceneDocument,\n operation: SceneOperation,\n): SceneDocument {\n return applySceneOperations(document, [operation])\n}\n\nfunction makeCounter(): () => string {\n let n = 0\n return () => `minted-${(n += 1)}`\n}\n\n// ---------------------------------------------------------------------------\n// Public: store-integrated helper\n// ---------------------------------------------------------------------------\n\n/** Detects the stale-revision sentinel thrown by drizzle-store. The store\n * throws a plain Error whose message contains \"stale rev\" — no custom class\n * is needed because the store contract documents exactly this phrase. */\nfunction isStaleRevError(err: unknown): boolean {\n return err instanceof Error && /stale rev/i.test(err.message)\n}\n\nexport async function storeApplyScenePlan(\n store: SceneStore,\n plan: ScenePlan,\n opts: { actorKind: NewSceneDecision['kind']; mintId: () => string },\n): Promise<{ record: SceneDocumentRecord; results: SceneApplyResult[] }> {\n let { document, rev } = await store.getDocument()\n\n validateSceneOperations(document, plan.operations)\n let applied = applySceneOperations(document, plan.operations, { mintId: opts.mintId })\n\n let record: SceneDocumentRecord\n try {\n record = await store.saveDocument(applied.document, rev)\n } catch (firstError) {\n // Only retry for stale-rev conflicts. Any other error (network, constraint,\n // permissions) is a hard failure — retrying would risk a double-apply if\n // the first save actually succeeded but the acknowledgement was lost.\n if (!isStaleRevError(firstError)) throw firstError\n const refreshed = await store.getDocument()\n validateSceneOperations(refreshed.document, plan.operations)\n applied = applySceneOperations(refreshed.document, plan.operations, { mintId: opts.mintId })\n try {\n record = await store.saveDocument(applied.document, refreshed.rev)\n } catch (secondError) {\n const reason = secondError instanceof Error ? secondError.message : String(secondError)\n throw new Error(`storeApplyScenePlan: stale rev persists after retry — ${reason}`)\n }\n }\n\n const opTypeCounts: Record<string, number> = {}\n for (const op of plan.operations) {\n opTypeCounts[op.type] = (opTypeCounts[op.type] ?? 0) + 1\n }\n\n await store.recordDecision({\n kind: opts.actorKind,\n instruction: plan.summary,\n metadata: { opTypeCounts, operationCount: plan.operations.length },\n })\n\n return { record, results: applied.results }\n}\n\n// ---------------------------------------------------------------------------\n// Per-operation dispatch (mutates cloned doc in-place)\n// ---------------------------------------------------------------------------\n\nfunction applyOneOperation(\n doc: SceneDocument,\n operation: SceneOperation,\n options: ApplySceneOptions,\n): SceneApplyResult {\n switch (operation.type) {\n case 'add_element':\n return applyAddElement(doc, operation)\n case 'set_attrs':\n return applySetAttrs(doc, operation)\n case 'reorder_element':\n return applyReorderElement(doc, operation)\n case 'delete_element':\n return applyDeleteElement(doc, operation)\n case 'group_elements':\n return applyGroupElements(doc, operation)\n case 'ungroup_element':\n return applyUngroupElement(doc, operation)\n case 'add_page':\n return applyAddPage(doc, operation)\n case 'duplicate_page':\n return applyDuplicatePage(doc, operation, options)\n case 'delete_page':\n return applyDeletePage(doc, operation)\n case 'reorder_page':\n return applyReorderPage(doc, operation)\n case 'set_page_props':\n return applySetPageProps(doc, operation)\n case 'set_page_guides':\n return applySetPageGuides(doc, operation)\n case 'bind_slot':\n return applyBindSlot(doc, operation)\n case 'apply_data':\n return applyApplyData(doc, operation)\n case 'set_document_title':\n return applySetDocumentTitle(doc, operation)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Operation implementations (all mutate the deep-cloned doc)\n// ---------------------------------------------------------------------------\n\nfunction applyAddElement(doc: SceneDocument, op: AddElementOperation): SceneApplyResult {\n const page = requirePage(doc, op.pageId)\n const owner: SceneElement[] = op.parentGroupId !== undefined\n ? (() => {\n const { element: g } = requireElement(page, op.parentGroupId)\n return (g as GroupElement).children\n })()\n : page.elements\n const index = op.index !== undefined ? op.index : owner.length\n owner.splice(index, 0, op.element)\n return { kind: 'element', pageId: op.pageId, element: op.element }\n}\n\nfunction applySetAttrs(doc: SceneDocument, op: SetAttrsOperation): SceneApplyResult {\n const page = requirePage(doc, op.pageId)\n const { element, owner, index } = requireElement(page, op.elementId)\n const patched = { ...element, ...op.attrs } as SceneElement\n owner[index] = patched\n return { kind: 'element', pageId: op.pageId, element: patched }\n}\n\nfunction applyReorderElement(doc: SceneDocument, op: ReorderElementOperation): SceneApplyResult {\n const page = requirePage(doc, op.pageId)\n const { element, owner, index } = requireElement(page, op.elementId)\n owner.splice(index, 1)\n owner.splice(op.toIndex, 0, element)\n return { kind: 'element', pageId: op.pageId, element }\n}\n\nfunction applyDeleteElement(doc: SceneDocument, op: DeleteElementOperation): SceneApplyResult {\n const page = requirePage(doc, op.pageId)\n const { element, owner, index } = requireElement(page, op.elementId)\n owner.splice(index, 1)\n return { kind: 'element', pageId: op.pageId, element }\n}\n\nfunction applyGroupElements(doc: SceneDocument, op: GroupElementsOperation): SceneApplyResult {\n const page = requirePage(doc, op.pageId)\n const members = op.elementIds.map((id) => requireElement(page, id))\n\n // Group origin = min AABB corner of all members in the owner coordinate space.\n let minX = Infinity, minY = Infinity\n for (const { element } of members) {\n const aabb = elementAabb(element)\n if (aabb.x < minX) minX = aabb.x\n if (aabb.y < minY) minY = aabb.y\n }\n\n const owner = members[0]!.owner\n // Sort ascending by index to preserve z-order in children array.\n const sortedByIndex = [...members].sort((a, b) => a.index - b.index)\n\n // Children retain rotation; x/y rebased to group-local space.\n const children: SceneElement[] = sortedByIndex.map(({ element }) => ({\n ...element,\n x: element.x - minX,\n y: element.y - minY,\n }))\n\n // Remove in reverse order to avoid index drift.\n for (const { index } of [...sortedByIndex].reverse()) {\n owner.splice(index, 1)\n }\n\n // Group inserted at the slot vacated by the bottommost former member.\n const insertAt = sortedByIndex[0]!.index\n\n const group: GroupElement = {\n id: op.groupId,\n kind: 'group',\n name: op.name ?? 'Group',\n x: minX,\n y: minY,\n rotation: 0,\n opacity: 1,\n locked: false,\n visible: true,\n children,\n }\n\n owner.splice(insertAt, 0, group)\n return { kind: 'element', pageId: op.pageId, element: group }\n}\n\nfunction applyUngroupElement(doc: SceneDocument, op: UngroupElementOperation): SceneApplyResult {\n const page = requirePage(doc, op.pageId)\n const { element: groupEl, owner, index: groupIndex } = requireElement(page, op.groupId)\n const group = groupEl as GroupElement\n\n // Re-absolutize: child.x/y += group.x/y; rotation unchanged.\n const promoted: SceneElement[] = group.children.map((child) => ({\n ...child,\n x: child.x + group.x,\n y: child.y + group.y,\n }))\n\n owner.splice(groupIndex, 1, ...promoted)\n return { kind: 'element', pageId: op.pageId, element: groupEl }\n}\n\nfunction applyAddPage(doc: SceneDocument, op: AddPageOperation): SceneApplyResult {\n const opts: NewPageOptions = op.options ?? {}\n const page = createPage(opts, op.pageId)\n const index = op.index !== undefined ? op.index : doc.pages.length\n doc.pages.splice(index, 0, page)\n return { kind: 'page', page }\n}\n\nfunction applyDuplicatePage(\n doc: SceneDocument,\n op: DuplicatePageOperation,\n options: ApplySceneOptions,\n): SceneApplyResult {\n const source = requirePage(doc, op.sourcePageId)\n const copy: ScenePage = JSON.parse(JSON.stringify(source)) as ScenePage\n copy.id = op.pageId\n // Re-mint all element ids so the copy has no id collisions with the source.\n remintElementIds(copy.elements, options.mintId)\n doc.pages.push(copy)\n return { kind: 'page', page: copy }\n}\n\nfunction remintElementIds(elements: SceneElement[], mintId: () => string): void {\n for (let i = 0; i < elements.length; i++) {\n const el = elements[i]!\n const newEl: SceneElement = { ...el, id: mintId() }\n if (newEl.kind === 'group') {\n remintElementIds((newEl as GroupElement).children, mintId)\n }\n elements[i] = newEl\n }\n}\n\nfunction applyDeletePage(doc: SceneDocument, op: DeletePageOperation): SceneApplyResult {\n if (doc.pages.length <= 1) throw new Error('delete_page: cannot delete the last page')\n const index = doc.pages.findIndex((p) => p.id === op.pageId)\n if (index < 0) throw new Error(`page ${op.pageId} not found`)\n const [page] = doc.pages.splice(index, 1) as [ScenePage]\n return { kind: 'page', page }\n}\n\nfunction applyReorderPage(doc: SceneDocument, op: ReorderPageOperation): SceneApplyResult {\n const index = doc.pages.findIndex((p) => p.id === op.pageId)\n if (index < 0) throw new Error(`page ${op.pageId} not found`)\n const [page] = doc.pages.splice(index, 1) as [ScenePage]\n doc.pages.splice(op.toIndex, 0, page)\n return { kind: 'page', page }\n}\n\nfunction applySetPageProps(doc: SceneDocument, op: SetPagePropsOperation): SceneApplyResult {\n const page = requirePage(doc, op.pageId)\n if (op.name !== undefined) page.name = op.name\n if (op.width !== undefined) page.width = op.width\n if (op.height !== undefined) page.height = op.height\n if (op.background !== undefined) page.background = op.background\n if (op.bleed !== undefined) page.bleed = op.bleed\n return { kind: 'page', page }\n}\n\nfunction applySetPageGuides(doc: SceneDocument, op: SetPageGuidesOperation): SceneApplyResult {\n const page = requirePage(doc, op.pageId)\n page.guides = op.guides\n return { kind: 'page', page }\n}\n\nfunction applyBindSlot(doc: SceneDocument, op: BindSlotOperation): SceneApplyResult {\n const page = requirePage(doc, op.pageId)\n const { element, owner, index } = requireElement(page, op.elementId)\n const patched: SceneElement = op.slot === null ? omitSlot(element) : { ...element, slot: op.slot }\n owner[index] = patched\n return { kind: 'element', pageId: op.pageId, element: patched }\n}\n\nfunction omitSlot(element: SceneElement): SceneElement {\n const { slot: _slot, ...rest } = element as SceneElement & { slot?: string }\n return rest as SceneElement\n}\n\nfunction applyApplyData(doc: SceneDocument, op: ApplyDataOperation): SceneApplyResult {\n const slots = collectSlots(doc)\n for (const [slotName, value] of Object.entries(op.bindings)) {\n const slot = slots.get(slotName)\n if (!slot) throw new Error(`slot \"${slotName}\" not found in document`)\n const page = requirePage(doc, slot.pageId)\n const { element, owner, index } = requireElement(page, slot.elementId)\n owner[index] = applySlotValue(element, value)\n }\n return { kind: 'document' }\n}\n\nfunction applySlotValue(element: SceneElement, value: string): SceneElement {\n switch (element.kind) {\n case 'text':\n return { ...element, text: value }\n case 'image':\n case 'video':\n assertSceneMediaSrc(value, 'slot value')\n return { ...element, src: value }\n case 'rect':\n case 'ellipse':\n assertColor(value, 'slot value')\n return { ...element, fill: value }\n case 'line':\n assertColor(value, 'slot value')\n return { ...element, stroke: value }\n case 'group':\n // Group recolor: propagate fill/stroke to children that carry the property.\n assertColor(value, 'slot value')\n return recolorGroupChildren(element as GroupElement, value)\n }\n}\n\nfunction recolorGroupChildren(group: GroupElement, color: string): GroupElement {\n const children = group.children.map((child): SceneElement => {\n switch (child.kind) {\n case 'rect':\n case 'ellipse':\n return { ...child, fill: color }\n case 'line':\n return { ...child, stroke: color }\n case 'text':\n return { ...child, fill: color }\n case 'image':\n case 'video':\n return child\n case 'group':\n return recolorGroupChildren(child, color)\n }\n })\n return { ...group, children }\n}\n\nfunction applySetDocumentTitle(doc: SceneDocument, op: SetDocumentTitleOperation): SceneApplyResult {\n doc.title = op.title\n return { kind: 'document' }\n}\n\n// ---------------------------------------------------------------------------\n// Deep clone\n// ---------------------------------------------------------------------------\n\nfunction deepCloneDocument(document: SceneDocument): SceneDocument {\n return JSON.parse(JSON.stringify(document)) as SceneDocument\n}\n","/**\n * Page size presets, channel-size output presets, and export math for the\n * design canvas.\n *\n * Size presets (SizePreset / SIZE_PRESETS) describe page dimensions for the\n * new-page dialog. Export presets (ExportPreset / EXPORT_PRESETS) pin pixel\n * ratio, output dimensions, bleed, and format for the export dialog and the\n * MCP export tool. Channel presets (ChannelPreset / CHANNEL_PRESETS) are the\n * fixed output resolutions the export UI offers; `scaleForPreset` + the\n * letterbox helper derive Konva stage parameters from them.\n *\n * All dimensions are CSS pixels at 96 DPI unless noted otherwise.\n */\n\nimport type { Bounds, PageBleed, ScenePage } from './model'\n\nexport interface SizePreset {\n id: string\n label: string\n category: 'social' | 'print' | 'presentation' | 'custom'\n width: number\n height: number\n}\n\nexport const SIZE_PRESETS: readonly SizePreset[] = [\n // Social\n { id: 'instagram-square', label: 'Instagram — Square', category: 'social', width: 1080, height: 1080 },\n { id: 'instagram-portrait', label: 'Instagram — Portrait', category: 'social', width: 1080, height: 1350 },\n { id: 'instagram-story', label: 'Instagram Story', category: 'social', width: 1080, height: 1920 },\n { id: 'twitter-post', label: 'X / Twitter Post', category: 'social', width: 1200, height: 675 },\n { id: 'linkedin-post', label: 'LinkedIn Post', category: 'social', width: 1200, height: 627 },\n { id: 'facebook-post', label: 'Facebook Post', category: 'social', width: 1200, height: 630 },\n { id: 'youtube-thumbnail', label: 'YouTube Thumbnail', category: 'social', width: 1280, height: 720 },\n { id: 'og-image', label: 'Open Graph Image', category: 'social', width: 1200, height: 630 },\n // Presentation\n { id: 'slide-16-9', label: 'Slide — 16:9', category: 'presentation', width: 1920, height: 1080 },\n { id: 'slide-4-3', label: 'Slide — 4:3', category: 'presentation', width: 1024, height: 768 },\n // Print (96 DPI px equivalents of A4, Letter — products may scale at export)\n { id: 'a4-landscape', label: 'A4 Landscape', category: 'print', width: 1123, height: 794 },\n { id: 'a4-portrait', label: 'A4 Portrait', category: 'print', width: 794, height: 1123 },\n { id: 'us-letter-landscape', label: 'US Letter Landscape', category: 'print', width: 1100, height: 850 },\n { id: 'us-letter-portrait', label: 'US Letter Portrait', category: 'print', width: 850, height: 1100 },\n] as const\n\nexport function findPreset(id: string): SizePreset | null {\n return SIZE_PRESETS.find((p) => p.id === id) ?? null\n}\n\n/** Match a (width, height) pair against the preset table. Returns the first\n * exact match or null — used to drive the dropdown selection indicator. */\nexport function matchPreset(width: number, height: number): SizePreset | null {\n return SIZE_PRESETS.find((p) => p.width === width && p.height === height) ?? null\n}\n\n// ---------------------------------------------------------------------------\n// Export quality presets\n// ---------------------------------------------------------------------------\n\nexport type ExportFormat = 'png' | 'jpeg'\n\n/**\n * Export quality preset — pins pixel density, optional output dimensions,\n * bleed inclusion, and raster format so callers pass a preset id rather than\n * a full parameter bag.\n *\n * `outputWidth` / `outputHeight`: when non-null, the pixel ratio is derived\n * from the crop rect width (see `scaleForPreset`) so the output is exactly\n * this many pixels wide. When null, `pixelRatio` applies directly.\n */\nexport interface ExportPreset {\n name: string\n /** Target pixels-per-document-px for stage.toDataURL. */\n pixelRatio: number\n outputWidth: number | null\n outputHeight: number | null\n /** Whether bleed margins are included in the crop rect. */\n includeBleed: boolean\n format: ExportFormat\n}\n\nexport const EXPORT_PRESETS: Record<string, ExportPreset> = {\n 'instagram-square': {\n name: 'Instagram square (1080×1080)',\n pixelRatio: 1,\n outputWidth: 1080,\n outputHeight: 1080,\n includeBleed: false,\n format: 'jpeg',\n },\n 'instagram-portrait': {\n name: 'Instagram portrait (1080×1350)',\n pixelRatio: 1,\n outputWidth: 1080,\n outputHeight: 1350,\n includeBleed: false,\n format: 'jpeg',\n },\n 'twitter-card': {\n name: 'Twitter/X card (1200×675)',\n pixelRatio: 1,\n outputWidth: 1200,\n outputHeight: 675,\n includeBleed: false,\n format: 'jpeg',\n },\n 'og-image': {\n name: 'OG image (1200×630)',\n pixelRatio: 1,\n outputWidth: 1200,\n outputHeight: 630,\n includeBleed: false,\n format: 'png',\n },\n 'print-a4': {\n name: 'Print A4 (300 dpi)',\n pixelRatio: 3.125,\n outputWidth: null,\n outputHeight: null,\n includeBleed: true,\n format: 'png',\n },\n 'screen-2x': {\n name: 'Screen @2×',\n pixelRatio: 2,\n outputWidth: null,\n outputHeight: null,\n includeBleed: false,\n format: 'png',\n },\n} as const\n\n// ---------------------------------------------------------------------------\n// Crop + scale math (pure, no canvas context)\n// ---------------------------------------------------------------------------\n\nexport interface ExportCropRect {\n /** Page-coordinate origin. Negative when bleed extends outside page bounds. */\n x: number\n y: number\n width: number\n height: number\n}\n\n/**\n * Crop rectangle in page coordinates for a given page, optionally expanded to\n * include bleed margins. Bleed extends OUTSIDE the page bounds, so the origin\n * goes negative when bleed is included.\n *\n * When `includeBleed` is true but `page.bleed` is null, the page rect is\n * returned unchanged — the caller must not assume symmetric expansion.\n */\nexport function bleedAwareExportBounds(page: ScenePage, includeBleed: boolean): ExportCropRect {\n if (!includeBleed || page.bleed === null) {\n return { x: 0, y: 0, width: page.width, height: page.height }\n }\n const bleed: PageBleed = page.bleed\n return {\n x: -bleed.left,\n y: -bleed.top,\n width: page.width + bleed.left + bleed.right,\n height: page.height + bleed.top + bleed.bottom,\n }\n}\n\n/**\n * Pixel ratio for stage.toDataURL given a crop rect and an export preset.\n *\n * When the preset pins `outputWidth`, the ratio is derived from the crop rect\n * width so the final raster is exactly that many pixels wide. When there is no\n * output pin, the preset's declared `pixelRatio` is returned directly.\n *\n * The crop rect must already account for bleed inclusion before this call.\n */\nexport function scaleForPreset(preset: ExportPreset, cropRect: ExportCropRect): number {\n if (preset.outputWidth !== null) {\n if (cropRect.width <= 0) {\n throw new Error(`export crop width must be positive, got ${cropRect.width}`)\n }\n return preset.outputWidth / cropRect.width\n }\n return preset.pixelRatio\n}\n\n// ---------------------------------------------------------------------------\n// Channel-size presets (fixed output resolutions for the export UI)\n// ---------------------------------------------------------------------------\n\nexport interface ChannelPreset {\n id: string\n label: string\n width: number\n height: number\n}\n\n/**\n * Fixed output resolution presets for the channel/platform export dialog.\n * Width × height are OUTPUT pixels (the raster the export produces), not page\n * CSS px — they describe the target delivery format, not the canvas layout.\n * The a4_print_2480x3508 preset corresponds to A4 at 300 dpi.\n */\nexport const CHANNEL_PRESETS: readonly ChannelPreset[] = [\n { id: 'square_1080', label: 'Square (1080×1080)', width: 1080, height: 1080 },\n { id: 'portrait_1080x1350', label: 'Portrait (1080×1350)', width: 1080, height: 1350 },\n { id: 'story_1080x1920', label: 'Story (1080×1920)', width: 1080, height: 1920 },\n { id: 'landscape_1200x628', label: 'Landscape (1200×628)', width: 1200, height: 628 },\n { id: 'wide_1920x1080', label: 'Wide (1920×1080)', width: 1920, height: 1080 },\n { id: 'og_1200x630', label: 'Open Graph (1200×630)', width: 1200, height: 630 },\n { id: 'a4_print_2480x3508', label: 'A4 Print (2480×3508 · 300 dpi)', width: 2480, height: 3508 },\n] as const\n\nexport type ChannelPresetId = (typeof CHANNEL_PRESETS)[number]['id']\n\n/** Throws when the id is unknown — callers should only pass ids sourced from\n * `CHANNEL_PRESETS`. */\nexport function requireChannelPreset(id: string): ChannelPreset {\n const found = CHANNEL_PRESETS.find((p) => p.id === id)\n if (!found) {\n throw new Error(\n `unknown channel preset \"${id}\" — valid ids: ${CHANNEL_PRESETS.map((p) => p.id).join(', ')}`,\n )\n }\n return found\n}\n\n// ---------------------------------------------------------------------------\n// Letterbox scale math (page → channel preset, centered contain)\n// ---------------------------------------------------------------------------\n\nexport interface ChannelScaleResult {\n /**\n * Konva stage pixelRatio: the stage logical size stays at page dimensions;\n * the backing canvas renders at `pixelRatio × page` px. Setting Konva's\n * pixelRatio to this value yields an output canvas exactly\n * `channelPreset.width × channelPreset.height` pixels.\n */\n pixelRatio: number\n /**\n * Horizontal letterbox offset in PAGE-coordinate px. Add to the stage x\n * translation so the page is centered horizontally in the output frame.\n * Zero when the page fills the full width after scaling.\n */\n offsetX: number\n /**\n * Vertical letterbox offset in PAGE-coordinate px. Add to the stage y\n * translation. Zero when the page fills the full height.\n */\n offsetY: number\n fit: 'contain'\n}\n\n/**\n * Computes the Konva stage parameters to render `page` centered inside\n * `channelPreset` without cropping (contain / letterbox).\n *\n * Exact-ratio fast path: when page and preset share the same aspect ratio\n * (within 1e-9 floating-point tolerance) both offsets are 0 and pixelRatio\n * is exact.\n *\n * Example — 1080×1080 page into 1920×1080 preset:\n * scaleX = 1920/1080 ≈ 1.7778, scaleY = 1080/1080 = 1.0\n * pixelRatio = 1.0, rendered page = 1080×1080 px\n * offsetX = (1920 − 1080) / 2 / 1.0 = 420 page-px, offsetY = 0\n */\nexport function scalePageForChannelPreset(\n page: Pick<ScenePage, 'width' | 'height'>,\n channelPreset: ChannelPreset,\n): ChannelScaleResult {\n if (page.width <= 0 || page.height <= 0) {\n throw new Error(`page dimensions must be positive; got ${page.width}×${page.height}`)\n }\n\n const scaleX = channelPreset.width / page.width\n const scaleY = channelPreset.height / page.height\n const pixelRatio = Math.min(scaleX, scaleY)\n\n const renderedW = page.width * pixelRatio\n const renderedH = page.height * pixelRatio\n\n // Back-project padding into page coordinates so the Konva offset is in the\n // stage's own coordinate space\n const offsetX = ((channelPreset.width - renderedW) / 2) / pixelRatio\n const offsetY = ((channelPreset.height - renderedH) / 2) / pixelRatio\n\n return { pixelRatio, offsetX, offsetY, fit: 'contain' }\n}\n\n// ---------------------------------------------------------------------------\n// Bleed-aware trim bounds (no-arg bleed variant for templates/channel exports)\n// ---------------------------------------------------------------------------\n\n/**\n * Export rectangle in page coordinates that includes bleed margins when\n * present. When `page.bleed` is null, returns the trim rectangle (origin 0,0;\n * size = page dimensions).\n *\n * The bleed rectangle extends OUTSIDE the page: x and y are negative (bleed\n * bleeds off the left/top edge), width and height exceed the page by the\n * combined bleed on each axis. Pass into Konva's clip/export bounds to\n * include the bleed zone in the render.\n *\n * Named `bleedAwareExportRect` to avoid collision with the two-arg\n * `bleedAwareExportBounds(page, includeBleed)` above.\n */\nexport function bleedAwareExportRect(page: Pick<ScenePage, 'width' | 'height' | 'bleed'>): Bounds {\n if (page.bleed === null) {\n return { x: 0, y: 0, width: page.width, height: page.height }\n }\n const bleed: PageBleed = page.bleed\n return {\n x: -bleed.left,\n y: -bleed.top,\n width: page.width + bleed.left + bleed.right,\n height: page.height + bleed.top + bleed.bottom,\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/runtime/model-catalog.ts","../src/runtime/openai-stream.ts","../src/runtime/agent.ts","../src/runtime/certified-delivery.ts","../src/runtime/surface-profile.ts","../src/runtime/index.ts"],"sourcesContent":["/**\n * Model catalogue — computed live from the Tangle Router, never hand-curated.\n * Lifted from tuner-agent so every agent app's model picker shares one\n * filter/dedupe/rank/feature pipeline instead of re-deriving it.\n *\n * The router's /models endpoint returns every routeable model (~200), which is\n * unusable as a picker list: it mixes chat models with TTS/embedding/realtime\n * endpoints, dated snapshots alias their parents, and provider-prefixed ids\n * duplicate canonical ones. This module turns that into a product catalogue:\n *\n * filter (chat-capable, routeable) → dedupe (snapshot/prefix/:free aliases)\n * → rank (provider tier, family, version) → feature (best model per family)\n * → default (env override or first featured)\n *\n * Freshness is automatic: everything is derived from the live router response,\n * so new models surface as soon as the router lists them. The only static\n * knowledge here is slow-moving: provider display order and family name\n * patterns (e.g. \"claude-sonnet-*\", \"gpt-N\"). A new Sonnet or GPT release\n * outranks its predecessor by version comparison with zero code change; only\n * a brand-new *family name* (rare) needs a one-line rule addition.\n */\n\nexport interface RouterModel {\n id: string\n name?: string\n description?: string\n _provider?: string\n pricing?: { prompt?: string | null; completion?: string | null }\n context_length?: number\n architecture?: {\n modality?: string\n input_modalities?: string[]\n output_modalities?: string[]\n }\n supported_parameters?: string[]\n routeability?: {\n status?: string\n routeable?: boolean\n provider?: string\n }\n}\n\nexport interface CatalogModel {\n id: string\n name: string\n provider: string\n description?: string\n contextLength?: number\n pricing?: { prompt?: string; completion?: string }\n supportsTools: boolean\n supportsReasoning: boolean\n featured: boolean\n}\n\nexport interface ModelCatalog {\n defaultModelId: string | null\n fetchedAt: string\n models: CatalogModel[]\n}\n\n/** Display order. Unlisted providers sort after these, alphabetically. */\nconst PROVIDER_TIER: string[] = [\n 'anthropic',\n 'openai',\n 'google',\n 'xai',\n 'deepseek',\n 'moonshotai',\n 'moonshot',\n 'zai',\n 'z-ai',\n 'mistral',\n 'groq',\n 'nvidia',\n 'cohere',\n 'cerebras',\n]\n\n/** Non-chat endpoints that pollute the router list (matched on normalized id). */\nconst EXCLUDED_ID = /(embedding|tts|transcribe|whisper|audio|realtime|image|lyria|sora|dall-e|moderation|content-safety|search-preview|search-api|deep-research)/\n\n/**\n * Featured families, in display order. Each rule surfaces the highest-version\n * routeable model whose normalized id matches. Patterns anchor on the family\n * name and stop before specialty suffixes (codex, nano, lite, …) so the\n * mainline model wins.\n */\nconst FEATURED_RULES: Array<{ providers: string[]; match: RegExp }> = [\n { providers: ['anthropic'], match: /^claude-sonnet-[\\d-]+$/ },\n { providers: ['anthropic'], match: /^claude-opus-[\\d-]+$/ },\n { providers: ['anthropic'], match: /^claude-haiku-[\\d-]+$/ },\n { providers: ['openai'], match: /^gpt-\\d+(\\.\\d+)?$/ },\n { providers: ['openai'], match: /^gpt-\\d+(\\.\\d+)?-mini$/ },\n { providers: ['google'], match: /^gemini-[\\d.]+-pro(-preview)?$/ },\n { providers: ['google'], match: /^gemini-[\\d.]+-flash(-preview)?$/ },\n { providers: ['xai'], match: /^grok-[\\d.]+$/ },\n { providers: ['deepseek'], match: /^deepseek-(chat|v[\\d.]+(-\\w+)?)$/ },\n { providers: ['moonshotai', 'moonshot'], match: /^kimi-k[\\d.]+$/ },\n { providers: ['zai', 'z-ai'], match: /^glm-[\\d.]+$/ },\n { providers: ['mistral'], match: /^mistral-(large|medium)-?[\\d.-]*$/ },\n]\n\n/** Families known to support tool calls even when router metadata omits it\n * (dated snapshots often lack the supported_parameters of their parent). */\nconst TOOL_CAPABLE_FAMILY = /^(claude|gpt-[45]|gpt-oss|o[134]|gemini|grok|deepseek|glm|kimi|mistral|ministral|magistral|command|nemotron|llama)/\n\n/** Strip provider prefix, :free suffix, and trailing date stamps. */\nexport function normalizeModelId(id: string): string {\n let tail = id.split('/').pop() ?? id\n tail = tail.replace(/:free$/, '')\n tail = tail.replace(/-\\d{8}$/, '')\n tail = tail.replace(/-\\d{4}-\\d{2}-\\d{2}$/, '')\n return tail\n}\n\n/** All numeric groups in a normalized id, for version comparison. */\nfunction versionOf(normId: string): number[] {\n return (normId.match(/\\d+/g) ?? []).map(Number)\n}\n\nfunction compareVersions(a: number[], b: number[]): number {\n const len = Math.max(a.length, b.length)\n for (let i = 0; i < len; i++) {\n const d = (a[i] ?? -1) - (b[i] ?? -1)\n if (d !== 0) return d\n }\n return 0\n}\n\n/** Lower = preferred representative for an alias group. */\nfunction aliasPenalty(id: string): number {\n let p = 0\n if (id.includes('/')) p += 4\n if (/-\\d{8}$|-\\d{4}-\\d{2}-\\d{2}$/.test(id.replace(/:free$/, ''))) p += 2\n if (id.endsWith(':free')) p += 1\n return p\n}\n\nfunction providerRank(provider: string): number {\n const i = PROVIDER_TIER.indexOf(provider)\n return i === -1 ? PROVIDER_TIER.length : i\n}\n\nfunction isChatModel(m: RouterModel): boolean {\n const arch = m.architecture\n if (!arch?.input_modalities || !arch?.output_modalities) return true\n return arch.input_modalities.includes('text') && arch.output_modalities.includes('text')\n}\n\nfunction isRouteable(m: RouterModel): boolean {\n return m.routeability?.routeable !== false && m.routeability?.status !== 'unavailable'\n}\n\nfunction familyOf(normId: string): string {\n return normId.replace(/[\\d.]+/g, '').replace(/-+/g, '-').replace(/-$/, '')\n}\n\n/**\n * Pure catalogue pipeline. `preferredDefault` (typically the MODEL_NAME env\n * var) wins when it survives filtering; otherwise the first featured model.\n */\nexport function buildCatalog(raw: RouterModel[], opts?: { preferredDefault?: string }): ModelCatalog {\n // Filter to chat-capable, routeable, non-specialty models\n const candidates = raw.filter(\n (m) => m.id && isRouteable(m) && isChatModel(m) && !EXCLUDED_ID.test(normalizeModelId(m.id)),\n )\n\n // Dedupe alias groups (dated snapshots, provider prefixes, :free variants).\n // Within a group, merge metadata so the representative keeps the richest\n // supported_parameters claim (snapshots often omit what the parent lists).\n const groups = new Map<string, RouterModel[]>()\n for (const m of candidates) {\n const key = `${m._provider ?? ''}::${normalizeModelId(m.id)}`\n const g = groups.get(key)\n if (g) g.push(m)\n else groups.set(key, [m])\n }\n\n const reps: Array<{ model: RouterModel; normId: string; mergedParams: Set<string> }> = []\n for (const group of groups.values()) {\n group.sort((a, b) => aliasPenalty(a.id) - aliasPenalty(b.id) || a.id.length - b.id.length)\n const rep = group[0]!\n const mergedParams = new Set<string>(group.flatMap((m) => m.supported_parameters ?? []))\n reps.push({ model: rep, normId: normalizeModelId(rep.id), mergedParams })\n }\n\n // Featured: best version per family rule, in rule order\n const featuredIds: string[] = []\n for (const rule of FEATURED_RULES) {\n const matches = reps.filter(\n (r) =>\n rule.providers.includes(r.model._provider ?? '') &&\n rule.match.test(r.normId) &&\n !featuredIds.includes(r.model.id),\n )\n if (!matches.length) continue\n matches.sort(\n (a, b) =>\n compareVersions(versionOf(b.normId), versionOf(a.normId)) ||\n Number(a.normId.includes('preview')) - Number(b.normId.includes('preview')) ||\n a.model.id.length - b.model.id.length,\n )\n featuredIds.push(matches[0]!.model.id)\n }\n\n const toCatalogModel = (r: (typeof reps)[number]): CatalogModel => {\n const m = r.model\n const provider = m._provider ?? 'unknown'\n return {\n id: m.id,\n name: m.name ?? m.id,\n provider,\n description: m.description ? m.description.slice(0, 160) : undefined,\n contextLength: m.context_length,\n pricing:\n m.pricing?.prompt || m.pricing?.completion\n ? { prompt: m.pricing.prompt ?? undefined, completion: m.pricing.completion ?? undefined }\n : undefined,\n supportsTools: r.mergedParams.has('tools') || TOOL_CAPABLE_FAMILY.test(r.normId),\n supportsReasoning: r.mergedParams.has('reasoning') || r.mergedParams.has('include_reasoning'),\n featured: featuredIds.includes(m.id),\n }\n }\n\n // Sort: featured first (rule order), then provider tier → family → version desc\n const featured = featuredIds\n .map((id) => reps.find((r) => r.model.id === id)!)\n .map(toCatalogModel)\n const rest = reps\n .filter((r) => !featuredIds.includes(r.model.id))\n .sort((a, b) => {\n const pa = providerRank(a.model._provider ?? '')\n const pb = providerRank(b.model._provider ?? '')\n if (pa !== pb) return pa - pb\n const fa = familyOf(a.normId)\n const fb = familyOf(b.normId)\n if (fa !== fb) return fa.localeCompare(fb)\n return compareVersions(versionOf(b.normId), versionOf(a.normId)) || a.model.id.localeCompare(b.model.id)\n })\n .map(toCatalogModel)\n\n const models = [...featured, ...rest]\n\n const preferred = opts?.preferredDefault\n const defaultModelId =\n (preferred && models.find((m) => m.id === preferred || normalizeModelId(m.id) === normalizeModelId(preferred))?.id) ||\n featured.find((m) => m.supportsTools)?.id ||\n models[0]?.id ||\n null\n\n return { defaultModelId, fetchedAt: new Date().toISOString(), models }\n}\n\n// ── Cached fetch ─────────────────────────────────────────────────────────\n\nconst CATALOG_TTL_MS = 5 * 60 * 1000\n\nlet _cache: { catalog: ModelCatalog; at: number } | null = null\n\n/**\n * Fetch the router model list and build the catalogue, with an in-isolate\n * cache (TTL 5 min). On router failure a stale catalogue is served rather\n * than erroring the picker.\n */\nexport async function fetchModelCatalog(cfg: {\n baseUrl: string\n apiKey: string\n preferredDefault?: string\n}): Promise<ModelCatalog> {\n if (_cache && Date.now() - _cache.at < CATALOG_TTL_MS) {\n return _cache.catalog\n }\n try {\n const res = await fetch(`${cfg.baseUrl}/models`, {\n headers: { Authorization: `Bearer ${cfg.apiKey}` },\n })\n if (!res.ok) throw new Error(`Router /models returned ${res.status}`)\n const data = (await res.json()) as { data?: RouterModel[] }\n const catalog = buildCatalog(data.data ?? [], { preferredDefault: cfg.preferredDefault })\n _cache = { catalog, at: Date.now() }\n return catalog\n } catch (err) {\n if (_cache) return _cache.catalog\n throw err\n }\n}\n\n/** Test-only: clear the catalogue cache. */\nexport function __resetCatalogCache(): void {\n _cache = null\n}\n","/**\n * OpenAI-compatible stream → `LoopEvent` adapter, for NON-sandbox copilots.\n *\n * `streamAppToolLoop` takes a `streamTurn` seam that yields `LoopEvent`s. A\n * sandboxed agent produces those from its container; a browser/edge copilot\n * instead calls a model directly. The Tangle Router, the tcloud SDK, and most\n * providers all speak the OpenAI Chat Completions streaming shape — so the ONE\n * reusable piece is assembling that stream (content deltas + FRAGMENTED\n * tool-call deltas) into `LoopEvent`s. That assembly is the boilerplate every\n * copilot would re-write (and get wrong — OpenAI streams tool-call arguments in\n * pieces across chunks).\n *\n * This does NOT implement an HTTP client beyond a minimal `fetch` + SSE reader\n * (browser/edge/Node-safe, zero deps). For richer transport use the tcloud SDK\n * or the Vercel AI SDK and pipe their stream through {@link toLoopEvents}.\n */\nimport type { LoopEvent, LoopMessage, LoopToolCall } from './index'\n\n/** Minimal OpenAI Chat Completions streaming chunk (structural — no `openai` dep). */\nexport interface OpenAIStreamChunk {\n choices?: Array<{\n delta?: {\n content?: string | null\n /** Reasoning deltas — DeepSeek/router use `reasoning_content`; some proxies use `thinking`. */\n reasoning_content?: string | null\n thinking?: string | null\n tool_calls?: Array<{\n index: number\n id?: string\n function?: { name?: string; arguments?: string }\n }>\n }\n finish_reason?: string | null\n }>\n /** Final-chunk token accounting (requires `stream_options.include_usage`). */\n usage?: {\n prompt_tokens?: number\n completion_tokens?: number\n } | null\n}\n\ninterface PartialToolCall {\n id?: string\n name: string\n args: string\n}\n\n/**\n * Map an OpenAI-compat streaming chunk iterator to `LoopEvent`s: each content\n * delta → a `text` event; tool-call deltas are accumulated by index across\n * chunks and emitted as one complete `tool_call` event when the stream finishes\n * (arguments JSON-parsed; an empty/garbled args string yields `{}` rather than\n * throwing). Works for the Tangle Router, tcloud, or any OpenAI-compat source.\n */\nexport async function* toLoopEvents(chunks: AsyncIterable<OpenAIStreamChunk>): AsyncIterable<LoopEvent> {\n const calls = new Map<number, PartialToolCall>()\n for await (const chunk of chunks) {\n // Usage rides the final chunk, which has an empty choices array — handle\n // it before the choice guard.\n if (chunk.usage?.prompt_tokens != null || chunk.usage?.completion_tokens != null) {\n yield {\n type: 'usage',\n usage: {\n promptTokens: chunk.usage.prompt_tokens ?? 0,\n completionTokens: chunk.usage.completion_tokens ?? 0,\n },\n }\n }\n const choice = chunk.choices?.[0]\n if (!choice) continue\n const content = choice.delta?.content\n if (content) yield { type: 'text', text: content }\n const reasoning = choice.delta?.reasoning_content ?? choice.delta?.thinking\n if (reasoning) yield { type: 'reasoning', text: reasoning }\n for (const tc of choice.delta?.tool_calls ?? []) {\n const cur = calls.get(tc.index) ?? { name: '', args: '' }\n if (tc.id) cur.id = tc.id\n if (tc.function?.name) cur.name += tc.function.name\n if (tc.function?.arguments) cur.args += tc.function.arguments\n calls.set(tc.index, cur)\n }\n }\n for (const [, c] of [...calls.entries()].sort((a, b) => a[0] - b[0])) {\n if (!c.name) continue\n yield { type: 'tool_call', call: { toolCallId: c.id, toolName: c.name, args: safeParse(c.args) } satisfies LoopToolCall }\n }\n}\n\nfunction safeParse(s: string): Record<string, unknown> {\n if (!s.trim()) return {}\n try {\n const v = JSON.parse(s)\n return v && typeof v === 'object' && !Array.isArray(v) ? (v as Record<string, unknown>) : {}\n } catch {\n return {}\n }\n}\n\nexport interface OpenAICompatStreamTurnOptions {\n /** OpenAI-compat base URL (e.g. the Tangle Router `https://router.tangle.tools/v1`). */\n baseUrl: string\n apiKey: string\n model: string\n /** OpenAI tool definitions — pass `buildAppToolOpenAITools(taxonomy)` so the\n * model can call the app tools. Omit for a tool-free copilot. */\n tools?: unknown[]\n temperature?: number\n fetchImpl?: typeof fetch\n /** Extra body fields (e.g. `max_tokens`). */\n extraBody?: Record<string, unknown>\n}\n\n/**\n * Build a `streamTurn` that calls an OpenAI-compatible `/chat/completions`\n * endpoint (Tangle Router / tcloud / any compat provider) with `stream: true`\n * and yields `LoopEvent`s via {@link toLoopEvents}. Browser/edge/Node-safe —\n * just `fetch` + an SSE reader. Drop straight into `streamAppToolLoop`:\n *\n * const cfg = resolveTangleModelConfig() // or { baseUrl, apiKey, model }\n * streamAppToolLoop({ streamTurn: createOpenAICompatStreamTurn({ ...cfg, tools }), executeToolCall, ... })\n */\nexport function createOpenAICompatStreamTurn(\n opts: OpenAICompatStreamTurnOptions,\n): (messages: LoopMessage[]) => AsyncIterable<LoopEvent> {\n const base = opts.baseUrl.replace(/\\/+$/, '')\n const doFetch = opts.fetchImpl ?? fetch\n return (messages) =>\n toLoopEvents(\n streamChatCompletions(doFetch, `${base}/chat/completions`, opts.apiKey, {\n model: opts.model,\n messages,\n stream: true,\n stream_options: { include_usage: true },\n ...(opts.tools && opts.tools.length > 0 ? { tools: opts.tools } : {}),\n ...(opts.temperature != null ? { temperature: opts.temperature } : {}),\n ...opts.extraBody,\n }),\n )\n}\n\n/** Stream + parse an OpenAI-compat SSE response into chunks. Tolerates `data:`\n * framing, multi-line buffers, and the terminal `[DONE]`. */\nasync function* streamChatCompletions(\n doFetch: typeof fetch,\n url: string,\n apiKey: string,\n body: Record<string, unknown>,\n): AsyncIterable<OpenAIStreamChunk> {\n const res = await doFetch(url, {\n method: 'POST',\n headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json', Accept: 'text/event-stream' },\n body: JSON.stringify(body),\n })\n if (!res.ok || !res.body) {\n const text = res.body ? await res.text().catch(() => '') : ''\n throw new Error(`OpenAI-compat stream failed (HTTP ${res.status})${text ? `: ${text.slice(0, 200)}` : ''}`)\n }\n const reader = res.body.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n for (const line of lines) {\n const trimmed = line.trim()\n if (!trimmed.startsWith('data:')) continue\n const data = trimmed.slice(5).trim()\n if (data === '[DONE]') return\n try {\n yield JSON.parse(data) as OpenAIStreamChunk\n } catch {\n /* skip a partial/garbled SSE frame */\n }\n }\n }\n}\n","/**\n * `createAgentRuntime` — the in-process agent core, assembled.\n *\n * The bricks to run an agent turn WITHOUT a sandbox already exist in this\n * package, but a consumer must hand-wire five of them every time: resolve the\n * model config, build the OpenAI tool schemas from the taxonomy, build a\n * `streamTurn` over the model endpoint, build an `executeToolCall` over the\n * product's handlers, and drive `runAppToolLoop` / `streamAppToolLoop` with an\n * `isExecutableTool` predicate. That boilerplate is identical across every\n * sandbox-free surface (an edge/browser copilot, an eval harness, a Node CLI),\n * and getting it subtly wrong — e.g. NOT advertising the tools, so the model\n * never emits a `tool_call` and no side effect ever fires — is exactly the\n * failure that makes a tool-driven agent score zero off-sandbox.\n *\n * This factory bundles those five into one object configured for ONE agent:\n *\n * const runtime = createAgentRuntime({ model, taxonomy, handlers, systemPrompt })\n * const result = await runtime.run(userMessage, { ctx }) // awaitable\n * for await (const y of runtime.stream(userMessage, { ctx })) {…} // streaming\n *\n * The model is advertised the app tools (so it CAN call them); each call is\n * dispatched against the product's `handlers` (so the side effect is real); the\n * `onProduced` hook fires at the real side-effect site (so an eval/UI credits a\n * persisted proposal or artifact). Substrate-free: no `@tangle-network/sandbox`,\n * no Durable Object, no `@tangle-network/agent-runtime` import. The SAME core\n * the Cloudflare Worker runs, runnable anywhere a `fetch` to an OpenAI-compatible\n * endpoint works.\n *\n * Domain stays out: the proposal taxonomy, the handlers, and the system prompt\n * are all injected — the factory knows nothing about insurance, law, tax, etc.\n */\nimport {\n type AppToolHandlers,\n type AppToolContext,\n type AppToolOutcome,\n type AppToolProducedEvent,\n type AppToolTaxonomy,\n} from '../tools/types'\nimport { buildAppToolOpenAITools, isAppToolName } from '../tools/openai'\nimport { createAppToolRuntimeExecutor } from '../tools/runtime'\nimport {\n runAppToolLoop,\n streamAppToolLoop,\n type LoopEvent,\n type LoopMessage,\n type LoopToolCall,\n type StreamLoopYield,\n type ToolLoopEvent,\n type ToolLoopResult,\n} from './index'\nimport { createOpenAICompatStreamTurn } from './openai-stream'\n\n/** OpenAI-compatible model endpoint (Tangle Router / tcloud / any compat\n * provider). Build from {@link resolveTangleModelConfig} or pass literals. */\nexport interface AgentRuntimeModelConfig {\n baseUrl: string\n apiKey: string\n model: string\n temperature?: number\n fetchImpl?: typeof fetch\n /** Extra request-body fields (e.g. `max_tokens`, a `reasoning` block). */\n extraBody?: Record<string, unknown>\n}\n\n/** The agent's resolved profile surfaces for one turn — the things a delivered\n * / certified `AgentProfile` can change. Profile-WIDE on purpose: certified\n * delivery folds prompt-surface + skills into `systemPrompt` AND can add\n * certified `tool` artifacts to `extraTools` (the model's advertised tools is\n * rebuilt when these change). MCP servers / memory / RAG that materialize as\n * files or servers deliver through the sandbox-provisioning seam, not here. */\nexport interface ResolvedAgentProfile {\n systemPrompt: string\n extraTools: unknown[]\n}\n\nexport interface CreateAgentRuntimeOptions {\n /** The model endpoint the turns stream from. */\n model: AgentRuntimeModelConfig\n /**\n * Optional transform applied to the resolved profile surfaces each turn —\n * the seam for certified-artifact delivery (`createCertifiedDelivery`). It is\n * profile-WIDE (not prompt-only): it returns the effective `systemPrompt` +\n * advertised `extraTools`. Kept generic + injected so this substrate-free core\n * never imports `@tangle-network/agent-runtime`. Fail-closed by contract: an\n * impl that can't reach the plane returns the base surfaces unchanged.\n */\n composeProfile?: (base: ResolvedAgentProfile) => ResolvedAgentProfile | Promise<ResolvedAgentProfile>\n /** The product's proposal taxonomy — advertises `submit_proposal`'s `type`\n * enum to the model and labels the regulated subset on the result. */\n taxonomy: AppToolTaxonomy\n /** Domain handlers persisting each tool to the product's store/vault. */\n handlers: AppToolHandlers\n /** Default agent identity / system prompt. A turn may override it. */\n systemPrompt: string\n /** Runaway-backstop cap. Default 200 — set far above any legitimate workflow.\n * For per-workflow limits use `deadlineMs` or `maxCostUsd` on the loop options. */\n maxToolTurns?: number\n /** Extra OpenAI tool definitions advertised ALONGSIDE the four app tools\n * (e.g. `integration_invoke`). Pair with {@link executeOtherTool}. */\n extraTools?: unknown[]\n /** Execute a tool that is NOT one of the four app tools (e.g. an integration\n * action). Only consulted for names {@link isOtherExecutableTool} accepts. */\n executeOtherTool?: (call: LoopToolCall, ctx: AppToolContext) => Promise<AppToolOutcome>\n /** Which non-app tool names are executable here. Required if {@link executeOtherTool} is set. */\n isOtherExecutableTool?: (toolName: string) => boolean\n}\n\nexport interface AgentTurnOptions {\n /** The trusted per-turn context (who/where the turn runs as). */\n ctx: AppToolContext\n /** Prior conversation turns, in order. */\n priorMessages?: Array<{ role: string; content: string }>\n /** Override the factory's default system prompt for this turn. */\n systemPrompt?: string\n /** Fires at the real side-effect site for each produced proposal/artifact. */\n onProduced?: (event: AppToolProducedEvent) => void\n}\n\nexport interface AgentRuntime {\n /** Run the bounded tool loop to completion; resolve with final text + every\n * executed tool outcome. */\n run(userMessage: string, turn: AgentTurnOptions): Promise<ToolLoopResult>\n /** Stream the bounded tool loop: yields each raw model event and each executed\n * tool result as it happens (for SSE re-emission + telemetry). */\n stream(userMessage: string, turn: AgentTurnOptions): AsyncGenerator<StreamLoopYield<LoopEvent>, void, unknown>\n}\n\n/**\n * Create an in-process agent runtime for one agent. See the module doc for the\n * full rationale; the short version: it advertises the app tools to the model,\n * dispatches each emitted call against `handlers`, and drives the bounded loop —\n * the whole agent core, sandbox-free.\n */\nexport function createAgentRuntime(opts: CreateAgentRuntimeOptions): AgentRuntime {\n if (opts.executeOtherTool && !opts.isOtherExecutableTool) {\n throw new Error('createAgentRuntime: isOtherExecutableTool is required when executeOtherTool is set')\n }\n\n // Tool schemas + the streamTurn are stable across turns — build once. The\n // model MUST be advertised the tools or it never emits a tool_call (the exact\n // failure that scores a tool-driven agent zero off-sandbox).\n const m = opts.model\n const buildStreamTurn = (extraTools: unknown[]) =>\n createOpenAICompatStreamTurn({\n baseUrl: m.baseUrl,\n apiKey: m.apiKey,\n model: m.model,\n tools: [...buildAppToolOpenAITools(opts.taxonomy), ...extraTools],\n temperature: m.temperature,\n fetchImpl: m.fetchImpl,\n extraBody: m.extraBody,\n })\n\n // The advertised tool set is stable across turns UNLESS a delivered profile\n // changes `extraTools` (certified-tool delivery, on the cache-refresh cadence\n // — not per turn). Memoize the streamTurn by the active extraTools identity so\n // it rebuilds only when the certified tools actually change.\n const baseExtraTools = opts.extraTools ?? []\n let activeExtraTools = baseExtraTools\n let activeStreamTurn = buildStreamTurn(baseExtraTools)\n const streamTurnFor = (extraTools: unknown[]) => {\n if (extraTools !== activeExtraTools) {\n activeExtraTools = extraTools\n activeStreamTurn = buildStreamTurn(extraTools)\n }\n return activeStreamTurn\n }\n\n // Resolve the per-turn profile surfaces, applying the optional profile\n // transform (certified-artifact delivery). Profile-wide: system prompt +\n // advertised tools.\n const resolveProfile = async (turn: AgentTurnOptions): Promise<ResolvedAgentProfile> => {\n const base: ResolvedAgentProfile = {\n systemPrompt: turn.systemPrompt ?? opts.systemPrompt,\n extraTools: baseExtraTools,\n }\n return opts.composeProfile ? opts.composeProfile(base) : base\n }\n\n const isExecutableTool = (name: string): boolean =>\n isAppToolName(name) || (opts.isOtherExecutableTool?.(name) ?? false)\n\n const buildExecutor = (turn: AgentTurnOptions) => {\n const appExecutor = createAppToolRuntimeExecutor({\n handlers: opts.handlers,\n taxonomy: opts.taxonomy,\n ctx: turn.ctx,\n onProduced: turn.onProduced,\n })\n return async (call: LoopToolCall): Promise<AppToolOutcome> => {\n if (isAppToolName(call.toolName)) return appExecutor({ toolName: call.toolName, args: call.args })\n if (opts.executeOtherTool && opts.isOtherExecutableTool?.(call.toolName)) {\n return opts.executeOtherTool(call, turn.ctx)\n }\n return { ok: false, code: 'unknown_tool', message: `No executor for tool: ${call.toolName}` }\n }\n }\n\n return {\n async run(userMessage, turn) {\n const profile = await resolveProfile(turn)\n return runAppToolLoop({\n systemPrompt: profile.systemPrompt,\n userMessage,\n priorMessages: turn.priorMessages,\n // The awaitable loop consumes only text + tool_call; the app's UI-only\n // reasoning/usage events ride the substrate's `other` channel.\n streamTurn: narrowToToolLoopEvents(streamTurnFor(profile.extraTools)),\n executeToolCall: buildExecutor(turn),\n isExecutableTool,\n maxToolTurns: opts.maxToolTurns,\n })\n },\n async *stream(userMessage, turn) {\n const profile = await resolveProfile(turn)\n yield* streamAppToolLoop<LoopEvent>({\n systemPrompt: profile.systemPrompt,\n userMessage,\n priorMessages: turn.priorMessages,\n streamTurn: streamTurnFor(profile.extraTools),\n extractText: (ev) => (ev.type === 'text' ? ev.text : ''),\n extractToolCall: (ev) => (ev.type === 'tool_call' ? ev.call : null),\n isExecutableTool,\n executeToolCall: buildExecutor(turn),\n maxToolTurns: opts.maxToolTurns,\n })\n },\n }\n}\n\n/**\n * Adapt the app's rich {@link LoopEvent} stream to the substrate awaitable\n * loop's `ToolLoopEvent` contract. The loop reads only `text` (accumulated into\n * the answer) and `tool_call` (dispatched); the app's UI-only `reasoning` /\n * `usage` events have no awaitable meaning, so they collapse onto the\n * substrate's `other` channel and are ignored by the loop.\n */\nfunction narrowToToolLoopEvents(\n streamTurn: (messages: LoopMessage[]) => AsyncIterable<LoopEvent>,\n): (messages: LoopMessage[]) => AsyncIterable<ToolLoopEvent> {\n return (messages) =>\n (async function* () {\n for await (const ev of streamTurn(messages)) {\n if (ev.type === 'text') yield { type: 'text', text: ev.text }\n else if (ev.type === 'tool_call') yield { type: 'tool_call', call: ev.call }\n else yield { type: 'other', event: ev }\n }\n })()\n}\n","/**\n * `createCertifiedDelivery` — the delivery truck for Tangle Intelligence.\n *\n * Pulls a tenant's CERTIFIED `AgentProfile` from the deployed plane\n * (`GET /v1/profiles/:target/composed`) and applies it to the agent's resolved\n * surfaces each turn, so an approved improvement reaches the running agent with\n * NO redeploy. This is the `composeProfile` transform `createAgentRuntime`\n * accepts — opt-in per product, fail-closed, cached + refreshed.\n *\n * Profile-WIDE by design (not prompt-only): the composed profile carries every\n * promoted artifact type keyed by kind. What's folded where:\n * - `prompt-surface` + `skill` → the system prompt (via `composeCertifiedPrompt`).\n * - `tool` artifacts that carry an OpenAI tool definition → `extraTools`\n * (advertised to the model; the matching executor is supplied by the product\n * via `executeOtherTool`). Until a `tool` artifact carries a runnable def,\n * it is surfaced (see `current()`) but not advertised — advertising a tool\n * with no executor would make the model call into a dead end.\n * - `mcp` / `memory` / `rag` artifacts materialize as servers/files and deliver\n * through the SANDBOX-provisioning seam, not this in-process one. The full\n * certified profile is exposed via `current()` so that seam can consume it.\n *\n * Substrate boundary: THIS module imports `@tangle-network/agent-runtime`; the\n * `createAgentRuntime` core does not (it only consumes the generic transform).\n */\n\nimport {\n composeCertifiedPrompt,\n type CertifiedProfile,\n pullCertified,\n} from '@tangle-network/agent-runtime/intelligence'\nimport type { ResolvedAgentProfile } from './agent'\n\nconst defaultRefreshMs = 300_000\n\nexport interface CertifiedDeliveryConfig {\n /** The tenant target whose certified artifacts to deliver (the agent id). */\n target: string\n /** Bearer for the plane. Reads `TANGLE_API_KEY` when omitted. */\n apiKey?: string\n /** Plane base URL. Reads `TANGLE_INTELLIGENCE_URL` then the public plane. */\n baseUrl?: string\n /** Min interval between certified-profile pulls. Default 5m. */\n refreshMs?: number\n /** fetch impl (tests / non-global-fetch runtimes). */\n fetchImpl?: typeof fetch\n}\n\nexport interface CertifiedDelivery {\n /** The `composeProfile` transform to pass to `createAgentRuntime`. Applies the\n * cached certified profile to the base surfaces; refreshes on the cadence. */\n composeProfile(base: ResolvedAgentProfile): Promise<ResolvedAgentProfile>\n /** Force a pull now (ignores the refresh window). Best-effort. */\n refresh(): Promise<void>\n /** The certified profile currently in effect (null = none promoted / pull\n * failed). Lets the sandbox-provisioning seam deliver the file/server\n * artifact types this in-process seam doesn't. */\n current(): CertifiedProfile | null\n}\n\n/**\n * Build a certified-delivery transform for one agent target. Fail-closed: a pull\n * error or 404 keeps the last-known certified profile (or null), and the agent\n * runs on its base surfaces — it never breaks because Intelligence is down.\n */\nexport function createCertifiedDelivery(config: CertifiedDeliveryConfig): CertifiedDelivery {\n const refreshMs = config.refreshMs ?? defaultRefreshMs\n let certified: CertifiedProfile | null = null\n let lastPullAt = 0\n let inflight: Promise<void> | null = null\n\n async function refresh(force = false): Promise<void> {\n if (!force && Date.now() - lastPullAt < refreshMs) return\n if (inflight) return inflight\n inflight = (async () => {\n const outcome = await pullCertified({\n target: config.target,\n apiKey: config.apiKey,\n baseUrl: config.baseUrl,\n fetchImpl: config.fetchImpl,\n })\n lastPullAt = Date.now()\n // Only replace on a real pull; a 404/error keeps the last-known profile.\n if (outcome.succeeded) certified = outcome.value\n })()\n try {\n await inflight\n } finally {\n inflight = null\n }\n }\n\n return {\n async composeProfile(base) {\n await refresh()\n return {\n // prompt-surface + skill fold into the system prompt.\n systemPrompt: composeCertifiedPrompt(base.systemPrompt, certified),\n // Certified `tool` artifacts deliver here once they carry a runnable\n // OpenAI def + the product wires the executor; until then pass through.\n extraTools: base.extraTools,\n }\n },\n refresh: () => refresh(true),\n current: () => certified,\n }\n}\n","/**\n * Surface-scoped profile overlay — the seam letting any product page (a\n * sequence editor, a brief composer, a dataset view) add MCP servers, a prompt\n * addendum, and permission tightening to the workspace agent profile for turns\n * initiated FROM that surface, without the chat orchestrator knowing any\n * surface's specifics. The orchestrator resolves `(kind, ctx)` through a\n * registry the REQUEST HANDLER constructs per request (construction is a Map\n * build — cheap) and merges the result into the base profile it was about to\n * send to the sandbox. Per-request construction is the trust mechanism, not an\n * optimization target: each `build()` closes over server-trusted request state\n * (env bindings, secrets, the AUTHENTICATED user/workspace), which on Workers\n * exists only per request — a startup-built registry would force identity\n * through the untrusted client `ctx`.\n *\n * SECURITY INVARIANT: the surface `kind` and the ids inside `ctx` arrive on\n * the client request and are pure ROUTING data — never trusted content, never\n * identity. Identity comes from the closure (see above). The registered\n * `build()` runs server-side only: it validates the routing ids against the\n * product's access control, then mints its own URLs and capability tokens from\n * server configuration (`buildHttpMcpServer` + `createCapabilityToken` in\n * ../tools). A client can therefore never inject an arbitrary MCP url, header,\n * or token into the agent profile: the overlay's `mcp` values are typed as\n * {@link SurfaceMcpServer} (= the server-built `AppToolMcpServer` entry shape),\n * and only build() constructs them.\n */\n\nimport type { AppToolMcpServer } from '../tools/mcp'\n\n/** Sandbox permission posture values, ranked deny > ask > allow for merging. */\nexport type SurfacePermissionValue = 'allow' | 'ask' | 'deny'\n\n/** The only MCP entry shape an overlay may carry: the server-built bridge\n * entry from ../tools/mcp (transport, url, headers, and capability token all\n * assembled server-side). The alias exists so overlay authors reach for the\n * builders in ../tools rather than hand-rolling `{ url: ctx.url }` shapes\n * that would let request data become a dialable endpoint. */\nexport type SurfaceMcpServer = AppToolMcpServer\n\n/** What one surface contributes to the agent profile for a single turn. */\nexport interface SurfaceOverlay {\n /** MCP servers to mount for this turn, keyed by tool-routing name. Names\n * must not collide with the base profile's — see {@link mergeSurfaceOverlay}. */\n mcp?: Record<string, SurfaceMcpServer>\n /** Appended to the base system-prompt addendum with a blank-line separator. */\n promptAddendum?: string\n /** Per-key posture the surface wants for its turns. Merging is monotone\n * fail-closed: the stricter of base/overlay wins, so a surface can tighten\n * the workspace posture but never relax it. */\n permissions?: Record<string, SurfacePermissionValue>\n}\n\n/**\n * One registered surface kind. `TCtx` is the shape build() expects — a CLAIM\n * about the client payload, not a guarantee: the registry hands build() the\n * request's `ctx` unvalidated, so build() must treat every field as an\n * untrusted id (resolve it through access control that throws on a bad or\n * foreign id) before minting anything from it.\n */\nexport interface SurfaceKindDefinition<TCtx> {\n kind: string\n build: (ctx: TCtx) => SurfaceOverlay | Promise<SurfaceOverlay>\n}\n\n/** The variance-erased form a registry accepts (`build` is contravariant in\n * `TCtx`, so every concrete definition is assignable to this). */\nexport type AnySurfaceKind = SurfaceKindDefinition<never>\n\n/**\n * Declare one surface kind. The `kind` string is the client-visible routing\n * key (e.g. `'sequences'`); `build` is the server-side factory that turns a\n * validated ctx into the overlay for one turn.\n */\nexport function defineSurfaceKind<TCtx>(opts: {\n kind: string\n build: (ctx: TCtx) => SurfaceOverlay | Promise<SurfaceOverlay>\n}): SurfaceKindDefinition<TCtx> {\n if (typeof opts.kind !== 'string' || opts.kind.length === 0 || /\\s/.test(opts.kind)) {\n throw new Error(`surface kind must be a non-empty string without whitespace (got ${JSON.stringify(opts.kind)})`)\n }\n if (typeof opts.build !== 'function') {\n throw new Error(`surface kind '${opts.kind}' requires a build function`)\n }\n return { kind: opts.kind, build: opts.build }\n}\n\nexport interface SurfaceRegistry {\n /** Build the overlay for one turn. Throws on an unknown kind — an unknown\n * surface is a routing bug (client and server registries drifted), and\n * silently returning an empty overlay would strip the surface's tools from\n * the turn with no signal anywhere. Build errors propagate unwrapped. */\n resolve(kind: string, ctx: unknown): Promise<SurfaceOverlay>\n}\n\n/**\n * Assemble the product's surface registry from its registered kinds. Duplicate\n * kinds throw at construction: two builders behind one routing key would make\n * the mounted toolset depend on registration order.\n */\nexport function createSurfaceRegistry(kinds: readonly AnySurfaceKind[]): SurfaceRegistry {\n const byKind = new Map<string, AnySurfaceKind>()\n for (const definition of kinds) {\n if (byKind.has(definition.kind)) {\n throw new Error(`duplicate surface kind '${definition.kind}' — each kind must be registered exactly once`)\n }\n byKind.set(definition.kind, definition)\n }\n\n return {\n async resolve(kind, ctx) {\n const definition = byKind.get(kind)\n if (!definition) {\n const known = [...byKind.keys()].join(', ') || '(none)'\n throw new Error(\n `unknown surface kind '${kind}' — registered kinds: ${known}. ` +\n 'An unknown surface is a routing bug: register the kind via defineSurfaceKind before clients can reference it.',\n )\n }\n // The trust boundary where static ctx typing ends: the request payload is\n // handed to build() as-is, and build() validates it (see SurfaceKindDefinition).\n const overlay = await definition.build(ctx as never)\n assertSurfaceOverlay(overlay, `surface kind '${kind}'`)\n return overlay\n },\n }\n}\n\n/** Base-profile slice the merge reads/writes. Real callers pass their full\n * profile object; every field outside this slice passes through untouched. */\nexport interface SurfaceMergeBase {\n mcp?: Record<string, unknown>\n systemPromptAddendum?: string\n permissions?: Record<string, SurfacePermissionValue>\n}\n\nconst PERMISSION_SEVERITY: Record<SurfacePermissionValue, number> = { allow: 0, ask: 1, deny: 2 }\n\n/**\n * Merge one surface overlay into a base profile, returning a new object\n * (the base is never mutated; untouched nested records are shared by\n * reference).\n *\n * - `mcp`: overlay servers are added under their own names. A name already\n * present on the base THROWS — a collision is two servers claiming one\n * routing name, and renaming either silently would corrupt tool routing for\n * whichever caller expected the original binding.\n * - `systemPromptAddendum`: the overlay's `promptAddendum` appends after a\n * blank-line separator (no separator when the base has no addendum).\n * - `permissions`: per key the STRICTER value wins (deny > ask > allow). A\n * surface can tighten the base posture for its turns; a base 'deny' survives\n * any overlay.\n */\nexport function mergeSurfaceOverlay<TBase extends SurfaceMergeBase>(\n base: TBase,\n overlay: SurfaceOverlay,\n): TBase & SurfaceMergeBase {\n assertSurfaceOverlay(overlay, 'surface overlay')\n const merged: SurfaceMergeBase = { ...base }\n\n if (overlay.mcp && Object.keys(overlay.mcp).length > 0) {\n const baseMcp = base.mcp ?? {}\n const collisions = Object.keys(overlay.mcp).filter((name) => name in baseMcp)\n if (collisions.length > 0) {\n throw new Error(\n `surface overlay MCP name collision: ${collisions.map((n) => `'${n}'`).join(', ')} already exist on the base profile. ` +\n 'Two servers cannot claim one name — give the surface server a distinct name.',\n )\n }\n merged.mcp = { ...baseMcp, ...overlay.mcp }\n }\n\n if (overlay.promptAddendum !== undefined) {\n merged.systemPromptAddendum = base.systemPromptAddendum\n ? `${base.systemPromptAddendum}\\n\\n${overlay.promptAddendum}`\n : overlay.promptAddendum\n }\n\n if (overlay.permissions && Object.keys(overlay.permissions).length > 0) {\n const permissions: Record<string, SurfacePermissionValue> = { ...(base.permissions ?? {}) }\n for (const [key, value] of Object.entries(overlay.permissions)) {\n const existing = permissions[key]\n permissions[key] =\n existing === undefined || PERMISSION_SEVERITY[value] > PERMISSION_SEVERITY[existing] ? value : existing\n }\n merged.permissions = permissions\n }\n\n // merged began as a shallow copy of base; only the three slice fields were\n // replaced, so the intersection type is the true shape.\n return merged as TBase & SurfaceMergeBase\n}\n\n/** Reject overlays a build() (or hand-rolled caller) malformed, with the exact\n * field named: a relative MCP url, a blank addendum, or an off-vocabulary\n * permission would otherwise surface only as an opaque sandbox failure. */\nfunction assertSurfaceOverlay(overlay: SurfaceOverlay, label: string): void {\n if (overlay.promptAddendum !== undefined) {\n if (typeof overlay.promptAddendum !== 'string' || overlay.promptAddendum.trim().length === 0) {\n throw new Error(`${label}: promptAddendum must be a non-blank string when provided`)\n }\n }\n if (overlay.mcp !== undefined) {\n for (const [name, server] of Object.entries(overlay.mcp)) {\n if (name.trim().length === 0) throw new Error(`${label}: MCP server names must be non-empty`)\n if (server.transport !== 'http') {\n throw new Error(`${label}: MCP server '${name}' must use transport 'http' (got ${JSON.stringify((server as { transport?: unknown }).transport)})`)\n }\n let parsed: URL\n try {\n parsed = new URL(server.url)\n } catch {\n throw new Error(`${label}: MCP server '${name}' url must be an absolute URL (got ${JSON.stringify(server.url)})`)\n }\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n throw new Error(`${label}: MCP server '${name}' url must be http(s) (got ${JSON.stringify(server.url)})`)\n }\n }\n }\n if (overlay.permissions !== undefined) {\n for (const [key, value] of Object.entries(overlay.permissions)) {\n if (!(value in PERMISSION_SEVERITY)) {\n throw new Error(`${label}: permission '${key}' must be 'allow' | 'ask' | 'deny' (got ${JSON.stringify(value)})`)\n }\n }\n }\n}\n","export * from './model-catalog'\nexport * from './model'\nexport * from './openai-stream'\nexport * from './agent'\nexport * from './certified-delivery'\nexport * from './surface-profile'\n/**\n * The bounded agent tool-loop — owned by `@tangle-network/agent-runtime`.\n *\n * A model turn may emit tool calls (integration-hub actions, the app tools from\n * `../tools`, delegation). The loop streams a turn, collects the executable tool\n * calls, dispatches each, appends the results to history in OpenAI\n * function-calling shape, and re-runs so the model reads them — bounded by\n * `maxToolTurns`, a wall-clock `deadlineMs`, and a `maxCostUsd` budget.\n *\n * The history shape is the OpenAI function-calling contract: the assistant turn\n * that emitted tool calls is preserved as an `assistant` message carrying its\n * `tool_calls` array, and each result is its own `{ role: 'tool', tool_call_id,\n * content }` message keyed to the call. A strict model (Claude, and any\n * OpenAI-compatible provider that validates tool history) needs this to read its\n * own tool use back; folding results into a `user` message makes such models\n * re-issue the same call in a loop.\n *\n * The loop is substrate-owned (`runToolLoop` / `streamToolLoop`); the app\n * supplies `streamTurn` (wrapping its model endpoint) and `executeToolCall`\n * (routing to its integration + app-tool executors). The app-facing names below\n * are 1:1 aliases of the canonical symbols, kept so this package's consumers and\n * the in-package `createAgentRuntime` read against a single, stable vocabulary.\n */\nexport {\n runToolLoop as runAppToolLoop,\n streamToolLoop as streamAppToolLoop,\n} from '@tangle-network/agent-runtime'\nexport type {\n ToolLoopCall as LoopToolCall,\n ToolLoopAssistantToolCall as LoopAssistantToolCall,\n ToolLoopMessage as LoopMessage,\n ToolLoopEvent,\n ToolLoopStopReason,\n ToolLoopResult,\n RunToolLoopOptions as AppToolLoopOptions,\n StreamToolLoopOptions as StreamAppToolLoopOptions,\n StreamToolLoopYield as StreamLoopYield,\n} from '@tangle-network/agent-runtime'\n\n/**\n * Events the app's OpenAI-compat stream adapter ({@link toLoopEvents}) yields.\n *\n * This is the app's own `Raw` event type for the streaming loop — the canonical\n * `streamToolLoop<Raw>` is generic over it. It widens the substrate's\n * tool-loop event with `reasoning` (DeepSeek/router `reasoning_content` /\n * `thinking` deltas, rendered as thinking sections) and `usage` (per-message\n * token accounting) — neither belongs in the substrate's loop contract, so they\n * stay here. The adapter maps each into the `streamTurn` seam; `text` and\n * `tool_call` drive the loop, `reasoning` / `usage` pass through to the UI.\n */\nexport type LoopEvent =\n | { type: 'text'; text: string }\n | { type: 'reasoning'; text: string }\n | { type: 'tool_call'; call: import('@tangle-network/agent-runtime').ToolLoopCall }\n | { type: 'usage'; usage: { promptTokens: number; completionTokens: number } }\n | { type: 'other'; event: unknown }\n"],"mappings":"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","names":[]}