@odla-ai/brand 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/errors.ts","../src/deps.ts","../src/schema.ts","../src/rules.ts","../src/validate.ts","../src/ops/books.ts","../src/ops/sections.ts","../src/ops/palettes.ts","../src/ops/assets.ts","../src/color/hex.ts","../src/color/convert.ts","../src/color/contrast.ts","../src/color/harmony.ts","../src/color/ramp.ts","../src/color/delta.ts","../src/color/names.ts","../src/color/palette.ts","../src/tokens/roles.ts","../src/tokens/map.ts","../src/tokens/dark.ts","../src/tokens/css.ts","../src/tokens/compile.ts","../src/skill/asset-tools.ts","../src/skill/book-tools.ts","../src/skill/palette-tools.ts","../src/skill/read-tools.ts","../src/skill/skill.ts","../src/skill/persona.ts","../src/routes/http.ts","../src/routes/assets.ts","../src/routes/books.ts","../src/routes/tokens.ts","../src/routes/index.ts","../src/triggers.ts","../src/dispatch.ts","../src/descriptor.ts"],"sourcesContent":["// @odla-ai/brand — conversational brand-book builder on odla-db.\n//\n// Pure re-export barrel; the public surface groups as:\n// data model — constants, types, schema, rules, errors, deps, validate\n// ops — pure odla-db op builders shared by routes and skill\n// color engine — zero-dep OKLCH/WCAG math (hex, convert, contrast, harmony,\n// ramp, delta, names, derivePalette)\n// tokens — brand book → --ui-* design tokens + theme CSS\n// skill — the @odla-ai/ai agent skill, persona factory, vision gate\n// worker — route factory, chat-trigger dispatch, integration descriptor\nexport * from \"./constants\";\nexport * from \"./types\";\nexport * from \"./errors\";\nexport * from \"./deps\";\nexport * from \"./schema\";\nexport * from \"./rules\";\nexport * from \"./validate\";\nexport * from \"./ops/index\";\nexport * from \"./color/index\";\nexport * from \"./tokens/index\";\nexport * from \"./skill/index\";\nexport * from \"./routes/index\";\nexport * from \"./dispatch\";\nexport * from \"./triggers\";\nexport * from \"./descriptor\";\n","// Namespaces + closed vocabularies for @odla-ai/brand. One brand book is a\n// row-tree across five namespaces: the book (auth roster + compiled tokens),\n// its sections (one per kind, natural-keyed `${bookId}:${kind}`), palettes\n// (the atomic proposal/approval unit), proposals (human-gated agent output),\n// and assets (worker-mediated uploads mirroring real R2 objects).\n\n/** The odla-db namespaces @odla-ai/brand installs and writes. */\nexport const BRAND_NS = {\n book: \"brand_book\",\n section: \"brand_section\",\n palette: \"brand_palette\",\n proposal: \"brand_proposal\",\n asset: \"brand_asset\",\n} as const;\n\n/** Brand-book lifecycle: authored → adopted → retired (delete is owner-only). */\nexport const BOOK_STATUSES = [\"draft\", \"active\", \"archived\"] as const;\n/** Where a brand book stands in its lifecycle. */\nexport type BookStatus = (typeof BOOK_STATUSES)[number];\n\n/** The section kinds a book carries — one row per kind, upserted by natural key. */\nexport const SECTION_KINDS = [\"palette\", \"typography\", \"voice\", \"logo\", \"imagery\"] as const;\n/** Which facet of the brand a section documents. */\nexport type SectionKind = (typeof SECTION_KINDS)[number];\n\n/** Section review state: agent drafts, a human approves. */\nexport const SECTION_STATUSES = [\"draft\", \"approved\"] as const;\n/** Whether a section's content has human sign-off. */\nexport type SectionStatus = (typeof SECTION_STATUSES)[number];\n\n/** Palette lifecycle — palettes are never row-deleted, only archived. */\nexport const PALETTE_STATUSES = [\"active\", \"archived\"] as const;\n/** Whether a palette is the live one or a retired predecessor. */\nexport type PaletteStatus = (typeof PALETTE_STATUSES)[number];\n\n/** How a palette came to be: pulled from an asset, derived from a seed color,\n * or entered by hand. */\nexport const PALETTE_SOURCES = [\"extracted\", \"derived\", \"manual\"] as const;\n/** Provenance of a palette's swatches. */\nexport type PaletteSource = (typeof PALETTE_SOURCES)[number];\n\n/** What a proposal proposes (palettes are the first-class case). */\nexport const PROPOSAL_KINDS = [\"palette\", \"typography\", \"voice\", \"logo\"] as const;\n/** Which brand facet a proposal targets. */\nexport type ProposalKind = (typeof PROPOSAL_KINDS)[number];\n\n/** Proposal lifecycle: open until a HUMAN accepts/rejects it (or a newer\n * proposal supersedes it) — agents never resolve their own proposals. */\nexport const PROPOSAL_STATUSES = [\"open\", \"accepted\", \"rejected\", \"superseded\"] as const;\n/** Where a proposal stands in the human-gated review flow. */\nexport type ProposalStatus = (typeof PROPOSAL_STATUSES)[number];\n\n/** What kind of upload an asset row records. */\nexport const ASSET_KINDS = [\"logo\", \"wordmark\", \"inspiration\", \"document\", \"font\", \"other\"] as const;\n/** The declared purpose of an uploaded asset. */\nexport type AssetKind = (typeof ASSET_KINDS)[number];\n\n/** Every swatch role a palette may assign. `chart` may repeat (a series);\n * `custom` is the escape hatch for roles the token compiler doesn't map. */\nexport const SWATCH_ROLES = [\n \"primary\",\n \"secondary\",\n \"highlight\",\n \"bg\",\n \"surface\",\n \"text\",\n \"neutral\",\n \"good\",\n \"warn\",\n \"danger\",\n \"chart\",\n \"custom\",\n] as const;\n","/** Caller-fault error (bad hex, oversized payload, blocked transition…) — the\n * route layer maps it to a 400. `fields` carries per-field messages when the\n * failure came from input validation. */\nexport class BrandInputError extends Error {\n readonly fields?: Record<string, string>;\n constructor(message: string, fields?: Record<string, string>) {\n super(message);\n this.name = \"BrandInputError\";\n this.fields = fields;\n }\n}\n\n/** Missing entity — the route layer maps it to a 404. */\nexport class BrandNotFoundError extends Error {\n constructor(what: string) {\n super(`${what} not found`);\n this.name = \"BrandNotFoundError\";\n }\n}\n","// The dependency bundle brand operations take. `now`/`newId` are injectable\n// so tests can pin time and ids and assert exact written attrs; `fetchBytes`\n// is the one network seam (asset bytes for vision), injectable so tests and\n// service-binding hosts never touch the public internet.\nimport type { BrandDb } from \"./types\";\n\n/** What `fetchBytes` resolves to: raw bytes plus the served content type. */\nexport interface BrandFetchedBytes {\n bytes: Uint8Array;\n contentType: string;\n}\n\n/** What every brand operation needs: the injected db, plus test overrides. */\nexport interface BrandDeps {\n db: BrandDb;\n /** Clock override (default `Date.now`). */\n now?: () => number;\n /** Id factory override (default `crypto.randomUUID`). */\n newId?: () => string;\n /** Asset-byte fetcher override (default: global `fetch`). */\n fetchBytes?: (url: string) => Promise<BrandFetchedBytes>;\n}\n\n/** {@link BrandDeps} with the injectable defaults filled in. */\nexport interface ResolvedBrandDeps {\n db: BrandDb;\n now: () => number;\n newId: () => string;\n fetchBytes: (url: string) => Promise<BrandFetchedBytes>;\n}\n\nasync function defaultFetchBytes(url: string): Promise<BrandFetchedBytes> {\n const res = await fetch(url);\n if (!res.ok) throw new Error(`asset fetch failed: ${res.status} for ${url}`);\n return {\n bytes: new Uint8Array(await res.arrayBuffer()),\n contentType: res.headers.get(\"content-type\") ?? \"application/octet-stream\",\n };\n}\n\n/** Fill the injectable defaults. */\nexport function resolveDeps(deps: BrandDeps): ResolvedBrandDeps {\n return {\n db: deps.db,\n now: deps.now ?? Date.now,\n newId: deps.newId ?? (() => crypto.randomUUID()),\n fetchBytes: deps.fetchBytes ?? defaultFetchBytes,\n };\n}\n","// The odla-db schema @odla-ai/brand installs (POST to `/app/:id/schema` as\n// `{ schema: BRAND_SCHEMA }`). Ships pre-serialized (the wire shape) so\n// provisioning needs no schema-builder dependency, mirroring @odla-ai/chat\n// and @odla-ai/crm. The serialized types are inlined so the package has zero\n// deps.\n//\n// Design notes (grounded in the odla-db engine):\n// - Swatches are json ON `brand_palette`, NOT their own namespace: the\n// palette is the atomic proposal/approval unit — a human accepts or rejects\n// a palette whole, and swatches are never queried across palettes.\n// - Access control is fully DENORMALIZED (no graph edges, `links: {}`):\n// `brand_book.memberIds` is the auth roster (including the bot agent's id),\n// and every child row carries an `audience` snapshot of it, so the CEL\n// rules never need the engine's single-hop `ref`.\n// - Entity ids are not attributes in odla-db (`where: { id }` matches\n// nothing), so id-addressed rows mirror their id as a unique attr — the\n// crm pattern.\nimport { BRAND_NS } from \"./constants\";\n\n/** Wire-shape attribute value kinds odla-db stores. */\nexport type AttrType = \"string\" | \"number\" | \"boolean\" | \"date\" | \"json\";\n/** One serialized attribute: its type and index/uniqueness flags. */\nexport interface SerializedAttr {\n type: AttrType;\n unique: boolean;\n indexed: boolean;\n optional: boolean;\n}\n/** One serialized namespace: its attribute map. */\nexport interface SerializedEntity {\n attrs: Record<string, SerializedAttr>;\n}\n/** One end of a serialized schema link. */\nexport interface SerializedLinkEnd {\n on: string;\n has: \"one\" | \"many\";\n label: string;\n}\n/** A serialized schema link (unused by brand — audiences are denormalized). */\nexport interface SerializedLink {\n forward: SerializedLinkEnd;\n reverse: SerializedLinkEnd;\n}\n/** The wire-shape schema POSTed to `/app/:id/schema`. */\nexport interface SerializedSchema {\n entities: Record<string, SerializedEntity>;\n links: Record<string, SerializedLink>;\n}\n\ntype Opt = Partial<Omit<SerializedAttr, \"type\">>;\nconst a = (type: AttrType, o: Opt = {}): SerializedAttr => ({\n type,\n unique: false,\n indexed: false,\n optional: false,\n ...o,\n});\nconst uniq = (type: AttrType): SerializedAttr => a(type, { unique: true, indexed: true });\nconst idx = (type: AttrType, optional = false): SerializedAttr => a(type, { indexed: true, optional });\nconst opt = (type: AttrType): SerializedAttr => a(type, { optional: true });\n\n/**\n * The pre-serialized (wire-shape) odla-db schema for the five brand\n * namespaces; POST it to `/app/:id/schema` as `{ schema: BRAND_SCHEMA }`.\n * Natural keys (`book.slug`, `section.key`) make provisioning and section\n * upserts idempotent; mirrored `id` attrs make rows addressable by\n * `where: { id }`.\n */\nexport const BRAND_SCHEMA: SerializedSchema = {\n entities: {\n [BRAND_NS.book]: {\n attrs: {\n id: uniq(\"string\"),\n slug: uniq(\"string\"),\n name: idx(\"string\"),\n status: idx(\"string\"), // draft | active | archived\n ownerId: idx(\"string\"),\n memberIds: a(\"json\"), // the auth roster, incl. the bot agent id\n channelId: idx(\"string\", true),\n activePaletteId: opt(\"string\"),\n tokens: opt(\"json\"), // { light, dark, warnings, compiledAt }\n summary: opt(\"string\"),\n createdAt: idx(\"date\"),\n updatedAt: idx(\"date\"),\n },\n },\n [BRAND_NS.section]: {\n attrs: {\n key: uniq(\"string\"), // `${bookId}:${kind}` — one section per kind\n bookId: idx(\"string\"),\n kind: idx(\"string\"), // palette | typography | voice | logo | imagery\n status: idx(\"string\"), // draft | approved\n content: a(\"json\"),\n audience: a(\"json\"),\n updatedBy: a(\"string\"),\n updatedAt: idx(\"date\"),\n },\n },\n [BRAND_NS.palette]: {\n attrs: {\n id: uniq(\"string\"),\n bookId: idx(\"string\"),\n name: a(\"string\"),\n status: idx(\"string\"), // active | archived\n swatches: a(\"json\"), // Swatch[] — see the header design note\n seedHex: opt(\"string\"),\n source: a(\"string\"), // extracted | derived | manual\n rationale: opt(\"string\"),\n proposalId: opt(\"string\"),\n audience: a(\"json\"),\n createdAt: idx(\"date\"),\n updatedAt: idx(\"date\"),\n },\n },\n [BRAND_NS.proposal]: {\n attrs: {\n id: uniq(\"string\"),\n bookId: idx(\"string\"),\n kind: idx(\"string\"), // palette | typography | voice | logo\n status: idx(\"string\"), // open | accepted | rejected | superseded\n payload: a(\"json\"),\n rationale: a(\"string\"),\n sourceAssetId: opt(\"string\"),\n messageId: opt(\"string\"),\n audience: a(\"json\"),\n createdBy: a(\"string\"),\n createdAt: idx(\"date\"),\n resolvedBy: opt(\"string\"),\n resolvedAt: opt(\"date\"),\n resolutionNote: opt(\"string\"),\n },\n },\n [BRAND_NS.asset]: {\n attrs: {\n id: uniq(\"string\"),\n bookId: idx(\"string\"),\n kind: idx(\"string\"), // logo | wordmark | inspiration | document | font | other\n path: idx(\"string\"),\n url: a(\"string\"),\n contentType: a(\"string\"),\n size: a(\"number\"),\n title: opt(\"string\"),\n analysis: opt(\"json\"), // { description, dominantColors: hex[], tags }\n analyzedAt: opt(\"date\"),\n audience: a(\"json\"),\n uploadedBy: a(\"string\"),\n createdAt: idx(\"date\"),\n deletedAt: opt(\"date\"), // tombstone — asset rows are never row-deleted\n },\n },\n },\n links: {},\n};\n","// Default-deny CEL rules for the brand namespaces (POST to\n// `/app/:id/admin/rules`). Access is denormalized (see schema.ts): books gate\n// on `data.memberIds`, child rows on their `data.audience` snapshot — no\n// `ref()`. The bot agent passes these checks because its agent id is written\n// into `memberIds`/`audience`, exactly like a human member.\n//\n// `brand_asset` is closed entirely: asset rows must mirror real R2 uploads,\n// and only the worker (admin key, bypasses rules) performs the\n// upload-then-row / delete-then-tombstone pairs that keep them in sync.\nimport { BRAND_NS } from \"./constants\";\n\n/** Per-namespace CEL rule strings (missing action = deny). */\nexport interface BrandRule {\n view?: string;\n create?: string;\n update?: string;\n delete?: string;\n}\n/** Namespace → rule set, as installed at `/app/:id/admin/rules`. */\nexport type BrandRules = Record<string, BrandRule>;\n\n// Membership in the audience snapshot — implies a signed identity, since an\n// anonymous auth.id is never written into an audience.\nconst AUDIENCE = \"auth.id in data.audience\";\n\n/**\n * Default-deny CEL rules for the five brand namespaces; install at\n * `/app/:id/admin/rules`. Books gate on the `memberIds` roster (owner-only\n * writes); sections/palettes/proposals gate on their denormalized `audience`\n * and are never client-deletable (archive via `status` instead); assets are\n * fully worker-mediated. Use {@link brandRules} for a per-install copy.\n */\nexport const BRAND_RULES: BrandRules = {\n [BRAND_NS.book]: {\n view: \"auth.id in data.memberIds\",\n // Creator is the owner and must include itself in the roster.\n create: \"auth.signedIn && auth.id == data.ownerId && auth.id in data.memberIds\",\n update: \"auth.id == data.ownerId\",\n delete: \"auth.id == data.ownerId\",\n },\n [BRAND_NS.section]: {\n view: AUDIENCE,\n create: AUDIENCE,\n update: AUDIENCE,\n delete: \"false\", // sections are upserted in place, never removed\n },\n [BRAND_NS.palette]: {\n view: AUDIENCE,\n create: AUDIENCE,\n update: AUDIENCE,\n delete: \"false\", // archive via status, keep provenance\n },\n [BRAND_NS.proposal]: {\n view: AUDIENCE,\n create: AUDIENCE,\n update: AUDIENCE,\n delete: \"false\", // resolution history is the audit trail\n },\n [BRAND_NS.asset]: {\n view: \"false\",\n create: \"false\",\n update: \"false\",\n delete: \"false\",\n },\n};\n\n/**\n * Rules factory, mirroring `chatRules()`/`crmRules()`. Today it returns a\n * fresh copy of {@link BRAND_RULES}; it exists so a future option (e.g.\n * locking views to an org email domain) can widen or tighten namespaces\n * without apps changing their provisioning call shape.\n */\nexport function brandRules(): BrandRules {\n return Object.fromEntries(Object.entries(BRAND_RULES).map(([ns, r]) => [ns, { ...r }]));\n}\n","// Input validation for brand writes — THE enforcement layer. Taint marking on\n// tool output is advisory; nothing an agent (or a route caller) produces\n// reaches odla-db without passing these checks. Every failure throws\n// {@link BrandInputError} with an actionable message.\nimport { SECTION_KINDS, SWATCH_ROLES } from \"./constants\";\nimport { BrandInputError } from \"./errors\";\nimport type {\n AssetAnalysis,\n ImagerySection,\n LogoSection,\n PaletteSection,\n Swatch,\n SwatchRole,\n TypographySection,\n VoiceSection,\n} from \"./types\";\n\n/** Most swatches a single palette may carry. */\nexport const MAX_SWATCHES = 24;\n\n/** Upload content types the asset pipeline accepts. */\nexport const ASSET_CONTENT_TYPES: ReadonlySet<string> = new Set([\n \"image/png\",\n \"image/jpeg\",\n \"image/gif\",\n \"image/webp\",\n \"image/svg+xml\",\n \"application/pdf\",\n]);\n\nconst HEX_RGB = /^#[0-9a-f]{3}$/;\nconst HEX_RRGGBB = /^#[0-9a-f]{6}$/;\nconst HEX_ALPHA = /^#[0-9a-f]{4}$|^#[0-9a-f]{8}$/;\n\nconst isRecord = (v: unknown): v is Record<string, unknown> =>\n typeof v === \"object\" && v !== null && !Array.isArray(v);\n\n/**\n * Assert `value` is a `#rgb`/`#rrggbb` hex color and normalize it to\n * lowercase `#rrggbb`. Alpha channels (`#rgba`/`#rrggbbaa`) and every other\n * color syntax are rejected — tokens and contrast math are defined on opaque\n * sRGB hex.\n */\nexport function assertHex(value: unknown, label = \"color\"): string {\n if (typeof value !== \"string\")\n throw new BrandInputError(`${label} must be a hex string like #1a2b3c`);\n const hex = value.trim().toLowerCase();\n if (HEX_ALPHA.test(hex))\n throw new BrandInputError(`${label} must not carry alpha (got ${value}); use #rrggbb`);\n if (HEX_RGB.test(hex)) return `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}`;\n if (!HEX_RRGGBB.test(hex))\n throw new BrandInputError(`${label} must be #rgb or #rrggbb hex (got ${value})`);\n return hex;\n}\n\n/** Assert a trimmed non-empty string of at most `max` characters. */\nexport function capString(value: unknown, label: string, max: number): string {\n if (typeof value !== \"string\") throw new BrandInputError(`${label} must be a string`);\n const s = value.trim();\n if (s === \"\") throw new BrandInputError(`${label} must not be empty`);\n if (s.length > max)\n throw new BrandInputError(`${label} must be at most ${max} characters (got ${s.length})`);\n return s;\n}\n\n/** Assert an array of capped strings (each trimmed and non-empty). */\nexport function capStringArray(\n value: unknown,\n label: string,\n opts: { maxItems: number; maxLen: number; minItems?: number },\n): string[] {\n if (!Array.isArray(value)) throw new BrandInputError(`${label} must be an array of strings`);\n const min = opts.minItems ?? 0;\n if (value.length < min)\n throw new BrandInputError(`${label} must have at least ${min} item${min === 1 ? \"\" : \"s\"}`);\n if (value.length > opts.maxItems)\n throw new BrandInputError(`${label} must have at most ${opts.maxItems} items`);\n return value.map((v, i) => capString(v, `${label}[${i}]`, opts.maxLen));\n}\n\n/**\n * Assert a swatch list: 1–{@link MAX_SWATCHES} entries, each with a known\n * role, a valid hex (normalized), and capped optional name/rationale.\n * Returns the normalized copy — write THAT, never the raw input.\n */\nexport function assertSwatches(value: unknown): Swatch[] {\n if (!Array.isArray(value) || value.length === 0)\n throw new BrandInputError(\"swatches must be a non-empty array\");\n if (value.length > MAX_SWATCHES)\n throw new BrandInputError(`swatches must have at most ${MAX_SWATCHES} entries`);\n return value.map((raw, i) => {\n if (!isRecord(raw)) throw new BrandInputError(`swatches[${i}] must be an object`);\n const role = raw.role;\n if (typeof role !== \"string\" || !(SWATCH_ROLES as readonly string[]).includes(role))\n throw new BrandInputError(\n `swatches[${i}].role must be one of: ${SWATCH_ROLES.join(\", \")}`,\n );\n const out: Swatch = { role: role as SwatchRole, hex: assertHex(raw.hex, `swatches[${i}].hex`) };\n if (raw.name !== undefined) out.name = capString(raw.name, `swatches[${i}].name`, 80);\n if (raw.rationale !== undefined)\n out.rationale = capString(raw.rationale, `swatches[${i}].rationale`, 500);\n return out;\n });\n}\n\nfunction assertPaletteSection(c: Record<string, unknown>): PaletteSection {\n return {\n paletteId: capString(c.paletteId, \"content.paletteId\", 128),\n name: capString(c.name, \"content.name\", 120),\n swatches: assertSwatches(c.swatches),\n };\n}\n\nfunction assertTypographySection(c: Record<string, unknown>): TypographySection {\n const out: TypographySection = {};\n if (c.fontDisplay !== undefined) out.fontDisplay = capString(c.fontDisplay, \"content.fontDisplay\", 120);\n if (c.fontBody !== undefined) out.fontBody = capString(c.fontBody, \"content.fontBody\", 120);\n if (c.fontMono !== undefined) out.fontMono = capString(c.fontMono, \"content.fontMono\", 120);\n if (c.scale !== undefined) {\n if (typeof c.scale !== \"number\" || !Number.isFinite(c.scale) || c.scale <= 1 || c.scale > 2)\n throw new BrandInputError(\"content.scale must be a modular type-scale ratio in (1, 2]\");\n out.scale = c.scale;\n }\n if (c.notes !== undefined) out.notes = capString(c.notes, \"content.notes\", 2000);\n return out;\n}\n\nfunction assertVoiceSection(c: Record<string, unknown>): VoiceSection {\n const out: VoiceSection = {\n tone: capString(c.tone, \"content.tone\", 200),\n principles: capStringArray(c.principles, \"content.principles\", { maxItems: 12, maxLen: 200, minItems: 1 }),\n };\n if (c.examples !== undefined)\n out.examples = capStringArray(c.examples, \"content.examples\", { maxItems: 12, maxLen: 500 });\n return out;\n}\n\nfunction assertLogoSection(c: Record<string, unknown>): LogoSection {\n const out: LogoSection = {\n usage: capStringArray(c.usage, \"content.usage\", { maxItems: 16, maxLen: 300, minItems: 1 }),\n donts: capStringArray(c.donts, \"content.donts\", { maxItems: 16, maxLen: 300 }),\n };\n if (c.clearspace !== undefined) out.clearspace = capString(c.clearspace, \"content.clearspace\", 120);\n if (c.minSize !== undefined) out.minSize = capString(c.minSize, \"content.minSize\", 120);\n return out;\n}\n\nfunction assertImagerySection(c: Record<string, unknown>): ImagerySection {\n return {\n style: capString(c.style, \"content.style\", 200),\n guidance: capStringArray(c.guidance, \"content.guidance\", { maxItems: 16, maxLen: 300, minItems: 1 }),\n };\n}\n\n/**\n * Validate + normalize a section's `content` payload for its kind. Returns\n * the normalized content to write; throws {@link BrandInputError} on an\n * unknown kind or an invalid payload.\n */\nexport function assertSectionContent(kind: string, content: unknown): Record<string, unknown> {\n if (!isRecord(content)) throw new BrandInputError(\"content must be an object\");\n switch (kind) {\n case \"palette\":\n return { ...assertPaletteSection(content) };\n case \"typography\":\n return { ...assertTypographySection(content) };\n case \"voice\":\n return { ...assertVoiceSection(content) };\n case \"logo\":\n return { ...assertLogoSection(content) };\n case \"imagery\":\n return { ...assertImagerySection(content) };\n default:\n throw new BrandInputError(`unknown section kind ${kind}; expected one of: ${SECTION_KINDS.join(\", \")}`);\n }\n}\n\n/**\n * Sanitize an upload's file name for use in an R2 key: strips path\n * separators, control characters, and leading dots, then caps at 120\n * characters. Throws when nothing survives sanitizing.\n */\nexport function safeFileName(name: unknown): string {\n if (typeof name !== \"string\") throw new BrandInputError(\"file name must be a string\");\n const cleaned = name\n .replace(/[/\\\\]/g, \"\")\n // eslint-disable-next-line no-control-regex\n .replace(/[\\u0000-\\u001f\\u007f]/g, \"\")\n .trim()\n .replace(/^\\.+/, \"\");\n if (cleaned === \"\") throw new BrandInputError(\"file name is empty after sanitizing\");\n return cleaned.slice(0, 120);\n}\n\n/**\n * Assert an upload content type is on the {@link ASSET_CONTENT_TYPES}\n * allowlist. Normalizes case and strips parameters (`; charset=…`) before\n * checking; returns the normalized bare type.\n */\nexport function assertAssetContentType(value: unknown): string {\n if (typeof value !== \"string\") throw new BrandInputError(\"contentType must be a string\");\n const ct = value.split(\";\")[0]!.trim().toLowerCase();\n if (!ASSET_CONTENT_TYPES.has(ct))\n throw new BrandInputError(\n `unsupported content type ${ct || \"(empty)\"}; allowed: ${[...ASSET_CONTENT_TYPES].join(\", \")}`,\n );\n return ct;\n}\n\n/**\n * Validate + normalize an agent's asset analysis: description ≤ 2000 chars,\n * ≤ 12 dominant colors (each normalized hex), ≤ 24 tags of ≤ 60 chars.\n */\nexport function assertAnalysis(value: unknown): AssetAnalysis {\n if (!isRecord(value)) throw new BrandInputError(\"analysis must be an object\");\n const colors = value.dominantColors;\n if (!Array.isArray(colors)) throw new BrandInputError(\"analysis.dominantColors must be an array\");\n if (colors.length > 12)\n throw new BrandInputError(\"analysis.dominantColors must have at most 12 entries\");\n return {\n description: capString(value.description, \"analysis.description\", 2000),\n dominantColors: colors.map((c, i) => assertHex(c, `analysis.dominantColors[${i}]`)),\n tags: capStringArray(value.tags, \"analysis.tags\", { maxItems: 24, maxLen: 60 }),\n };\n}\n","// Pure op-builders for brand_book rows: (input) -> BrandOp[]. No I/O — this\n// is the unit-test seam (assert the emitted ops); routes and the skill share\n// these so every write path commits identical shapes.\nimport { BOOK_STATUSES, BRAND_NS, type BookStatus } from \"../constants\";\nimport { BrandInputError } from \"../errors\";\nimport type { BrandAttrs, BrandBook, BrandEntityRef, BrandOp } from \"../types\";\nimport { capString } from \"../validate\";\n\nconst SLUG_RE = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;\n\n/** Input to {@link createBookOps}. `memberIds` is the full auth roster —\n * include the bot agent's id so it can pass the audience rules. */\nexport interface CreateBookInput {\n id: string;\n slug: string;\n name: string;\n ownerId: string;\n memberIds: string[];\n channelId?: string;\n now: number;\n}\n\n/**\n * Ops to create a brand book: one `brand_book` row in `draft` status with the\n * id mirrored as an attr (odla-db ids aren't attrs), the owner always folded\n * into the deduplicated `memberIds` roster (the create rule requires it), and\n * both timestamps set to `now`.\n */\nexport function createBookOps(input: CreateBookInput): BrandOp[] {\n const slug = capString(input.slug, \"slug\", 80);\n if (!SLUG_RE.test(slug))\n throw new BrandInputError(\"slug must be lowercase letters, digits, and inner hyphens\");\n const name = capString(input.name, \"name\", 120);\n const memberIds = Array.from(new Set([input.ownerId, ...input.memberIds]));\n return [\n {\n t: \"update\",\n ns: BRAND_NS.book,\n id: input.id,\n attrs: {\n id: input.id,\n slug,\n name,\n status: \"draft\",\n ownerId: input.ownerId,\n memberIds,\n createdAt: input.now,\n updatedAt: input.now,\n ...(input.channelId ? { channelId: input.channelId } : {}),\n },\n },\n ];\n}\n\n// Patchable book columns, and which of them may be cleared with `null`\n// (required attrs can't be retracted — the final-state schema check fails).\nconst PATCHABLE = new Set([\"name\", \"status\", \"channelId\", \"summary\", \"activePaletteId\", \"tokens\"]);\nconst CLEARABLE = new Set([\"channelId\", \"summary\", \"activePaletteId\", \"tokens\"]);\n\nfunction validateBookField(key: string, value: unknown): unknown {\n switch (key) {\n case \"name\":\n return capString(value, \"name\", 120);\n case \"status\":\n if (typeof value !== \"string\" || !(BOOK_STATUSES as readonly string[]).includes(value))\n throw new BrandInputError(`status must be one of: ${BOOK_STATUSES.join(\", \")}`);\n return value as BookStatus;\n case \"channelId\":\n return capString(value, \"channelId\", 128);\n case \"summary\":\n return capString(value, \"summary\", 2000);\n case \"activePaletteId\":\n return capString(value, \"activePaletteId\", 128);\n default: {\n // tokens: the compiled-token cache; shaped by our own compiler, so only\n // require an object here.\n if (typeof value !== \"object\" || value === null || Array.isArray(value))\n throw new BrandInputError(\"tokens must be an object\");\n return value;\n }\n }\n}\n\n/**\n * Ops to patch a brand book. `null` means CLEAR (a `retract` op — odla-db has\n * no storable scalar null) and is only allowed on optional columns;\n * `undefined` keys are dropped. Any write stamps `updatedAt`; an empty\n * effective patch emits no ops at all.\n */\nexport function updateBookOps(\n bookId: BrandEntityRef,\n patch: Record<string, unknown>,\n now: number,\n): BrandOp[] {\n const attrs: BrandAttrs = {};\n const retract: string[] = [];\n for (const [key, value] of Object.entries(patch)) {\n if (value === undefined) continue;\n if (!PATCHABLE.has(key)) throw new BrandInputError(`unknown book field: ${key}`);\n if (value === null) {\n if (!CLEARABLE.has(key)) throw new BrandInputError(`${key} is required and cannot be cleared`);\n retract.push(key);\n continue;\n }\n attrs[key] = validateBookField(key, value);\n }\n if (Object.keys(attrs).length === 0 && retract.length === 0) return [];\n const ops: BrandOp[] = [\n { t: \"update\", ns: BRAND_NS.book, id: bookId, attrs: { ...attrs, updatedAt: now } },\n ];\n if (retract.length > 0) ops.push({ t: \"retract\", ns: BRAND_NS.book, id: bookId, attrs: retract });\n return ops;\n}\n\n/** The child rows whose `audience` snapshots a roster change must rewrite.\n * Pass the rows you queried — the builder stays pure. */\nexport interface AudienceChildren {\n sections?: Array<{ id: string }>;\n palettes?: Array<{ id: string }>;\n proposals?: Array<{ id: string }>;\n assets?: Array<{ id: string }>;\n}\n\n/**\n * Ops to change a book's roster: rewrite `memberIds` (owner always kept) and\n * fan the new list out to every child row's denormalized `audience` snapshot.\n * Query the children first and pass them in — this builder does no I/O, so\n * tests can assert the exact fan-out.\n */\nexport function audienceFanoutOps(\n book: Pick<BrandBook, \"id\" | \"ownerId\">,\n newMemberIds: string[],\n children: AudienceChildren,\n now: number,\n): BrandOp[] {\n const memberIds = Array.from(new Set([book.ownerId, ...newMemberIds]));\n const ops: BrandOp[] = [\n { t: \"update\", ns: BRAND_NS.book, id: book.id, attrs: { memberIds, updatedAt: now } },\n ];\n const fan = (ns: string, rows?: Array<{ id: string }>) => {\n for (const row of rows ?? []) ops.push({ t: \"update\", ns, id: row.id, attrs: { audience: memberIds } });\n };\n fan(BRAND_NS.section, children.sections);\n fan(BRAND_NS.palette, children.palettes);\n fan(BRAND_NS.proposal, children.proposals);\n fan(BRAND_NS.asset, children.assets);\n return ops;\n}\n","// Pure op-builder for brand_section rows. One section per (book, kind),\n// upserted through a natural-key Lookup ref so re-writing the same section\n// binds to the same row (no read-modify-write race, idempotent installs).\nimport { BRAND_NS, SECTION_STATUSES, type SectionKind, type SectionStatus } from \"../constants\";\nimport { BrandInputError } from \"../errors\";\nimport type { BrandOp } from \"../types\";\nimport { assertSectionContent } from \"../validate\";\n\n/** The natural key a section row is upserted by: `${bookId}:${kind}`. */\nexport function sectionKey(bookId: string, kind: SectionKind): string {\n return `${bookId}:${kind}`;\n}\n\n/** Input to {@link upsertSectionOps}. `audience` is the book's roster\n * snapshot; `status` defaults to `draft` (a human approves later). */\nexport interface UpsertSectionInput {\n bookId: string;\n kind: SectionKind;\n content: Record<string, unknown>;\n status?: SectionStatus;\n audience: string[];\n updatedBy: string;\n now: number;\n}\n\n/**\n * Ops to create-or-replace a book's section of one kind: a single `update`\n * addressed by the `{ ns, attr: \"key\", value }` Lookup ref, carrying the\n * validated + normalized content (see `assertSectionContent`), the audience\n * snapshot, and the author/timestamp.\n */\nexport function upsertSectionOps(input: UpsertSectionInput): BrandOp[] {\n const status = input.status ?? \"draft\";\n if (!(SECTION_STATUSES as readonly string[]).includes(status))\n throw new BrandInputError(`status must be one of: ${SECTION_STATUSES.join(\", \")}`);\n const content = assertSectionContent(input.kind, input.content);\n const key = sectionKey(input.bookId, input.kind);\n return [\n {\n t: \"update\",\n ns: BRAND_NS.section,\n id: { ns: BRAND_NS.section, attr: \"key\", value: key },\n attrs: {\n key,\n bookId: input.bookId,\n kind: input.kind,\n status,\n content,\n audience: input.audience,\n updatedBy: input.updatedBy,\n updatedAt: input.now,\n },\n },\n ];\n}\n","// Pure op-builders for the palette proposal flow: the agent proposes, a\n// HUMAN accepts or rejects. Acceptance is one atomic transact — palette row,\n// approved palette section, the book's `activePaletteId`, and the proposal's\n// resolution — so a book can never point at a palette that wasn't written.\n//\n// Note on op kinds: the design doc says \"book merge {activePaletteId}\", but\n// odla-db's `merge` op deep-merges JSON attributes only (the engine rejects\n// it on scalars) — a scalar write on an existing row is an attr-merge\n// `update`, which is what these builders emit.\nimport { BRAND_NS } from \"../constants\";\nimport { BrandInputError } from \"../errors\";\nimport type { BrandOp, BrandProposal } from \"../types\";\nimport { assertHex, assertSwatches, capString } from \"../validate\";\nimport { upsertSectionOps } from \"./sections\";\n\n/** Input to {@link proposePaletteOps}. `audience` is the book's roster\n * snapshot; `contrastReport` is stored verbatim in the payload for the\n * human reviewer. */\nexport interface ProposePaletteInput {\n id: string;\n bookId: string;\n name: string;\n rationale: string;\n swatches: unknown;\n seedHex?: string;\n contrastReport?: unknown;\n createdBy: string;\n audience: string[];\n sourceAssetId?: string;\n messageId?: string;\n now: number;\n}\n\n/**\n * Ops to park a palette proposal for human review: one `brand_proposal` row,\n * `kind: \"palette\"`, `status: \"open\"`, with the validated swatches (and\n * optional seed/contrast report) in `payload`.\n */\nexport function proposePaletteOps(input: ProposePaletteInput): BrandOp[] {\n const name = capString(input.name, \"name\", 120);\n const rationale = capString(input.rationale, \"rationale\", 2000);\n const swatches = assertSwatches(input.swatches);\n const seedHex = input.seedHex === undefined ? undefined : assertHex(input.seedHex, \"seedHex\");\n const payload: Record<string, unknown> = {\n name,\n swatches,\n ...(seedHex ? { seedHex } : {}),\n ...(input.contrastReport !== undefined ? { contrastReport: input.contrastReport } : {}),\n };\n return [\n {\n t: \"update\",\n ns: BRAND_NS.proposal,\n id: input.id,\n attrs: {\n id: input.id,\n bookId: input.bookId,\n kind: \"palette\",\n status: \"open\",\n payload,\n rationale,\n audience: input.audience,\n createdBy: input.createdBy,\n createdAt: input.now,\n ...(input.sourceAssetId ? { sourceAssetId: input.sourceAssetId } : {}),\n ...(input.messageId ? { messageId: input.messageId } : {}),\n },\n },\n ];\n}\n\n/** Input to {@link acceptProposalOps}: the proposal row as read back, the id\n * to mint the palette under, and who resolved it. */\nexport interface AcceptProposalInput {\n proposal: BrandProposal;\n paletteId: string;\n resolvedBy: string;\n now: number;\n resolutionNote?: string;\n}\n\n/**\n * Ops to accept an OPEN palette proposal, all in one transact:\n * 1. the `brand_palette` row (source: `extracted` when the proposal came from\n * an asset, `derived` when seeded, else `manual`),\n * 2. the approved `palette` section upsert,\n * 3. the book's `activePaletteId` pointer,\n * 4. the proposal flipped to `accepted` with resolver + timestamp.\n * Throws on a non-palette or already-resolved proposal, or a payload that no\n * longer validates.\n */\nexport function acceptProposalOps(input: AcceptProposalInput): BrandOp[] {\n const { proposal, paletteId, resolvedBy, now } = input;\n if (proposal.kind !== \"palette\")\n throw new BrandInputError(`proposal ${proposal.id} is a ${proposal.kind} proposal, not a palette`);\n if (proposal.status !== \"open\")\n throw new BrandInputError(`proposal ${proposal.id} is ${proposal.status}, not open`);\n const payload = proposal.payload;\n const name = capString(payload.name, \"payload.name\", 120);\n const swatches = assertSwatches(payload.swatches);\n const seedHex = payload.seedHex === undefined ? undefined : assertHex(payload.seedHex, \"payload.seedHex\");\n const source = proposal.sourceAssetId ? \"extracted\" : seedHex ? \"derived\" : \"manual\";\n return [\n {\n t: \"update\",\n ns: BRAND_NS.palette,\n id: paletteId,\n attrs: {\n id: paletteId,\n bookId: proposal.bookId,\n name,\n status: \"active\",\n swatches,\n source,\n rationale: proposal.rationale,\n proposalId: proposal.id,\n audience: proposal.audience,\n createdAt: now,\n updatedAt: now,\n ...(seedHex ? { seedHex } : {}),\n },\n },\n ...upsertSectionOps({\n bookId: proposal.bookId,\n kind: \"palette\",\n content: { paletteId, name, swatches },\n status: \"approved\",\n audience: proposal.audience,\n updatedBy: resolvedBy,\n now,\n }),\n {\n t: \"update\",\n ns: BRAND_NS.book,\n id: proposal.bookId,\n attrs: { activePaletteId: paletteId, updatedAt: now },\n },\n {\n t: \"update\",\n ns: BRAND_NS.proposal,\n id: proposal.id,\n attrs: {\n status: \"accepted\",\n resolvedBy,\n resolvedAt: now,\n ...(input.resolutionNote ? { resolutionNote: input.resolutionNote } : {}),\n },\n },\n ];\n}\n\n/** Input to {@link rejectProposalOps}. */\nexport interface RejectProposalInput {\n proposal: BrandProposal;\n resolvedBy: string;\n now: number;\n resolutionNote?: string;\n}\n\n/**\n * Ops to reject an OPEN proposal (any kind): flip it to `rejected` with\n * resolver, timestamp, and the optional note. Nothing else is touched — the\n * proposal row itself is the audit trail.\n */\nexport function rejectProposalOps(input: RejectProposalInput): BrandOp[] {\n const { proposal, resolvedBy, now } = input;\n if (proposal.status !== \"open\")\n throw new BrandInputError(`proposal ${proposal.id} is ${proposal.status}, not open`);\n return [\n {\n t: \"update\",\n ns: BRAND_NS.proposal,\n id: proposal.id,\n attrs: {\n status: \"rejected\",\n resolvedBy,\n resolvedAt: now,\n ...(input.resolutionNote ? { resolutionNote: input.resolutionNote } : {}),\n },\n },\n ];\n}\n","// Pure op-builders for brand_asset rows. Asset rows are worker-mediated\n// (rules deny everything): the caller uploads to storage FIRST, then commits\n// the mirroring row — and \"delete\" is a `deletedAt` tombstone (an attr-merge\n// `update`; odla-db's `merge` op is json-only), never a row delete, so\n// analyses and proposals that reference the asset stay coherent.\nimport { ASSET_KINDS, BRAND_NS, type AssetKind } from \"../constants\";\nimport { BrandInputError } from \"../errors\";\nimport type { BrandEntityRef, BrandOp } from \"../types\";\nimport { assertAnalysis, assertAssetContentType, capString } from \"../validate\";\n\n/** Input to {@link createAssetOps} — the storage upload's receipt fields\n * (`path`/`url`/`size`) plus the declared kind and the book's audience. */\nexport interface CreateAssetInput {\n id: string;\n bookId: string;\n kind: AssetKind;\n path: string;\n url: string;\n contentType: string;\n size: number;\n uploadedBy: string;\n audience: string[];\n title?: string;\n now: number;\n}\n\n/**\n * Ops to record one uploaded asset: a single `brand_asset` row mirroring the\n * storage object, with the content type re-checked against the allowlist and\n * the size required to be a positive byte count.\n */\nexport function createAssetOps(input: CreateAssetInput): BrandOp[] {\n if (!(ASSET_KINDS as readonly string[]).includes(input.kind))\n throw new BrandInputError(`kind must be one of: ${ASSET_KINDS.join(\", \")}`);\n const contentType = assertAssetContentType(input.contentType);\n if (typeof input.size !== \"number\" || !Number.isFinite(input.size) || input.size <= 0)\n throw new BrandInputError(\"size must be a positive byte count\");\n const title = input.title === undefined ? undefined : capString(input.title, \"title\", 160);\n return [\n {\n t: \"update\",\n ns: BRAND_NS.asset,\n id: input.id,\n attrs: {\n id: input.id,\n bookId: input.bookId,\n kind: input.kind,\n path: capString(input.path, \"path\", 512),\n url: capString(input.url, \"url\", 1024),\n contentType,\n size: input.size,\n audience: input.audience,\n uploadedBy: input.uploadedBy,\n createdAt: input.now,\n ...(title ? { title } : {}),\n },\n },\n ];\n}\n\n/**\n * Ops to tombstone an asset after its storage object was deleted: stamp\n * `deletedAt` in place. List reads exclude tombstoned rows; the row itself\n * stays for provenance.\n */\nexport function tombstoneAssetOps(assetId: BrandEntityRef, now: number): BrandOp[] {\n return [{ t: \"update\", ns: BRAND_NS.asset, id: assetId, attrs: { deletedAt: now } }];\n}\n\n/**\n * Ops to record an agent's analysis of an asset: the validated + capped\n * analysis json plus `analyzedAt`. Replaces any prior analysis whole.\n */\nexport function recordAnalysisOps(assetId: BrandEntityRef, analysis: unknown, now: number): BrandOp[] {\n return [\n {\n t: \"update\",\n ns: BRAND_NS.asset,\n id: assetId,\n attrs: { analysis: assertAnalysis(analysis), analyzedAt: now },\n },\n ];\n}\n","// Hex color parsing and formatting.\n//\n// Accepts the CSS `#rgb` and `#rrggbb` forms (case-insensitive, surrounding\n// whitespace tolerated) and normalizes to lowercase `#rrggbb`. Alpha forms\n// (`#rgba` / `#rrggbbaa`) are rejected on purpose: the brand color engine\n// works in opaque colors only — translucency is expressed downstream by the\n// token compiler via `color-mix()` strings, never baked into swatches.\n//\n// Errors are plain RangeError so the color engine stays dependency-free;\n// the package's validate layer wraps them in BrandInputError where a typed\n// HTTP-facing error is needed.\n\n/** An sRGB color with channels as fractions in [0, 1]. */\nexport interface Rgb {\n /** Red channel, 0..1. */\n r: number;\n /** Green channel, 0..1. */\n g: number;\n /** Blue channel, 0..1. */\n b: number;\n}\n\n/** Clamps a number into [0, 1]; NaN clamps to 0. */\nexport function clamp01(x: number): number {\n return Number.isNaN(x) ? 0 : x < 0 ? 0 : x > 1 ? 1 : x;\n}\n\nconst HEX3 = /^#[0-9a-f]{3}$/;\nconst HEX6 = /^#[0-9a-f]{6}$/;\nconst HEX_ALPHA = /^#([0-9a-f]{4}|[0-9a-f]{8})$/;\n\n/**\n * Parses `#rgb` or `#rrggbb` (case-insensitive) into channel fractions.\n *\n * @throws RangeError for anything else — named colors, missing `#`, wrong\n * digit counts; alpha forms (`#rgba`/`#rrggbbaa`) get a dedicated message.\n */\nexport function parseHex(hex: string): Rgb {\n const s = hex.trim().toLowerCase();\n if (HEX_ALPHA.test(s)) {\n throw new RangeError(\n `parseHex: alpha hex \"${hex}\" is not supported — use an opaque \"#rrggbb\" value`,\n );\n }\n if (HEX3.test(s)) {\n return {\n r: parseInt(s[1]! + s[1]!, 16) / 255,\n g: parseInt(s[2]! + s[2]!, 16) / 255,\n b: parseInt(s[3]! + s[3]!, 16) / 255,\n };\n }\n if (HEX6.test(s)) {\n return {\n r: parseInt(s.slice(1, 3), 16) / 255,\n g: parseInt(s.slice(3, 5), 16) / 255,\n b: parseInt(s.slice(5, 7), 16) / 255,\n };\n }\n throw new RangeError(`parseHex: expected \"#rgb\" or \"#rrggbb\", got \"${hex}\"`);\n}\n\n/** Formats channel fractions as lowercase `#rrggbb`, clamping each into [0, 1]. */\nexport function toHex(rgb: Rgb): string {\n const ch = (c: number): string =>\n Math.round(clamp01(c) * 255)\n .toString(16)\n .padStart(2, \"0\");\n return `#${ch(rgb.r)}${ch(rgb.g)}${ch(rgb.b)}`;\n}\n\n/** Normalizes any accepted hex form to lowercase `#rrggbb` (throws like parseHex). */\nexport function normalizeHex(hex: string): string {\n return toHex(parseHex(hex));\n}\n","// Color space conversions: sRGB ↔ HSL, sRGB ↔ linear-light, sRGB ↔ OKLab,\n// OKLab ↔ OKLCH — plus an sRGB gamut clamp that reduces OKLCH chroma while\n// preserving lightness and hue.\n//\n// Algorithm sources:\n// - sRGB transfer function: IEC 61966-2-1 (electro-optical form: c ≤ 0.04045\n// → c/12.92, else ((c+0.055)/1.055)^2.4), as restated in CSS Color 4.\n// - HSL: CSS Color 4 § 7 (the hue-sextant algorithm inherited from CSS3\n// Color § 4.2.4).\n// - OKLab: Björn Ottosson, \"A perceptual color space for image processing\"\n// (bottosson.github.io/posts/oklab, 2020; sRGB matrices as updated\n// 2021-01-25): linear sRGB → LMS via M1, per-component cube root, → OKLab\n// via M2, and his published inverse matrices for the way back.\n// - OKLCH: CSS Color 4 § 9.2 — the polar form of OKLab (C = chroma radius,\n// h = hue angle in degrees).\n\nimport { clamp01, parseHex, toHex, type Rgb } from \"./hex\";\n\n/** A color in HSL: hue in degrees [0, 360), saturation and lightness 0..1. */\nexport interface Hsl {\n /** Hue angle in degrees, [0, 360); 0 when achromatic. */\n h: number;\n /** Saturation, 0..1. */\n s: number;\n /** Lightness, 0..1. */\n l: number;\n}\n\n/** A color in OKLab: L 0..1, a/b roughly within ±0.4 for sRGB colors. */\nexport interface Oklab {\n /** Perceived lightness, 0..1. */\n L: number;\n /** Green–red axis. */\n a: number;\n /** Blue–yellow axis. */\n b: number;\n}\n\n/** A color in OKLCH — OKLab in polar form. */\nexport interface Oklch {\n /** Perceived lightness, 0..1. */\n L: number;\n /** Chroma (radius in the a/b plane), ≥ 0. */\n C: number;\n /** Hue angle in degrees, [0, 360); 0 when achromatic. */\n h: number;\n}\n\n/** IEC 61966-2-1 sRGB decoding: gamma-encoded channel → linear-light, both 0..1. */\nexport function srgbToLinear(c: number): number {\n return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;\n}\n\n/** IEC 61966-2-1 sRGB encoding: linear-light channel → gamma-encoded, both 0..1. */\nexport function linearToSrgb(c: number): number {\n return c <= 0.0031308 ? c * 12.92 : 1.055 * c ** (1 / 2.4) - 0.055;\n}\n\n/** Converts sRGB channel fractions to HSL (CSS Color 4 § 7). */\nexport function rgbToHsl({ r, g, b }: Rgb): Hsl {\n const max = Math.max(r, g, b);\n const min = Math.min(r, g, b);\n const l = (max + min) / 2;\n const d = max - min;\n if (d === 0) return { h: 0, s: 0, l };\n const s = d / (1 - Math.abs(2 * l - 1));\n let h: number;\n if (max === r) h = 60 * (((g - b) / d) % 6);\n else if (max === g) h = 60 * ((b - r) / d + 2);\n else h = 60 * ((r - g) / d + 4);\n return { h: (h + 360) % 360, s, l };\n}\n\n/** Converts HSL to sRGB channel fractions (CSS Color 4 § 7; any hue angle accepted). */\nexport function hslToRgb({ h, s, l }: Hsl): Rgb {\n const hue = ((h % 360) + 360) % 360;\n const c = (1 - Math.abs(2 * l - 1)) * s;\n const x = c * (1 - Math.abs(((hue / 60) % 2) - 1));\n const m = l - c / 2;\n const sextants: ReadonlyArray<readonly [number, number, number]> = [\n [c, x, 0],\n [x, c, 0],\n [0, c, x],\n [0, x, c],\n [x, 0, c],\n [c, 0, x],\n ];\n const [r, g, b] = sextants[Math.floor(hue / 60) % 6]!;\n return { r: r + m, g: g + m, b: b + m };\n}\n\n/** Converts gamma-encoded sRGB to OKLab (Ottosson M1 → cbrt → M2). */\nexport function rgbToOklab({ r, g, b }: Rgb): Oklab {\n const R = srgbToLinear(r);\n const G = srgbToLinear(g);\n const B = srgbToLinear(b);\n const l = Math.cbrt(0.4122214708 * R + 0.5363325363 * G + 0.0514459929 * B);\n const m = Math.cbrt(0.2119034982 * R + 0.6806995451 * G + 0.1073969566 * B);\n const s = Math.cbrt(0.0883024619 * R + 0.2817188376 * G + 0.6299787005 * B);\n return {\n L: 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,\n a: 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,\n b: 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s,\n };\n}\n\n/**\n * Converts OKLab to gamma-encoded sRGB (Ottosson's inverse matrices).\n * Out-of-gamut inputs yield channels outside [0, 1] — feed the OKLCH form\n * through {@link clampToGamut} first when hue fidelity matters (toHex would\n * clip per channel, which distorts hue).\n */\nexport function oklabToRgb({ L, a, b }: Oklab): Rgb {\n const l = (L + 0.3963377774 * a + 0.2158037573 * b) ** 3;\n const m = (L - 0.1055613458 * a - 0.0638541728 * b) ** 3;\n const s = (L - 0.0894841775 * a - 1.291485548 * b) ** 3;\n return {\n r: linearToSrgb(4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s),\n g: linearToSrgb(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s),\n b: linearToSrgb(-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s),\n };\n}\n\n/** OKLab → OKLCH (polar form). Hue is 0 for achromatic colors (C ≈ 0). */\nexport function oklabToOklch({ L, a, b }: Oklab): Oklch {\n const C = Math.hypot(a, b);\n const h = C < 1e-9 ? 0 : ((Math.atan2(b, a) * 180) / Math.PI + 360) % 360;\n return { L, C, h };\n}\n\n/** OKLCH → OKLab (rectangular form). */\nexport function oklchToOklab({ L, C, h }: Oklch): Oklab {\n const rad = (h * Math.PI) / 180;\n return { L, a: C * Math.cos(rad), b: C * Math.sin(rad) };\n}\n\n/** Convenience: hex → OKLCH (throws like parseHex on bad input). */\nexport function hexToOklch(hex: string): Oklch {\n return oklabToOklch(rgbToOklab(parseHex(hex)));\n}\n\n/**\n * Convenience: OKLCH → lowercase `#rrggbb`. Out-of-gamut input is clipped\n * per channel by toHex; call {@link clampToGamut} first to reduce chroma\n * instead (preserving hue) when the input may be out of gamut.\n */\nexport function oklchToHex(lch: Oklch): string {\n return toHex(oklabToRgb(oklchToOklab(lch)));\n}\n\nconst GAMUT_EPS = 1e-6;\n\n/** True when every sRGB channel is within [0, 1] (tiny epsilon for float noise). */\nexport function inSrgbGamut({ r, g, b }: Rgb): boolean {\n const ok = (c: number): boolean => c >= -GAMUT_EPS && c <= 1 + GAMUT_EPS;\n return ok(r) && ok(g) && ok(b);\n}\n\n/**\n * Brings an OKLCH color into the sRGB gamut by reducing chroma only —\n * lightness and hue are preserved exactly (binary search, 24 iterations,\n * chroma precision well under one 8-bit quantum). L is clamped into [0, 1]\n * first; C = 0 (a pure gray) is always representable, so the search always\n * lands in gamut.\n */\nexport function clampToGamut(lch: Oklch): Oklch {\n const L = clamp01(lch.L);\n const h = lch.h;\n if (inSrgbGamut(oklabToRgb(oklchToOklab({ L, C: lch.C, h })))) return { L, C: lch.C, h };\n let lo = 0;\n let hi = lch.C;\n for (let i = 0; i < 24; i++) {\n const mid = (lo + hi) / 2;\n if (inSrgbGamut(oklabToRgb(oklchToOklab({ L, C: mid, h })))) lo = mid;\n else hi = mid;\n }\n return { L, C: lo, h };\n}\n","// WCAG 2.1 contrast — relative luminance, contrast ratio, and AA/AAA checks\n// per technique G18 and success criteria 1.4.3 / 1.4.6 / 1.4.11.\n//\n// relativeLuminance: Y = 0.2126·R + 0.7152·G + 0.0722·B over linear-light\n// sRGB channels. Channels are linearized with the IEC 61966-2-1 constant\n// (0.04045) rather than WCAG's 0.03928; the two never disagree for 8-bit\n// channel values (no n/255 falls between them) and CSS Color 4 standardizes\n// on 0.04045.\n// contrastRatio: (L1 + 0.05) / (L2 + 0.05) with the lighter luminance on\n// top — white on black is exactly 21:1, a color against itself 1:1.\n\nimport { parseHex } from \"./hex\";\nimport { srgbToLinear } from \"./convert\";\n\n/** Contrast contexts with distinct WCAG thresholds. */\nexport type ContrastKind = \"text\" | \"large-text\" | \"ui\";\n\n/** WCAG 2.1 relative luminance of a hex color: 0 = black, 1 = white. */\nexport function relativeLuminance(hex: string): number {\n const { r, g, b } = parseHex(hex);\n return 0.2126 * srgbToLinear(r) + 0.7152 * srgbToLinear(g) + 0.0722 * srgbToLinear(b);\n}\n\n/** WCAG 2.1 contrast ratio between two hex colors, in [1, 21]; order-independent. */\nexport function contrastRatio(hexA: string, hexB: string): number {\n const la = relativeLuminance(hexA);\n const lb = relativeLuminance(hexB);\n return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);\n}\n\nconst AA: Record<ContrastKind, number> = { text: 4.5, \"large-text\": 3, ui: 3 };\nconst AAA: Record<Exclude<ContrastKind, \"ui\">, number> = { text: 7, \"large-text\": 4.5 };\n\n/**\n * WCAG 2.1 level-AA check for a contrast ratio: 4.5:1 for body text\n * (SC 1.4.3), 3:1 for large text, 3:1 for UI components and graphical\n * objects (SC 1.4.11).\n */\nexport function meetsAA(ratio: number, kind: ContrastKind = \"text\"): boolean {\n return ratio >= AA[kind];\n}\n\n/**\n * WCAG 2.1 level-AAA check for a contrast ratio (SC 1.4.6): 7:1 for body\n * text, 4.5:1 for large text. WCAG defines no AAA bar for non-text UI, so\n * \"ui\" is not an accepted kind here.\n */\nexport function meetsAAA(ratio: number, kind: Exclude<ContrastKind, \"ui\"> = \"text\"): boolean {\n return ratio >= AAA[kind];\n}\n\n/**\n * Default {@link pickTextOn} candidates: white and a near-black chosen so\n * that the better of the two clears 4.5:1 against ANY background. The worst\n * case is a mid-gray backdrop (luminance ≈ 0.18), where this pair still\n * yields ≈ 4.51:1; a softer near-black (e.g. #111111) would drop that floor\n * below 4.5.\n */\nexport const PICK_TEXT_DEFAULT_CANDIDATES: readonly string[] = [\"#ffffff\", \"#050505\"];\n\n/**\n * Picks the candidate with the highest contrast ratio against `bgHex`\n * (earliest candidate wins ties). With the default candidates the winner is\n * always ≥ 4.5:1 — see {@link PICK_TEXT_DEFAULT_CANDIDATES}.\n *\n * @throws RangeError when `candidates` is empty.\n */\nexport function pickTextOn(\n bgHex: string,\n candidates: readonly string[] = PICK_TEXT_DEFAULT_CANDIDATES,\n): string {\n if (candidates.length === 0) throw new RangeError(\"pickTextOn: candidates must be non-empty\");\n let best = candidates[0]!;\n let bestRatio = -1;\n for (const candidate of candidates) {\n const ratio = contrastRatio(bgHex, candidate);\n if (ratio > bestRatio) {\n best = candidate;\n bestRatio = ratio;\n }\n }\n return best;\n}\n","// Classic color-harmony companions, computed as OKLCH hue rotations at\n// constant lightness and chroma so companions keep the seed's perceived\n// lightness and vividness (the same rotations in HSL shift both wildly —\n// compare HSL yellow vs blue at \"50%\"). Results are chroma-clamped into the\n// sRGB gamut, which preserves hue and lightness exactly.\n//\n// Angles follow the traditional color-wheel definitions: complementary 180°,\n// analogous ±30°, triadic ±120°, split-complementary ±150°, tetradic\n// (square) +90°/180°/270°. Hue math per CSS Color 4 § 9.2 OKLCH.\n\nimport { clampToGamut, hexToOklch, oklchToHex } from \"./convert\";\n\n/**\n * Rotates a color's OKLCH hue by `degrees` (any sign/magnitude), keeping\n * lightness and chroma constant, then chroma-clamps into sRGB. Rotating an\n * achromatic color is a no-op up to 8-bit rounding (its chroma is ~0).\n */\nexport function rotateHue(hex: string, degrees: number): string {\n const { L, C, h } = hexToOklch(hex);\n return oklchToHex(clampToGamut({ L, C, h: (((h + degrees) % 360) + 360) % 360 }));\n}\n\n/** The 180° opposite on the OKLCH hue wheel. */\nexport function complementary(hex: string): string {\n return rotateHue(hex, 180);\n}\n\n/** The two neighbors at ±`angle` (default 30°), as [minus, plus]. */\nexport function analogous(hex: string, angle = 30): [string, string] {\n return [rotateHue(hex, -angle), rotateHue(hex, angle)];\n}\n\n/** The two colors completing an equilateral triad (±120°), as [minus, plus]. */\nexport function triadic(hex: string): [string, string] {\n return [rotateHue(hex, -120), rotateHue(hex, 120)];\n}\n\n/** The two colors flanking the complement (±150°), as [minus, plus]. */\nexport function splitComplementary(hex: string): [string, string] {\n return [rotateHue(hex, -150), rotateHue(hex, 150)];\n}\n\n/** The three colors completing a square (+90°, +180°, +270°). */\nexport function tetradic(hex: string): [string, string, string] {\n return [rotateHue(hex, 90), rotateHue(hex, 180), rotateHue(hex, 270)];\n}\n\n/**\n * A monochrome chroma scale: `steps` colors at the seed's lightness and hue,\n * fading chroma linearly from the seed's own chroma down to 0 (a pure gray).\n * The first entry is the seed itself (normalized).\n *\n * @throws RangeError when `steps` is not an integer ≥ 2.\n */\nexport function monochrome(hex: string, steps = 5): string[] {\n if (!Number.isInteger(steps) || steps < 2) {\n throw new RangeError(`monochrome: steps must be an integer >= 2, got ${steps}`);\n }\n const { L, C, h } = hexToOklch(hex);\n const out: string[] = [];\n for (let i = 0; i < steps; i++) {\n out.push(oklchToHex(clampToGamut({ L, C: C * (1 - i / (steps - 1)), h })));\n }\n return out;\n}\n","// Tint/shade ramps and predicate-driven lightness adjustment, both in OKLCH\n// so steps are perceptually even (equal OKLab ΔL per step; OKLab per Björn\n// Ottosson — see convert.ts for the matrices and citations).\n//\n// tintShadeRamp walks lightness from RAMP_L_MAX down to RAMP_L_MIN with a\n// chroma taper: full chroma mid-ramp, fading toward both ends. Very light\n// tints and very dark shades can't hold much chroma in sRGB anyway, and a\n// deliberate taper reads better than the hard cut the gamut clamp would\n// otherwise apply.\n//\n// adjustLightnessUntil binary-searches lightness (constant chroma and hue,\n// gamut-clamped) for the value nearest the input that satisfies a caller\n// predicate — the workhorse behind \"darken this accent until it clears\n// 4.5:1 on the page background\".\n\nimport { normalizeHex } from \"./hex\";\nimport { clampToGamut, hexToOklch, oklchToHex } from \"./convert\";\n\n/** Lightest OKLab L emitted by {@link tintShadeRamp} (first step). */\nexport const RAMP_L_MAX = 0.96;\n\n/** Darkest OKLab L emitted by {@link tintShadeRamp} (last step). */\nexport const RAMP_L_MIN = 0.27;\n\n/** Fraction of the seed's chroma kept at the ramp's endpoints. */\nconst CHROMA_FLOOR = 0.25;\n\n/**\n * A light-to-dark ramp of `steps` colors (default 9) built from the seed's\n * hue and chroma: OKLab lightness runs linearly from {@link RAMP_L_MAX} down\n * to {@link RAMP_L_MIN}, and chroma tapers from 100% of the seed's chroma at\n * mid-ramp to 25% at both ends (then gamut-clamps, preserving hue).\n *\n * @throws RangeError when `steps` is not an integer ≥ 2.\n */\nexport function tintShadeRamp(hex: string, steps = 9): string[] {\n if (!Number.isInteger(steps) || steps < 2) {\n throw new RangeError(`tintShadeRamp: steps must be an integer >= 2, got ${steps}`);\n }\n const { C, h } = hexToOklch(hex);\n const out: string[] = [];\n for (let i = 0; i < steps; i++) {\n const t = i / (steps - 1);\n const L = RAMP_L_MAX + (RAMP_L_MIN - RAMP_L_MAX) * t;\n const taper = CHROMA_FLOOR + (1 - CHROMA_FLOOR) * (1 - Math.abs(2 * t - 1));\n out.push(oklchToHex(clampToGamut({ L, C: C * taper, h })));\n }\n return out;\n}\n\n/** Direction {@link adjustLightnessUntil} may move lightness. */\nexport type LightnessDirection = \"lighten\" | \"darken\";\n\nconst SCAN_STEPS = 16;\nconst BISECT_STEPS = 22;\n\n/**\n * Moves a color's OKLCH lightness toward white (\"lighten\") or black\n * (\"darken\") until `predicate(hex)` holds, returning the passing color\n * nearest the input (chroma and hue constant, gamut-clamped per step).\n *\n * Returns the input itself (normalized) when it already passes, and null\n * when no lightness in that direction satisfies the predicate. Work is\n * bounded: a 16-step coarse scan plus 22 bisection rounds. The predicate is\n * always evaluated on exact 8-bit hex strings, including the returned one.\n */\nexport function adjustLightnessUntil(\n hex: string,\n predicate: (hex: string) => boolean,\n direction: LightnessDirection,\n): string | null {\n const start = normalizeHex(hex);\n if (predicate(start)) return start;\n const { L, C, h } = hexToOklch(start);\n const bound = direction === \"lighten\" ? 1 : 0;\n const at = (l: number): string => oklchToHex(clampToGamut({ L: l, C, h }));\n let lastFail = L;\n let firstPass = Number.NaN;\n for (let i = 1; i <= SCAN_STEPS; i++) {\n const l = L + ((bound - L) * i) / SCAN_STEPS;\n if (predicate(at(l))) {\n firstPass = l;\n break;\n }\n lastFail = l;\n }\n if (Number.isNaN(firstPass)) return null;\n for (let i = 0; i < BISECT_STEPS; i++) {\n const mid = (lastFail + firstPass) / 2;\n if (predicate(at(mid))) firstPass = mid;\n else lastFail = mid;\n }\n return at(firstPass);\n}\n","// ΔEOK — the OKLab color-difference metric adopted by CSS Color 4 (§ \"the\n// deltaEOK function\"): plain Euclidean distance in OKLab coordinates.\n// Scale intuition: ~0.02 is roughly one just-noticeable difference, and\n// white ↔ black is exactly 1.\n\nimport { parseHex } from \"./hex\";\nimport { rgbToOklab, type Oklab } from \"./convert\";\n\n/** Euclidean distance between two OKLab coordinates (CSS Color 4 ΔEOK). */\nexport function deltaEOKLab(x: Oklab, y: Oklab): number {\n return Math.hypot(x.L - y.L, x.a - y.a, x.b - y.b);\n}\n\n/**\n * ΔEOK between two hex colors. 0 = identical; ~0.02 ≈ one just-noticeable\n * difference; white ↔ black = 1. Symmetric in its arguments.\n *\n * @throws RangeError on malformed hex (see parseHex).\n */\nexport function deltaEOK(hexA: string, hexB: string): number {\n return deltaEOKLab(rgbToOklab(parseHex(hexA)), rgbToOklab(parseHex(hexB)));\n}\n","// The 148 CSS named colors — CSS Color 4 § 6.1 \"Named colors\" (the CSS3\n// keyword list plus rebeccapurple), with the spec's exact hex values —\n// and nearest-name lookup by ΔEOK (CSS Color 4's OKLab distance metric,\n// see delta.ts). Alphabetical order; gray/grey-style aliases are separate\n// entries sharing one hex.\n\nimport { parseHex } from \"./hex\";\nimport { rgbToOklab, type Oklab } from \"./convert\";\nimport { deltaEOKLab } from \"./delta\";\n\n/** One CSS named color: the keyword and its spec hex value. */\nexport interface NamedColor {\n /** The CSS keyword, e.g. \"rebeccapurple\". */\n name: string;\n /** The spec's `#rrggbb` value, lowercase. */\n hex: string;\n}\n\nconst TABLE: ReadonlyArray<readonly [string, string]> = [\n [\"aliceblue\", \"#f0f8ff\"],\n [\"antiquewhite\", \"#faebd7\"],\n [\"aqua\", \"#00ffff\"],\n [\"aquamarine\", \"#7fffd4\"],\n [\"azure\", \"#f0ffff\"],\n [\"beige\", \"#f5f5dc\"],\n [\"bisque\", \"#ffe4c4\"],\n [\"black\", \"#000000\"],\n [\"blanchedalmond\", \"#ffebcd\"],\n [\"blue\", \"#0000ff\"],\n [\"blueviolet\", \"#8a2be2\"],\n [\"brown\", \"#a52a2a\"],\n [\"burlywood\", \"#deb887\"],\n [\"cadetblue\", \"#5f9ea0\"],\n [\"chartreuse\", \"#7fff00\"],\n [\"chocolate\", \"#d2691e\"],\n [\"coral\", \"#ff7f50\"],\n [\"cornflowerblue\", \"#6495ed\"],\n [\"cornsilk\", \"#fff8dc\"],\n [\"crimson\", \"#dc143c\"],\n [\"cyan\", \"#00ffff\"],\n [\"darkblue\", \"#00008b\"],\n [\"darkcyan\", \"#008b8b\"],\n [\"darkgoldenrod\", \"#b8860b\"],\n [\"darkgray\", \"#a9a9a9\"],\n [\"darkgreen\", \"#006400\"],\n [\"darkgrey\", \"#a9a9a9\"],\n [\"darkkhaki\", \"#bdb76b\"],\n [\"darkmagenta\", \"#8b008b\"],\n [\"darkolivegreen\", \"#556b2f\"],\n [\"darkorange\", \"#ff8c00\"],\n [\"darkorchid\", \"#9932cc\"],\n [\"darkred\", \"#8b0000\"],\n [\"darksalmon\", \"#e9967a\"],\n [\"darkseagreen\", \"#8fbc8f\"],\n [\"darkslateblue\", \"#483d8b\"],\n [\"darkslategray\", \"#2f4f4f\"],\n [\"darkslategrey\", \"#2f4f4f\"],\n [\"darkturquoise\", \"#00ced1\"],\n [\"darkviolet\", \"#9400d3\"],\n [\"deeppink\", \"#ff1493\"],\n [\"deepskyblue\", \"#00bfff\"],\n [\"dimgray\", \"#696969\"],\n [\"dimgrey\", \"#696969\"],\n [\"dodgerblue\", \"#1e90ff\"],\n [\"firebrick\", \"#b22222\"],\n [\"floralwhite\", \"#fffaf0\"],\n [\"forestgreen\", \"#228b22\"],\n [\"fuchsia\", \"#ff00ff\"],\n [\"gainsboro\", \"#dcdcdc\"],\n [\"ghostwhite\", \"#f8f8ff\"],\n [\"gold\", \"#ffd700\"],\n [\"goldenrod\", \"#daa520\"],\n [\"gray\", \"#808080\"],\n [\"green\", \"#008000\"],\n [\"greenyellow\", \"#adff2f\"],\n [\"grey\", \"#808080\"],\n [\"honeydew\", \"#f0fff0\"],\n [\"hotpink\", \"#ff69b4\"],\n [\"indianred\", \"#cd5c5c\"],\n [\"indigo\", \"#4b0082\"],\n [\"ivory\", \"#fffff0\"],\n [\"khaki\", \"#f0e68c\"],\n [\"lavender\", \"#e6e6fa\"],\n [\"lavenderblush\", \"#fff0f5\"],\n [\"lawngreen\", \"#7cfc00\"],\n [\"lemonchiffon\", \"#fffacd\"],\n [\"lightblue\", \"#add8e6\"],\n [\"lightcoral\", \"#f08080\"],\n [\"lightcyan\", \"#e0ffff\"],\n [\"lightgoldenrodyellow\", \"#fafad2\"],\n [\"lightgray\", \"#d3d3d3\"],\n [\"lightgreen\", \"#90ee90\"],\n [\"lightgrey\", \"#d3d3d3\"],\n [\"lightpink\", \"#ffb6c1\"],\n [\"lightsalmon\", \"#ffa07a\"],\n [\"lightseagreen\", \"#20b2aa\"],\n [\"lightskyblue\", \"#87cefa\"],\n [\"lightslategray\", \"#778899\"],\n [\"lightslategrey\", \"#778899\"],\n [\"lightsteelblue\", \"#b0c4de\"],\n [\"lightyellow\", \"#ffffe0\"],\n [\"lime\", \"#00ff00\"],\n [\"limegreen\", \"#32cd32\"],\n [\"linen\", \"#faf0e6\"],\n [\"magenta\", \"#ff00ff\"],\n [\"maroon\", \"#800000\"],\n [\"mediumaquamarine\", \"#66cdaa\"],\n [\"mediumblue\", \"#0000cd\"],\n [\"mediumorchid\", \"#ba55d3\"],\n [\"mediumpurple\", \"#9370db\"],\n [\"mediumseagreen\", \"#3cb371\"],\n [\"mediumslateblue\", \"#7b68ee\"],\n [\"mediumspringgreen\", \"#00fa9a\"],\n [\"mediumturquoise\", \"#48d1cc\"],\n [\"mediumvioletred\", \"#c71585\"],\n [\"midnightblue\", \"#191970\"],\n [\"mintcream\", \"#f5fffa\"],\n [\"mistyrose\", \"#ffe4e1\"],\n [\"moccasin\", \"#ffe4b5\"],\n [\"navajowhite\", \"#ffdead\"],\n [\"navy\", \"#000080\"],\n [\"oldlace\", \"#fdf5e6\"],\n [\"olive\", \"#808000\"],\n [\"olivedrab\", \"#6b8e23\"],\n [\"orange\", \"#ffa500\"],\n [\"orangered\", \"#ff4500\"],\n [\"orchid\", \"#da70d6\"],\n [\"palegoldenrod\", \"#eee8aa\"],\n [\"palegreen\", \"#98fb98\"],\n [\"paleturquoise\", \"#afeeee\"],\n [\"palevioletred\", \"#db7093\"],\n [\"papayawhip\", \"#ffefd5\"],\n [\"peachpuff\", \"#ffdab9\"],\n [\"peru\", \"#cd853f\"],\n [\"pink\", \"#ffc0cb\"],\n [\"plum\", \"#dda0dd\"],\n [\"powderblue\", \"#b0e0e6\"],\n [\"purple\", \"#800080\"],\n [\"rebeccapurple\", \"#663399\"],\n [\"red\", \"#ff0000\"],\n [\"rosybrown\", \"#bc8f8f\"],\n [\"royalblue\", \"#4169e1\"],\n [\"saddlebrown\", \"#8b4513\"],\n [\"salmon\", \"#fa8072\"],\n [\"sandybrown\", \"#f4a460\"],\n [\"seagreen\", \"#2e8b57\"],\n [\"seashell\", \"#fff5ee\"],\n [\"sienna\", \"#a0522d\"],\n [\"silver\", \"#c0c0c0\"],\n [\"skyblue\", \"#87ceeb\"],\n [\"slateblue\", \"#6a5acd\"],\n [\"slategray\", \"#708090\"],\n [\"slategrey\", \"#708090\"],\n [\"snow\", \"#fffafa\"],\n [\"springgreen\", \"#00ff7f\"],\n [\"steelblue\", \"#4682b4\"],\n [\"tan\", \"#d2b48c\"],\n [\"teal\", \"#008080\"],\n [\"thistle\", \"#d8bfd8\"],\n [\"tomato\", \"#ff6347\"],\n [\"turquoise\", \"#40e0d0\"],\n [\"violet\", \"#ee82ee\"],\n [\"wheat\", \"#f5deb3\"],\n [\"white\", \"#ffffff\"],\n [\"whitesmoke\", \"#f5f5f5\"],\n [\"yellow\", \"#ffff00\"],\n [\"yellowgreen\", \"#9acd32\"],\n];\n\n/**\n * All 148 CSS named colors in alphabetical order. Aliases (aqua/cyan,\n * fuchsia/magenta, the gray/grey pairs) are separate entries sharing a hex.\n */\nexport const CSS_NAMED_COLORS: readonly NamedColor[] = TABLE.map(([name, hex]) => ({\n name,\n hex,\n}));\n\n/** A nearest-name match: the keyword, its spec hex, and the ΔEOK distance. */\nexport interface NearestNamedColor extends NamedColor {\n /** ΔEOK from the query color to this named color (0 = exact hit). */\n deltaEOK: number;\n}\n\nlet labCache: Oklab[] | null = null;\n\n/**\n * The CSS named color perceptually closest to `hex`, by ΔEOK in OKLab.\n * Exact hits return distance 0. Ties — including shared-hex aliases like\n * aqua/cyan — go to the alphabetically first keyword. As a rough guide,\n * distances ≲ 0.02 are visually indistinguishable and ≳ 0.1 is only a loose\n * \"same family\" match.\n *\n * @throws RangeError on malformed hex (see parseHex).\n */\nexport function nearestNamedColor(hex: string): NearestNamedColor {\n const target = rgbToOklab(parseHex(hex));\n labCache ??= TABLE.map(([, value]) => rgbToOklab(parseHex(value)));\n let bestIdx = 0;\n let bestD = Infinity;\n for (let i = 0; i < labCache.length; i++) {\n const d = deltaEOKLab(target, labCache[i]!);\n if (d < bestD) {\n bestD = d;\n bestIdx = i;\n }\n }\n const [name, value] = TABLE[bestIdx]!;\n return { name, hex: value, deltaEOK: bestD };\n}\n","// Full-palette derivation from a single seed color. All deterministic —\n// no randomness, no clock — so the same seed always yields the same\n// palette (proposals are reviewable and reproducible).\n//\n// Strategy:\n// - primary: the seed itself (normalized).\n// - secondary / highlight: classic harmony rotations of the seed in OKLCH\n// (complementary 180°, triadic ±120°, split-complementary ±150°).\n// secondary maximizes ΔEOK from the primary; highlight maximizes the\n// minimum ΔEOK to both, so the three accents spread apart. Near-achromatic\n// seeds (C < 0.02) have no usable hue, so rotations run from a synthetic\n// chromatic anchor at ANCHOR_HUE instead (a gray seed still deserves\n// real accents).\n// - good / warn / danger: hue-anchored at 145° / 85° / 25° OKLCH (green /\n// amber / red), darkened via adjustLightnessUntil until they clear WCAG's\n// 3:1 non-text bar (SC 1.4.11) against the derived background.\n// - chart set: hue rotations at a constant, gamut-safe (CHART_L, CHART_C)\n// chosen so every hue fits sRGB without chroma clamping. Adjacent 60°\n// hues then sit 2·C·sin(30°) = C apart in OKLab, so with C = 0.115 the\n// pairwise ΔEOK floor CHART_DELTA_MIN = 0.1 holds by construction.\n// - neutrals bg / surface / neutral / text: a near-achromatic ladder tinted\n// with the seed hue (C ≤ 0.012), L 0.985 → 0.24; text vs bg lands well\n// past 7:1 (AAA).\n\nimport type { Swatch, SwatchRole } from \"../types\";\nimport { normalizeHex } from \"./hex\";\nimport { clampToGamut, hexToOklch, oklchToHex } from \"./convert\";\nimport { contrastRatio } from \"./contrast\";\nimport { rotateHue } from \"./harmony\";\nimport { adjustLightnessUntil } from \"./ramp\";\nimport { deltaEOK } from \"./delta\";\nimport { nearestNamedColor } from \"./names\";\n\n/**\n * Documented floor for pairwise ΔEOK between derived chart colors. The\n * default chart constants guarantee it by construction (see file header);\n * deriveChartColors also enforces it as a filter.\n */\nexport const CHART_DELTA_MIN = 0.1;\n\n/** Chart lightness: chosen with CHART_C so all hues are in-gamut (min max-chroma over hues ≈ 0.119 at L = 0.7). */\nconst CHART_L = 0.7;\n/** Chart chroma: gamut-safe at CHART_L for every hue, so no clamping ever shifts a chart color. */\nconst CHART_C = 0.115;\n/** Hue used when a seed is too gray to have one (a mid blue). */\nconst ANCHOR_HUE = 250;\n/** Below this OKLCH chroma a seed is treated as achromatic. */\nconst ACHROMATIC_C = 0.02;\n/** Status swatch anchors (OKLCH hue degrees): green / amber / red. */\nconst STATUS_HUES = { good: 145, warn: 85, danger: 25 } as const;\nconst STATUS_L = 0.7;\nconst STATUS_C = 0.14;\n/** Harmony rotations tried for secondary/highlight, in tie-break order. */\nconst ROTATIONS = [180, 120, -120, 150, -150] as const;\n\n/** Options for {@link deriveChartColors}. */\nexport interface DeriveChartOptions {\n /** How many colors to return, 1..12 (default 6). */\n count?: number;\n /** Pairwise ΔEOK floor (default {@link CHART_DELTA_MIN}). Best-effort above the default. */\n deltaMin?: number;\n}\n\n/**\n * Derives categorical chart colors from a seed: hue rotations (60° grid,\n * then 30° offsets) around the seed's hue at a constant, gamut-safe\n * lightness/chroma. Candidates are kept only if they stay at least\n * `deltaMin` ΔEOK from every accepted color; if the requested floor is\n * unattainable for `count` colors, remaining distinct candidates fill the\n * set in hue order so callers always get `count` colors back.\n *\n * @throws RangeError when `count` is not an integer in 1..12, or on\n * malformed hex.\n */\nexport function deriveChartColors(seedHex: string, opts: DeriveChartOptions = {}): string[] {\n const count = opts.count ?? 6;\n const deltaMin = opts.deltaMin ?? CHART_DELTA_MIN;\n if (!Number.isInteger(count) || count < 1 || count > 12) {\n throw new RangeError(`deriveChartColors: count must be an integer in 1..12, got ${count}`);\n }\n const lch = hexToOklch(normalizeHex(seedHex));\n const base = lch.C < ACHROMATIC_C ? ANCHOR_HUE : lch.h;\n const offsets = [0, 60, 120, 180, 240, 300, 30, 90, 150, 210, 270, 330];\n const candidates = offsets.map((deg) =>\n oklchToHex(clampToGamut({ L: CHART_L, C: CHART_C, h: (base + deg) % 360 })),\n );\n const picked: string[] = [];\n for (const hex of candidates) {\n if (picked.length >= count) break;\n if (picked.includes(hex)) continue;\n if (picked.every((p) => deltaEOK(p, hex) >= deltaMin)) picked.push(hex);\n }\n for (const hex of candidates) {\n if (picked.length >= count) break;\n if (!picked.includes(hex)) picked.push(hex);\n }\n return picked;\n}\n\n/** Options for {@link derivePalette}. */\nexport interface DerivePaletteOptions {\n /** Attach nearest-CSS-keyword `name`s to every swatch (default true). */\n names?: boolean;\n}\n\n/**\n * Derives a complete brand palette from one seed color: one swatch each for\n * primary, secondary, highlight, good, warn, danger, bg, surface, text and\n * neutral, plus six chart swatches (16 total) — see the file header for the\n * derivation strategy and its WCAG guarantees.\n *\n * @throws RangeError on malformed seed hex (see parseHex).\n */\nexport function derivePalette(seedHex: string, opts: DerivePaletteOptions = {}): Swatch[] {\n const withNames = opts.names !== false;\n const seed = normalizeHex(seedHex);\n const seedLch = hexToOklch(seed);\n const achromatic = seedLch.C < ACHROMATIC_C;\n const hue = achromatic ? ANCHOR_HUE : seedLch.h;\n\n const neutralAt = (L: number, C: number): string => oklchToHex(clampToGamut({ L, C, h: hue }));\n const bg = neutralAt(0.985, 0.005);\n const surface = neutralAt(0.955, 0.008);\n const text = neutralAt(0.24, 0.012);\n const neutral = neutralAt(0.62, 0.012);\n\n const accentBase = achromatic\n ? oklchToHex(\n clampToGamut({ L: Math.min(Math.max(seedLch.L, 0.45), 0.75), C: CHART_C, h: ANCHOR_HUE }),\n )\n : seed;\n const cands = ROTATIONS.map((deg) => {\n const hex = rotateHue(accentBase, deg);\n return { deg, hex, d: deltaEOK(seed, hex) };\n });\n let secondary = cands[0]!;\n for (const c of cands) if (c.d > secondary.d) secondary = c;\n let highlight = cands[0] === secondary ? cands[1]! : cands[0]!;\n let hiScore = Math.min(highlight.d, deltaEOK(highlight.hex, secondary.hex));\n for (const c of cands) {\n if (c === secondary || c === highlight) continue;\n const score = Math.min(c.d, deltaEOK(c.hex, secondary.hex));\n if (score > hiScore) {\n highlight = c;\n hiScore = score;\n }\n }\n\n const status = (anchor: number): string => {\n const start = oklchToHex(clampToGamut({ L: STATUS_L, C: STATUS_C, h: anchor }));\n return adjustLightnessUntil(start, (c) => contrastRatio(c, bg) >= 3, \"darken\") ?? text;\n };\n\n const sw = (role: SwatchRole, hex: string, rationale: string): Swatch => ({\n role,\n hex,\n rationale,\n });\n const swatches: Swatch[] = [\n sw(\"primary\", seed, \"seed color\"),\n sw(\"secondary\", secondary.hex, `harmony rotation ${secondary.deg}°, ΔEOK ${secondary.d.toFixed(3)} from primary`),\n sw(\"highlight\", highlight.hex, `harmony rotation ${highlight.deg}°, spread from primary and secondary`),\n sw(\"good\", status(STATUS_HUES.good), \"green anchor 145°, ≥3:1 on bg\"),\n sw(\"warn\", status(STATUS_HUES.warn), \"amber anchor 85°, ≥3:1 on bg\"),\n sw(\"danger\", status(STATUS_HUES.danger), \"red anchor 25°, ≥3:1 on bg\"),\n sw(\"bg\", bg, \"near-white tinted with the seed hue\"),\n sw(\"surface\", surface, \"raised surface, one step below bg\"),\n sw(\"text\", text, \"near-black tinted with the seed hue\"),\n sw(\"neutral\", neutral, \"mid neutral for borders and muted marks\"),\n ...deriveChartColors(seed).map((hex, i) => sw(\"chart\", hex, `chart series ${i + 1}`)),\n ];\n return withNames\n ? swatches.map((s) => ({ ...s, name: nearestNamedColor(s.hex).name }))\n : swatches;\n}\n","// The --ui-* custom-property names @odla-ai/brand emits, as data.\n//\n// PROVENANCE: mirrors @odla-ai/ui — the REQUIRED_TOKENS / ACCENT_TOKENS /\n// CHART_TOKENS / CHAT_TOKENS tiers in js/tokens.js, and the\n// :where([data-ui-accent]) re-declaration block in css/tokens.css.\n//\n// Why more than the required tier: a compiled token object doubles as a\n// runtime override-island payload, and CSS custom properties substitute\n// var() at computed-value time ON THE DECLARING ELEMENT. An island that\n// overrides --ui-accent alone would keep the :root-computed --ui-chart-1,\n// --ui-accent-glow, … stale. So besides the required tier the compiler\n// re-declares the whole accent-composing derived family — everything the\n// ui root's :where([data-ui-accent]) block re-declares, plus the remaining\n// chart series and chat roles so charts and chat surfaces recompute against\n// the brand palette too. test/tokens/map.test.ts cross-checks these lists\n// against the real @odla-ai/ui export, so they cannot drift silently.\n\n/**\n * The @odla-ai/ui required tier (js/tokens.js REQUIRED_TOKENS), in the ui\n * contract's order. The compiler always emits a concrete value for every\n * one of these.\n */\nexport const BRAND_REQUIRED_TOKENS = [\n \"--ui-bg\",\n \"--ui-surface\",\n \"--ui-surface-2\",\n \"--ui-text\",\n \"--ui-text-muted\",\n \"--ui-text-faint\",\n \"--ui-border\",\n \"--ui-border-strong\",\n \"--ui-accent\",\n \"--ui-accent-strong\",\n \"--ui-accent-soft\",\n \"--ui-on-accent\",\n \"--ui-good\",\n \"--ui-good-soft\",\n \"--ui-warn\",\n \"--ui-warn-soft\",\n \"--ui-danger\",\n \"--ui-danger-soft\",\n \"--ui-code-bg\",\n \"--ui-code-text\",\n \"--ui-shadow\",\n \"--ui-font-sans\",\n \"--ui-font-serif\",\n \"--ui-font-mono\",\n \"--ui-font-display\",\n] as const;\n\n/**\n * Accent-composing derived roles from the ui defaulted tier. All five\n * accent-family members of the :where([data-ui-accent]) re-declaration set\n * (--ui-accent-glow, --ui-accent-2, --ui-accent-2-soft, --ui-highlight,\n * --ui-focus) plus --ui-shadow-strong, which composes var(--ui-shadow) —\n * a token this compiler re-declares, so the island rule applies to it too.\n */\nexport const BRAND_DERIVED_TOKENS = [\n \"--ui-accent-glow\",\n \"--ui-accent-2\",\n \"--ui-accent-2-soft\",\n \"--ui-highlight\",\n \"--ui-focus\",\n \"--ui-shadow-strong\",\n] as const;\n\n/**\n * Chart roles the compiler emits: the six series slots (real palette\n * swatches when the palette carries them, ui var() compositions otherwise)\n * and the four accent-composing chart roles the :where([data-ui-accent])\n * block re-declares (band, band-strong, flow, glow).\n */\nexport const BRAND_CHART_TOKENS = [\n \"--ui-chart-1\",\n \"--ui-chart-2\",\n \"--ui-chart-3\",\n \"--ui-chart-4\",\n \"--ui-chart-5\",\n \"--ui-chart-6\",\n \"--ui-chart-band\",\n \"--ui-chart-band-strong\",\n \"--ui-chart-flow\",\n \"--ui-chart-glow\",\n] as const;\n\n/**\n * Chat surface roles (js/tokens.js CHAT_TOKENS, complete). Two are in the\n * accent-swap set (user-bg, tool-accent); the other four compose surface /\n * text tokens the compiler also re-declares, so an island stays coherent.\n */\nexport const BRAND_CHAT_TOKENS = [\n \"--ui-chat-user-bg\",\n \"--ui-chat-user-text\",\n \"--ui-chat-assistant-bg\",\n \"--ui-chat-thinking-bg\",\n \"--ui-chat-thinking-text\",\n \"--ui-chat-tool-accent\",\n] as const;\n\n/**\n * Every token the compiler emits, in emission order — also the deterministic\n * declaration order renderTokensCss uses, so golden CSS fixtures are stable.\n */\nexport const BRAND_EMITTED_TOKENS: readonly string[] = [\n ...BRAND_REQUIRED_TOKENS,\n ...BRAND_DERIVED_TOKENS,\n ...BRAND_CHART_TOKENS,\n ...BRAND_CHAT_TOKENS,\n];\n","// Palette → light-mode @odla-ai/ui tokens.\n//\n// Every name in BRAND_EMITTED_TOKENS is always emitted. Real swatches map\n// directly; missing roles fall back to derivePalette() output seeded from\n// the accent (with a warning); derived/composed roles use the EXACT\n// var()/color-mix() composition strings @odla-ai/ui css/tokens.css uses as\n// defaults, so the object works as a scoped override-island payload (the\n// compositions recompute at the declaring element — see roles.ts).\n//\n// Contrast guarantees (WCAG 2.1, enforced with adjustLightnessUntil and\n// recorded as warnings with `adjustedFrom` whenever a color had to move):\n// --ui-text ≥ 4.5:1 and --ui-text-muted ≥ 4.5:1 on --ui-bg (SC 1.4.3),\n// --ui-accent-strong ≥ 4.5:1 on --ui-bg (darkened on a light bg),\n// --ui-good/warn/danger ≥ 3:1 on --ui-bg (SC 1.4.11), and --ui-on-accent\n// via pickTextOn (≥ 4.5:1 with the default white/near-black candidates).\n\nimport type { Swatch, SwatchRole } from \"../types\";\nimport {\n adjustLightnessUntil,\n clamp01,\n clampToGamut,\n contrastRatio,\n deriveChartColors,\n derivePalette,\n hexToOklch,\n normalizeHex,\n oklchToHex,\n parseHex,\n pickTextOn,\n relativeLuminance,\n type LightnessDirection,\n} from \"../color\";\nimport type { BrandTokens, CompileInput, TokenWarning } from \"./types\";\n\n/**\n * Seed used when the palette has no usable chromatic swatch at all — the\n * @odla-ai/ui neutral default accent (css/tokens.css restrained blue).\n */\nexport const DEFAULT_ACCENT_SEED = \"#3b5e8c\";\n\n/** Below this OKLCH chroma a swatch is too gray for accent/chart duty. */\nconst CHROMATIC_C = 0.02;\n\n// System font stacks, verbatim from @odla-ai/ui css/tokens.css.\nconst SANS_STACK = `ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif`;\nconst SERIF_STACK = `\"Iowan Old Style\", \"Palatino Linotype\", Palatino, Georgia, serif`;\nconst MONO_STACK = `ui-monospace, \"SF Mono\", Menlo, Consolas, monospace`;\nconst DISPLAY_STACK = `ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif`;\n\n// ui css/tokens.css chart defaults — emitted when the palette lacks ≥3\n// chromatic chart swatches, so an override island never keeps root values.\nconst UI_CHART_DEFAULTS = [\n \"var(--ui-accent)\",\n \"var(--ui-good)\",\n \"var(--ui-warn)\",\n \"var(--ui-danger)\",\n \"color-mix(in srgb, var(--ui-accent) 45%, var(--ui-text))\",\n \"var(--ui-text-faint)\",\n] as const;\n\n/** The token a missing swatch role is reported against. */\nconst ROLE_TOKEN: Partial<Record<SwatchRole, string>> = {\n primary: \"--ui-accent\",\n secondary: \"--ui-accent-2\",\n highlight: \"--ui-highlight\",\n bg: \"--ui-bg\",\n surface: \"--ui-surface\",\n text: \"--ui-text\",\n neutral: \"--ui-border-strong\",\n good: \"--ui-good\",\n warn: \"--ui-warn\",\n danger: \"--ui-danger\",\n chart: \"--ui-chart-1\",\n};\n\nconst mixVar = (name: string, pct: number): string =>\n `color-mix(in srgb, var(${name}) ${pct}%, transparent)`;\n\n/** Shift OKLab lightness by dL, constant chroma/hue, gamut-clamped. */\nfunction shiftL(hex: string, dL: number): string {\n const { L, C, h } = hexToOklch(hex);\n return oklchToHex(clampToGamut({ L: clamp01(L + dL), C, h }));\n}\n\n/** Move `fromHex` a fraction `t` of the way to `toHex` in OKLab lightness only. */\nfunction lerpL(fromHex: string, toHex: string, t: number): string {\n const a = hexToOklch(fromHex);\n const b = hexToOklch(toHex);\n return oklchToHex(clampToGamut({ L: a.L + (b.L - a.L) * t, C: a.C, h: a.h }));\n}\n\n/**\n * Push `hex` away from `bg` in lightness until it clears `min`:1. Tries the\n * away-from-bg direction (darken on a light bg), then the other direction,\n * then falls back to pickTextOn(bg) — which always clears 4.5:1.\n */\nfunction ensureOnBg(hex: string, bg: string, min: number): string {\n const pred = (c: string): boolean => contrastRatio(c, bg) >= min;\n const away: LightnessDirection =\n relativeLuminance(bg) >= relativeLuminance(hex) ? \"darken\" : \"lighten\";\n return (\n adjustLightnessUntil(hex, pred, away) ??\n adjustLightnessUntil(hex, pred, away === \"darken\" ? \"lighten\" : \"darken\") ??\n pickTextOn(bg)\n );\n}\n\nconst rgbaOf = (hex: string, alpha: number): string => {\n const { r, g, b } = parseHex(hex);\n const c = (v: number): number => Math.round(v * 255);\n return `rgba(${c(r)}, ${c(g)}, ${c(b)}, ${alpha})`;\n};\n\n/** Brand family + system stack; quotes a single family name when needed. */\nfunction fontValue(family: string | undefined, stack: string): string {\n const f = family?.trim() ?? \"\";\n if (f === \"\") return stack;\n if (f.includes(\",\") || /^[\"']/.test(f) || /^[a-zA-Z][a-zA-Z0-9-]*$/.test(f))\n return `${f}, ${stack}`;\n return `\"${f.replaceAll('\"', \"\")}\", ${stack}`;\n}\n\ninterface Collected {\n byRole: Partial<Record<SwatchRole, string>>;\n charts: string[];\n}\n\n/** First-wins role → hex map + ordered chart swatches; bad hexes dropped. */\nfunction collect(swatches: unknown, warnings: TokenWarning[]): Collected {\n const byRole: Partial<Record<SwatchRole, string>> = {};\n const charts: string[] = [];\n if (!Array.isArray(swatches)) {\n warnings.push({ token: \"--ui-accent\", message: \"swatches is not an array; compiling from defaults\" });\n return { byRole, charts };\n }\n (swatches as Array<Swatch | null>).forEach((s, i) => {\n let hex: string;\n try {\n hex = normalizeHex(String(s?.hex));\n } catch {\n warnings.push({\n token: (s?.role !== undefined && ROLE_TOKEN[s.role]) || \"--ui-accent\",\n message: `swatches[${i}] dropped: invalid hex ${String(s?.hex)}`,\n });\n return;\n }\n const role = s?.role;\n if (role === \"chart\") charts.push(hex);\n else if (role !== undefined && role !== \"custom\" && byRole[role] === undefined) byRole[role] = hex;\n });\n return { byRole, charts };\n}\n\n/** What {@link mapPaletteToTokens} returns. */\nexport interface MapResult {\n /** The light-mode token map — every name in BRAND_EMITTED_TOKENS. */\n tokens: BrandTokens;\n /** Fallbacks taken and contrast adjustments made. */\n warnings: TokenWarning[];\n}\n\n/**\n * Maps a palette (+ optional typography) onto the full light-mode\n * @odla-ai/ui token set. Never throws on bad palettes: invalid swatches are\n * dropped, missing roles derive from the accent via derivePalette, and every\n * degradation is recorded as a {@link TokenWarning}.\n */\nexport function mapPaletteToTokens(input: CompileInput): MapResult {\n const warnings: TokenWarning[] = [];\n const { byRole, charts } = collect(input?.swatches, warnings);\n\n let accent = byRole.primary;\n if (accent === undefined) {\n const chromatic = [...Object.values(byRole), ...charts].find(\n (h) => h !== undefined && hexToOklch(h).C >= CHROMATIC_C,\n );\n accent = chromatic ?? DEFAULT_ACCENT_SEED;\n warnings.push({\n token: \"--ui-accent\",\n message: `no primary swatch; using ${chromatic !== undefined ? \"the first chromatic swatch\" : \"the neutral default seed\"} ${accent}`,\n });\n }\n\n const fb = new Map<SwatchRole, string>();\n for (const s of derivePalette(accent, { names: false })) if (!fb.has(s.role)) fb.set(s.role, s.hex);\n const pick = (role: SwatchRole): string => {\n const own = byRole[role];\n if (own !== undefined) return own;\n const derived = fb.get(role)!;\n warnings.push({ token: ROLE_TOKEN[role]!, message: `no ${role} swatch; derived ${derived} from ${accent}` });\n return derived;\n };\n\n const bg = pick(\"bg\");\n const surface = pick(\"surface\");\n const neutral = pick(\"neutral\");\n const surface2 = shiftL(surface, -0.035);\n\n const rawText = pick(\"text\");\n const text = ensureOnBg(rawText, bg, 4.5);\n if (text !== rawText)\n warnings.push({ token: \"--ui-text\", message: \"adjusted to reach 4.5:1 on --ui-bg\", adjustedFrom: rawText });\n\n const accentStrong = ensureOnBg(accent, bg, 4.5);\n if (accentStrong !== accent)\n warnings.push({\n token: \"--ui-accent-strong\",\n message: \"moved --ui-accent in lightness to reach 4.5:1 on --ui-bg\",\n adjustedFrom: accent,\n });\n\n const status = (role: \"good\" | \"warn\" | \"danger\"): string => {\n const own = pick(role);\n const fixed = ensureOnBg(own, bg, 3);\n if (fixed !== own)\n warnings.push({ token: ROLE_TOKEN[role]!, message: \"adjusted to reach 3:1 on --ui-bg\", adjustedFrom: own });\n return fixed;\n };\n\n const chromaticCharts = charts.filter((h) => hexToOklch(h).C >= CHROMATIC_C);\n let chartValues: readonly string[];\n if (chromaticCharts.length >= 3) {\n const picked = chromaticCharts.slice(0, 6);\n for (const c of deriveChartColors(accent, { count: 12 })) {\n if (picked.length >= 6) break;\n if (!picked.includes(c)) picked.push(c);\n }\n chartValues = picked;\n } else {\n warnings.push({\n token: \"--ui-chart-1\",\n message: `fewer than 3 chromatic chart swatches (${chromaticCharts.length}); using the ui derived chart defaults`,\n });\n chartValues = UI_CHART_DEFAULTS;\n }\n\n const secondary = byRole.secondary;\n if (secondary === undefined)\n warnings.push({ token: \"--ui-accent-2\", message: \"no secondary swatch; --ui-accent-2 collapses onto var(--ui-accent) (ui default)\" });\n const highlight = byRole.highlight;\n if (highlight === undefined)\n warnings.push({ token: \"--ui-highlight\", message: \"no highlight swatch; --ui-highlight aliases var(--ui-accent-strong) (ui default)\" });\n\n const t = input?.typography ?? {};\n const tokens: BrandTokens = {\n \"--ui-bg\": bg,\n \"--ui-surface\": surface,\n \"--ui-surface-2\": surface2,\n \"--ui-text\": text,\n \"--ui-text-muted\": ensureOnBg(lerpL(text, bg, 0.35), bg, 4.5),\n \"--ui-text-faint\": lerpL(text, bg, 0.58),\n \"--ui-border\": lerpL(neutral, bg, 0.55),\n \"--ui-border-strong\": neutral,\n \"--ui-accent\": accent,\n \"--ui-accent-strong\": accentStrong,\n \"--ui-accent-soft\": mixVar(\"--ui-accent\", 10),\n \"--ui-on-accent\": pickTextOn(accent),\n \"--ui-good\": status(\"good\"),\n \"--ui-good-soft\": mixVar(\"--ui-good\", 12),\n \"--ui-warn\": status(\"warn\"),\n \"--ui-warn-soft\": mixVar(\"--ui-warn\", 12),\n \"--ui-danger\": status(\"danger\"),\n \"--ui-danger-soft\": mixVar(\"--ui-danger\", 10),\n \"--ui-code-bg\": surface2,\n \"--ui-code-text\": text,\n \"--ui-shadow\": `0 1px 2px ${rgbaOf(text, 0.04)}, 0 8px 24px ${rgbaOf(text, 0.06)}`,\n \"--ui-font-sans\": fontValue(t.fontBody, SANS_STACK),\n \"--ui-font-serif\": SERIF_STACK,\n \"--ui-font-mono\": fontValue(t.fontMono, MONO_STACK),\n \"--ui-font-display\": fontValue(t.fontDisplay ?? t.fontBody, DISPLAY_STACK),\n \"--ui-accent-glow\": mixVar(\"--ui-accent\", 16),\n \"--ui-accent-2\": secondary ?? \"var(--ui-accent)\",\n \"--ui-accent-2-soft\": mixVar(\"--ui-accent-2\", 10),\n \"--ui-highlight\": highlight ?? \"var(--ui-accent-strong)\",\n \"--ui-focus\": \"0 0 0 3px var(--ui-accent-soft)\",\n \"--ui-shadow-strong\": \"var(--ui-shadow)\",\n \"--ui-chart-1\": chartValues[0]!,\n \"--ui-chart-2\": chartValues[1]!,\n \"--ui-chart-3\": chartValues[2]!,\n \"--ui-chart-4\": chartValues[3]!,\n \"--ui-chart-5\": chartValues[4]!,\n \"--ui-chart-6\": chartValues[5]!,\n \"--ui-chart-band\": mixVar(\"--ui-accent\", 10),\n \"--ui-chart-band-strong\": mixVar(\"--ui-accent\", 22),\n \"--ui-chart-flow\": \"var(--ui-accent)\",\n \"--ui-chart-glow\": \"var(--ui-accent-strong)\",\n \"--ui-chat-user-bg\": \"var(--ui-accent-soft)\",\n \"--ui-chat-user-text\": \"var(--ui-text)\",\n \"--ui-chat-assistant-bg\": \"var(--ui-surface)\",\n \"--ui-chat-thinking-bg\": \"var(--ui-surface-2)\",\n \"--ui-chat-thinking-text\": \"var(--ui-text-muted)\",\n \"--ui-chat-tool-accent\": \"var(--ui-accent)\",\n };\n return { tokens, warnings };\n}\n","// Light → dark token derivation.\n//\n// Neutrals (bg / text ladder / borders / code text) flip around OKLab L 0.5:\n// L' = 1 − L reflected, then squeezed into [DARK_FLIP_L_MIN,\n// DARK_FLIP_L_MAX] so the background lands dark but never black and text\n// lands light but never pure white. Chroma and hue are untouched, so tinted\n// neutrals keep their brand tint.\n//\n// Surfaces are the one place a mirror is wrong: light surfaces sit ABOVE\n// the light bg (lighter), so mirroring would drop them BELOW the dark bg —\n// inverted elevation. Instead the surface ladder is rebuilt upward from the\n// derived dark bg (the @odla-ai/ui dark-neutral pattern: bg < surface <\n// surface-2), reusing the light ladder's own lightness gaps as rung heights\n// (with a small minimum so equal-ish inputs still separate).\n//\n// Accents (accent family, statuses) and chart series keep their hue/chroma\n// and are re-lightened with adjustLightnessUntil until they clear WCAG bars\n// against the derived dark bg: 4.5:1 for the accent family and statuses\n// (SC 1.4.3), 3:1 for chart series (SC 1.4.11 graphics). --ui-on-accent is\n// re-picked against the dark accent. Everything else — var()/color-mix()\n// compositions, font stacks, focus ring — passes through unchanged and\n// recomputes in CSS against the dark values (--ui-shadow is the one literal\n// that gets the ui dark shadow instead).\n\nimport {\n adjustLightnessUntil,\n clampToGamut,\n contrastRatio,\n hexToOklch,\n oklchToHex,\n pickTextOn,\n} from \"../color\";\nimport type { BrandTokens } from \"./types\";\n\n/** Darkest OKLab L a flipped neutral may take — the dark bg floor (never black). */\nexport const DARK_FLIP_L_MIN = 0.2;\n\n/** Lightest OKLab L a flipped neutral may take — keeps flipped text off pure white. */\nexport const DARK_FLIP_L_MAX = 0.95;\n\n/** The ui neutral dark shadow (css/tokens.css `[data-theme=\"dark\"]` value). */\nconst DARK_SHADOW = \"0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 24px rgba(0, 0, 0, 0.35)\";\n\nconst HEX6 = /^#[0-9a-f]{6}$/;\n\n/** Neutral ladder: flipped around L 0.5 with the floor/ceiling squeeze.\n * Surfaces/code-bg are here only as a fallback — with a hex bg present they\n * are rebuilt upward from the dark bg instead (see file header). */\nconst NEUTRAL_FLIP = new Set([\n \"--ui-bg\",\n \"--ui-surface\",\n \"--ui-surface-2\",\n \"--ui-text\",\n \"--ui-text-muted\",\n \"--ui-text-faint\",\n \"--ui-border\",\n \"--ui-border-strong\",\n \"--ui-code-bg\",\n \"--ui-code-text\",\n]);\n\n/** Minimum OKLab ΔL between dark surface rungs, so elevation always reads. */\nconst MIN_RUNG = 0.02;\n\n/** Re-lightened until ≥ 4.5:1 on the dark bg (readable as text). */\nconst ACCENT_AA = [\n \"--ui-accent\",\n \"--ui-accent-strong\",\n \"--ui-accent-2\",\n \"--ui-highlight\",\n \"--ui-good\",\n \"--ui-warn\",\n \"--ui-danger\",\n] as const;\n\n/** Re-lightened until ≥ 3:1 on the dark bg (graphics bar). */\nconst CHART_UI = [\n \"--ui-chart-1\",\n \"--ui-chart-2\",\n \"--ui-chart-3\",\n \"--ui-chart-4\",\n \"--ui-chart-5\",\n \"--ui-chart-6\",\n] as const;\n\nfunction flipNeutral(hex: string): string {\n const { L, C, h } = hexToOklch(hex);\n const flipped = DARK_FLIP_L_MIN + (1 - L) * (DARK_FLIP_L_MAX - DARK_FLIP_L_MIN);\n return oklchToHex(clampToGamut({ L: flipped, C, h }));\n}\n\n/**\n * Rebuild bg < surface < surface-2 upward from the dark bg, using the light\n * ladder's own |ΔL| gaps as rung heights (floored at MIN_RUNG). code-bg\n * follows surface-2 when the light map aliased them, else takes its own\n * bg-relative rung. Mutates `out`; skips any token that is absent/non-hex.\n */\nfunction rebuildSurfaces(out: BrandTokens, light: BrandTokens, lightBgL: number, darkBgL: number): void {\n const lightSurface = light[\"--ui-surface\"];\n const lightSurface2 = light[\"--ui-surface-2\"];\n let surfaceDarkL: number | undefined;\n let surfaceLightL = lightBgL;\n if (lightSurface !== undefined && HEX6.test(lightSurface)) {\n const { L, C, h } = hexToOklch(lightSurface);\n surfaceLightL = L;\n surfaceDarkL = darkBgL + Math.max(Math.abs(L - lightBgL), MIN_RUNG);\n out[\"--ui-surface\"] = oklchToHex(clampToGamut({ L: surfaceDarkL, C, h }));\n }\n if (lightSurface2 !== undefined && HEX6.test(lightSurface2)) {\n const { L, C, h } = hexToOklch(lightSurface2);\n const base = surfaceDarkL ?? darkBgL + MIN_RUNG;\n const L2 = base + Math.max(Math.abs(L - surfaceLightL), MIN_RUNG);\n out[\"--ui-surface-2\"] = oklchToHex(clampToGamut({ L: L2, C, h }));\n }\n const lightCodeBg = light[\"--ui-code-bg\"];\n if (lightCodeBg !== undefined && HEX6.test(lightCodeBg)) {\n if (lightCodeBg === lightSurface2 && out[\"--ui-surface-2\"] !== undefined) {\n out[\"--ui-code-bg\"] = out[\"--ui-surface-2\"];\n } else {\n const { L, C, h } = hexToOklch(lightCodeBg);\n const codeL = darkBgL + Math.max(Math.abs(L - lightBgL), MIN_RUNG);\n out[\"--ui-code-bg\"] = oklchToHex(clampToGamut({ L: codeL, C, h }));\n }\n }\n}\n\n/** Lighten (else darken; else pickTextOn) until ≥ `min`:1 on `bg`. */\nfunction relight(hex: string, bg: string, min: number): string {\n const pred = (c: string): boolean => contrastRatio(c, bg) >= min;\n return (\n adjustLightnessUntil(hex, pred, \"lighten\") ??\n adjustLightnessUntil(hex, pred, \"darken\") ??\n pickTextOn(bg)\n );\n}\n\n/**\n * Derives the dark-mode token map from a light one (see the file header for\n * the flip / re-lighten / passthrough rules). Pure and total: tokens the\n * rules don't recognize — including non-hex values in recognized slots —\n * pass through unchanged, and the result always has exactly the input's\n * key set. Deterministic, never throws.\n */\nexport function deriveDarkTokens(light: BrandTokens): BrandTokens {\n const out: BrandTokens = {};\n for (const [name, value] of Object.entries(light)) {\n if (name === \"--ui-shadow\") out[name] = DARK_SHADOW;\n else if (NEUTRAL_FLIP.has(name) && HEX6.test(value)) out[name] = flipNeutral(value);\n else out[name] = value;\n }\n\n const bg = out[\"--ui-bg\"];\n const lightBg = light[\"--ui-bg\"];\n if (bg === undefined || !HEX6.test(bg) || lightBg === undefined || !HEX6.test(lightBg)) return out;\n\n rebuildSurfaces(out, light, hexToOklch(lightBg).L, hexToOklch(bg).L);\n\n for (const name of [\"--ui-text\", \"--ui-text-muted\"]) {\n const v = out[name];\n if (v !== undefined && HEX6.test(v)) out[name] = relight(v, bg, 4.5);\n }\n const codeBg = out[\"--ui-code-bg\"];\n const codeText = out[\"--ui-code-text\"];\n if (codeText !== undefined && HEX6.test(codeText))\n out[\"--ui-code-text\"] = relight(codeText, codeBg !== undefined && HEX6.test(codeBg) ? codeBg : bg, 4.5);\n\n for (const name of ACCENT_AA) {\n const v = out[name];\n if (v !== undefined && HEX6.test(v)) out[name] = relight(v, bg, 4.5);\n }\n for (const name of CHART_UI) {\n const v = out[name];\n if (v !== undefined && HEX6.test(v)) out[name] = relight(v, bg, 3);\n }\n\n const accent = out[\"--ui-accent\"];\n const onAccent = out[\"--ui-on-accent\"];\n if (accent !== undefined && HEX6.test(accent) && onAccent !== undefined && HEX6.test(onAccent))\n out[\"--ui-on-accent\"] = pickTextOn(accent);\n\n return out;\n}\n","// Compiled tokens → theme CSS text.\n//\n// The output's shape mirrors a real @odla-ai/ui theme tokens file\n// (themes/juniper/tokens.css): a light block, a `[data-theme=\"dark\"]`\n// attribute block, the same dark values repeated under\n// `@media (prefers-color-scheme: dark)` guarded by\n// `:root:not([data-theme=\"light\"])` (so an explicit light preference beats\n// the system's dark scheme), and optionally a `.ui-invert` island block.\n// The attribute/media blocks carry only the tokens that CHANGE in dark —\n// the ui convention, since var()/color-mix() compositions declared in the\n// light block recompute on the same element. The `.ui-invert` island gets\n// the FULL dark payload: on an island, root-computed composites would be\n// stale (see roles.ts).\n//\n// Declarations render in BRAND_EMITTED_TOKENS order (unknown tokens last,\n// alphabetically), so output is deterministic and golden-testable.\n\nimport { BRAND_EMITTED_TOKENS } from \"./roles\";\nimport type { BrandTokens } from \"./types\";\n\n/** Options for {@link renderTokensCss}. */\nexport interface RenderTokensCssOptions {\n /** Dark-mode tokens (deriveDarkTokens output). Omit for a light-only sheet. */\n dark?: BrandTokens;\n /**\n * Selector the light block declares on (default \":root\"). A scoped\n * selector (e.g. `[data-brand=\"acme\"]`) gets its dark blocks nested under\n * the document-level theme guards instead of replacing them.\n */\n selector?: string;\n /**\n * Also emit a `.ui-invert` block carrying the full dark payload, so the\n * subtree renders dark regardless of the global mode. Requires `dark`.\n */\n includeInvert?: boolean;\n}\n\nconst HEADER =\n \"/* Generated by @odla-ai/brand — @odla-ai/ui token overrides; pair with @odla-ai/ui css/tokens.css. */\";\n\nconst ORDER = new Map<string, number>(\n BRAND_EMITTED_TOKENS.map((name, i): [string, number] => [name, i]),\n);\n\n/** Emission order: BRAND_EMITTED_TOKENS index, then unknown names A→Z. */\nfunction orderedNames(tokens: BrandTokens): string[] {\n return Object.keys(tokens).sort((a, b) => {\n const ia = ORDER.get(a);\n const ib = ORDER.get(b);\n if (ia !== undefined && ib !== undefined) return ia - ib;\n if (ia !== undefined) return -1;\n if (ib !== undefined) return 1;\n return a < b ? -1 : a > b ? 1 : 0;\n });\n}\n\nfunction renderBlock(\n selector: string,\n tokens: BrandTokens,\n names: readonly string[],\n indent: string,\n lead?: string,\n): string {\n const lines: string[] = [];\n if (lead !== undefined) lines.push(`${indent} ${lead}`);\n for (const name of names) lines.push(`${indent} ${name}: ${tokens[name]};`);\n if (lines.length === 0) return `${indent}${selector} {\\n${indent}}`;\n return `${indent}${selector} {\\n${lines.join(\"\\n\")}\\n${indent}}`;\n}\n\n/**\n * Renders a compiled token map (plus optional dark map) as CSS text shaped\n * like an @odla-ai/ui theme tokens file — see the file header for the block\n * structure and ordering guarantees. Deterministic for a given input.\n */\nexport function renderTokensCss(light: BrandTokens, opts: RenderTokensCssOptions = {}): string {\n const selector = opts.selector ?? \":root\";\n const scoped = selector !== \":root\";\n const parts: string[] = [HEADER, renderBlock(selector, light, orderedNames(light), \"\")];\n\n const dark = opts.dark;\n if (dark !== undefined) {\n const diffNames = orderedNames(dark).filter((name) => light[name] !== dark[name]);\n if (diffNames.length > 0) {\n const attrSelector = scoped\n ? `[data-theme=\"dark\"] ${selector}, ${selector}[data-theme=\"dark\"]`\n : `[data-theme=\"dark\"]`;\n const mediaSelector = scoped\n ? `:root:not([data-theme=\"light\"]) ${selector}`\n : `:root:not([data-theme=\"light\"])`;\n parts.push(renderBlock(attrSelector, dark, diffNames, \"\"));\n parts.push(\n `@media (prefers-color-scheme: dark) {\\n${renderBlock(mediaSelector, dark, diffNames, \" \")}\\n}`,\n );\n }\n if (opts.includeInvert === true) {\n parts.push(renderBlock(\".ui-invert\", dark, orderedNames(dark), \"\", \"color-scheme: dark;\"));\n }\n }\n return `${parts.join(\"\\n\\n\")}\\n`;\n}\n","// The compiler facade: palette + typography (+ author overrides) →\n// { light, dark, warnings }. Light comes from mapPaletteToTokens, overrides\n// are layered on last (so authors can pin any token, including ones this\n// compiler doesn't own, like --ui-radius-md), and dark derives from the\n// FINAL light map — an overridden accent flows into the dark derivation.\n//\n// Never throws on bad palettes: map.ts drops invalid swatches and derives\n// missing roles (each degradation becomes a TokenWarning), and malformed\n// overrides are skipped with a warning rather than rejected.\n\nimport { deriveDarkTokens } from \"./dark\";\nimport { mapPaletteToTokens } from \"./map\";\nimport type { CompiledBrandTokens, CompileInput, TokenWarning } from \"./types\";\n\nconst isRecord = (v: unknown): v is Record<string, unknown> =>\n typeof v === \"object\" && v !== null && !Array.isArray(v);\n\n/** Layer `overrides` onto `light` in place; malformed entries warn + skip. */\nfunction applyOverrides(\n light: Record<string, string>,\n overrides: unknown,\n warnings: TokenWarning[],\n): void {\n if (overrides === undefined) return;\n if (!isRecord(overrides)) {\n warnings.push({ token: \"--ui-accent\", message: \"overrides ignored: not an object\" });\n return;\n }\n for (const [name, value] of Object.entries(overrides)) {\n if (!name.startsWith(\"--\")) {\n warnings.push({ token: name, message: \"override ignored: token names must start with --\" });\n continue;\n }\n if (typeof value !== \"string\" || value.trim() === \"\") {\n warnings.push({ token: name, message: \"override ignored: value must be a non-empty string\" });\n continue;\n }\n light[name] = value.trim();\n }\n}\n\n/**\n * Compiles a brand palette into @odla-ai/ui design tokens: a light map\n * covering every name in BRAND_EMITTED_TOKENS (plus overrides), a dark map\n * derived from it (same key set), and the warnings accumulated along the\n * way — missing-role fallbacks and contrast adjustments (`adjustedFrom`).\n *\n * Both maps double as runtime override-island payloads: every derived\n * default that composes the accent family is re-declared, so applying the\n * map on any element recomputes charts, softs, focus and chat roles against\n * the brand palette (see roles.ts). Deterministic; never throws on bad\n * palettes.\n */\nexport function compileBrandTokens(input: CompileInput): CompiledBrandTokens {\n const { tokens: light, warnings } = mapPaletteToTokens(input);\n applyOverrides(light, input?.overrides, warnings);\n const dark = deriveDarkTokens(light);\n return { light, dark, warnings };\n}\n","// Asset tools: view_asset feeds real uploaded bytes to the model as an\n// image/document block inside the tool result (the vision path), and\n// record_asset_analysis persists what the model saw through the validated\n// ops layer. view_asset output is model-visible content fetched from\n// storage, so the tool declares `tool_untrusted:view_asset` taint.\n// @odla-ai/ai is imported for types only.\nimport type { DocumentBlock, ImageBlock, ImageMediaType, TextBlock, ToolDef, ToolOutput } from \"@odla-ai/ai\";\nimport { BRAND_NS } from \"../constants\";\nimport { BrandNotFoundError } from \"../errors\";\nimport { recordAnalysisOps } from \"../ops/assets\";\nimport type { BrandAsset } from \"../types\";\nimport { assertAnalysis } from \"../validate\";\nimport type { BrandToolCtx } from \"./skill\";\n\n/** Byte cap for in-conversation asset viewing (4.5 MiB — providers reject\n * larger inline payloads well before context does). */\nexport const MAX_VIEW_BYTES = 4_718_592;\n\nconst IMAGE_TYPES: ReadonlySet<string> = new Set([\"image/png\", \"image/jpeg\", \"image/gif\", \"image/webp\"]);\n\n/**\n * Base64-encode bytes without assuming a platform: `btoa` over a chunked\n * binary string where available (workerd, browsers, Node ≥ 16), `Buffer`\n * otherwise. Chunking keeps `String.fromCharCode` off the argument-count\n * cliff for multi-megabyte assets.\n */\nexport function base64FromBytes(bytes: Uint8Array): string {\n if (typeof btoa === \"function\") {\n let binary = \"\";\n for (let i = 0; i < bytes.length; i += 0x2000) {\n binary += String.fromCharCode(...bytes.subarray(i, i + 0x2000));\n }\n return btoa(binary);\n }\n return Buffer.from(bytes).toString(\"base64\");\n}\n\nconst bareType = (ct: string): string => (ct.split(\";\")[0] ?? \"\").trim().toLowerCase();\n\nfunction viewOutput(asset: BrandAsset, bytes: Uint8Array, contentType: string): ToolOutput {\n const caption: TextBlock = {\n type: \"text\",\n text: `Asset ${asset.id} (${asset.kind}, ${contentType}, ${bytes.byteLength} bytes${asset.title ? `, \"${asset.title}\"` : \"\"}):`,\n };\n if (IMAGE_TYPES.has(contentType)) {\n const image: ImageBlock = {\n type: \"image\",\n source: { type: \"base64\", mediaType: contentType as ImageMediaType, data: base64FromBytes(bytes) },\n };\n return { content: [caption, image] };\n }\n const document: DocumentBlock = {\n type: \"document\",\n source: { type: \"base64\", mediaType: \"application/pdf\", data: base64FromBytes(bytes) },\n };\n return { content: [caption, document] };\n}\n\n/**\n * The asset tools, scoped to the context's book: `view_asset` (fetch the\n * stored bytes and return them as an image/PDF block, with size and type\n * gates) and `record_asset_analysis` (persist the model's description,\n * dominant colors, and tags onto the asset row).\n */\nexport function assetTools(ctx: BrandToolCtx): ToolDef[] {\n const loadAsset = async (assetId: string): Promise<BrandAsset> => {\n const res = await ctx.db.query({\n [BRAND_NS.asset]: { $: { where: { id: assetId, bookId: ctx.bookId } } },\n });\n const row = (res[BRAND_NS.asset] ?? [])[0] as BrandAsset | undefined;\n if (!row || row.deletedAt) throw new BrandNotFoundError(`asset ${assetId}`);\n return row;\n };\n\n const viewAsset: ToolDef = {\n name: \"view_asset\",\n description:\n \"Look at an uploaded asset: fetches its bytes and returns the image (png/jpeg/gif/webp) or PDF for you to view. Other types cannot be viewed in-conversation.\",\n inputSchema: {\n type: \"object\",\n required: [\"assetId\"],\n properties: { assetId: { type: \"string\", description: \"The asset row id from list_assets.\" } },\n },\n outputTaint: [\"tool_untrusted:view_asset\"],\n handler: ctx.guard(async (input) => {\n const asset = await loadAsset(String(input.assetId));\n if (!ctx.visionInToolResults) {\n return {\n content:\n `This model cannot take images inside tool results. Asset ${asset.id} must be attached ` +\n \"as a pre-turn image by the host instead — ask the human to re-send their message \" +\n \"referencing the asset (the dispatcher attaches it up front), or describe it from \" +\n \"what they tell you.\",\n };\n }\n const url = asset.url.startsWith(\"http\") ? asset.url : ctx.fileBaseUrl + asset.url;\n const fetched = await ctx.fetchBytes(url);\n if (fetched.bytes.byteLength > MAX_VIEW_BYTES) {\n return {\n content:\n `Asset ${asset.id} is ${fetched.bytes.byteLength} bytes — over the ${MAX_VIEW_BYTES}-byte ` +\n \"(4.5 MB) in-conversation viewing cap. Ask the human for a smaller export of this file.\",\n };\n }\n const served = bareType(fetched.contentType);\n const effective = IMAGE_TYPES.has(served) || served === \"application/pdf\" ? served : bareType(asset.contentType);\n if (!IMAGE_TYPES.has(effective) && effective !== \"application/pdf\") {\n return {\n content:\n `Asset ${asset.id} is ${effective} — stored but not viewable in-conversation. ` +\n \"Ask the human to describe it, or to upload a PNG/JPEG export you can view.\",\n };\n }\n return viewOutput(asset, fetched.bytes, effective);\n }),\n };\n\n const recordAnalysis: ToolDef = {\n name: \"record_asset_analysis\",\n description:\n \"Record what you observed in an asset you viewed: a description, its dominant colors as #rrggbb hex, and tags. Overwrites any prior analysis.\",\n inputSchema: {\n type: \"object\",\n required: [\"assetId\", \"description\", \"dominantColors\", \"tags\"],\n properties: {\n assetId: { type: \"string\" },\n description: { type: \"string\", description: \"What the asset shows (max 2000 chars).\" },\n dominantColors: { type: \"array\", items: { type: \"string\" }, maxItems: 12, description: \"Dominant colors as hex.\" },\n tags: { type: \"array\", items: { type: \"string\" }, maxItems: 24 },\n },\n },\n handler: ctx.guard(async (input) => {\n const asset = await loadAsset(String(input.assetId));\n const analysis = assertAnalysis({\n description: input.description,\n dominantColors: input.dominantColors,\n tags: input.tags,\n });\n await ctx.db.transact(recordAnalysisOps(asset.id, analysis, ctx.now()), { mutationId: ctx.newId() });\n return {\n content: `Recorded analysis for asset ${asset.id}: ${analysis.dominantColors.length} dominant color(s), ${analysis.tags.length} tag(s).`,\n };\n }),\n };\n\n return [viewAsset, recordAnalysis];\n}\n","// Book documentation tools: update_section upserts one section per kind\n// through the natural-key ops builder, and compile_tokens turns the active\n// (or named) palette + typography section into the @odla-ai/ui token cache\n// on the book row. Compile never throws on a bad palette — its warnings are\n// relayed for the agent to explain. @odla-ai/ai is types-only.\nimport type { ToolDef } from \"@odla-ai/ai\";\nimport { BRAND_NS, SECTION_KINDS, type SectionKind, type SectionStatus } from \"../constants\";\nimport { BrandInputError, BrandNotFoundError } from \"../errors\";\nimport { updateBookOps } from \"../ops/books\";\nimport { sectionKey, upsertSectionOps } from \"../ops/sections\";\nimport { compileBrandTokens } from \"../tokens/compile\";\nimport type { BrandPalette, BrandSection, BrandTokensSnapshot, TypographySection } from \"../types\";\nimport { capString } from \"../validate\";\nimport type { BrandToolCtx } from \"./skill\";\n\n/**\n * The book tools: `update_section` (validated per-kind content, upserted by\n * natural key with the book's audience snapshot) and `compile_tokens`\n * (palette + typography → light/dark token cache on the book row).\n */\nexport function bookTools(ctx: BrandToolCtx): ToolDef[] {\n const updateSection: ToolDef = {\n name: \"update_section\",\n description:\n \"Create or replace one brand-book section (palette, typography, voice, logo, or imagery). Content is validated per kind. Status defaults to draft; a human approves.\",\n inputSchema: {\n type: \"object\",\n required: [\"kind\", \"content\"],\n properties: {\n kind: { type: \"string\", enum: [...SECTION_KINDS] },\n content: { type: \"object\", description: \"The section payload for its kind.\" },\n status: { type: \"string\", enum: [\"draft\", \"approved\"] },\n },\n },\n handler: ctx.guard(async (input) => {\n const book = await ctx.loadBook();\n const kind = String(input.kind) as SectionKind;\n const ops = upsertSectionOps({\n bookId: ctx.bookId,\n kind,\n content: input.content as Record<string, unknown>,\n status: input.status === undefined ? undefined : (input.status as SectionStatus),\n audience: book.memberIds,\n updatedBy: ctx.self.selfId,\n now: ctx.now(),\n });\n await ctx.db.transact(ops, { mutationId: ctx.newId() });\n const status = input.status === undefined ? \"draft\" : String(input.status);\n return { content: `Saved ${kind} section as ${status} (key ${sectionKey(ctx.bookId, kind)}).` };\n }),\n };\n\n const compileTokens: ToolDef = {\n name: \"compile_tokens\",\n description:\n \"Compile the book's palette (paletteId argument, or the active palette) plus the typography section into light/dark @odla-ai/ui tokens, cached on the book. Returns the compiler's warnings.\",\n inputSchema: {\n type: \"object\",\n properties: { paletteId: { type: \"string\", description: \"Defaults to the book's active palette.\" } },\n },\n handler: ctx.guard(async (input) => {\n const book = await ctx.loadBook();\n const paletteId = input.paletteId === undefined ? book.activePaletteId : capString(input.paletteId, \"paletteId\", 128);\n if (!paletteId) {\n throw new BrandInputError(\"no palette to compile: pass paletteId, or get a palette proposal accepted first\");\n }\n const pres = await ctx.db.query({\n [BRAND_NS.palette]: { $: { where: { id: paletteId, bookId: ctx.bookId } } },\n });\n const palette = (pres[BRAND_NS.palette] ?? [])[0] as BrandPalette | undefined;\n if (!palette) throw new BrandNotFoundError(`palette ${paletteId}`);\n const sres = await ctx.db.query({\n [BRAND_NS.section]: { $: { where: { key: sectionKey(ctx.bookId, \"typography\") } } },\n });\n const typography = (sres[BRAND_NS.section] ?? [])[0] as BrandSection | undefined;\n const compiled = compileBrandTokens({\n swatches: palette.swatches,\n ...(typography ? { typography: typography.content as TypographySection } : {}),\n });\n const now = ctx.now();\n const snapshot: BrandTokensSnapshot = {\n light: compiled.light,\n dark: compiled.dark,\n warnings: compiled.warnings,\n compiledAt: now,\n };\n await ctx.db.transact(updateBookOps(ctx.bookId, { tokens: snapshot }, now), { mutationId: ctx.newId() });\n const count = compiled.warnings.length;\n const lines = [\n `Compiled ${Object.keys(compiled.light).length} light + ${Object.keys(compiled.dark).length} dark tokens from palette ${paletteId}; ${count} warning(s).`,\n ...compiled.warnings\n .slice(0, 3)\n .map((w) => `- ${w.token}: ${w.message}${w.adjustedFrom ? ` (adjusted from ${w.adjustedFrom})` : \"\"}`),\n ];\n if (count > 3) lines.push(`… and ${count - 3} more.`);\n return { content: lines.join(\"\\n\") };\n }),\n };\n\n return [updateSection, compileTokens];\n}\n","// Color-exploration + proposal tools. analyze_color / evaluate_contrast are\n// pure math over the color engine (the agent's grounding for every hex it\n// suggests); propose_palette PARKS a proposal for human review — it never\n// activates anything; resolve_proposal applies the human's verdict through\n// the atomic accept/reject op builders. @odla-ai/ai is types-only.\nimport type { ToolDef } from \"@odla-ai/ai\";\nimport { BRAND_NS } from \"../constants\";\nimport {\n analogous,\n complementary,\n contrastRatio,\n derivePalette,\n hexToOklch,\n meetsAA,\n meetsAAA,\n monochrome,\n nearestNamedColor,\n pickTextOn,\n relativeLuminance,\n splitComplementary,\n tetradic,\n tintShadeRamp,\n triadic,\n} from \"../color/index\";\nimport { BrandInputError, BrandNotFoundError } from \"../errors\";\nimport { acceptProposalOps, proposePaletteOps, rejectProposalOps } from \"../ops/palettes\";\nimport type { BrandProposal, Swatch, SwatchRole } from \"../types\";\nimport { assertHex, assertSwatches, capString } from \"../validate\";\nimport type { BrandToolCtx } from \"./skill\";\n\nconst r2 = (n: number): string => n.toFixed(2);\nconst pf = (ok: boolean): string => (ok ? \"pass\" : \"FAIL\");\n\nconst verdict = (ratio: number): string =>\n `${r2(ratio)}:1 — AA text ${pf(meetsAA(ratio))}, AA large/ui ${pf(meetsAA(ratio, \"large-text\"))}, AAA text ${pf(meetsAAA(ratio))}`;\n\n/** Harmony strategies propose_palette accepts, mapped to the seed companions\n * that replace the derived secondary/highlight (in that order). */\nconst HARMONY_COMPANIONS: Record<string, (seed: string) => string[]> = {\n complementary: (s) => [complementary(s)],\n analogous: (s) => [...analogous(s)],\n triadic: (s) => [...triadic(s)],\n split: (s) => [...splitComplementary(s)],\n tetradic: (s) => tetradic(s).slice(0, 2),\n monochrome: (s) => monochrome(s, 4).slice(1, 3),\n};\n\nfunction applyHarmony(swatches: Swatch[], seedHex: string, harmony: unknown): Swatch[] {\n if (typeof harmony !== \"string\" || !(harmony in HARMONY_COMPANIONS)) {\n throw new BrandInputError(`harmony must be one of: ${Object.keys(HARMONY_COMPANIONS).join(\", \")}`);\n }\n const [secondary, highlight] = HARMONY_COMPANIONS[harmony]!(seedHex);\n return swatches.map((s) => {\n const hex = s.role === \"secondary\" ? secondary : s.role === \"highlight\" ? highlight : undefined;\n if (!hex) return s;\n return { ...s, hex, name: nearestNamedColor(hex).name, rationale: `${harmony} companion of the seed` };\n });\n}\n\n/** WCAG ratios (2 dp) for the palette's key role pairs — stored verbatim in\n * the proposal payload so the human reviews numbers, not vibes. */\nfunction contrastReport(swatches: Swatch[]): Record<string, number> {\n const byRole = (role: SwatchRole): string | undefined => swatches.find((s) => s.role === role)?.hex;\n const bg = byRole(\"bg\") ?? \"#ffffff\";\n const report: Record<string, number> = {};\n const put = (label: string, fg?: string): void => {\n if (fg) report[label] = Math.round(contrastRatio(fg, bg) * 100) / 100;\n };\n put(\"text-on-bg\", byRole(\"text\"));\n put(\"primary-on-bg\", byRole(\"primary\"));\n put(\"good-on-bg\", byRole(\"good\"));\n put(\"warn-on-bg\", byRole(\"warn\"));\n put(\"danger-on-bg\", byRole(\"danger\"));\n const primary = byRole(\"primary\");\n if (primary) report[\"text-on-primary\"] = Math.round(contrastRatio(pickTextOn(primary), primary) * 100) / 100;\n return report;\n}\n\nconst INCLUDE_SECTIONS = [\"harmony\", \"ramp\"] as const;\n\nfunction analyzeLines(hex: string, include: string[]): string[] {\n const lch = hexToOklch(hex);\n const named = nearestNamedColor(hex);\n const lines = [\n `${hex} — nearest CSS name: ${named.name} (ΔEOK ${named.deltaEOK.toFixed(3)})`,\n `OKLCH: L ${lch.L.toFixed(3)}, C ${lch.C.toFixed(3)}, h ${lch.h.toFixed(1)}°`,\n `relative luminance ${relativeLuminance(hex).toFixed(3)}; contrast ${r2(contrastRatio(hex, \"#ffffff\"))}:1 on white, ${r2(contrastRatio(hex, \"#000000\"))}:1 on black`,\n `readable text on it: ${pickTextOn(hex)}`,\n ];\n if (include.includes(\"harmony\")) {\n const [aMinus, aPlus] = analogous(hex);\n const [tMinus, tPlus] = triadic(hex);\n const [sMinus, sPlus] = splitComplementary(hex);\n lines.push(\n `harmony — complementary ${complementary(hex)}; analogous ${aMinus} ${aPlus}; triadic ${tMinus} ${tPlus}; split ${sMinus} ${sPlus}`,\n );\n }\n if (include.includes(\"ramp\")) lines.push(`ramp — ${tintShadeRamp(hex).join(\" \")}`);\n return lines;\n}\n\n/**\n * The palette tools: `analyze_color`, `evaluate_contrast` (pure math),\n * `propose_palette` (writes an OPEN proposal with an automatic contrast\n * report), and `resolve_proposal` (applies the human's explicit verdict).\n */\nexport function paletteTools(ctx: BrandToolCtx): ToolDef[] {\n const analyzeColor: ToolDef = {\n name: \"analyze_color\",\n description:\n \"Analyze one color: nearest CSS name, OKLCH coordinates, luminance, contrast on white/black. Optionally include harmony companions and a tint/shade ramp.\",\n inputSchema: {\n type: \"object\",\n required: [\"hex\"],\n properties: {\n hex: { type: \"string\", description: \"#rgb or #rrggbb\" },\n include: { type: \"array\", items: { type: \"string\", enum: [...INCLUDE_SECTIONS] } },\n },\n },\n handler: ctx.guard(async (input) => {\n const hex = assertHex(input.hex, \"hex\");\n const include = input.include === undefined ? [] : input.include;\n if (!Array.isArray(include) || include.some((s) => !(INCLUDE_SECTIONS as readonly unknown[]).includes(s))) {\n throw new BrandInputError(`include entries must be one of: ${INCLUDE_SECTIONS.join(\", \")}`);\n }\n return { content: analyzeLines(hex, include as string[]).join(\"\\n\") };\n }),\n };\n\n const evaluateContrast: ToolDef = {\n name: \"evaluate_contrast\",\n description:\n \"WCAG 2.1 contrast: pass pairs ([{fg,bg}]) for specific combinations, or hexes ([…]) for every pairwise ratio. Reports AA/AAA verdicts.\",\n inputSchema: {\n type: \"object\",\n properties: {\n pairs: {\n type: \"array\",\n maxItems: 20,\n items: { type: \"object\", required: [\"fg\", \"bg\"], properties: { fg: { type: \"string\" }, bg: { type: \"string\" } } },\n },\n hexes: { type: \"array\", minItems: 2, maxItems: 8, items: { type: \"string\" } },\n },\n },\n handler: ctx.guard(async (input) => {\n if (Array.isArray(input.pairs) && input.pairs.length > 0) {\n if (input.pairs.length > 20) throw new BrandInputError(\"pairs must have at most 20 entries\");\n const lines = input.pairs.map((raw, i) => {\n const p = (raw ?? {}) as Record<string, unknown>;\n const fg = assertHex(p.fg, `pairs[${i}].fg`);\n const bg = assertHex(p.bg, `pairs[${i}].bg`);\n return `${fg} on ${bg}: ${verdict(contrastRatio(fg, bg))}`;\n });\n return { content: lines.join(\"\\n\") };\n }\n if (Array.isArray(input.hexes)) {\n if (input.hexes.length < 2 || input.hexes.length > 8) {\n throw new BrandInputError(\"hexes must have 2 to 8 entries\");\n }\n const hexes = input.hexes.map((h, i) => assertHex(h, `hexes[${i}]`));\n const lines: string[] = [];\n for (let i = 0; i < hexes.length; i++) {\n for (let j = i + 1; j < hexes.length; j++) {\n lines.push(`${hexes[i]} vs ${hexes[j]}: ${verdict(contrastRatio(hexes[i]!, hexes[j]!))}`);\n }\n }\n return { content: lines.join(\"\\n\") };\n }\n throw new BrandInputError(\"provide pairs ([{fg,bg}, …]) or hexes ([#rrggbb, …])\");\n }),\n };\n\n const proposePalette: ToolDef = {\n name: \"propose_palette\",\n description:\n \"Park a palette proposal for human review (does NOT change the brand). Pass explicit swatches, or a seedHex (and optional harmony) to derive a full palette. A WCAG contrast report is attached automatically.\",\n inputSchema: {\n type: \"object\",\n required: [\"name\", \"rationale\"],\n properties: {\n name: { type: \"string\" },\n rationale: { type: \"string\", description: \"Why these colors — cite assets, harmony, contrast.\" },\n seedHex: { type: \"string\" },\n harmony: { type: \"string\", enum: Object.keys(HARMONY_COMPANIONS) },\n swatches: {\n type: \"array\",\n items: {\n type: \"object\",\n required: [\"role\", \"hex\"],\n properties: {\n role: { type: \"string\" },\n hex: { type: \"string\" },\n name: { type: \"string\" },\n rationale: { type: \"string\" },\n },\n },\n },\n },\n },\n handler: ctx.guard(async (input) => {\n const book = await ctx.loadBook();\n const seedHex = input.seedHex === undefined ? undefined : assertHex(input.seedHex, \"seedHex\");\n let swatches: Swatch[];\n if (input.swatches !== undefined) {\n swatches = assertSwatches(input.swatches);\n } else {\n if (!seedHex) throw new BrandInputError(\"provide swatches, or a seedHex to derive a palette from\");\n swatches = derivePalette(seedHex);\n if (input.harmony !== undefined) swatches = applyHarmony(swatches, seedHex, input.harmony);\n }\n const report = contrastReport(swatches);\n const id = ctx.newId();\n const ops = proposePaletteOps({\n id,\n bookId: ctx.bookId,\n name: String(input.name),\n rationale: String(input.rationale),\n swatches,\n seedHex,\n contrastReport: report,\n createdBy: ctx.self.selfId,\n audience: book.memberIds,\n now: ctx.now(),\n });\n await ctx.db.transact(ops, { mutationId: id });\n const reportText = Object.entries(report).map(([k, v]) => `${k} ${r2(v)}:1`).join(\", \");\n return {\n content:\n `Parked palette proposal ${id} (\"${String(input.name).trim()}\", ${swatches.length} swatches` +\n `${seedHex ? `, seeded from ${seedHex}` : \"\"}). Contrast: ${reportText}. ` +\n \"Awaiting explicit human approval — call resolve_proposal only after the human confirms.\",\n };\n }),\n };\n\n const resolveProposal: ToolDef = {\n name: \"resolve_proposal\",\n description:\n \"Apply the human's explicit verdict on an open proposal. ONLY call this after the human has clearly accepted or rejected in the conversation — never on your own initiative.\",\n inputSchema: {\n type: \"object\",\n required: [\"proposalId\", \"resolution\"],\n properties: {\n proposalId: { type: \"string\" },\n resolution: { type: \"string\", enum: [\"accepted\", \"rejected\"] },\n note: { type: \"string\" },\n },\n },\n handler: ctx.guard(async (input) => {\n const proposalId = String(input.proposalId);\n const res = await ctx.db.query({\n [BRAND_NS.proposal]: { $: { where: { id: proposalId, bookId: ctx.bookId } } },\n });\n const proposal = (res[BRAND_NS.proposal] ?? [])[0] as BrandProposal | undefined;\n if (!proposal) throw new BrandNotFoundError(`proposal ${proposalId}`);\n const note = input.note === undefined ? undefined : capString(input.note, \"note\", 1000);\n const now = ctx.now();\n if (input.resolution === \"accepted\") {\n const paletteId = ctx.newId();\n const ops = acceptProposalOps({ proposal, paletteId, resolvedBy: ctx.self.selfId, now, resolutionNote: note });\n await ctx.db.transact(ops, { mutationId: paletteId });\n return { content: `Proposal ${proposalId} accepted — palette ${paletteId} written and set as the book's active palette.` };\n }\n if (input.resolution === \"rejected\") {\n const ops = rejectProposalOps({ proposal, resolvedBy: ctx.self.selfId, now, resolutionNote: note });\n await ctx.db.transact(ops, { mutationId: ctx.newId() });\n return { content: `Proposal ${proposalId} rejected.` };\n }\n throw new BrandInputError('resolution must be \"accepted\" or \"rejected\"');\n }),\n };\n\n return [analyzeColor, evaluateContrast, proposePalette, resolveProposal];\n}\n","// Read-only brand tools: a compact structured-text snapshot of the book\n// (read_brand_book) and the live asset list (list_assets). Deterministic, no\n// AI calls, no writes — the agent's orientation step before it proposes\n// anything. @odla-ai/ai is imported for types only.\nimport type { ToolDef } from \"@odla-ai/ai\";\nimport { ASSET_KINDS, BRAND_NS } from \"../constants\";\nimport { BrandInputError, BrandNotFoundError } from \"../errors\";\nimport type { BrandAsset, BrandBook, BrandPalette, BrandProposal, BrandSection } from \"../types\";\nimport type { BrandToolCtx } from \"./skill\";\n\nconst iso = (ms: number): string => new Date(ms).toISOString();\n\nfunction bookLines(book: BrandBook): string[] {\n const lines = [\n `brand book \"${book.name}\" (${book.slug}) — ${book.status}`,\n `members: ${book.memberIds.join(\", \")}`,\n `active palette: ${book.activePaletteId ?? \"(none accepted yet)\"}`,\n book.tokens\n ? `tokens: compiled ${iso(book.tokens.compiledAt)} with ${book.tokens.warnings.length} warning(s)`\n : \"tokens: (not compiled)\",\n ];\n if (book.summary) lines.push(`summary: ${book.summary}`);\n return lines;\n}\n\nfunction childLines(sections: BrandSection[], palettes: BrandPalette[], proposals: BrandProposal[]): string[] {\n return [\n \"sections:\",\n ...(sections.length\n ? sections.map((s) => `- ${s.kind}: ${s.status}, updated ${iso(s.updatedAt)} by ${s.updatedBy}`)\n : [\"- (none)\"]),\n \"palettes:\",\n ...(palettes.length\n ? palettes.map((p) => `- ${p.id} \"${p.name}\" (${p.status}, ${p.source}, ${p.swatches.length} swatches)`)\n : [\"- (none)\"]),\n \"open proposals:\",\n ...(proposals.length\n ? proposals.map((p) => `- ${p.id} (${p.kind}) by ${p.createdBy}: ${p.rationale}`)\n : [\"- (none)\"]),\n ];\n}\n\n/**\n * The two read-only tools, scoped to the context's book:\n * `read_brand_book` (book + sections + palettes + open proposals as compact\n * text) and `list_assets` (live, non-tombstoned asset rows, optionally\n * filtered by kind).\n */\nexport function readTools(ctx: BrandToolCtx): ToolDef[] {\n const readBrandBook: ToolDef = {\n name: \"read_brand_book\",\n description:\n \"Read the current brand book: status, members, active palette, compiled tokens, sections, palettes, and open proposals.\",\n inputSchema: { type: \"object\", properties: {} },\n handler: ctx.guard(async () => {\n const res = await ctx.db.query({\n [BRAND_NS.book]: { $: { where: { id: ctx.bookId } } },\n [BRAND_NS.section]: { $: { where: { bookId: ctx.bookId }, order: { updatedAt: \"asc\" } } },\n [BRAND_NS.palette]: { $: { where: { bookId: ctx.bookId }, order: { createdAt: \"asc\" } } },\n [BRAND_NS.proposal]: { $: { where: { bookId: ctx.bookId, status: \"open\" }, order: { createdAt: \"asc\" } } },\n });\n const book = (res[BRAND_NS.book] ?? [])[0] as BrandBook | undefined;\n if (!book) throw new BrandNotFoundError(`brand book ${ctx.bookId}`);\n const sections = (res[BRAND_NS.section] ?? []) as BrandSection[];\n const palettes = (res[BRAND_NS.palette] ?? []) as BrandPalette[];\n const proposals = (res[BRAND_NS.proposal] ?? []) as BrandProposal[];\n return { content: [...bookLines(book), ...childLines(sections, palettes, proposals)].join(\"\\n\") };\n }),\n };\n\n const listAssets: ToolDef = {\n name: \"list_assets\",\n description:\n \"List the book's uploaded assets (id, kind, content type, size, title, analysis state). Tombstoned assets are excluded.\",\n inputSchema: {\n type: \"object\",\n properties: {\n kind: { type: \"string\", enum: [...ASSET_KINDS], description: \"Only assets of this kind.\" },\n },\n },\n handler: ctx.guard(async (input) => {\n const where: Record<string, unknown> = { bookId: ctx.bookId };\n if (input.kind !== undefined) {\n if (typeof input.kind !== \"string\" || !(ASSET_KINDS as readonly string[]).includes(input.kind)) {\n throw new BrandInputError(`kind must be one of: ${ASSET_KINDS.join(\", \")}`);\n }\n where.kind = input.kind;\n }\n const res = await ctx.db.query({ [BRAND_NS.asset]: { $: { where, order: { createdAt: \"asc\" } } } });\n const rows = ((res[BRAND_NS.asset] ?? []) as BrandAsset[]).filter((a) => !a.deletedAt);\n if (rows.length === 0) {\n return { content: input.kind ? `(no ${String(input.kind)} assets uploaded yet)` : \"(no assets uploaded yet)\" };\n }\n const lines = rows.map(\n (a) =>\n `${a.id} — ${a.kind}, ${a.contentType}, ${a.size} bytes` +\n `${a.title ? `, \"${a.title}\"` : \"\"}${a.analysis ? \" (analyzed)\" : \" (not analyzed)\"}`,\n );\n return { content: lines.join(\"\\n\") };\n }),\n };\n\n return [readBrandBook, listAssets];\n}\n","// The @odla-ai/ai Skill assembly for a brand book: one shared tool context\n// (injected db + identity + defaults), the workflow instructions, and the\n// tools from the four tool modules. @odla-ai/ai is an optional peer imported\n// for TYPES ONLY (chat's pattern) — the package works without it installed.\n//\n// Error contract with the agent loop: runAgent converts a handler throw into\n// a SANITIZED error tool_result (`Tool \"x\" failed.`) because arbitrary\n// handler messages may carry credentials. Brand's own validation errors are\n// crafted, secret-free, and actionable, so `guard` catches BrandInputError /\n// BrandNotFoundError and returns the message as an error output the model can\n// act on; anything unexpected still propagates into the loop's sanitizer.\nimport type { Skill, ToolHandler } from \"@odla-ai/ai\";\nimport { BRAND_NS } from \"../constants\";\nimport { resolveDeps, type BrandFetchedBytes } from \"../deps\";\nimport { BrandInputError, BrandNotFoundError } from \"../errors\";\nimport type { BrandBook, BrandDb } from \"../types\";\nimport { assetTools } from \"./asset-tools\";\nimport { bookTools } from \"./book-tools\";\nimport { paletteTools } from \"./palette-tools\";\nimport { readTools } from \"./read-tools\";\n\n/** The bot identity the skill writes as (`selfId` = the agent id carried in\n * the book's `memberIds` roster and every audience snapshot). */\nexport interface BrandSkillSelf {\n selfId: string;\n kind: \"bot\";\n displayName?: string;\n}\n\n/** Options for {@link brandSkill}. */\nexport interface BrandSkillOpts {\n /** The injected odla client (a real @odla-ai/db AdminDb satisfies it). */\n db: BrandDb;\n /** The one brand book this skill instance is scoped to. */\n bookId: string;\n /** The bot identity acting through these tools. */\n self: BrandSkillSelf;\n /** Origin prefixed onto relative asset urls (`url` starting with `/`). */\n fileBaseUrl: string;\n /** Asset-byte fetcher override (default: global fetch via resolveDeps). */\n fetchBytes?: (url: string) => Promise<BrandFetchedBytes>;\n /** False when the model cannot take image/document blocks inside a\n * tool_result — view_asset then steers to the pre-turn attachment path. */\n visionInToolResults?: boolean;\n /** Clock override (tests). */\n now?: () => number;\n /** Id factory override (tests). */\n newId?: () => string;\n}\n\n/** The resolved context every brand tool module builds its tools from. */\nexport interface BrandToolCtx {\n db: BrandDb;\n bookId: string;\n self: BrandSkillSelf;\n fileBaseUrl: string;\n fetchBytes: (url: string) => Promise<BrandFetchedBytes>;\n /** Whether image/document blocks may be returned inside tool results. */\n visionInToolResults: boolean;\n now: () => number;\n newId: () => string;\n /** Load the scoped book row; throws BrandNotFoundError when missing. */\n loadBook(): Promise<BrandBook>;\n /** Wrap a handler so Brand validation errors come back as actionable\n * error text instead of the loop's sanitized generic failure. */\n guard(handler: ToolHandler): ToolHandler;\n}\n\n/** Workflow guidance appended to the persona's system prompt as the skill's\n * instruction section. */\nexport const BRAND_INSTRUCTIONS =\n \"You help build and maintain ONE brand book. Workflow, in order:\\n\" +\n \"1. Understand the brand first: read_brand_book and list_assets before proposing anything.\\n\" +\n \"2. Look at the real material: view_asset on logos and inspiration, then record what you \" +\n \"saw with record_asset_analysis (description, dominant colors as hex, tags).\\n\" +\n \"3. Explore with the math tools: analyze_color and evaluate_contrast. Never invent a hex \" +\n \"without a rationale — ground every color in an asset's dominant color, a harmony \" +\n \"companion, or a contrast fix, and say which.\\n\" +\n \"4. propose_palette parks a proposal for review. It does NOT change the brand.\\n\" +\n \"5. WAIT for the human to explicitly accept or reject in the conversation before calling \" +\n \"resolve_proposal — never resolve a proposal on your own initiative.\\n\" +\n \"6. After acceptance, document the rest with update_section (typography, voice, logo, \" +\n \"imagery) and run compile_tokens.\\n\" +\n \"7. Relay compile warnings conversationally — explain what was adjusted and why, don't \" +\n \"just paste them.\";\n\n/**\n * Build the brand Skill: read/asset/palette/book tools scoped to one book,\n * acting as one bot identity, plus {@link BRAND_INSTRUCTIONS}. Attach it to\n * a Persona (or use `createBrandPersona`).\n */\nexport function brandSkill(opts: BrandSkillOpts): Skill {\n const deps = resolveDeps({ db: opts.db, now: opts.now, newId: opts.newId, fetchBytes: opts.fetchBytes });\n const ctx: BrandToolCtx = {\n db: deps.db,\n bookId: opts.bookId,\n self: opts.self,\n fileBaseUrl: opts.fileBaseUrl,\n fetchBytes: deps.fetchBytes,\n visionInToolResults: opts.visionInToolResults !== false,\n now: deps.now,\n newId: deps.newId,\n loadBook: async () => {\n const res = await deps.db.query({ [BRAND_NS.book]: { $: { where: { id: opts.bookId } } } });\n const row = (res[BRAND_NS.book] ?? [])[0] as BrandBook | undefined;\n if (!row) throw new BrandNotFoundError(`brand book ${opts.bookId}`);\n return row;\n },\n guard: (handler) => async (input, toolCtx) => {\n try {\n return await handler(input, toolCtx);\n } catch (error) {\n if (error instanceof BrandInputError || error instanceof BrandNotFoundError) {\n return { content: error.message, isError: true };\n }\n throw error;\n }\n },\n };\n return {\n name: \"brand\",\n instructions: BRAND_INSTRUCTIONS,\n tools: [...readTools(ctx), ...assetTools(ctx), ...paletteTools(ctx), ...bookTools(ctx)],\n };\n}\n","// The brand-director Persona: the brand skill pre-attached, a default system\n// prompt encoding the explore → propose → human approves → compile loop, and\n// the capability probe hosts use to decide whether asset bytes can ride\n// inside tool results or must be attached pre-turn. @odla-ai/ai types only.\nimport type { Persona, Skill } from \"@odla-ai/ai\";\nimport { brandSkill, type BrandSkillOpts } from \"./skill\";\n\n/** The default brand-director system prompt (override via `system`). */\nexport const DEFAULT_BRAND_SYSTEM =\n \"You are a meticulous brand director for one brand book. You study the real material \" +\n \"before forming opinions, you justify every color with math (harmony, ΔEOK, WCAG \" +\n \"contrast) or provenance (an asset's dominant colors), and you present options rather \" +\n \"than dictating. Proposals are yours to make; decisions are the human's — never \" +\n \"resolve a proposal without their explicit confirmation in this conversation. When \" +\n \"tokens compile with warnings, explain each adjustment in plain language.\";\n\n/** Options for {@link createBrandPersona}. */\nexport interface CreateBrandPersonaOpts {\n /** Canonical model id the persona runs on. */\n model: string;\n /** System prompt override (default {@link DEFAULT_BRAND_SYSTEM}). */\n system?: string;\n /** Provider web-search server tool (default true — brand research). */\n webSearch?: boolean;\n /** Max model turns per run (default 10 — the workflow is multi-step). */\n maxSteps?: number;\n /** Extra skills appended after the brand skill (e.g. a chat skill). */\n skills?: Skill[];\n /** Options for the attached {@link brandSkill}. */\n brand: BrandSkillOpts;\n}\n\n/** Build the brand-director Persona: {@link brandSkill} first, extra skills\n * after, defaults per the option JSDoc above. */\nexport function createBrandPersona(opts: CreateBrandPersonaOpts): Persona {\n return {\n name: \"brand-director\",\n model: opts.model,\n system: opts.system ?? DEFAULT_BRAND_SYSTEM,\n webSearch: opts.webSearch ?? true,\n maxSteps: opts.maxSteps ?? 10,\n skills: [brandSkill(opts.brand), ...(opts.skills ?? [])],\n };\n}\n\n/** The capability flags {@link supportsBrandVision} inspects. */\nexport interface BrandVisionCapabilities {\n toolResultBlocks?: boolean;\n imageIn?: boolean;\n documentIn?: boolean;\n}\n\n/** The (structural) slice of an @odla-ai/ai ModelSpec the probe reads. */\nexport interface BrandVisionSpec {\n capabilities?: BrandVisionCapabilities;\n}\n\n/**\n * Whether a model can view assets THROUGH view_asset — i.e. take image and\n * document blocks inside tool results. Pure capabilities-metadata check\n * (`toolResultBlocks && imageIn && documentIn`), never provider names; when\n * false, hosts attach asset bytes as pre-turn input blocks instead and pass\n * `visionInToolResults: false` to the skill.\n */\nexport function supportsBrandVision(spec: BrandVisionSpec | null | undefined): boolean {\n const caps = spec?.capabilities;\n return !!(caps?.toolResultBlocks && caps.imageIn && caps.documentIn);\n}\n","// HTTP plumbing for the brand route factory (crm's http.ts contract): option\n// shapes, JSON helpers, the error → status mapping (BrandInputError → 400,\n// BrandNotFoundError → 404, anything else an OPAQUE 500), body-field\n// extractors, and the shared book loaders.\n//\n// Access parity note: routes run on the host's ADMIN db credential, which\n// bypasses odla-db rules — so the loaders re-enforce what the CEL rules would:\n// a book you are not in `memberIds` of answers 404, the same as one that does\n// not exist (a non-member cannot probe for existence).\nimport { BRAND_NS } from \"../constants\";\nimport { BrandInputError, BrandNotFoundError } from \"../errors\";\nimport type { BrandActor, BrandBook, BrandDb } from \"../types\";\n\n/** Everything {@link import(\"./index\").createBrandRoutes} needs from the host worker. */\nexport interface BrandRouteOpts {\n /** The injected odla client (a real @odla-ai/db AdminDb satisfies it). */\n db: BrandDb;\n /** Host-owned auth: resolve the request to an actor or null (→ 401). The\n * tokens routes skip this only when `publicTokens` is true. */\n authorize: (req: Request) => Promise<BrandActor | null> | BrandActor | null;\n /** Mount point. Default \"/api/brand\". */\n basePath?: string;\n /** Upload size cap in bytes (checked against `File.size`). Default 8 MiB. */\n maxUploadBytes?: number;\n /** Serve `GET /books/:id/tokens.css` and `tokens.json` without authorize —\n * compiled tokens only; every other route always authorizes. Default false. */\n publicTokens?: boolean;\n /** Injectable clock (tests). Default `Date.now`. */\n now?: () => number;\n /** Injectable id factory (tests). Default `crypto.randomUUID`. */\n newId?: () => string;\n}\n\n/** Per-request context handed to the authorized route handlers. */\nexport interface BrandRouteCtx {\n db: BrandDb;\n actor: BrandActor;\n now: () => number;\n newId: () => string;\n maxUploadBytes: number;\n}\n\n/** JSON response helper (`content-type: application/json`). */\nexport const json = (body: unknown, status = 200, headers: Record<string, string> = {}): Response =>\n new Response(JSON.stringify(body), {\n status,\n headers: { \"content-type\": \"application/json\", ...headers },\n });\n\n/** Map a thrown error to its response: 400 input, 404 missing, opaque 500. */\nexport const errorResponse = (error: unknown): Response => {\n if (error instanceof BrandInputError)\n return json({ error: error.message, ...(error.fields ? { fields: error.fields } : {}) }, 400);\n if (error instanceof BrandNotFoundError) return json({ error: error.message }, 404);\n return json({ error: \"internal error\" }, 500);\n};\n\n/** The uniform 405 for a known path hit with the wrong method. */\nexport const methodNotAllowed = (): Response => json({ error: \"method not allowed\" }, 405);\n\n/** Parse a JSON object body; {@link BrandInputError} (→ 400) on anything else. */\nexport async function readJson(req: Request): Promise<Record<string, unknown>> {\n let body: unknown;\n try {\n body = await req.json();\n } catch {\n throw new BrandInputError(\"invalid JSON body\");\n }\n if (typeof body !== \"object\" || body === null || Array.isArray(body))\n throw new BrandInputError(\"body must be a JSON object\");\n return body as Record<string, unknown>;\n}\n\n/** Required non-empty string body field. */\nexport function str(body: Record<string, unknown>, key: string): string {\n const value = body[key];\n if (typeof value !== \"string\" || value === \"\")\n throw new BrandInputError(`\"${key}\" must be a non-empty string`);\n return value;\n}\n\n/** Optional string body field (absent/null → undefined). */\nexport function optStr(body: Record<string, unknown>, key: string): string | undefined {\n const value = body[key];\n if (value === undefined || value === null) return undefined;\n if (typeof value !== \"string\") throw new BrandInputError(`\"${key}\" must be a string`);\n return value;\n}\n\n/** Optional string-array body field (absent/null → undefined). */\nexport function optStrArray(body: Record<string, unknown>, key: string): string[] | undefined {\n const value = body[key];\n if (value === undefined || value === null) return undefined;\n if (!Array.isArray(value) || value.some((v) => typeof v !== \"string\"))\n throw new BrandInputError(`\"${key}\" must be an array of strings`);\n return value as string[];\n}\n\n/** Whether `actorId` is on the book's auth roster. */\nexport function isMember(book: BrandBook, actorId: string): boolean {\n return Array.isArray(book.memberIds) && book.memberIds.includes(actorId);\n}\n\n/** Load a book by id; {@link BrandNotFoundError} (→ 404) when missing. */\nexport async function loadBook(db: BrandDb, bookId: string): Promise<BrandBook> {\n const res = await db.query({ [BRAND_NS.book]: { $: { where: { id: bookId } } } });\n const row = (res[BRAND_NS.book] ?? [])[0] as BrandBook | undefined;\n if (!row) throw new BrandNotFoundError(`brand book ${bookId}`);\n return row;\n}\n\n/** {@link loadBook}, plus the rules-parity membership gate: a non-member gets\n * the same 404 a missing book gets (see the header note). */\nexport async function loadMemberBook(db: BrandDb, bookId: string, actorId: string): Promise<BrandBook> {\n const book = await loadBook(db, bookId);\n if (!isMember(book, actorId)) throw new BrandNotFoundError(`brand book ${bookId}`);\n return book;\n}\n","// /books/:id/assets handlers — the worker-mediated side of the closed\n// `brand_asset` namespace: multipart upload → storage first, then the\n// mirroring row; list (tombstones excluded); delete = storage delete + a\n// `deletedAt` tombstone, never a row delete.\n//\n// Ordering note: EVERY input check (content type → 415, size → 413, kind /\n// title / file name → 400) runs BEFORE `storage.upload`, so a 4xx can never\n// strand an orphaned object in the file store.\nimport { ASSET_KINDS, BRAND_NS, type AssetKind } from \"../constants\";\nimport { BrandInputError, BrandNotFoundError } from \"../errors\";\nimport { createAssetOps, tombstoneAssetOps } from \"../ops/assets\";\nimport type { BrandAsset } from \"../types\";\nimport { assertAssetContentType, capString, safeFileName } from \"../validate\";\nimport { json, loadMemberBook, methodNotAllowed, type BrandRouteCtx } from \"./http\";\n\n/** Read the multipart form; 400 (as {@link BrandInputError}) on any other body. */\nasync function readForm(req: Request): Promise<FormData> {\n try {\n return await req.formData();\n } catch {\n throw new BrandInputError('expected a multipart/form-data body with a \"file\" field');\n }\n}\n\nasync function uploadAsset(ctx: BrandRouteCtx, req: Request, bookId: string): Promise<Response> {\n const book = await loadMemberBook(ctx.db, bookId, ctx.actor.id);\n const form = await readForm(req);\n const file = form.get(\"file\");\n if (!(file instanceof File)) throw new BrandInputError('\"file\" must be an uploaded file field');\n\n // 415 (unsupported type) and 413 (too large) get their own statuses; the\n // remaining input checks are plain 400s.\n let contentType: string;\n try {\n contentType = assertAssetContentType(file.type);\n } catch (error) {\n if (error instanceof BrandInputError) return json({ error: error.message }, 415);\n throw error;\n }\n if (file.size > ctx.maxUploadBytes)\n return json(\n { error: `file is ${file.size} bytes; the upload limit is ${ctx.maxUploadBytes}` },\n 413,\n );\n const kindRaw = form.get(\"kind\");\n const kind = typeof kindRaw === \"string\" && kindRaw !== \"\" ? kindRaw : \"other\";\n if (!(ASSET_KINDS as readonly string[]).includes(kind))\n throw new BrandInputError(`kind must be one of: ${ASSET_KINDS.join(\", \")}`);\n const titleRaw = form.get(\"title\");\n const title =\n typeof titleRaw === \"string\" && titleRaw !== \"\" ? capString(titleRaw, \"title\", 160) : undefined;\n\n const id = ctx.newId();\n const path = `brand/${book.id}/assets/${id}/${safeFileName(file.name)}`;\n const record = await ctx.db.storage.upload(path, file, contentType);\n await ctx.db.transact(\n createAssetOps({\n id,\n bookId: book.id,\n kind: kind as AssetKind,\n path: record.path,\n url: record.url,\n contentType,\n size: record.size,\n uploadedBy: ctx.actor.id,\n audience: book.memberIds,\n title,\n now: ctx.now(),\n }),\n { mutationId: id },\n );\n const res = await ctx.db.query({ [BRAND_NS.asset]: { $: { where: { id } } } });\n return json((res[BRAND_NS.asset] ?? [])[0], 201);\n}\n\n/**\n * `GET /books/:id/assets` — the book's live assets (tombstoned rows\n * excluded), oldest first, as `{ assets }`. `POST /books/:id/assets` —\n * multipart upload (`file`, optional `kind` default \"other\", optional\n * `title`): 415 off-allowlist type, 413 over `maxUploadBytes`, else upload to\n * `brand/{bookId}/assets/{id}/{safeFileName}` and answer 201 with the asset\n * row. Members only; other methods 405.\n */\nexport async function handleAssetsRoot(ctx: BrandRouteCtx, req: Request, bookId: string): Promise<Response> {\n if (req.method === \"GET\") {\n const book = await loadMemberBook(ctx.db, bookId, ctx.actor.id);\n const res = await ctx.db.query({\n [BRAND_NS.asset]: {\n // `deletedAt: null` matches ONLY rows without the attr — odla-db's\n // \"absence is a missing triple\" semantics — i.e. not tombstoned.\n $: { where: { bookId: book.id, deletedAt: null }, order: { createdAt: \"asc\" } },\n },\n });\n return json({ assets: res[BRAND_NS.asset] ?? [] });\n }\n if (req.method === \"POST\") return uploadAsset(ctx, req, bookId);\n return methodNotAllowed();\n}\n\n/**\n * `DELETE /books/:id/assets/:assetId` — delete the storage object, then\n * tombstone the row (`deletedAt`); answers `{ id, deletedAt }`. 404 when the\n * asset is unknown, belongs to another book, or is already tombstoned (the\n * storage object is gone — never double-delete). Other methods: 405.\n */\nexport async function handleAssetItem(\n ctx: BrandRouteCtx,\n req: Request,\n bookId: string,\n assetId: string,\n): Promise<Response> {\n if (req.method !== \"DELETE\") return methodNotAllowed();\n const book = await loadMemberBook(ctx.db, bookId, ctx.actor.id);\n const res = await ctx.db.query({\n [BRAND_NS.asset]: { $: { where: { id: assetId, bookId: book.id } } },\n });\n const asset = (res[BRAND_NS.asset] ?? [])[0] as unknown as BrandAsset | undefined;\n if (!asset || asset.deletedAt != null) throw new BrandNotFoundError(`asset ${assetId}`);\n await ctx.db.storage.delete(asset.path);\n const deletedAt = ctx.now();\n await ctx.db.transact(tombstoneAssetOps(asset.id, deletedAt));\n return json({ id: asset.id, deletedAt });\n}\n","// /books handlers: create, member-filtered list, and detail.\n//\n// List filtering note: odla-db's query where-clauses have no \"roster array\n// CONTAINS value\" operator (`$in` tests a scalar column against a candidate\n// list, the inverse), so membership on the json `memberIds` roster is not\n// expressible as a where. The handler fetches the namespace and filters in\n// code — exactly the visibility the `brand_book.view` CEL rule\n// (`auth.id in data.memberIds`) enforces for rules-scoped credentials; the\n// admin-key route path just re-applies it server-side.\nimport { BRAND_NS } from \"../constants\";\nimport { createBookOps } from \"../ops/books\";\nimport type { BrandBook } from \"../types\";\nimport {\n isMember,\n json,\n loadBook,\n loadMemberBook,\n methodNotAllowed,\n optStr,\n optStrArray,\n readJson,\n str,\n type BrandRouteCtx,\n} from \"./http\";\n\n/**\n * `GET /books` — every book whose `memberIds` roster contains the actor,\n * oldest first, as `{ books }`. `POST /books` — create a draft book owned by\n * the actor (`slug`, `name`, optional `memberIds` roster and `channelId`);\n * answers 201 with the created row. Other methods: 405.\n */\nexport async function handleBooksRoot(ctx: BrandRouteCtx, req: Request): Promise<Response> {\n if (req.method === \"GET\") {\n const res = await ctx.db.query({ [BRAND_NS.book]: { $: { order: { createdAt: \"asc\" } } } });\n const books = ((res[BRAND_NS.book] ?? []) as unknown as BrandBook[]).filter((b) =>\n isMember(b, ctx.actor.id),\n );\n return json({ books });\n }\n if (req.method === \"POST\") {\n const body = await readJson(req);\n const id = ctx.newId();\n const ops = createBookOps({\n id,\n slug: str(body, \"slug\"),\n name: str(body, \"name\"),\n ownerId: ctx.actor.id,\n memberIds: optStrArray(body, \"memberIds\") ?? [],\n channelId: optStr(body, \"channelId\"),\n now: ctx.now(),\n });\n await ctx.db.transact(ops, { mutationId: id });\n return json(await loadBook(ctx.db, id), 201);\n }\n return methodNotAllowed();\n}\n\n/** `GET /books/:id` — the book row, members only (404 otherwise, matching the\n * view rule — see http.ts). Other methods: 405. */\nexport async function handleBookItem(ctx: BrandRouteCtx, req: Request, bookId: string): Promise<Response> {\n if (req.method !== \"GET\") return methodNotAllowed();\n return json(await loadMemberBook(ctx.db, bookId, ctx.actor.id));\n}\n","// /books/:id/tokens.css + tokens.json — the consumption side of the token\n// compiler. Serves the book's cached `tokens` snapshot when present;\n// otherwise compiles on the fly from the active palette (+ the typography\n// section's content) and serves WITHOUT writing — a GET never mutates, so a\n// cache miss under crawler traffic can't fan out writes. `compile_tokens`\n// (the skill tool) is what persists the cache.\n//\n// Conditional requests: the ETag is derived from `book.updatedAt` — every\n// palette acceptance and token compile stamps it — so If-None-Match answers\n// 304 until the brand actually changes.\nimport { BRAND_NS } from \"../constants\";\nimport { BrandNotFoundError } from \"../errors\";\nimport { compileBrandTokens } from \"../tokens/compile\";\nimport { renderTokensCss } from \"../tokens/css\";\nimport type { BrandTokens } from \"../tokens/types\";\nimport type { BrandActor, BrandBook, BrandDb, BrandPalette, TypographySection } from \"../types\";\nimport { json, loadBook, loadMemberBook, methodNotAllowed } from \"./http\";\n\nconst isRecord = (v: unknown): v is Record<string, unknown> =>\n typeof v === \"object\" && v !== null && !Array.isArray(v);\n\n/** The book's token maps: the cached snapshot when usable, else a fresh\n * side-effect-free compile from the active palette + typography section.\n * 404 (as {@link BrandNotFoundError}) when there is neither. */\nasync function resolveTokenMaps(\n db: BrandDb,\n book: BrandBook,\n): Promise<{ light: BrandTokens; dark: BrandTokens }> {\n const cache = book.tokens;\n if (cache && isRecord(cache.light) && isRecord(cache.dark))\n return { light: cache.light as BrandTokens, dark: cache.dark as BrandTokens };\n if (!book.activePaletteId) throw new BrandNotFoundError(`compiled tokens for brand book ${book.id}`);\n const pres = await db.query({\n [BRAND_NS.palette]: { $: { where: { id: book.activePaletteId } } },\n });\n const palette = (pres[BRAND_NS.palette] ?? [])[0] as unknown as BrandPalette | undefined;\n if (!palette) throw new BrandNotFoundError(`compiled tokens for brand book ${book.id}`);\n const sres = await db.query({\n [BRAND_NS.section]: { $: { where: { key: `${book.id}:typography` } } },\n });\n const typography = ((sres[BRAND_NS.section] ?? [])[0] as { content?: unknown } | undefined)\n ?.content as TypographySection | undefined;\n const compiled = compileBrandTokens({ swatches: palette.swatches, typography });\n return { light: compiled.light, dark: compiled.dark };\n}\n\n/**\n * Serve `GET /books/:id/tokens.css` (theme CSS via `renderTokensCss`, strong\n * `updatedAt`-derived ETag, If-None-Match → 304) or `tokens.json`\n * (`{ light, dark }`). `actor` null means the publicTokens path — no\n * membership gate; when an actor is present the member-or-404 rule applies\n * like every other route. Other methods: 405.\n */\nexport async function handleTokens(\n db: BrandDb,\n req: Request,\n bookId: string,\n which: \"tokens.css\" | \"tokens.json\",\n actor: BrandActor | null,\n): Promise<Response> {\n if (req.method !== \"GET\") return methodNotAllowed();\n const book = actor === null ? await loadBook(db, bookId) : await loadMemberBook(db, bookId, actor.id);\n const maps = await resolveTokenMaps(db, book);\n if (which === \"tokens.json\") return json({ light: maps.light, dark: maps.dark });\n const etag = `\"brand-tokens-${book.updatedAt}\"`;\n const inm = req.headers.get(\"if-none-match\");\n if (inm && inm.split(\",\").map((v) => v.trim()).includes(etag))\n return new Response(null, { status: 304, headers: { etag } });\n return new Response(renderTokensCss(maps.light, { dark: maps.dark }), {\n status: 200,\n headers: { \"content-type\": \"text/css; charset=utf-8\", etag },\n });\n}\n","// The mountable route factory (crm's createCrmRoutes contract): the host\n// worker composes it —\n// const brandRoutes = createBrandRoutes({ db, authorize, … });\n// return (await brandRoutes(request)) ?? next(request);\n// — keeping full ownership of auth (`authorize`) and the admin db key.\nimport { handleAssetItem, handleAssetsRoot } from \"./assets\";\nimport { handleBookItem, handleBooksRoot } from \"./books\";\nimport { errorResponse, json, type BrandRouteCtx, type BrandRouteOpts } from \"./http\";\nimport { handleTokens } from \"./tokens\";\n\nexport type { BrandRouteCtx, BrandRouteOpts } from \"./http\";\n\ntype TokensFile = \"tokens.css\" | \"tokens.json\";\n\n/** `/books/:id/tokens.css|json` recognizer — the only publicTokens-eligible paths. */\nconst tokensFile = (seg: string[]): TokensFile | null =>\n seg.length === 3 && seg[0] === \"books\" && (seg[2] === \"tokens.css\" || seg[2] === \"tokens.json\")\n ? (seg[2] as TokensFile)\n : null;\n\nasync function route(ctx: BrandRouteCtx, req: Request, seg: string[]): Promise<Response | null> {\n const [head, id, sub, subId] = seg;\n if (head !== \"books\") return null;\n if (seg.length === 1) return handleBooksRoot(ctx, req);\n if (seg.length === 2) return handleBookItem(ctx, req, id!);\n if (sub === \"assets\") {\n if (seg.length === 3) return handleAssetsRoot(ctx, req, id!);\n if (seg.length === 4) return handleAssetItem(ctx, req, id!, subId!);\n return null;\n }\n const file = tokensFile(seg);\n if (file) return handleTokens(ctx.db, req, id!, file, ctx.actor);\n return null;\n}\n\n/**\n * Build the brand fetch handler. Returns `null` for requests outside\n * `basePath` (default `/api/brand`) so the host worker falls through to its\n * own routes; inside it, every route requires `authorize` to resolve an\n * actor (401 otherwise) EXCEPT the two tokens routes when `publicTokens` is\n * true — compiled tokens are the one intentionally publishable surface.\n * Domain errors map to 400/404 JSON; anything unexpected is an opaque 500.\n */\nexport function createBrandRoutes(options: BrandRouteOpts): (req: Request) => Promise<Response | null> {\n const basePath = options.basePath ?? \"/api/brand\";\n const publicTokens = options.publicTokens === true;\n const base = {\n db: options.db,\n now: options.now ?? Date.now,\n newId: options.newId ?? (() => crypto.randomUUID()),\n maxUploadBytes: options.maxUploadBytes ?? 8 * 1024 * 1024,\n };\n return async (req: Request): Promise<Response | null> => {\n const url = new URL(req.url);\n if (url.pathname !== basePath && !url.pathname.startsWith(`${basePath}/`)) return null;\n const seg = url.pathname.slice(basePath.length).split(\"/\").filter(Boolean);\n try {\n const file = tokensFile(seg);\n if (publicTokens && file) return await handleTokens(base.db, req, seg[1]!, file, null);\n const actor = await options.authorize(req);\n if (!actor) return json({ error: \"unauthorized\" }, 401);\n return (await route({ ...base, actor }, req, seg)) ?? json({ error: \"not found\" }, 404);\n } catch (error) {\n return errorResponse(error);\n }\n };\n}\n","// Commit-trigger config brand installs into odla-db (POST to\n// `/app/:id/admin/triggers`), mirroring @odla-ai/chat's botTrigger: the\n// @odla/server commit hook fires on matching human-authored chat-message\n// writes and dispatches to the host's agent worker with `skill: \"brand\"`.\n// Two deployment modes, both this one builder:\n// - global mention-gated: one bot, `mention: \"@brand\"`, no `channels`;\n// - per-book channel-scoped: `channels: [book.channelId]`, no mention.\n\n/** The odla-db namespace chat messages live in.\n * PROVENANCE: @odla-ai/chat `CHAT_NS.message` (packages/chat/src/constants.ts)\n * — duplicated as a local const because this package is zero-dep and the wire\n * value is a stable contract, not an implementation detail. */\nexport const CHAT_MESSAGE_NS = \"chat_message\";\n\n/** Matches @odla/server's `Trigger` (the exact JSON POSTed to\n * `/app/:id/admin/triggers`) — the same wire shape as chat's `ChatTrigger`. */\nexport interface BrandTrigger {\n id: string;\n watch: { ns: string; on: \"create\" | \"update\" };\n when?: string;\n runAs: { agentId: string; persona: string };\n skill: string;\n maxDepth?: number;\n channels?: string[];\n validate?: string;\n}\n\n/** Options for {@link brandBotTrigger}. */\nexport interface BrandBotTriggerOpts {\n /** Trigger id (unique per app). */\n id: string;\n /** The bot's agent id — must be in each target book's `memberIds` roster. */\n agentId: string;\n /** Persona name the dispatching worker runs. */\n persona: string;\n /** Fire only on messages mentioning this handle, e.g. \"@brand\". */\n mention?: string;\n /** Extra CEL over the new message row, ANDed with the built-in guards. */\n when?: string;\n /** Restrict to specific channel ids (the per-book mode). */\n channels?: string[];\n /** Watched write kind (default \"create\"). */\n on?: \"create\" | \"update\";\n}\n\n/** Escape a value for embedding inside a CEL double-quoted string literal —\n * copied verbatim from @odla-ai/chat (packages/chat/src/triggers.ts):\n * `mention` is matched as literal text, so a stray quote or backslash must\n * not break out of the literal into CEL syntax. */\nconst celString = (value: string): string => value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n\n/**\n * Build a trigger that runs the brand agent on human chat messages\n * (optionally @-mentions, optionally channel-scoped): chat's `botTrigger`\n * shape with `skill: \"brand\"` and `maxDepth: 1` so a bot reply can never\n * re-trigger a bot.\n */\nexport function brandBotTrigger(opts: BrandBotTriggerOpts): BrandTrigger {\n const clauses = ['data.authorKind == \"human\"'];\n if (opts.mention) clauses.push(`data.body.contains(\"${celString(opts.mention)}\")`);\n // `when` is documented as caller-authored CEL, so it is intentionally not escaped.\n if (opts.when) clauses.push(`(${opts.when})`);\n return {\n id: opts.id,\n watch: { ns: CHAT_MESSAGE_NS, on: opts.on ?? \"create\" },\n when: clauses.join(\" && \"),\n runAs: { agentId: opts.agentId, persona: opts.persona },\n skill: \"brand\",\n maxDepth: 1,\n ...(opts.channels ? { channels: opts.channels } : {}),\n };\n}\n","// Dispatch: what a host agent worker does when a brandBotTrigger fires.\n// The envelope mirrors chat-agent's DispatchBody STRUCTURALLY (see\n// packages/chat-agent/src/dispatch.ts) without importing it — this package is\n// zero-dep and the wire shape is the odla-db commit hook's contract, not\n// chat-agent's. The pure helpers (parse / book lookup / input assembly) are\n// I/O-light and unit-testable; dispatchBrandTurn wires them into one turn of\n// the injected runAgent.\nimport type { AgentRunInput, ImageBlock, Inference, OracleContentBlock, Persona, Skill } from \"@odla-ai/ai\";\nimport { BRAND_NS } from \"./constants\";\nimport { BrandNotFoundError } from \"./errors\";\nimport { createBrandPersona, supportsBrandVision, type BrandVisionSpec } from \"./skill/persona\";\nimport type { BrandSkillSelf } from \"./skill/skill\";\nimport { CHAT_MESSAGE_NS } from \"./triggers\";\nimport type { BrandAsset, BrandBook, BrandDb } from \"./types\";\n\n/** The dispatch envelope odla-db's commit hook POSTs when a trigger fires —\n * the same wire shape chat-agent consumes (see the header provenance note). */\nexport interface BrandDispatchBody {\n v: number;\n appId: string;\n trigger: { id: string; skill: string; runAs: { agentId: string; persona: string }; maxDepth?: number };\n event: { ns: string; id: string; row: Record<string, unknown> };\n}\n\n/**\n * Validate an untrusted dispatch body; null if malformed. SHAPE-only,\n * mirroring chat-agent's parseDispatch: it does NOT gate on `trigger.skill` —\n * routing on skill is the HOST's job (a worker serving several skills checks\n * `body.trigger.skill === \"brand\"` before calling {@link dispatchBrandTurn}),\n * so an absent skill parses as `\"\"`, never silently defaulted to \"brand\".\n */\nexport function parseBrandDispatch(raw: unknown): BrandDispatchBody | null {\n if (!raw || typeof raw !== \"object\") return null;\n const b = raw as Record<string, unknown>;\n const t = b.trigger as { runAs?: { agentId?: unknown; persona?: unknown }; id?: unknown; skill?: unknown; maxDepth?: unknown } | undefined;\n const e = b.event as { ns?: unknown; id?: unknown; row?: unknown } | undefined;\n if (typeof b.appId !== \"string\" || !b.appId) return null;\n if (!t || typeof t.runAs?.agentId !== \"string\" || typeof t.runAs?.persona !== \"string\") return null;\n if (!e || typeof e.id !== \"string\" || !e.row || typeof e.row !== \"object\") return null;\n return {\n v: typeof b.v === \"number\" ? b.v : 1,\n appId: b.appId,\n trigger: {\n id: typeof t.id === \"string\" ? t.id : \"\",\n skill: typeof t.skill === \"string\" ? t.skill : \"\",\n runAs: { agentId: t.runAs.agentId, persona: t.runAs.persona },\n ...(typeof t.maxDepth === \"number\" ? { maxDepth: t.maxDepth } : {}),\n },\n event: { ns: typeof e.ns === \"string\" ? e.ns : CHAT_MESSAGE_NS, id: e.id, row: e.row as Record<string, unknown> },\n };\n}\n\n/** The brand book bound to a chat channel (`brand_book.channelId`), or null\n * when the channel id is empty or no book claims it. */\nexport async function bookForChannel(db: BrandDb, channelId: string): Promise<BrandBook | null> {\n if (!channelId) return null;\n const res = await db.query({ [BRAND_NS.book]: { $: { where: { channelId } } } });\n return ((res[BRAND_NS.book] ?? [])[0] as BrandBook | undefined) ?? null;\n}\n\n/**\n * The agent-turn input for a dispatched message: a brand-flavored prompt\n * (mirrors chat-agent's `inputFor` tone, steering into the brand workflow),\n * and — when pre-turn `attachments` are supplied (the no-vision-in-tool-\n * results path) — an explicit block array with the images BEFORE the text.\n */\nexport function brandInputFor(\n body: BrandDispatchBody,\n attachments?: ImageBlock[],\n): string | OracleContentBlock[] {\n const row = body.event.row;\n const prompt =\n `A new message arrived in this brand book's channel from ${String(row.authorId ?? \"someone\")}: ` +\n `\"${String(row.body ?? \"\")}\". Ground yourself first (read_brand_book, list_assets), then move ` +\n \"the brand work forward: study the material, justify colors with the math tools, and park \" +\n \"palette ideas as proposals — a human must explicitly approve before you resolve or compile. \" +\n \"If you reply in chat, keep it concise.\";\n return attachments?.length ? [...attachments, { type: \"text\", text: prompt }] : prompt;\n}\n\n/** Image content types eligible for pre-turn attachment (ImageBlock media). */\nconst PRETURN_IMAGE_TYPES: ReadonlySet<string> = new Set([\n \"image/png\",\n \"image/jpeg\",\n \"image/gif\",\n \"image/webp\",\n]);\n\n/** Resolve the row's `assetIds` to URL-source ImageBlocks: live image assets\n * only (tombstoned, unknown, and non-image assets — pdf, svg — are skipped),\n * relative asset urls absolutized against `fileBaseUrl` (asset-tools' join). */\nasync function assetBlocksFor(db: BrandDb, fileBaseUrl: string, raw: unknown): Promise<ImageBlock[]> {\n if (!Array.isArray(raw)) return [];\n const ids = raw.filter((v): v is string => typeof v === \"string\");\n if (ids.length === 0) return [];\n const res = await db.query({ [BRAND_NS.asset]: { $: { where: { id: { $in: ids } } } } });\n const byId = new Map(((res[BRAND_NS.asset] ?? []) as unknown as BrandAsset[]).map((a) => [a.id, a]));\n const blocks: ImageBlock[] = [];\n for (const id of ids) {\n const asset = byId.get(id);\n if (!asset || asset.deletedAt != null || !PRETURN_IMAGE_TYPES.has(asset.contentType)) continue;\n const url = asset.url.startsWith(\"http\") ? asset.url : fileBaseUrl + asset.url;\n blocks.push({ type: \"image\", source: { type: \"url\", url } });\n }\n return blocks;\n}\n\n/** Everything {@link dispatchBrandTurn} needs from the host worker. The\n * @odla-ai/ai pieces (`inference`, `runAgent`) are injected so this module\n * keeps the package's types-only relationship with the peer. */\nexport interface BrandDispatchDeps {\n /** The injected odla client (a real @odla-ai/db AdminDb satisfies it). */\n db: BrandDb;\n /** The @odla-ai/ai inference facade the turn runs on. */\n inference: Inference;\n /** Canonical model id for the persona. */\n model: string;\n /** The agent loop — pass @odla-ai/ai's `runAgent`. */\n runAgent: (inference: Inference, persona: Persona, input: AgentRunInput) => Promise<{ finalText: string }>;\n /** Origin prefixed onto relative asset urls (skill + pre-turn attachments). */\n fileBaseUrl: string;\n /** Optional chat-skill factory (e.g. wrapping @odla-ai/chat's chatSkill) so\n * the bot can reply in the triggering channel. */\n chatSkillFor?: (channelId: string, self: BrandSkillSelf) => Skill;\n /** Model catalog slice (model id → capability spec) for the vision probe;\n * an absent catalog or unknown model fails safe to the pre-turn path. */\n catalog?: Record<string, BrandVisionSpec>;\n}\n\n/**\n * Run one brand-agent turn for a dispatched chat message: resolve the book\n * via the row's `channelId` (throws {@link BrandNotFoundError} when no book\n * claims the channel), probe `supportsBrandVision(catalog[model])`, build the\n * persona (brand skill + optional chat skill, bot identity from\n * `trigger.runAs`), and run the loop. When the model can NOT take blocks in\n * tool results, assets are attached PRE-TURN as URL image blocks — v1 keeps\n * detection simple: only when the message row carries an `assetIds` array\n * (no body-text scraping), and only live image assets.\n */\nexport async function dispatchBrandTurn(\n deps: BrandDispatchDeps,\n body: BrandDispatchBody,\n): Promise<{ finalText: string }> {\n const channelId = String(body.event.row.channelId ?? \"\");\n const book = await bookForChannel(deps.db, channelId);\n if (!book) throw new BrandNotFoundError(`brand book for channel ${channelId || \"(missing channelId)\"}`);\n const visionInToolResults = supportsBrandVision(deps.catalog?.[deps.model]);\n const self: BrandSkillSelf = {\n selfId: body.trigger.runAs.agentId,\n kind: \"bot\",\n displayName: body.trigger.runAs.persona,\n };\n const persona = createBrandPersona({\n model: deps.model,\n brand: { db: deps.db, bookId: book.id, self, fileBaseUrl: deps.fileBaseUrl, visionInToolResults },\n skills: deps.chatSkillFor ? [deps.chatSkillFor(channelId, self)] : [],\n });\n const attachments = visionInToolResults\n ? []\n : await assetBlocksFor(deps.db, deps.fileBaseUrl, body.event.row.assetIds);\n const run = await deps.runAgent(deps.inference, persona, { input: brandInputFor(body, attachments) });\n return { finalText: run.finalText };\n}\n","// The integration descriptor: documentation-as-data for the brand capability,\n// following the crm/chat pattern — what installing @odla-ai/brand into an app\n// means (schema, deny-all rules, per-bot triggers, worker mount), described\n// but not performed. The base shapes are inlined so the zero-dependency\n// package does not depend on the CLI; the CLI consumes descriptors\n// structurally from odla.config.mjs.\nimport { BRAND_SCHEMA, type SerializedSchema } from \"./schema\";\nimport { brandRules, type BrandRules } from \"./rules\";\nimport type { BrandTrigger } from \"./triggers\";\n\n/** One owner-facing setting the integration exposes (crm's shape). */\nexport interface IntegrationSetting {\n key: string;\n description: string;\n public: boolean;\n pattern?: string;\n perEnv: boolean;\n source: string;\n}\n\n/** One vault secret the integration needs (brand core needs none). */\nexport interface IntegrationSecret {\n key: string;\n description: string;\n pattern?: string;\n vault: boolean;\n}\n\n/** Human / CLI / doctor provisioning steps, documentation-as-data. */\nexport interface IntegrationProvision {\n human: string[];\n cli: string[];\n doctor: string[];\n}\n\n/** Unauthenticated route check run by `odla-ai smoke`. */\nexport interface BrandIntegrationProbe {\n path: string;\n expectedStatus: number;\n}\n\n/** The brand descriptor shape: the shared base plus the schema + rules\n * provisioning installs and the (app-supplied) bot triggers. */\nexport interface BrandIntegrationDescriptor {\n id: string;\n title: string;\n npm: string;\n settings: IntegrationSetting[];\n secrets: IntegrationSecret[];\n /** The schema to POST at /app/:id/schema. */\n schema: SerializedSchema;\n /** The default-deny rules to install at /app/:id/admin/rules (merged with existing). */\n rules: BrandRules;\n /** Commit triggers to register at /app/:id/admin/triggers (per-bot; app-supplied). */\n triggers: BrandTrigger[];\n probes?: BrandIntegrationProbe[];\n provision: IntegrationProvision;\n}\n\n/**\n * The static documentation descriptor for @odla-ai/brand. Data only: it\n * describes what installing the brand capability means — push\n * {@link BRAND_SCHEMA}, install the default-deny `brandRules()`, register\n * `brandBotTrigger`s (app-supplied, in either the global mention-gated or the\n * per-book channel-scoped mode), mount `createBrandRoutes` in the app worker\n * — and performs none of it. Use {@link createBrandIntegration} in\n * `odla.config.mjs` to add the live route probe the CLI can execute.\n */\nexport const brandIntegration: BrandIntegrationDescriptor = {\n id: \"brand\",\n title: \"Brand books (assets, palettes, proposals, design tokens)\",\n npm: \"@odla-ai/brand\",\n settings: [\n {\n key: \"basePath\",\n description: 'Route mount point the app worker serves brand under. Default \"/api/brand\".',\n public: true,\n perEnv: false,\n source: \"createBrandRoutes({ basePath }) in the app worker\",\n },\n {\n key: \"publicTokens\",\n description:\n \"Serve GET /books/:id/tokens.css and tokens.json without authorization (compiled tokens \" +\n \"only — books and assets always authorize). Default false.\",\n public: true,\n perEnv: false,\n source: \"createBrandRoutes({ publicTokens }) in the app worker\",\n },\n ],\n secrets: [],\n schema: BRAND_SCHEMA,\n rules: brandRules(),\n triggers: [],\n provision: {\n human: [\n \"Choose the trigger mode: one global mention-gated bot (brandBotTrigger({ mention: \\\"@brand\\\" }), no channels) or per-book channel-scoped bots (channels: [book.channelId], no mention).\",\n \"Pick the bot's model; when supportsBrandVision(catalog[model]) is false, dispatch attaches assets pre-turn instead of inside tool results.\",\n \"Decide whether compiled tokens are public (publicTokens serves tokens.css/tokens.json unauthenticated).\",\n ],\n cli: [\n \"POST BRAND_SCHEMA to /app/:id/schema.\",\n \"Install brandRules() at /app/:id/admin/rules (merged with the app's existing rules).\",\n \"Register a brandBotTrigger(...) per bot at /app/:id/admin/triggers — global mention-gated, or channel-scoped per book.\",\n \"Seed the bot's agent id into each brand_book.memberIds roster (audienceFanoutOps covers existing child rows).\",\n \"Provision the bot's AI provider secret (via @odla-ai/ai) and the worker's admin odla-db credential.\",\n ],\n doctor: [\n \"All five brand_* namespaces have rules installed (brand_asset fully closed — worker-mediated only).\",\n \"brand_book.id/slug, brand_section.key, and the other mirrored id attrs are unique.\",\n \"Each registered trigger's agent id is present in its target books' memberIds rosters.\",\n \"Books with channelId set point at real chat channels when @odla-ai/chat is installed.\",\n ],\n },\n};\n\n/** Options for {@link createBrandIntegration}. */\nexport interface CreateBrandIntegrationOptions {\n /** Worker mount path; defaults to `/api/brand`. */\n basePath?: string;\n /** Document (and probe-plan for) unauthenticated tokens routes. */\n publicTokens?: boolean;\n /** The global bot's mention handle — folded into the CLI trigger step. */\n mention?: string;\n}\n\n/**\n * Build the project-specific, CLI-consumable brand integration descriptor:\n * {@link brandIntegration} plus a live smoke probe (an anonymous GET of\n * `<basePath>/books` must answer 401 — routes mounted, authorize enforced,\n * exactly crm's probe idiom) and, when `mention`/`publicTokens` are given,\n * concrete CLI steps carrying the app's actual values. Mounting\n * `createBrandRoutes` remains app-owned source work.\n */\nexport function createBrandIntegration(\n options: CreateBrandIntegrationOptions = {},\n): BrandIntegrationDescriptor {\n const basePath = options.basePath ?? \"/api/brand\";\n if (!/^\\/[A-Za-z0-9._~!$&'()*+,;=:@%/-]*$/.test(basePath) || basePath.endsWith(\"/\")) {\n throw new Error(\"createBrandIntegration: basePath must be an absolute path without a trailing slash\");\n }\n const cli = [...brandIntegration.provision.cli];\n if (options.mention !== undefined)\n cli.push(`Gate the global bot on mentions: brandBotTrigger({ mention: ${JSON.stringify(options.mention)}, ... }).`);\n if (options.publicTokens === true)\n cli.push(`Mount createBrandRoutes({ publicTokens: true }) so ${basePath}/books/:id/tokens.css serves unauthenticated.`);\n return {\n ...brandIntegration,\n provision: { ...brandIntegration.provision, cli },\n probes: [{ path: `${basePath}/books`, expectedStatus: 401 }],\n };\n}\n"],"mappings":"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","names":["HEX_ALPHA","a","value","toHex","a","HEX6","a","isRecord","a","isRecord","a"]}