@ai-gui/core 0.22.0 → 0.23.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.
package/README.md CHANGED
@@ -113,6 +113,7 @@ const runtime = createActionRuntime({ registry: actions, cardStore })
113
113
  - `Renderer` — `push(chunk)`, `feed(AsyncIterable | ReadableStream)`, `reset()`, `setPlugins(plugins)`; constructor `{ registry?, plugins?, sanitize?, rawHtml?, onPatch?(patches, nodes) }`.
114
114
  - `setPlugins` swaps the grammar and reparses the buffered source, so plugins deferred behind a dynamic import can arrive mid-answer without the host replaying what it pushed.
115
115
  - `rawHtml: false` escapes raw HTML the model wrote instead of interpreting it — a stray `<code>` in prose otherwise swallows the rest of the line.
116
+ - Emphasis is parsed CJK-friendly, one deliberate deviation from CommonMark. CommonMark will not let `**` close when it follows punctuation and precedes a character that is neither whitespace nor punctuation, so `**严格单调(单射)**的函数` renders its asterisks literally. ASCII is unaffected: `a * b * c` and `snake_case_word` parse exactly as before.
116
117
  - `StreamRouter` — demultiplex one stream into named channels: `.channel(name, sink)`, `.on(name, cb)`, `.feed(source)`.
117
118
  - `CardRegistry` — `register(def)`, `parse(type, rawJson)`, `getRender(type)`, `toPromptSpec()`, `toJSONSchema()`.
118
119
  - `CardStore` — `register`, `get`, `list`, `subscribe`, `apply`, `applyAll`, `delete`, `clear`, `snapshot`, and `restore` for Cards with stable IDs.
package/dist/index.cjs CHANGED
@@ -24,6 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  //#endregion
25
25
  const dompurify = __toESM(require("dompurify"));
26
26
  const markdown_it = __toESM(require("markdown-it"));
27
+ const markdown_it_cjk_friendly = __toESM(require("markdown-it-cjk-friendly"));
27
28
 
28
29
  //#region src/partial-json.ts
29
30
  /**
@@ -1465,8 +1466,32 @@ function samePlugins(a, b) {
1465
1466
  if (!a || !b || a.length !== b.length) return false;
1466
1467
  return a.every((plugin, index) => plugin === b[index]);
1467
1468
  }
1469
+ /**
1470
+ * Reject anything in the list that is not a plugin, naming what to do about it.
1471
+ *
1472
+ * Every plugin package exports a factory, so `plugins: [katex]` instead of `plugins: [katex()]` is
1473
+ * the easiest mistake to make — and it used to be the quietest. A function has a `name` of its
1474
+ * own, `"katex"`, so the renderer accepted it, found no `extendParser` and no `nodeRenderers`, and
1475
+ * did nothing: no error, no warning, markdown still rendering, only the maths and the diagrams
1476
+ * missing. A product can ship like that and nobody notices until someone asks why an equation is
1477
+ * plain text. Misconfiguration should be loud, so this throws.
1478
+ */
1479
+ function assertPlugins(plugins, label = "plugins") {
1480
+ if (!plugins) return;
1481
+ plugins.forEach((plugin, index) => {
1482
+ const at = `${label}[${index}]`;
1483
+ if (typeof plugin === "function") {
1484
+ const called = plugin.name ? `${plugin.name}()` : "the factory";
1485
+ throw new TypeError(`${at} is a factory function, not a plugin. Call it: ${called}`);
1486
+ }
1487
+ if (plugin === null || typeof plugin !== "object") throw new TypeError(`${at} is ${plugin === null ? "null" : typeof plugin}, not a plugin object`);
1488
+ const { name } = plugin;
1489
+ if (typeof name !== "string" || name.length === 0) throw new TypeError(`${at} has no \`name\`. A plugin needs one: its stylesheet and its debug events are keyed by it`);
1490
+ });
1491
+ }
1468
1492
  /** Merge every plugin's `nodeRenderers` into a single map (later plugins win). */
1469
1493
  function collectNodeRenderers(plugins = [], debugOptions = {}) {
1494
+ assertPlugins(plugins);
1470
1495
  const map = {};
1471
1496
  const target = debugOptions.debugTarget;
1472
1497
  const debug = target ? void 0 : debugOptions.debug === true ? new DebugEmitter("renderer", debugOptions) : void 0;
@@ -1554,6 +1579,7 @@ function createParserWithMetadata(options = {}) {
1554
1579
  html: options.rawHtml !== false,
1555
1580
  linkify: true
1556
1581
  });
1582
+ md.use(markdown_it_cjk_friendly.default);
1557
1583
  options.configureMd?.(md);
1558
1584
  for (const plugin of options.plugins ?? []) plugin.extendParser?.(md);
1559
1585
  const hasParserExtensions = Boolean(options.configureMd || options.plugins?.some((plugin) => plugin.extendParser));
@@ -2067,6 +2093,7 @@ var Renderer = class {
2067
2093
  debug;
2068
2094
  constructor(options = {}) {
2069
2095
  this.options = options;
2096
+ assertPlugins(options.plugins);
2070
2097
  this.debug = new DebugEmitter(this.debugSource, options);
2071
2098
  this.sanitize = options.sanitize === false ? false : typeof options.sanitize === "object" ? options.sanitize : {};
2072
2099
  this.registerPluginCards(options.plugins);
@@ -2092,6 +2119,7 @@ var Renderer = class {
2092
2119
  * Passing the same plugins again is a no-op, so a host may call this on every render.
2093
2120
  */
2094
2121
  setPlugins(plugins) {
2122
+ assertPlugins(plugins);
2095
2123
  if (samePlugins(this.options.plugins, plugins)) return;
2096
2124
  this.options = {
2097
2125
  ...this.options,
@@ -2887,6 +2915,7 @@ exports.Renderer = Renderer
2887
2915
  exports.StreamRouter = StreamRouter
2888
2916
  exports.actionOutcome = actionOutcome
2889
2917
  exports.applyPatches = applyPatches
2918
+ exports.assertPlugins = assertPlugins
2890
2919
  exports.availableLocales = availableLocales
2891
2920
  exports.baseCss = baseCss
2892
2921
  exports.buildSystemPrompt = buildSystemPrompt
package/dist/index.d.cts CHANGED
@@ -255,6 +255,12 @@ interface AIGuiPlugin {
255
255
  /**
256
256
  * LLM-facing guidance describing this plugin's fence syntax.
257
257
  *
258
+ * A host does not read this field itself: `buildSystemPrompt({ registry, plugins, locale })`
259
+ * collects the card specs and every enabled plugin's spec in one call, already in the product's
260
+ * language. Assembling it by hand — reading each plugin's spec, joining them, writing the
261
+ * localized wording again — reinvents that badly, and a plugin added later is then missing from
262
+ * the prompt while its renderer is installed.
263
+ *
258
264
  * Receives the locale asked of `buildSystemPrompt`, so the rules can be written in the language
259
265
  * the product answers in — a Chinese persona followed by English rules reads as a contradiction
260
266
  * to the model. Plugins that only ship English simply ignore the argument.
@@ -284,9 +290,7 @@ interface FeedOptions {
284
290
  signal?: AbortSignal;
285
291
  }
286
292
  type FeedChunk = string | Uint8Array;
287
- type FeedSource = AsyncIterable<FeedChunk> | ReadableStream<FeedChunk>;
288
-
289
- //#endregion
293
+ type FeedSource = AsyncIterable<FeedChunk> | ReadableStream<FeedChunk>; //#endregion
290
294
  //#region src/card-registry.d.ts
291
295
  interface CardParseResult {
292
296
  data: unknown;
@@ -684,6 +688,17 @@ declare function loadPlugins(source?: PluginSource): AIGuiPlugin[] | Promise<AIG
684
688
  * What matters is the members, not the array.
685
689
  */
686
690
  declare function samePlugins(a?: AIGuiPlugin[], b?: AIGuiPlugin[]): boolean;
691
+ /**
692
+ * Reject anything in the list that is not a plugin, naming what to do about it.
693
+ *
694
+ * Every plugin package exports a factory, so `plugins: [katex]` instead of `plugins: [katex()]` is
695
+ * the easiest mistake to make — and it used to be the quietest. A function has a `name` of its
696
+ * own, `"katex"`, so the renderer accepted it, found no `extendParser` and no `nodeRenderers`, and
697
+ * did nothing: no error, no warning, markdown still rendering, only the maths and the diagrams
698
+ * missing. A product can ship like that and nobody notices until someone asks why an equation is
699
+ * plain text. Misconfiguration should be loud, so this throws.
700
+ */
701
+ declare function assertPlugins(plugins: readonly unknown[] | undefined, label?: string): void;
687
702
  /** Merge every plugin's `nodeRenderers` into a single map (later plugins win). */
688
703
  declare function collectNodeRenderers(plugins?: AIGuiPlugin[], debugOptions?: CollectNodeRendererOptions): Record<string, NodeRenderer>;
689
704
  /** The set of node types claimed by the given plugins. */
@@ -985,4 +1000,4 @@ declare function mockModelStream(events: Iterable<ModelStreamEvent> | AsyncItera
985
1000
  declare function readableBytes(chunks: Iterable<string | Uint8Array> | AsyncIterable<string | Uint8Array>): ReadableStream<Uint8Array>;
986
1001
 
987
1002
  //#endregion
988
- export { AIGuiPlugin, ASTNode, ActionAbortedError, ActionAlreadyRegisteredError, ActionContext, ActionDefinition, ActionDestroyedError, ActionDispatchOptions, ActionErrorEvent, ActionEventBase, ActionExecutionError, ActionNotFoundError, ActionOutcome, ActionRegisterOptions, ActionRegistry, ActionRequest, ActionRuntime, ActionRuntimeError, ActionRuntimeOptions, ActionStartEvent, ActionState, ActionStateListener, ActionStatus, ActionSuccessEvent, ActionTimeoutError, ActionValidationError, BuildSystemPromptOptions, ByteStreamSource, CARD_ID_MAX_LENGTH, CARD_JSON_MAX_DEPTH, CARD_JSON_MAX_NODES, CARD_PATCH_BATCH_MAX_SIZE, CardAction, CardActionError, CardDef, CardJSONError, CardLimitError, CardListener, CardNotFoundError, CardParseResult, CardPatch, CardPatchBatch, CardPatchResult, CardRecord, CardRegistry, CardRevisionConflictError, CardSnapshot, CardSnapshotError, CardStore, CardStoreError, CardStoreListener, CardStoreOptions, CardTypeConflictError, CardValidationError, ChannelSink, Citation, CollectNodeRendererOptions, DEFAULT_LOCALE, DebugEmitter, DebugEvent, DebugEventListener, DebugEventTarget, DebugInstrumentationTarget, DebugOptions, DebugRedactContext, DebugSource, ExportImageOptions, ExportedImage, FeedChunk, FeedOptions, FeedSource, JSONSchema, JSONSchemaValidationResult, Locale, MessageBundle, Messages, ModelStreamEvent, MountCardSlotRequest, MountedCardSlot, NodeRenderContext, NodeRenderer, OutcomeTone, ParseResult, ParserOptions, PartialJSONResult, Patch, PluginCommitContext, PluginSource, PluginStyle, PluginsLoader, RenderMountContext, RenderOutput, Renderer, RendererOptions, SSEEvent, SSEOptions, SafeDebugValueOptions, SanitizeHtmlOptions, SanitizeSetting, SourceBlock, StreamParseOptions, StreamRouter, Usage, actionOutcome, applyPatches, availableLocales, baseCss, buildSystemPrompt, collectNodeRenderers, collectPluginStyles, contentDeltas, createActionRuntime, createParser, createParserWithMetadata, diffAst, downloadImage, exportRenderedImages, exportSVGToImage, getActionKey, getIdleActionState, injectPluginStyles, isCardPatchResult, jsonLines, loadPlugins, mockModelStream, ndjson, parsePartialJSON, parseSSE, pluginNodeTypes, readableBytes, repairMarkdown, resolveMessages, safeDebugValue, samePlugins, sanitizeHtml, sanitizeRenderedHtml, textLines, translate, translator, validateJSONSchema };
1003
+ export { AIGuiPlugin, ASTNode, ActionAbortedError, ActionAlreadyRegisteredError, ActionContext, ActionDefinition, ActionDestroyedError, ActionDispatchOptions, ActionErrorEvent, ActionEventBase, ActionExecutionError, ActionNotFoundError, ActionOutcome, ActionRegisterOptions, ActionRegistry, ActionRequest, ActionRuntime, ActionRuntimeError, ActionRuntimeOptions, ActionStartEvent, ActionState, ActionStateListener, ActionStatus, ActionSuccessEvent, ActionTimeoutError, ActionValidationError, BuildSystemPromptOptions, ByteStreamSource, CARD_ID_MAX_LENGTH, CARD_JSON_MAX_DEPTH, CARD_JSON_MAX_NODES, CARD_PATCH_BATCH_MAX_SIZE, CardAction, CardActionError, CardDef, CardJSONError, CardLimitError, CardListener, CardNotFoundError, CardParseResult, CardPatch, CardPatchBatch, CardPatchResult, CardRecord, CardRegistry, CardRevisionConflictError, CardSnapshot, CardSnapshotError, CardStore, CardStoreError, CardStoreListener, CardStoreOptions, CardTypeConflictError, CardValidationError, ChannelSink, Citation, CollectNodeRendererOptions, DEFAULT_LOCALE, DebugEmitter, DebugEvent, DebugEventListener, DebugEventTarget, DebugInstrumentationTarget, DebugOptions, DebugRedactContext, DebugSource, ExportImageOptions, ExportedImage, FeedChunk, FeedOptions, FeedSource, JSONSchema, JSONSchemaValidationResult, Locale, MessageBundle, Messages, ModelStreamEvent, MountCardSlotRequest, MountedCardSlot, NodeRenderContext, NodeRenderer, OutcomeTone, ParseResult, ParserOptions, PartialJSONResult, Patch, PluginCommitContext, PluginSource, PluginStyle, PluginsLoader, RenderMountContext, RenderOutput, Renderer, RendererOptions, SSEEvent, SSEOptions, SafeDebugValueOptions, SanitizeHtmlOptions, SanitizeSetting, SourceBlock, StreamParseOptions, StreamRouter, Usage, actionOutcome, applyPatches, assertPlugins, availableLocales, baseCss, buildSystemPrompt, collectNodeRenderers, collectPluginStyles, contentDeltas, createActionRuntime, createParser, createParserWithMetadata, diffAst, downloadImage, exportRenderedImages, exportSVGToImage, getActionKey, getIdleActionState, injectPluginStyles, isCardPatchResult, jsonLines, loadPlugins, mockModelStream, ndjson, parsePartialJSON, parseSSE, pluginNodeTypes, readableBytes, repairMarkdown, resolveMessages, safeDebugValue, samePlugins, sanitizeHtml, sanitizeRenderedHtml, textLines, translate, translator, validateJSONSchema };
package/dist/index.d.ts CHANGED
@@ -255,6 +255,12 @@ interface AIGuiPlugin {
255
255
  /**
256
256
  * LLM-facing guidance describing this plugin's fence syntax.
257
257
  *
258
+ * A host does not read this field itself: `buildSystemPrompt({ registry, plugins, locale })`
259
+ * collects the card specs and every enabled plugin's spec in one call, already in the product's
260
+ * language. Assembling it by hand — reading each plugin's spec, joining them, writing the
261
+ * localized wording again — reinvents that badly, and a plugin added later is then missing from
262
+ * the prompt while its renderer is installed.
263
+ *
258
264
  * Receives the locale asked of `buildSystemPrompt`, so the rules can be written in the language
259
265
  * the product answers in — a Chinese persona followed by English rules reads as a contradiction
260
266
  * to the model. Plugins that only ship English simply ignore the argument.
@@ -284,9 +290,7 @@ interface FeedOptions {
284
290
  signal?: AbortSignal;
285
291
  }
286
292
  type FeedChunk = string | Uint8Array;
287
- type FeedSource = AsyncIterable<FeedChunk> | ReadableStream<FeedChunk>;
288
-
289
- //#endregion
293
+ type FeedSource = AsyncIterable<FeedChunk> | ReadableStream<FeedChunk>; //#endregion
290
294
  //#region src/card-registry.d.ts
291
295
  interface CardParseResult {
292
296
  data: unknown;
@@ -684,6 +688,17 @@ declare function loadPlugins(source?: PluginSource): AIGuiPlugin[] | Promise<AIG
684
688
  * What matters is the members, not the array.
685
689
  */
686
690
  declare function samePlugins(a?: AIGuiPlugin[], b?: AIGuiPlugin[]): boolean;
691
+ /**
692
+ * Reject anything in the list that is not a plugin, naming what to do about it.
693
+ *
694
+ * Every plugin package exports a factory, so `plugins: [katex]` instead of `plugins: [katex()]` is
695
+ * the easiest mistake to make — and it used to be the quietest. A function has a `name` of its
696
+ * own, `"katex"`, so the renderer accepted it, found no `extendParser` and no `nodeRenderers`, and
697
+ * did nothing: no error, no warning, markdown still rendering, only the maths and the diagrams
698
+ * missing. A product can ship like that and nobody notices until someone asks why an equation is
699
+ * plain text. Misconfiguration should be loud, so this throws.
700
+ */
701
+ declare function assertPlugins(plugins: readonly unknown[] | undefined, label?: string): void;
687
702
  /** Merge every plugin's `nodeRenderers` into a single map (later plugins win). */
688
703
  declare function collectNodeRenderers(plugins?: AIGuiPlugin[], debugOptions?: CollectNodeRendererOptions): Record<string, NodeRenderer>;
689
704
  /** The set of node types claimed by the given plugins. */
@@ -985,4 +1000,4 @@ declare function mockModelStream(events: Iterable<ModelStreamEvent> | AsyncItera
985
1000
  declare function readableBytes(chunks: Iterable<string | Uint8Array> | AsyncIterable<string | Uint8Array>): ReadableStream<Uint8Array>;
986
1001
 
987
1002
  //#endregion
988
- export { AIGuiPlugin, ASTNode, ActionAbortedError, ActionAlreadyRegisteredError, ActionContext, ActionDefinition, ActionDestroyedError, ActionDispatchOptions, ActionErrorEvent, ActionEventBase, ActionExecutionError, ActionNotFoundError, ActionOutcome, ActionRegisterOptions, ActionRegistry, ActionRequest, ActionRuntime, ActionRuntimeError, ActionRuntimeOptions, ActionStartEvent, ActionState, ActionStateListener, ActionStatus, ActionSuccessEvent, ActionTimeoutError, ActionValidationError, BuildSystemPromptOptions, ByteStreamSource, CARD_ID_MAX_LENGTH, CARD_JSON_MAX_DEPTH, CARD_JSON_MAX_NODES, CARD_PATCH_BATCH_MAX_SIZE, CardAction, CardActionError, CardDef, CardJSONError, CardLimitError, CardListener, CardNotFoundError, CardParseResult, CardPatch, CardPatchBatch, CardPatchResult, CardRecord, CardRegistry, CardRevisionConflictError, CardSnapshot, CardSnapshotError, CardStore, CardStoreError, CardStoreListener, CardStoreOptions, CardTypeConflictError, CardValidationError, ChannelSink, Citation, CollectNodeRendererOptions, DEFAULT_LOCALE, DebugEmitter, DebugEvent, DebugEventListener, DebugEventTarget, DebugInstrumentationTarget, DebugOptions, DebugRedactContext, DebugSource, ExportImageOptions, ExportedImage, FeedChunk, FeedOptions, FeedSource, JSONSchema, JSONSchemaValidationResult, Locale, MessageBundle, Messages, ModelStreamEvent, MountCardSlotRequest, MountedCardSlot, NodeRenderContext, NodeRenderer, OutcomeTone, ParseResult, ParserOptions, PartialJSONResult, Patch, PluginCommitContext, PluginSource, PluginStyle, PluginsLoader, RenderMountContext, RenderOutput, Renderer, RendererOptions, SSEEvent, SSEOptions, SafeDebugValueOptions, SanitizeHtmlOptions, SanitizeSetting, SourceBlock, StreamParseOptions, StreamRouter, Usage, actionOutcome, applyPatches, availableLocales, baseCss, buildSystemPrompt, collectNodeRenderers, collectPluginStyles, contentDeltas, createActionRuntime, createParser, createParserWithMetadata, diffAst, downloadImage, exportRenderedImages, exportSVGToImage, getActionKey, getIdleActionState, injectPluginStyles, isCardPatchResult, jsonLines, loadPlugins, mockModelStream, ndjson, parsePartialJSON, parseSSE, pluginNodeTypes, readableBytes, repairMarkdown, resolveMessages, safeDebugValue, samePlugins, sanitizeHtml, sanitizeRenderedHtml, textLines, translate, translator, validateJSONSchema };
1003
+ export { AIGuiPlugin, ASTNode, ActionAbortedError, ActionAlreadyRegisteredError, ActionContext, ActionDefinition, ActionDestroyedError, ActionDispatchOptions, ActionErrorEvent, ActionEventBase, ActionExecutionError, ActionNotFoundError, ActionOutcome, ActionRegisterOptions, ActionRegistry, ActionRequest, ActionRuntime, ActionRuntimeError, ActionRuntimeOptions, ActionStartEvent, ActionState, ActionStateListener, ActionStatus, ActionSuccessEvent, ActionTimeoutError, ActionValidationError, BuildSystemPromptOptions, ByteStreamSource, CARD_ID_MAX_LENGTH, CARD_JSON_MAX_DEPTH, CARD_JSON_MAX_NODES, CARD_PATCH_BATCH_MAX_SIZE, CardAction, CardActionError, CardDef, CardJSONError, CardLimitError, CardListener, CardNotFoundError, CardParseResult, CardPatch, CardPatchBatch, CardPatchResult, CardRecord, CardRegistry, CardRevisionConflictError, CardSnapshot, CardSnapshotError, CardStore, CardStoreError, CardStoreListener, CardStoreOptions, CardTypeConflictError, CardValidationError, ChannelSink, Citation, CollectNodeRendererOptions, DEFAULT_LOCALE, DebugEmitter, DebugEvent, DebugEventListener, DebugEventTarget, DebugInstrumentationTarget, DebugOptions, DebugRedactContext, DebugSource, ExportImageOptions, ExportedImage, FeedChunk, FeedOptions, FeedSource, JSONSchema, JSONSchemaValidationResult, Locale, MessageBundle, Messages, ModelStreamEvent, MountCardSlotRequest, MountedCardSlot, NodeRenderContext, NodeRenderer, OutcomeTone, ParseResult, ParserOptions, PartialJSONResult, Patch, PluginCommitContext, PluginSource, PluginStyle, PluginsLoader, RenderMountContext, RenderOutput, Renderer, RendererOptions, SSEEvent, SSEOptions, SafeDebugValueOptions, SanitizeHtmlOptions, SanitizeSetting, SourceBlock, StreamParseOptions, StreamRouter, Usage, actionOutcome, applyPatches, assertPlugins, availableLocales, baseCss, buildSystemPrompt, collectNodeRenderers, collectPluginStyles, contentDeltas, createActionRuntime, createParser, createParserWithMetadata, diffAst, downloadImage, exportRenderedImages, exportSVGToImage, getActionKey, getIdleActionState, injectPluginStyles, isCardPatchResult, jsonLines, loadPlugins, mockModelStream, ndjson, parsePartialJSON, parseSSE, pluginNodeTypes, readableBytes, repairMarkdown, resolveMessages, safeDebugValue, samePlugins, sanitizeHtml, sanitizeRenderedHtml, textLines, translate, translator, validateJSONSchema };
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import DOMPurify from "dompurify";
2
2
  import MarkdownIt from "markdown-it";
3
+ import cjkFriendly from "markdown-it-cjk-friendly";
3
4
 
4
5
  //#region src/partial-json.ts
5
6
  /**
@@ -1441,8 +1442,32 @@ function samePlugins(a, b) {
1441
1442
  if (!a || !b || a.length !== b.length) return false;
1442
1443
  return a.every((plugin, index) => plugin === b[index]);
1443
1444
  }
1445
+ /**
1446
+ * Reject anything in the list that is not a plugin, naming what to do about it.
1447
+ *
1448
+ * Every plugin package exports a factory, so `plugins: [katex]` instead of `plugins: [katex()]` is
1449
+ * the easiest mistake to make — and it used to be the quietest. A function has a `name` of its
1450
+ * own, `"katex"`, so the renderer accepted it, found no `extendParser` and no `nodeRenderers`, and
1451
+ * did nothing: no error, no warning, markdown still rendering, only the maths and the diagrams
1452
+ * missing. A product can ship like that and nobody notices until someone asks why an equation is
1453
+ * plain text. Misconfiguration should be loud, so this throws.
1454
+ */
1455
+ function assertPlugins(plugins, label = "plugins") {
1456
+ if (!plugins) return;
1457
+ plugins.forEach((plugin, index) => {
1458
+ const at = `${label}[${index}]`;
1459
+ if (typeof plugin === "function") {
1460
+ const called = plugin.name ? `${plugin.name}()` : "the factory";
1461
+ throw new TypeError(`${at} is a factory function, not a plugin. Call it: ${called}`);
1462
+ }
1463
+ if (plugin === null || typeof plugin !== "object") throw new TypeError(`${at} is ${plugin === null ? "null" : typeof plugin}, not a plugin object`);
1464
+ const { name } = plugin;
1465
+ if (typeof name !== "string" || name.length === 0) throw new TypeError(`${at} has no \`name\`. A plugin needs one: its stylesheet and its debug events are keyed by it`);
1466
+ });
1467
+ }
1444
1468
  /** Merge every plugin's `nodeRenderers` into a single map (later plugins win). */
1445
1469
  function collectNodeRenderers(plugins = [], debugOptions = {}) {
1470
+ assertPlugins(plugins);
1446
1471
  const map = {};
1447
1472
  const target = debugOptions.debugTarget;
1448
1473
  const debug = target ? void 0 : debugOptions.debug === true ? new DebugEmitter("renderer", debugOptions) : void 0;
@@ -1530,6 +1555,7 @@ function createParserWithMetadata(options = {}) {
1530
1555
  html: options.rawHtml !== false,
1531
1556
  linkify: true
1532
1557
  });
1558
+ md.use(cjkFriendly);
1533
1559
  options.configureMd?.(md);
1534
1560
  for (const plugin of options.plugins ?? []) plugin.extendParser?.(md);
1535
1561
  const hasParserExtensions = Boolean(options.configureMd || options.plugins?.some((plugin) => plugin.extendParser));
@@ -2043,6 +2069,7 @@ var Renderer = class {
2043
2069
  debug;
2044
2070
  constructor(options = {}) {
2045
2071
  this.options = options;
2072
+ assertPlugins(options.plugins);
2046
2073
  this.debug = new DebugEmitter(this.debugSource, options);
2047
2074
  this.sanitize = options.sanitize === false ? false : typeof options.sanitize === "object" ? options.sanitize : {};
2048
2075
  this.registerPluginCards(options.plugins);
@@ -2068,6 +2095,7 @@ var Renderer = class {
2068
2095
  * Passing the same plugins again is a no-op, so a host may call this on every render.
2069
2096
  */
2070
2097
  setPlugins(plugins) {
2098
+ assertPlugins(plugins);
2071
2099
  if (samePlugins(this.options.plugins, plugins)) return;
2072
2100
  this.options = {
2073
2101
  ...this.options,
@@ -2833,4 +2861,4 @@ function delay(ms, signal) {
2833
2861
  }
2834
2862
 
2835
2863
  //#endregion
2836
- export { ActionAbortedError, ActionAlreadyRegisteredError, ActionDestroyedError, ActionExecutionError, ActionNotFoundError, ActionRegistry, ActionRuntime, ActionRuntimeError, ActionTimeoutError, ActionValidationError, CARD_ID_MAX_LENGTH, CARD_JSON_MAX_DEPTH, CARD_JSON_MAX_NODES, CARD_PATCH_BATCH_MAX_SIZE, CardJSONError, CardLimitError, CardNotFoundError, CardRegistry, CardRevisionConflictError, CardSnapshotError, CardStore, CardStoreError, CardTypeConflictError, CardValidationError, DEFAULT_LOCALE, DebugEmitter, Renderer, StreamRouter, actionOutcome, applyPatches, availableLocales, baseCss, buildSystemPrompt, collectNodeRenderers, collectPluginStyles, contentDeltas, createActionRuntime, createParser, createParserWithMetadata, diffAst, downloadImage, exportRenderedImages, exportSVGToImage, getActionKey, getIdleActionState, injectPluginStyles, isCardPatchResult, jsonLines, loadPlugins, mockModelStream, ndjson, parsePartialJSON, parseSSE, pluginNodeTypes, readableBytes, repairMarkdown, resolveMessages, safeDebugValue, samePlugins, sanitizeHtml, sanitizeRenderedHtml, textLines, translate, translator, validateJSONSchema };
2864
+ export { ActionAbortedError, ActionAlreadyRegisteredError, ActionDestroyedError, ActionExecutionError, ActionNotFoundError, ActionRegistry, ActionRuntime, ActionRuntimeError, ActionTimeoutError, ActionValidationError, CARD_ID_MAX_LENGTH, CARD_JSON_MAX_DEPTH, CARD_JSON_MAX_NODES, CARD_PATCH_BATCH_MAX_SIZE, CardJSONError, CardLimitError, CardNotFoundError, CardRegistry, CardRevisionConflictError, CardSnapshotError, CardStore, CardStoreError, CardTypeConflictError, CardValidationError, DEFAULT_LOCALE, DebugEmitter, Renderer, StreamRouter, actionOutcome, applyPatches, assertPlugins, availableLocales, baseCss, buildSystemPrompt, collectNodeRenderers, collectPluginStyles, contentDeltas, createActionRuntime, createParser, createParserWithMetadata, diffAst, downloadImage, exportRenderedImages, exportSVGToImage, getActionKey, getIdleActionState, injectPluginStyles, isCardPatchResult, jsonLines, loadPlugins, mockModelStream, ndjson, parsePartialJSON, parseSSE, pluginNodeTypes, readableBytes, repairMarkdown, resolveMessages, safeDebugValue, samePlugins, sanitizeHtml, sanitizeRenderedHtml, textLines, translate, translator, validateJSONSchema };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-gui/core",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "Headless streaming renderer core for LLM-generated UI — markdown/JSON repair, card registry, AST diff, plugin engine, sanitizer.",
5
5
  "keywords": [
6
6
  "llm",
@@ -52,7 +52,8 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "dompurify": "^3.4.12",
55
- "markdown-it": "^14.1.0"
55
+ "markdown-it": "^14.1.0",
56
+ "markdown-it-cjk-friendly": "^2.0.2"
56
57
  },
57
58
  "devDependencies": {
58
59
  "@types/markdown-it": "^14.1.2",