@aparte/core 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +2 -2
  2. package/dist/client/aparte-client.d.ts +36 -35
  3. package/dist/client/aparte-client.d.ts.map +1 -1
  4. package/dist/client/stream-adapter.d.ts +37 -145
  5. package/dist/client/stream-adapter.d.ts.map +1 -1
  6. package/dist/components/bubble/aparte-chat-bubble.d.ts +10 -2
  7. package/dist/components/bubble/aparte-chat-bubble.d.ts.map +1 -1
  8. package/dist/components/composer/aparte-composer-add-attachment.d.ts +6 -6
  9. package/dist/components/composer/aparte-composer-add-attachment.d.ts.map +1 -1
  10. package/dist/components/composer/aparte-composer-input.d.ts +9 -1
  11. package/dist/components/composer/aparte-composer-input.d.ts.map +1 -1
  12. package/dist/components/composer/aparte-composer.d.ts +12 -3
  13. package/dist/components/composer/aparte-composer.d.ts.map +1 -1
  14. package/dist/components/context/aparte-context.d.ts +81 -0
  15. package/dist/components/context/aparte-context.d.ts.map +1 -0
  16. package/dist/components/context/index.d.ts +3 -0
  17. package/dist/components/context/index.d.ts.map +1 -0
  18. package/dist/components/elicitation/aparte-elicitation.d.ts.map +1 -1
  19. package/dist/components/index.d.ts +4 -0
  20. package/dist/components/index.d.ts.map +1 -1
  21. package/dist/components/suggestions/aparte-suggestions.d.ts +95 -0
  22. package/dist/components/suggestions/aparte-suggestions.d.ts.map +1 -0
  23. package/dist/components/suggestions/index.d.ts +3 -0
  24. package/dist/components/suggestions/index.d.ts.map +1 -0
  25. package/dist/components/viewport/aparte-chat-viewport.d.ts +37 -7
  26. package/dist/components/viewport/aparte-chat-viewport.d.ts.map +1 -1
  27. package/dist/config/aparte-config.d.ts +1 -21
  28. package/dist/config/aparte-config.d.ts.map +1 -1
  29. package/dist/config/index.d.ts +0 -2
  30. package/dist/config/index.d.ts.map +1 -1
  31. package/dist/config/locale.d.ts +30 -1
  32. package/dist/config/locale.d.ts.map +1 -1
  33. package/dist/conversations/index.d.ts +1 -1
  34. package/dist/conversations/index.d.ts.map +1 -1
  35. package/dist/conversations/types.d.ts +9 -57
  36. package/dist/conversations/types.d.ts.map +1 -1
  37. package/dist/custom-elements.json +6484 -6103
  38. package/dist/generated/element-attributes.d.ts +34 -6
  39. package/dist/generated/element-attributes.d.ts.map +1 -1
  40. package/dist/{glyphs-QXfbL3n_.js → glyphs-B3BZ2TIb.js} +21 -21
  41. package/dist/glyphs-B3BZ2TIb.js.map +1 -0
  42. package/dist/host/aparte-chat-host.d.ts +1 -1
  43. package/dist/host/aparte-chat-host.d.ts.map +1 -1
  44. package/dist/icons/extended.d.ts +87 -85
  45. package/dist/icons/extended.d.ts.map +1 -1
  46. package/dist/icons/glyphs.d.ts +53 -50
  47. package/dist/icons/glyphs.d.ts.map +1 -1
  48. package/dist/icons.js +41 -41
  49. package/dist/icons.js.map +1 -1
  50. package/dist/index.css +539 -172
  51. package/dist/index.d.ts +10 -4
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +609 -183
  54. package/dist/index.js.map +1 -1
  55. package/dist/index.node.d.ts +6 -4
  56. package/dist/index.node.d.ts.map +1 -1
  57. package/dist/index.node.js +56 -56
  58. package/dist/index.node.js.map +1 -1
  59. package/dist/{is-awaiting-reply-DrO_oR32.js → is-awaiting-reply-BZ_iJ3s3.js} +188 -1017
  60. package/dist/is-awaiting-reply-BZ_iJ3s3.js.map +1 -0
  61. package/dist/parsers/aparte-stream-parser.d.ts +6 -17
  62. package/dist/parsers/aparte-stream-parser.d.ts.map +1 -1
  63. package/dist/primitives/select/aparte-select.d.ts +2 -2
  64. package/dist/primitives/select/aparte-select.d.ts.map +1 -1
  65. package/dist/renderers/segment-renderers.d.ts +18 -0
  66. package/dist/renderers/segment-renderers.d.ts.map +1 -1
  67. package/dist/renderers/segments/tool-call.d.ts.map +1 -1
  68. package/dist/runtime/message-repository.d.ts +7 -0
  69. package/dist/runtime/message-repository.d.ts.map +1 -1
  70. package/dist/transport/types.d.ts +8 -0
  71. package/dist/transport/types.d.ts.map +1 -1
  72. package/dist/types/chat.d.ts +32 -38
  73. package/dist/types/chat.d.ts.map +1 -1
  74. package/dist/types/errors.d.ts +16 -1
  75. package/dist/types/errors.d.ts.map +1 -1
  76. package/dist/types/event-map.d.ts +4 -0
  77. package/dist/types/event-map.d.ts.map +1 -1
  78. package/dist/types/events.d.ts +15 -0
  79. package/dist/types/events.d.ts.map +1 -1
  80. package/dist/types/index.d.ts +1 -1
  81. package/dist/types/index.d.ts.map +1 -1
  82. package/dist/types/models.d.ts +1 -8
  83. package/dist/types/models.d.ts.map +1 -1
  84. package/dist/types/segments.d.ts +1 -13
  85. package/dist/types/segments.d.ts.map +1 -1
  86. package/dist/types/tools.d.ts +1 -1
  87. package/dist/utils/chat-boundary.d.ts +8 -4
  88. package/dist/utils/chat-boundary.d.ts.map +1 -1
  89. package/package.json +5 -2
  90. package/dist/client/xml-artifact-feed.d.ts +0 -76
  91. package/dist/client/xml-artifact-feed.d.ts.map +0 -1
  92. package/dist/config/skeleton-provider.d.ts +0 -26
  93. package/dist/config/skeleton-provider.d.ts.map +0 -1
  94. package/dist/glyphs-QXfbL3n_.js.map +0 -1
  95. package/dist/is-awaiting-reply-DrO_oR32.js.map +0 -1
  96. package/dist/renderers/segments/pipeline-waiting.d.ts +0 -3
  97. package/dist/renderers/segments/pipeline-waiting.d.ts.map +0 -1
@@ -1,4 +1,5 @@
1
- import { A as APARTE_ICON_GLYPHS } from "./glyphs-QXfbL3n_.js";
1
+ import { A as APARTE_ICON_GLYPHS } from "./glyphs-B3BZ2TIb.js";
2
+ import { deriveArtifactKind, runStreamAgent } from "@aparte/engine";
2
3
  function contentToText(content) {
3
4
  if (typeof content === "string") return content;
4
5
  return content.filter((p) => p.type === "text").map((p) => p.text).join("");
@@ -6,6 +7,7 @@ function contentToText(content) {
6
7
  var AparteErrorCode = /* @__PURE__ */ ((AparteErrorCode2) => {
7
8
  AparteErrorCode2["CONFIG_NO_PROVIDER"] = "CONFIG_NO_PROVIDER";
8
9
  AparteErrorCode2["CONFIG_MISSING_KEY"] = "CONFIG_MISSING_KEY";
10
+ AparteErrorCode2["CONFIG_INVALID_KEY"] = "CONFIG_INVALID_KEY";
9
11
  AparteErrorCode2["CONFIG_INVALID_MODEL"] = "CONFIG_INVALID_MODEL";
10
12
  AparteErrorCode2["USAGE_RATE_LIMIT"] = "USAGE_RATE_LIMIT";
11
13
  AparteErrorCode2["USAGE_CONTEXT_EXCEEDED"] = "USAGE_CONTEXT_EXCEEDED";
@@ -29,11 +31,50 @@ class AparteError extends Error {
29
31
  this.httpStatus = httpStatus;
30
32
  this.name = "AparteError";
31
33
  }
34
+ /**
35
+ * The code an HTTP status stands for — the table the enum's own comments promise.
36
+ * `undefined` for a status the enum does not name (a 404 can be a wrong model or a
37
+ * wrong URL; the message says which, a code would guess).
38
+ */
39
+ static codeForStatus(status) {
40
+ switch (status) {
41
+ case 400:
42
+ return "USAGE_BAD_REQUEST";
43
+ case 401:
44
+ case 403:
45
+ return "CONFIG_INVALID_KEY";
46
+ case 408:
47
+ return "NET_TIMEOUT";
48
+ case 429:
49
+ return "USAGE_RATE_LIMIT";
50
+ case 503:
51
+ return "PROVIDER_UNAVAILABLE";
52
+ default:
53
+ return status !== void 0 && status >= 500 ? "PROVIDER_ERROR" : void 0;
54
+ }
55
+ }
32
56
  static from(error, defaultCode = "UNKNOWN_ERROR", defaultStatus) {
33
57
  if (error instanceof AparteError) return error;
34
58
  const message = error instanceof Error ? error.message : String(error);
35
59
  const status = error?.status || error?.statusCode || defaultStatus;
36
- return new AparteError(message, defaultCode, void 0, error, status);
60
+ const code = defaultCode !== "UNKNOWN_ERROR" ? defaultCode : AparteError.codeForStatus(status) ?? AparteError.codeForFailure(error) ?? defaultCode;
61
+ return new AparteError(message, code, void 0, error, status);
62
+ }
63
+ /**
64
+ * What a thrown, statusless error says about itself. `fetch` rejects a network
65
+ * failure with a TypeError whose message names the fetch ("Failed to fetch",
66
+ * "NetworkError when attempting to fetch resource.", "Load failed") — any other
67
+ * TypeError is a bug, not the network, and stays unclassified. `AbortSignal.timeout()`
68
+ * rejects with a TimeoutError.
69
+ */
70
+ static codeForFailure(error) {
71
+ if (!(error instanceof Error)) return void 0;
72
+ if (error.name === "TimeoutError") return "NET_TIMEOUT";
73
+ if (error instanceof TypeError && /fetch|network|load failed/i.test(error.message)) {
74
+ const offline = typeof navigator !== "undefined" && navigator.onLine === false;
75
+ return offline ? "NET_OFFLINE" : "NET_ERROR";
76
+ }
77
+ return void 0;
37
78
  }
38
79
  }
39
80
  const APARTE_DEFAULT_ICON_FALLBACKS = APARTE_ICON_GLYPHS;
@@ -57,6 +98,10 @@ const APARTE_DEFAULT_LOCALE = {
57
98
  approvalInstructionPlaceholder: "Or tell it what to do instead…",
58
99
  approvalAsk: "Run {tool}?",
59
100
  approvalWaiting: "waiting for you",
101
+ scrollToBottom: "Scroll to bottom",
102
+ suggestionsLabel: "Suggested prompts",
103
+ contextLabel: "Context window",
104
+ compactionSummaryTitle: "Conversation summary",
60
105
  toolRunning: "Running",
61
106
  toolCompleted: "Done",
62
107
  toolRejected: "Rejected",
@@ -101,14 +146,6 @@ const APARTE_DEFAULT_LOCALE = {
101
146
  unarchiveConversation: "Unarchive conversation",
102
147
  direction: "ltr"
103
148
  };
104
- const APARTE_DEFAULT_SKELETON_FALLBACKS = {
105
- message: '<div class="aparte-skeleton-fallback">Loading...</div>',
106
- code: '<div class="aparte-skeleton-fallback aparte-skeleton-fallback--code">Loading code...</div>',
107
- thinking: '<div class="aparte-skeleton-fallback aparte-skeleton-fallback--tight">Thinking...</div>',
108
- input: '<div class="aparte-skeleton-fallback aparte-skeleton-fallback--snug">...</div>',
109
- list: '<div class="aparte-skeleton-fallback">Loading items...</div>',
110
- text: '<div class="aparte-skeleton-fallback aparte-skeleton-fallback--tight">...</div>'
111
- };
112
149
  function isFormatAdapter(p) {
113
150
  return typeof p.buildRequest === "function" && typeof p.parseStream === "function" && typeof p.defaultEndpoint === "string" && (typeof p.authHeaders === "function" || typeof p.authQuery === "function");
114
151
  }
@@ -123,6 +160,11 @@ async function vendorErrorMessage(response, label = "HTTP") {
123
160
  const msg = typeof raw === "object" ? raw?.message : raw;
124
161
  return typeof msg === "string" && msg ? msg : `${label} ${response.status}`;
125
162
  }
163
+ async function vendorError(response, label = "HTTP") {
164
+ const message = await vendorErrorMessage(response, label);
165
+ const code = AparteError.codeForStatus(response.status) ?? AparteErrorCode.UNKNOWN_ERROR;
166
+ return new AparteError(message, code, void 0, void 0, response.status);
167
+ }
126
168
  async function parseNonStreamText(provider, response, providerId) {
127
169
  const json = await response.json();
128
170
  if (typeof provider.parseText !== "function") {
@@ -163,7 +205,7 @@ class AparteDirectTransport {
163
205
  signal: ctx.signal
164
206
  });
165
207
  if (!response.ok) {
166
- throw new Error(await vendorErrorMessage(response));
208
+ throw await vendorError(response);
167
209
  }
168
210
  if (request.stream === false) {
169
211
  return parseNonStreamText(provider, response, ctx.providerId);
@@ -241,7 +283,7 @@ class AparteStreamParser {
241
283
  if ("content" in this._state.activeSegment) {
242
284
  const seg = this._state.activeSegment;
243
285
  let content = seg.content + this._state.buffer;
244
- if (seg.type === "text") content = stripTrailingFence(content);
286
+ if (seg.type === "text" || seg.type === "code") content = stripTrailingFence(content);
245
287
  seg.content = content;
246
288
  }
247
289
  finalSegments.push(this._closed(this._state.activeSegment));
@@ -362,16 +404,6 @@ class AparteStreamParser {
362
404
  _parseCodeMode(buffer) {
363
405
  const closeIndex = buffer.indexOf("\n```");
364
406
  if (closeIndex === -1) {
365
- if (buffer.endsWith("```")) {
366
- const codeContent2 = buffer.slice(0, -3);
367
- if (this._state.activeSegment && "content" in this._state.activeSegment) {
368
- this._state.activeSegment.content += codeContent2;
369
- }
370
- const segment2 = this._closed(this._state.activeSegment);
371
- this._state.mode = "text";
372
- this._state.activeSegment = null;
373
- return { segment: segment2, remaining: "" };
374
- }
375
407
  if (buffer.length > 4) {
376
408
  const safeContent = buffer.slice(0, -4);
377
409
  if (this._state.activeSegment && "content" in this._state.activeSegment) {
@@ -594,28 +626,6 @@ function parseMarkdownToSegments(content, options) {
594
626
  const finalSegments = parser.finalize();
595
627
  return [...result.segments, ...finalSegments];
596
628
  }
597
- function deriveArtifactKind(mimeType, fallback = "unknown") {
598
- const m = (mimeType || "").toLowerCase().trim();
599
- const ant = m.match(/^application\/vnd\.ant\.([a-z0-9-]+)/);
600
- if (ant) return ant[1];
601
- if (m === "text/html" || m === "application/xhtml+xml") return "html";
602
- if (m === "application/javascript" || m === "text/javascript") return "js";
603
- if (m === "text/css") return "css";
604
- if (m === "image/svg+xml") return "svg";
605
- if (m === "application/json") return "json";
606
- if (m === "text/markdown") return "markdown";
607
- if (m === "text/csv") return "csv";
608
- if (m === "text/plain") return "text";
609
- if (m.includes("react")) return "react";
610
- if (m.includes("html")) return "html";
611
- if (m.includes("javascript")) return "js";
612
- if (m.includes("css")) return "css";
613
- if (m.includes("svg")) return "svg";
614
- if (m.includes("json")) return "json";
615
- if (m.includes("csv")) return "csv";
616
- if (m.includes("markdown")) return "markdown";
617
- return fallback;
618
- }
619
629
  function parseAparteEventStream(stream) {
620
630
  const decoder = new TextDecoder();
621
631
  let buffer = "";
@@ -668,7 +678,7 @@ class AparteBackendTransport {
668
678
  signal: ctx.signal
669
679
  });
670
680
  if (!response.ok) {
671
- throw new Error(await vendorErrorMessage(response, "Backend HTTP"));
681
+ throw await vendorError(response, "Backend HTTP");
672
682
  }
673
683
  if (request.stream === false) {
674
684
  const json = await response.json();
@@ -1056,7 +1066,7 @@ const escapeAttr = escapeHtml;
1056
1066
  function isChatBoundary(el2) {
1057
1067
  if (!el2) return false;
1058
1068
  const tag = el2.tagName?.toLowerCase();
1059
- return tag === "aparte-chat" || tag === "aparte-chat-component" || el2.hasAttribute?.("data-aparte-chat") === true;
1069
+ return tag === "aparte-chat" || el2.hasAttribute?.("data-aparte-chat") === true;
1060
1070
  }
1061
1071
  function chatBoundaryOf(el2) {
1062
1072
  let node = el2 ?? null;
@@ -1090,7 +1100,6 @@ class AparteConfig {
1090
1100
  _sanitizer = defaultSanitizer;
1091
1101
  _systemPromptTemplate;
1092
1102
  _systemPromptVarsProvider;
1093
- _skeletonProvider;
1094
1103
  _statusRenderer;
1095
1104
  _errorRenderer;
1096
1105
  _attachmentRenderer;
@@ -1367,13 +1376,6 @@ class AparteConfig {
1367
1376
  const resolved = this._systemPromptTemplate.replace(/\{\{([^}]+)\}\}/g, (_, key) => vars[key.trim()] ?? "");
1368
1377
  return resolved.trim() || null;
1369
1378
  }
1370
- /**
1371
- * Set a custom Skeleton generator for loading states
1372
- * @param provider Object implementing AparteSkeletonProvider interface
1373
- */
1374
- setSkeletonProvider(provider) {
1375
- this._skeletonProvider = provider;
1376
- }
1377
1379
  /**
1378
1380
  * Set a custom Icon provider (e.g., Lucide, FontAwesome, Material)
1379
1381
  * @param provider Object implementing AparteIconProvider interface
@@ -1831,16 +1833,6 @@ class AparteConfig {
1831
1833
  }
1832
1834
  return this._defaultHighlightRenderer(code);
1833
1835
  }
1834
- /**
1835
- * Get HTML for a skeleton loader
1836
- * Fallback: Simple CSS-animated box
1837
- */
1838
- getSkeleton(type) {
1839
- if (this._skeletonProvider) {
1840
- return this._skeletonProvider.getSkeleton(type);
1841
- }
1842
- return this._defaultSkeletonRenderer(type);
1843
- }
1844
1836
  // ─────────────────────────────────────────────────────────────────────────
1845
1837
  // Tool Registry
1846
1838
  // ─────────────────────────────────────────────────────────────────────────
@@ -2087,7 +2079,6 @@ class AparteConfig {
2087
2079
  this._highlightProvider = void 0;
2088
2080
  this._systemPromptTemplate = void 0;
2089
2081
  this._systemPromptVarsProvider = void 0;
2090
- this._skeletonProvider = void 0;
2091
2082
  this._statusRenderer = void 0;
2092
2083
  this._errorRenderer = void 0;
2093
2084
  this._attachmentRenderer = void 0;
@@ -2128,16 +2119,6 @@ class AparteConfig {
2128
2119
  _defaultHighlightRenderer(code) {
2129
2120
  return `<pre><code>${escapeHtml(code)}</code></pre>`;
2130
2121
  }
2131
- /**
2132
- * ONE owner. This used to hold a second, hand-written copy of
2133
- * `APARTE_DEFAULT_SKELETON_FALLBACKS`, and the two had already drifted — `message`
2134
- * said "Loading message…" here and "Loading…" there, `text` the reverse. A duplicate
2135
- * that disagrees with its original is worse than either: whichever one you read, you
2136
- * cannot tell which one ships.
2137
- */
2138
- _defaultSkeletonRenderer(type) {
2139
- return APARTE_DEFAULT_SKELETON_FALLBACKS[type] ?? APARTE_DEFAULT_SKELETON_FALLBACKS.text;
2140
- }
2141
2122
  }
2142
2123
  const GLOBAL_CONFIG_KEY = Symbol.for("aparte.globalConfig.1");
2143
2124
  function getGlobalConfig() {
@@ -2475,7 +2456,7 @@ function stateBadge(segment) {
2475
2456
  if (status === "pending") return word("toolRunning");
2476
2457
  if (status === "resolved") return `${cfg.getIcon("check")}${word("toolCompleted")}`;
2477
2458
  if (status === "rejected") return `${cfg.getIcon("close")}${word("toolRejected")}`;
2478
- if (status === "aborted") return `${cfg.getIcon("close")}${word("toolStopped")}`;
2459
+ if (status === "aborted") return `${cfg.getIcon("stop")}${word("toolStopped")}`;
2479
2460
  return "";
2480
2461
  }
2481
2462
  function setPart(element, part, text) {
@@ -2539,6 +2520,7 @@ const toolCallRenderer = {
2539
2520
  const toolCallId = segment.toolCall?.id ?? "";
2540
2521
  const customRenderer = contextConfig().getToolRenderer(segment.toolCall?.name);
2541
2522
  if (customRenderer) {
2523
+ injectToolRendererStyles(segment.toolCall?.name ?? "", customRenderer);
2542
2524
  const out = customRenderer.render(segment);
2543
2525
  if (out) return out;
2544
2526
  }
@@ -2635,44 +2617,6 @@ const toolCallRenderer = {
2635
2617
  // has no other way onto the page.
2636
2618
  getStyles: () => ""
2637
2619
  };
2638
- const waitingLabel = () => contextConfig().t("generating");
2639
- const pipelineWaitingRenderer = {
2640
- type: "pipeline-waiting",
2641
- render: (segment) => {
2642
- return `
2643
- <div class="aparte-segment aparte-segment-pipeline-waiting" data-segment-id="${escapeHtml(segment.id)}" aria-label="${escapeAttr(waitingLabel())}" role="status">
2644
- <span class="aparte-dot aparte-pw-dot"></span>
2645
- <span class="aparte-dot aparte-pw-dot"></span>
2646
- <span class="aparte-dot aparte-pw-dot"></span>
2647
- </div>`;
2648
- },
2649
- update: () => {
2650
- },
2651
- /**
2652
- * Three dots and an accessible name. The dots are CSS; the name is the whole
2653
- * content as far as a screen reader is concerned, and it was hardcoded English in
2654
- * every locale — the one string in this library where being untranslated is
2655
- * invisible to everyone who can see the screen.
2656
- */
2657
- relabel: (element) => {
2658
- element.setAttribute("aria-label", waitingLabel());
2659
- },
2660
- setup: (el2) => {
2661
- const parent = el2.parentElement;
2662
- if (!parent) return;
2663
- const observer = new MutationObserver(() => {
2664
- if (el2.nextElementSibling) {
2665
- observer.disconnect();
2666
- el2.remove();
2667
- }
2668
- });
2669
- observer.observe(parent, { childList: true });
2670
- },
2671
- // CSS lives in styles/segment/ — see that folder for why a
2672
- // built-in's rules belong there. `getStyles` stays for a CONSUMER's renderer, which
2673
- // has no other way onto the page.
2674
- getStyles: () => ""
2675
- };
2676
2620
  function stripCodeFences(content) {
2677
2621
  let s = content;
2678
2622
  if (s.startsWith("```") || s.startsWith("~~~")) {
@@ -3440,6 +3384,17 @@ function injectRendererStyles(config = contextConfig()) {
3440
3384
  }
3441
3385
  if (added) styleElement.textContent = Array.from(injectedRendererStyles).join("\n");
3442
3386
  }
3387
+ function injectToolRendererStyles(toolName, renderer) {
3388
+ if (typeof document === "undefined") return;
3389
+ const css = renderer.getStyles?.();
3390
+ if (!css) return;
3391
+ const id = `aparte-tool-renderer-${toolName}`;
3392
+ if (document.getElementById(id)) return;
3393
+ const el2 = document.createElement("style");
3394
+ el2.id = id;
3395
+ el2.textContent = css;
3396
+ document.head.appendChild(el2);
3397
+ }
3443
3398
  function registerDefaultRenderers(config = contextConfig()) {
3444
3399
  const reg = registryFor(config);
3445
3400
  reg.defaultsInstalled = true;
@@ -3455,8 +3410,7 @@ const DEFAULT_RENDERERS = [
3455
3410
  codeRenderer,
3456
3411
  errorRenderer,
3457
3412
  toolCallRenderer,
3458
- artifactRenderer,
3459
- pipelineWaitingRenderer
3413
+ artifactRenderer
3460
3414
  ];
3461
3415
  const OPEN_TOOL_STATUSES = /* @__PURE__ */ new Set(["pending", "awaiting-approval"]);
3462
3416
  const ACTIVITY_KEYS = ["content", "output", "result"];
@@ -4416,7 +4370,7 @@ class AparteChatHost {
4416
4370
  this.binding.setMessages(next);
4417
4371
  this.binding.onMessagesChange?.(next);
4418
4372
  }
4419
- /** Remove a transient segment (e.g. pipeline-waiting indicator). */
4373
+ /** Remove a transient segment (a status row the host added itself). */
4420
4374
  removeSegment(segmentId) {
4421
4375
  this._flushStreamState();
4422
4376
  this._lastBubble()?.removeSegment?.(segmentId);
@@ -4595,7 +4549,7 @@ class AparteChatHost {
4595
4549
  this._bufferToken(messageId, res.value);
4596
4550
  }
4597
4551
  this._flushStreamState();
4598
- if (!ac.signal.aborted) this._vp()?.completeMessage?.(messageId);
4552
+ this._vp()?.completeMessage?.(messageId);
4599
4553
  this._setStreamingId(null);
4600
4554
  } catch (err) {
4601
4555
  this._flushStreamState();
@@ -4852,176 +4806,6 @@ function dispatchArtifactLifecycle(target, messageId, segment, progress, isFinal
4852
4806
  }));
4853
4807
  }
4854
4808
  }
4855
- const INLINE_MAX_LINES = 15;
4856
- const XML_OPEN_TAG = "<artifact";
4857
- function partialXmlOpenTagLength(text) {
4858
- const max = Math.min(text.length, XML_OPEN_TAG.length - 1);
4859
- for (let k = max; k > 0; k--) {
4860
- if (text.endsWith(XML_OPEN_TAG.slice(0, k))) return k;
4861
- }
4862
- return 0;
4863
- }
4864
- function feedXmlArtifactDelta(delta, xml, ctx) {
4865
- const { targetElement, messageId, textParser, streamingSegmentIds, artifactProgress, artifactXmlHint } = ctx;
4866
- let remaining = delta;
4867
- const emitChatText = (text, syncActive = true) => {
4868
- if (!text) return;
4869
- const r = textParser.parse(text);
4870
- for (const seg of r.segments) {
4871
- if (!streamingSegmentIds.has(seg.id)) {
4872
- targetElement.addSegment?.(seg);
4873
- streamingSegmentIds.add(seg.id);
4874
- } else if ("content" in seg) {
4875
- targetElement.updateSegment?.(seg.id, segmentContentUpdate(seg));
4876
- }
4877
- }
4878
- const active = textParser.getState().activeSegment;
4879
- if (syncActive && active) {
4880
- if (!streamingSegmentIds.has(active.id)) {
4881
- targetElement.addSegment?.(active);
4882
- streamingSegmentIds.add(active.id);
4883
- } else {
4884
- targetElement.updateSegment?.(active.id, segmentContentUpdate(active));
4885
- }
4886
- } else if (!r.segments.length && !active) ;
4887
- };
4888
- while (remaining.length > 0) {
4889
- if (xml.state === "normal") {
4890
- const tagStart = remaining.indexOf(XML_OPEN_TAG);
4891
- if (tagStart === -1) {
4892
- const held = partialXmlOpenTagLength(remaining);
4893
- if (held > 0) {
4894
- emitChatText(remaining.slice(0, remaining.length - held), false);
4895
- xml.scanBuf = remaining.slice(remaining.length - held);
4896
- xml.state = "scanning";
4897
- } else {
4898
- emitChatText(remaining);
4899
- }
4900
- remaining = "";
4901
- } else {
4902
- emitChatText(remaining.slice(0, tagStart), false);
4903
- xml.scanBuf = "";
4904
- remaining = remaining.slice(tagStart);
4905
- xml.state = "scanning";
4906
- }
4907
- } else if (xml.state === "scanning") {
4908
- xml.scanBuf += remaining;
4909
- remaining = "";
4910
- const cmp = Math.min(xml.scanBuf.length, XML_OPEN_TAG.length);
4911
- if (xml.scanBuf.slice(0, cmp) !== XML_OPEN_TAG.slice(0, cmp)) {
4912
- emitChatText(xml.scanBuf);
4913
- xml.scanBuf = "";
4914
- xml.state = "normal";
4915
- continue;
4916
- }
4917
- const gtIdx = xml.scanBuf.indexOf(">");
4918
- if (gtIdx !== -1) {
4919
- const tag = xml.scanBuf.slice(0, gtIdx + 1);
4920
- const mimeMatch = /mimeType=['"]([^'"]+)['"]/.exec(tag);
4921
- const titleMatch = /title=['"]([^'"]+)['"]/.exec(tag);
4922
- xml.mime = mimeMatch?.[1] ?? artifactXmlHint.mimeType;
4923
- xml.title = titleMatch?.[1] ?? artifactXmlHint.kind;
4924
- xml.kind = deriveArtifactKind(xml.mime, artifactXmlHint.kind);
4925
- xml.segId = `artifact-xml-${uuid()}`;
4926
- xml.content = "";
4927
- const openSeg = {
4928
- id: xml.segId,
4929
- type: "artifact",
4930
- mimeType: xml.mime,
4931
- artifactType: xml.kind,
4932
- title: xml.title,
4933
- content: ""
4934
- };
4935
- targetElement.addSegment?.(openSeg);
4936
- streamingSegmentIds.add(xml.segId);
4937
- dispatchArtifactLifecycle(targetElement, messageId, openSeg, artifactProgress, false);
4938
- xml.state = "in-artifact";
4939
- remaining = xml.scanBuf.slice(gtIdx + 1);
4940
- xml.scanBuf = "";
4941
- }
4942
- } else {
4943
- const CLOSE = "</artifact>";
4944
- const combined = xml.closeBuf + remaining;
4945
- const closeIdx = combined.indexOf(CLOSE);
4946
- if (closeIdx !== -1) {
4947
- xml.content += combined.slice(0, closeIdx);
4948
- const lineCount = xml.content.split("\n").length;
4949
- const isInline = lineCount < INLINE_MAX_LINES;
4950
- const finalSeg = {
4951
- id: xml.segId,
4952
- mimeType: xml.mime,
4953
- artifactType: xml.kind,
4954
- title: xml.title,
4955
- content: xml.content
4956
- };
4957
- targetElement.updateSegment?.(xml.segId, { content: xml.content, inline: isInline });
4958
- dispatchArtifactLifecycle(targetElement, messageId, finalSeg, artifactProgress, true);
4959
- xml.state = "normal";
4960
- xml.closeBuf = "";
4961
- remaining = combined.slice(closeIdx + CLOSE.length);
4962
- } else {
4963
- const safeLen = Math.max(0, combined.length - CLOSE.length + 1);
4964
- const safe = combined.slice(0, safeLen);
4965
- xml.content += safe;
4966
- xml.closeBuf = combined.slice(safeLen);
4967
- remaining = "";
4968
- if (xml.segId) {
4969
- targetElement.updateSegment?.(xml.segId, { content: xml.content });
4970
- dispatchArtifactLifecycle(targetElement, messageId, {
4971
- id: xml.segId,
4972
- mimeType: xml.mime,
4973
- artifactType: xml.kind,
4974
- title: xml.title,
4975
- content: xml.content
4976
- }, artifactProgress, false);
4977
- }
4978
- }
4979
- }
4980
- }
4981
- }
4982
- function finalizeXmlArtifact(xml, ctx) {
4983
- const { targetElement, messageId, artifactProgress, artifactXmlHint } = ctx;
4984
- if (xml.state === "scanning") {
4985
- const held = xml.scanBuf;
4986
- xml.scanBuf = "";
4987
- xml.state = "normal";
4988
- if (held) {
4989
- const r = ctx.textParser.parse(held);
4990
- for (const seg of r.segments) {
4991
- if (!ctx.streamingSegmentIds.has(seg.id)) {
4992
- targetElement.addSegment?.(seg);
4993
- ctx.streamingSegmentIds.add(seg.id);
4994
- } else if ("content" in seg) {
4995
- targetElement.updateSegment?.(seg.id, segmentContentUpdate(seg));
4996
- }
4997
- }
4998
- const active = ctx.textParser.getState().activeSegment;
4999
- if (active) {
5000
- if (!ctx.streamingSegmentIds.has(active.id)) {
5001
- targetElement.addSegment?.(active);
5002
- ctx.streamingSegmentIds.add(active.id);
5003
- } else {
5004
- targetElement.updateSegment?.(active.id, segmentContentUpdate(active));
5005
- }
5006
- }
5007
- }
5008
- return;
5009
- }
5010
- if (artifactXmlHint && xml.state === "in-artifact" && xml.segId) {
5011
- xml.content += xml.closeBuf;
5012
- const lineCount = xml.content.split("\n").length;
5013
- const isInline = lineCount < INLINE_MAX_LINES;
5014
- targetElement.updateSegment?.(xml.segId, { content: xml.content, inline: isInline });
5015
- dispatchArtifactLifecycle(targetElement, messageId, {
5016
- id: xml.segId,
5017
- mimeType: xml.mime,
5018
- artifactType: xml.kind,
5019
- title: xml.title,
5020
- content: xml.content
5021
- }, artifactProgress, true);
5022
- console.warn("[AparteClient] XML artifact finalized without closing tag — content may be partial.");
5023
- }
5024
- }
5025
4809
  function createStreamAdapter(opts) {
5026
4810
  const { target, config, messageId, artifactHint } = opts;
5027
4811
  let parser = new AparteStreamParser();
@@ -5031,7 +4815,6 @@ function createStreamAdapter(opts) {
5031
4815
  let thinkingCollapsed = false;
5032
4816
  let artifactProgress = /* @__PURE__ */ new Map();
5033
4817
  let artifactPromoted = false;
5034
- let artifactMeta = /* @__PURE__ */ new Map();
5035
4818
  return (e) => {
5036
4819
  switch (e.type) {
5037
4820
  case "run-start":
@@ -5045,7 +4828,6 @@ function createStreamAdapter(opts) {
5045
4828
  thinkingCollapsed = false;
5046
4829
  artifactProgress = /* @__PURE__ */ new Map();
5047
4830
  artifactPromoted = false;
5048
- artifactMeta = /* @__PURE__ */ new Map();
5049
4831
  break;
5050
4832
  case "thinking-delta": {
5051
4833
  thinkingContent += e.delta;
@@ -5070,18 +4852,6 @@ function createStreamAdapter(opts) {
5070
4852
  target.updateSegment?.(thinkingId, { collapsed: true, isStreaming: false });
5071
4853
  thinkingCollapsed = true;
5072
4854
  }
5073
- if (e.reduced) {
5074
- const r = parser.parse(e.delta);
5075
- for (const segment of r.segments) {
5076
- if (!streaming.has(segment.id)) {
5077
- target.addSegment?.(segment);
5078
- streaming.add(segment.id);
5079
- } else if ("content" in segment) {
5080
- target.updateSegment?.(segment.id, segmentContentUpdate(segment));
5081
- }
5082
- }
5083
- break;
5084
- }
5085
4855
  const result = parser.parse(e.delta);
5086
4856
  for (let segment of result.segments) {
5087
4857
  if (artifactHint && !artifactPromoted && segment.type === "code") {
@@ -5154,33 +4924,6 @@ function createStreamAdapter(opts) {
5154
4924
  }
5155
4925
  break;
5156
4926
  }
5157
- case "artifact-open": {
5158
- const seg = {
5159
- id: e.id,
5160
- type: "artifact",
5161
- mimeType: e.mimeType,
5162
- artifactType: e.kind,
5163
- title: e.title,
5164
- content: ""
5165
- };
5166
- target.addSegment?.(seg);
5167
- streaming.add(e.id);
5168
- artifactMeta.set(e.id, { mimeType: e.mimeType, artifactType: e.kind, title: e.title });
5169
- dispatchArtifactLifecycle(target, messageId, seg, artifactProgress, false);
5170
- break;
5171
- }
5172
- case "artifact-chunk": {
5173
- const meta = artifactMeta.get(e.id);
5174
- target.updateSegment?.(e.id, { content: e.content });
5175
- dispatchArtifactLifecycle(target, messageId, { id: e.id, content: e.content, ...meta }, artifactProgress, false);
5176
- break;
5177
- }
5178
- case "artifact-close": {
5179
- const meta = artifactMeta.get(e.id);
5180
- target.updateSegment?.(e.id, { content: e.content, inline: e.inline });
5181
- dispatchArtifactLifecycle(target, messageId, { id: e.id, content: e.content, ...meta }, artifactProgress, true);
5182
- break;
5183
- }
5184
4927
  case "artifact-ready": {
5185
4928
  const seg = {
5186
4929
  id: e.id,
@@ -5203,18 +4946,7 @@ function createStreamAdapter(opts) {
5203
4946
  };
5204
4947
  const renderer = config.getToolRenderer(e.name);
5205
4948
  if (renderer) {
5206
- if (renderer.getStyles) {
5207
- const styles = renderer.getStyles();
5208
- if (styles) {
5209
- const styleId = `aparte-tool-renderer-${e.name}`;
5210
- if (!document.getElementById(styleId)) {
5211
- const styleEl = document.createElement("style");
5212
- styleEl.id = styleId;
5213
- styleEl.textContent = styles;
5214
- document.head.appendChild(styleEl);
5215
- }
5216
- }
5217
- }
4949
+ injectToolRendererStyles(e.name, renderer);
5218
4950
  const html = renderer.render(toolSeg);
5219
4951
  if (html) target.addSegment?.(toolSeg);
5220
4952
  } else {
@@ -5250,9 +4982,6 @@ function createStreamAdapter(opts) {
5250
4982
  target.updateSegment?.(`tool-${e.toolCallId}`, { status: "aborted" });
5251
4983
  }
5252
4984
  break;
5253
- case "phase-advance":
5254
- target.addSegment?.({ id: `pw-${uuid()}`, type: "pipeline-waiting" });
5255
- break;
5256
4985
  case "run-aborted":
5257
4986
  dispatchLifecycleEvent(target, "aparte-message-aborted", { messageId });
5258
4987
  break;
@@ -5790,20 +5519,6 @@ function buildApprovalPanel(message, options, onChange) {
5790
5519
  function requestUserInput(request) {
5791
5520
  return resolveConfig(request.target ?? null).requestUserInput(request);
5792
5521
  }
5793
- const DEFAULT_TOOL_HANDLER_TIMEOUT_MS = 5 * 60 * 1e3;
5794
- function withToolTimeout(work, controller, timeoutMs) {
5795
- let timer;
5796
- const timedOut = new Promise((_resolve, reject) => {
5797
- timer = setTimeout(() => {
5798
- controller.abort();
5799
- reject(Object.assign(new Error(`tool handler exceeded ${timeoutMs}ms`), { name: "AbortError" }));
5800
- }, timeoutMs);
5801
- });
5802
- return {
5803
- raced: Promise.race([work(controller.signal), timedOut]),
5804
- done: () => clearTimeout(timer)
5805
- };
5806
- }
5807
5522
  class AparteClient {
5808
5523
  _boundHandler = null;
5809
5524
  _boundAbortHandler = null;
@@ -5838,6 +5553,8 @@ class AparteClient {
5838
5553
  }
5839
5554
  /** Aborts the in-flight vendor/transport fetch when the user stops a stream. */
5840
5555
  _streamController = null;
5556
+ /** Aborts an in-flight `compact()` summarisation — its own slot, see `compact`. */
5557
+ _compactController = null;
5841
5558
  options;
5842
5559
  /** Config read by this client — an instance config, or the global default. */
5843
5560
  _config;
@@ -5963,6 +5680,7 @@ class AparteClient {
5963
5680
  abort() {
5964
5681
  this._isAborted = true;
5965
5682
  this._streamController?.abort();
5683
+ this._compactController?.abort();
5966
5684
  for (const controller of this._activeToolControllers) {
5967
5685
  controller.abort();
5968
5686
  }
@@ -6070,6 +5788,7 @@ class AparteClient {
6070
5788
  }
6071
5789
  if (this._isAborted) {
6072
5790
  dispatchLifecycleEvent(targetElement, "aparte-message-aborted", { messageId });
5791
+ this._updateMessage(targetElement, messageId, { status: "completed" });
6073
5792
  return;
6074
5793
  }
6075
5794
  dispatchLifecycleEvent(targetElement, "aparte-message-start", { messageId, role: "assistant" });
@@ -6149,7 +5868,7 @@ class AparteClient {
6149
5868
  stream: false
6150
5869
  };
6151
5870
  const compactController = new AbortController();
6152
- this._streamController = compactController;
5871
+ this._compactController = compactController;
6153
5872
  const response = await this._config.getTransport().chat(
6154
5873
  provider,
6155
5874
  summarizeRequest,
@@ -6180,7 +5899,11 @@ class AparteClient {
6180
5899
  target.appendMessage?.({
6181
5900
  id: uuid(),
6182
5901
  role: "assistant",
6183
- content: `📝 **Conversation summary**
5902
+ // Through the locale, like every string the user reads — this was a
5903
+ // hardcoded English title (with an emoji) in an otherwise localised
5904
+ // transcript. The engine compactor's `summaryLabel` is a separate,
5905
+ // per-call knob; this is the UI's own title.
5906
+ content: `**${this._config.t("compactionSummaryTitle")}**
6184
5907
 
6185
5908
  ${summary}`,
6186
5909
  timestamp: Date.now(),
@@ -6634,67 +6357,6 @@ ${content}
6634
6357
  );
6635
6358
  return parts.filter((p) => p !== null);
6636
6359
  }
6637
- /**
6638
- * Turn-1 forced tool call. When `toolChoice = { name, input }`
6639
- * (orchestrator-driven), execute the handler directly instead of consulting
6640
- * the LLM, render the tool segment, inject the result as `tool_result`, and
6641
- * strip `toolChoice` for the follow-up turn. Returns the (possibly-updated)
6642
- * request and whether the loop should skip to the next turn (handler missing
6643
- * or aborted). Extracted from `_streamLoop`. `messages` is mutated in place.
6644
- */
6645
- async _maybeRunSyntheticTool(baseRequest, turns, messages, targetElement) {
6646
- const toolChoice = baseRequest.toolChoice;
6647
- if (!(turns === 1 && toolChoice && typeof toolChoice === "object" && toolChoice.input !== void 0)) {
6648
- return { baseRequest, skip: false };
6649
- }
6650
- const syntheticId = uuid();
6651
- const syntheticCall = { id: syntheticId, name: toolChoice.name, input: toolChoice.input };
6652
- const toolSeg = {
6653
- id: `tool-${syntheticId}`,
6654
- type: "tool_call",
6655
- toolCall: syntheticCall,
6656
- status: "pending"
6657
- };
6658
- const toolRenderer = this._config.getToolRenderer(toolChoice.name);
6659
- if (toolRenderer) {
6660
- const html = toolRenderer.render(toolSeg);
6661
- if (html) targetElement.addSegment?.(toolSeg);
6662
- } else {
6663
- targetElement.addSegment?.(toolSeg);
6664
- }
6665
- const handler = this._config.getToolHandler(toolChoice.name);
6666
- if (!handler) {
6667
- console.warn(`[AparteClient] No handler for synthetic tool "${toolChoice.name}"`);
6668
- targetElement.updateSegment?.(toolSeg.id, { status: "aborted" });
6669
- return { baseRequest, skip: true };
6670
- }
6671
- const controller = new AbortController();
6672
- this._activeToolControllers.add(controller);
6673
- const { raced, done: clearToolTimeout } = withToolTimeout(
6674
- (sig) => handler(syntheticCall, sig, {
6675
- target: targetElement,
6676
- config: this._config
6677
- }),
6678
- controller,
6679
- this.options.toolTimeoutMs ?? DEFAULT_TOOL_HANDLER_TIMEOUT_MS
6680
- );
6681
- try {
6682
- const result = await raced;
6683
- targetElement.updateSegment?.(toolSeg.id, { status: "resolved", result: result.content });
6684
- messages.push({ role: "tool_call", content: "", toolCalls: [syntheticCall] });
6685
- messages.push({ role: "tool_result", content: result.content, toolCallId: syntheticId });
6686
- return { baseRequest: { ...baseRequest, toolChoice: "none", tools: void 0 }, skip: false };
6687
- } catch (err) {
6688
- if (err?.name === "AbortError") {
6689
- targetElement.updateSegment?.(toolSeg.id, { status: "aborted" });
6690
- return { baseRequest, skip: true };
6691
- }
6692
- throw err;
6693
- } finally {
6694
- clearToolTimeout();
6695
- this._activeToolControllers.delete(controller);
6696
- }
6697
- }
6698
6360
  /**
6699
6361
  * What the loop does after one tool call. THREE outcomes, because a boolean
6700
6362
  * conflated two questions that a refusal answers differently.
@@ -6707,536 +6369,41 @@ ${content}
6707
6369
  * - `'halt'` — the run is over: an abort, a turn limit, a missing handler.
6708
6370
  */
6709
6371
  /**
6710
- * Handle one `tool_use` stream event from {@link _streamLoop}: the built-in
6711
- * `create_artifact`, per-tool renderer selection, the human-in-the-loop
6712
- * approval gate, and running the registered handler (timeout / abort).
6713
- * Mutates the shared `messages` / `toolCallsThisTurn` history in place and
6714
- * returns what the loop should do next.
6715
- */
6716
- async _handleToolUseEvent(event, ctx) {
6717
- const { targetElement, messageId, messages, toolCallsThisTurn, precedingText, artifactProgress, turns, globalMaxTurns } = ctx;
6718
- let flow = "continue";
6719
- toolCallsThisTurn.push({ id: event.id, name: event.name, input: event.input });
6720
- if (event.name === "create_artifact") {
6721
- const input = event.input;
6722
- const mimeType = input.mimeType ?? "text/plain";
6723
- const kind = deriveArtifactKind(mimeType, "text");
6724
- const artifactSeg = {
6725
- id: `artifact-${event.id}`,
6726
- type: "artifact",
6727
- mimeType,
6728
- artifactType: kind,
6729
- title: input.title ?? kind,
6730
- content: input.content ?? ""
6731
- };
6732
- targetElement.addSegment?.(artifactSeg);
6733
- dispatchArtifactLifecycle(targetElement, messageId, artifactSeg, artifactProgress, true);
6734
- messages.push({
6735
- role: "tool_call",
6736
- content: "",
6737
- toolCalls: [{ id: event.id, name: event.name, input: event.input }]
6738
- });
6739
- messages.push({
6740
- role: "tool_result",
6741
- content: "Artifact created successfully.",
6742
- toolCallId: event.id
6743
- });
6744
- return { flow: "continue" };
6745
- }
6746
- const toolSeg = {
6747
- id: `tool-${event.id}`,
6748
- type: "tool_call",
6749
- toolCall: { id: event.id, name: event.name, input: event.input },
6750
- status: "pending"
6751
- };
6752
- const toolRenderer = this._config.getToolRenderer(event.name);
6753
- if (toolRenderer) {
6754
- if (toolRenderer.getStyles) {
6755
- const styles = toolRenderer.getStyles();
6756
- if (styles) {
6757
- const styleId = `aparte-tool-renderer-${event.name}`;
6758
- if (!document.getElementById(styleId)) {
6759
- const el2 = document.createElement("style");
6760
- el2.id = styleId;
6761
- el2.textContent = styles;
6762
- document.head.appendChild(el2);
6763
- }
6764
- }
6765
- }
6766
- const html = toolRenderer.render(toolSeg);
6767
- if (html) {
6768
- targetElement.addSegment?.(toolSeg);
6769
- }
6770
- } else {
6771
- targetElement.addSegment?.(toolSeg);
6772
- }
6773
- const toolDef = this._config.getTools().find((t) => t.name === event.name);
6774
- const effectiveMaxTurns = toolDef?.maxTurns ?? globalMaxTurns;
6775
- if (turns >= effectiveMaxTurns) {
6776
- console.warn(`[AparteClient] Tool "${event.name}" maxTurns (${effectiveMaxTurns}) reached.`);
6777
- targetElement.updateSegment?.(toolSeg.id, { status: "aborted" });
6778
- return { flow: "halt" };
6779
- }
6780
- const handler = this._config.getToolHandler(event.name);
6781
- if (handler) {
6782
- let effectiveInput = event.input;
6783
- if (toolDef?.needsApproval) {
6784
- const approvalController = new AbortController();
6785
- this._activeToolControllers.add(approvalController);
6786
- targetElement.updateSegment?.(toolSeg.id, { status: "awaiting-approval" });
6787
- dispatchLifecycleEvent(targetElement, "aparte-tool-approval-request", {
6788
- toolCallId: event.id,
6789
- toolName: event.name,
6790
- input: event.input
6791
- });
6792
- let decision;
6793
- const resolveApproval = this.options.approvalResolver ?? ((call, sig) => this._askForApproval(call, sig, targetElement));
6794
- try {
6795
- decision = await resolveApproval(event, approvalController.signal);
6796
- } catch (err) {
6797
- if (err?.name === "AbortError") {
6798
- targetElement.updateSegment?.(toolSeg.id, { status: "aborted" });
6799
- return { flow: "halt" };
6800
- }
6801
- throw err;
6802
- } finally {
6803
- this._activeToolControllers.delete(approvalController);
6804
- }
6805
- if (approvalController.signal.aborted) {
6806
- targetElement.updateSegment?.(toolSeg.id, { status: "aborted" });
6807
- return { flow: "halt" };
6808
- }
6809
- if (!decision.approved) {
6810
- const rejection = decision.instruction ? `The user rejected this tool call and said: ${decision.instruction}` : "Tool execution was rejected by the user.";
6811
- targetElement.updateSegment?.(toolSeg.id, { status: "rejected", result: rejection });
6812
- const existingToolCallMsg = messages.find(
6813
- (m) => m.role === "tool_call" && m.toolCalls?.some((tc) => tc.id === event.id)
6814
- );
6815
- if (!existingToolCallMsg) {
6816
- messages.push({
6817
- role: "tool_call",
6818
- content: "",
6819
- toolCalls: toolCallsThisTurn,
6820
- precedingText: precedingText.trim() || void 0
6821
- });
6822
- }
6823
- messages.push({ role: "tool_result", content: rejection, toolCallId: event.id });
6824
- return { flow: "respond" };
6825
- }
6826
- if (decision.payload && typeof decision.payload === "object" && !Array.isArray(decision.payload)) {
6827
- effectiveInput = { ...event.input, ...decision.payload };
6828
- }
6829
- targetElement.updateSegment?.(toolSeg.id, { status: "pending" });
6830
- }
6831
- const controller = new AbortController();
6832
- this._activeToolControllers.add(controller);
6833
- const { raced, done: clearToolTimeout } = withToolTimeout(
6834
- (sig) => handler(
6835
- { id: event.id, name: event.name, input: effectiveInput },
6836
- sig,
6837
- { target: targetElement, config: this._config }
6838
- ),
6839
- controller,
6840
- this.options.toolTimeoutMs ?? DEFAULT_TOOL_HANDLER_TIMEOUT_MS
6841
- );
6842
- try {
6843
- const result = await raced;
6844
- targetElement.updateSegment?.(toolSeg.id, { status: "resolved", result: result.content });
6845
- const existingToolCallMsg = messages.find(
6846
- (m) => m.role === "tool_call" && m.toolCalls?.some((tc) => tc.id === event.id)
6847
- );
6848
- if (!existingToolCallMsg) {
6849
- messages.push({
6850
- role: "tool_call",
6851
- content: "",
6852
- toolCalls: toolCallsThisTurn,
6853
- precedingText: precedingText.trim() || void 0
6854
- });
6855
- }
6856
- messages.push({
6857
- role: "tool_result",
6858
- content: result.content,
6859
- toolCallId: event.id
6860
- });
6861
- } catch (err) {
6862
- if (err?.name === "AbortError") {
6863
- targetElement.updateSegment?.(toolSeg.id, { status: "aborted" });
6864
- flow = "halt";
6865
- } else {
6866
- throw err;
6867
- }
6868
- } finally {
6869
- clearToolTimeout();
6870
- this._activeToolControllers.delete(controller);
6871
- }
6872
- } else {
6873
- console.warn(`[AparteClient] No handler registered for tool "${event.name}"`);
6874
- targetElement.updateSegment?.(toolSeg.id, { status: "aborted" });
6875
- flow = "halt";
6876
- }
6877
- return { flow };
6878
- }
6879
- /**
6880
- * Stream loop: runs one provider.chat() call and repeats if a tool was called.
6881
- * Maintains a running messages array to inject tool_call / tool_result turns.
6372
+ * The turn's agent loop the engine's `runStreamAgent` rendered through the core
6373
+ * adapter, or the runner a host injected — after the fetch-level abort bookkeeping.
6882
6374
  */
6883
6375
  async _streamLoop(targetElement, messageId, provider, baseRequest, authConfig) {
6884
6376
  this._streamController?.abort();
6885
6377
  const streamController = new AbortController();
6886
6378
  this._streamController = streamController;
6887
- if (this.options.streamRunner) {
6888
- return this._runViaStreamRunner(this.options.streamRunner, targetElement, messageId, provider, baseRequest, authConfig, streamController);
6889
- }
6890
- const messages = [...baseRequest.messages];
6891
- let continueLoop = true;
6892
- let turns = 0;
6893
- const globalMaxTurns = this.options.maxTurns ?? 10;
6894
- let lastUsage;
6895
- const pipeline = baseRequest._meta?.pipeline;
6896
- let pipelineIndex = 0;
6897
- this._updateMessage(targetElement, messageId, { status: "streaming" });
6898
- for (const seg of baseRequest._meta?.prefixSegments ?? []) {
6899
- targetElement.addSegment?.(seg);
6900
- }
6901
- while (continueLoop) {
6902
- let endTurnEarly = false;
6903
- if (this._isAborted) {
6904
- dispatchLifecycleEvent(targetElement, "aparte-message-aborted", { messageId });
6905
- break;
6906
- }
6907
- turns++;
6908
- if (turns > globalMaxTurns) {
6909
- console.warn(`[AparteClient] maxTurns (${globalMaxTurns}) exceeded — stopping loop.`);
6910
- targetElement.addSegment?.({
6911
- id: `max-turns-${uuid()}`,
6912
- type: "error",
6913
- content: `Stopped after ${globalMaxTurns} tool calls to prevent an infinite loop.`,
6914
- details: "MAX_TURNS_EXCEEDED"
6915
- });
6916
- break;
6917
- }
6918
- const synthetic = await this._maybeRunSyntheticTool(baseRequest, turns, messages, targetElement);
6919
- baseRequest = synthetic.baseRequest;
6920
- if (synthetic.skip) {
6921
- continueLoop = false;
6922
- continue;
6923
- }
6924
- let phaseMessages = messages;
6925
- let phaseMeta = baseRequest._meta;
6926
- if (pipeline && pipelineIndex < pipeline.length) {
6927
- const phase = pipeline[pipelineIndex];
6928
- phaseMessages = [{ role: "system", content: phase.system }, ...messages];
6929
- if (phase.mode === "artifact") {
6930
- phaseMeta = { ...phaseMeta, artifactRaw: { mimeType: phase.mimeType, kind: phase.kind } };
6931
- } else {
6932
- const { artifactRaw: _dropped, pipeline: _p, ...restMeta } = phaseMeta ?? {};
6933
- phaseMeta = restMeta;
6934
- }
6935
- }
6936
- const request = { ...baseRequest, messages: phaseMessages, _meta: phaseMeta };
6937
- const response = await this._config.getTransport().chat(provider, request, authConfig, { providerId: provider.id, signal: streamController.signal });
6938
- if (typeof response === "string") {
6939
- const wholeParser = new AparteStreamParser();
6940
- const parsed = [
6941
- ...wholeParser.parse(response).segments,
6942
- ...wholeParser.finalize()
6943
- ];
6944
- const stringHint = baseRequest._meta?.artifactHint;
6945
- if (stringHint) {
6946
- const codeIdx = parsed.findIndex((s) => s.type === "code");
6947
- if (codeIdx !== -1) {
6948
- const codeSeg = parsed[codeIdx];
6949
- parsed[codeIdx] = {
6950
- id: codeSeg.id,
6951
- type: "artifact",
6952
- mimeType: stringHint.mimeType,
6953
- artifactType: stringHint.kind,
6954
- title: codeSeg.filename ?? stringHint.kind,
6955
- content: codeSeg.content
6956
- };
6957
- }
6958
- }
6959
- if (parsed.length > 0) {
6960
- for (const segment of parsed) targetElement.addSegment?.(segment);
6961
- this._updateMessage(targetElement, messageId, { status: "completed" });
6962
- } else {
6963
- this._updateMessage(targetElement, messageId, { content: response, status: "completed" });
6964
- }
6965
- return void 0;
6966
- }
6967
- const reader = response.getReader();
6968
- const textParser = new AparteStreamParser();
6969
- const streamingSegmentIds = /* @__PURE__ */ new Set();
6970
- const artifactProgress = /* @__PURE__ */ new Map();
6971
- let thinkingSegmentId = null;
6972
- let thinkingContent = "";
6973
- let thinkingCollapsed = false;
6974
- const artifactHint = baseRequest._meta?.artifactHint;
6975
- let artifactPromoted = false;
6976
- const artifactRawHint = request._meta?.artifactRaw;
6977
- let rawSegId = null;
6978
- let rawContent = "";
6979
- if (artifactRawHint) {
6980
- rawSegId = `artifact-raw-${uuid()}`;
6981
- const rawSeg = {
6982
- id: rawSegId,
6983
- type: "artifact",
6984
- mimeType: artifactRawHint.mimeType,
6985
- artifactType: artifactRawHint.kind,
6986
- title: artifactRawHint.kind,
6987
- content: ""
6988
- };
6989
- targetElement.addSegment?.(rawSeg);
6990
- streamingSegmentIds.add(rawSegId);
6991
- dispatchArtifactLifecycle(targetElement, messageId, rawSeg, artifactProgress, false);
6992
- }
6993
- const artifactXmlHint = baseRequest._meta?.artifactXml;
6994
- const xmlCtx = {
6995
- state: "normal",
6996
- scanBuf: "",
6997
- closeBuf: "",
6998
- segId: null,
6999
- content: "",
7000
- mime: "",
7001
- kind: "",
7002
- title: ""
7003
- };
7004
- let precedingText = "";
7005
- const toolCallsThisTurn = [];
7006
- const bailOnAbort = () => {
7007
- if (!this._isAborted) return false;
7008
- try {
7009
- void reader.cancel();
7010
- } catch {
7011
- }
7012
- dispatchLifecycleEvent(targetElement, "aparte-message-aborted", { messageId });
7013
- continueLoop = false;
7014
- return true;
7015
- };
7016
- let streamDrained = false;
7017
- try {
7018
- while (true) {
7019
- if (bailOnAbort()) break;
7020
- const { done, value: event } = await reader.read();
7021
- if (bailOnAbort()) break;
7022
- if (done) {
7023
- streamDrained = true;
7024
- break;
7025
- }
7026
- switch (event.type) {
7027
- case "thinking": {
7028
- thinkingContent += event.delta;
7029
- if (!thinkingSegmentId) {
7030
- const seg = {
7031
- id: `think-${uuid()}`,
7032
- type: "thinking",
7033
- content: thinkingContent,
7034
- collapsed: true,
7035
- label: "Thinking"
7036
- };
7037
- thinkingSegmentId = seg.id;
7038
- streamingSegmentIds.add(seg.id);
7039
- targetElement.addSegment?.(seg);
7040
- } else {
7041
- targetElement.updateSegment?.(thinkingSegmentId, { content: thinkingContent });
7042
- }
7043
- break;
7044
- }
7045
- case "text": {
7046
- if (thinkingSegmentId && !thinkingCollapsed) {
7047
- targetElement.updateSegment?.(thinkingSegmentId, { collapsed: true, isStreaming: false });
7048
- thinkingCollapsed = true;
7049
- }
7050
- precedingText += event.delta;
7051
- if (artifactRawHint && rawSegId) {
7052
- rawContent += event.delta;
7053
- targetElement.updateSegment?.(rawSegId, { content: rawContent });
7054
- dispatchArtifactLifecycle(targetElement, messageId, {
7055
- id: rawSegId,
7056
- type: "artifact",
7057
- mimeType: artifactRawHint.mimeType,
7058
- artifactType: artifactRawHint.kind,
7059
- title: artifactRawHint.kind,
7060
- content: rawContent
7061
- }, artifactProgress, false);
7062
- break;
7063
- }
7064
- if (artifactXmlHint) {
7065
- feedXmlArtifactDelta(event.delta, xmlCtx, {
7066
- targetElement,
7067
- messageId,
7068
- textParser,
7069
- streamingSegmentIds,
7070
- artifactProgress,
7071
- artifactXmlHint
7072
- });
7073
- break;
7074
- }
7075
- const result = textParser.parse(event.delta);
7076
- for (let segment of result.segments) {
7077
- if (artifactHint && !artifactPromoted && segment.type === "code") {
7078
- const codeSeg = segment;
7079
- const promoted = {
7080
- id: codeSeg.id,
7081
- type: "artifact",
7082
- mimeType: artifactHint.mimeType,
7083
- artifactType: artifactHint.kind,
7084
- title: codeSeg.filename ?? artifactHint.kind,
7085
- content: codeSeg.content
7086
- };
7087
- segment = promoted;
7088
- artifactPromoted = true;
7089
- }
7090
- if (!streamingSegmentIds.has(segment.id)) {
7091
- targetElement.addSegment?.(segment);
7092
- streamingSegmentIds.add(segment.id);
7093
- } else if ("content" in segment) {
7094
- targetElement.updateSegment?.(segment.id, segmentContentUpdate(segment));
7095
- }
7096
- if (segment.type === "artifact") {
7097
- dispatchArtifactLifecycle(targetElement, messageId, segment, artifactProgress, true);
7098
- }
7099
- }
7100
- const active = textParser.getState().activeSegment;
7101
- if (active) {
7102
- if (!streamingSegmentIds.has(active.id)) {
7103
- targetElement.addSegment?.(active);
7104
- streamingSegmentIds.add(active.id);
7105
- if (active.type === "artifact") {
7106
- dispatchArtifactLifecycle(targetElement, messageId, active, artifactProgress, false);
7107
- }
7108
- } else {
7109
- targetElement.updateSegment?.(active.id, segmentContentUpdate(active));
7110
- if (active.type === "artifact") {
7111
- dispatchArtifactLifecycle(targetElement, messageId, active, artifactProgress, false);
7112
- }
7113
- }
7114
- } else if (result.segments.length === 0) {
7115
- }
7116
- break;
7117
- }
7118
- case "tool_use": {
7119
- const toolResult = await this._handleToolUseEvent(
7120
- { id: event.id, name: event.name, input: event.input },
7121
- { targetElement, messageId, messages, toolCallsThisTurn, precedingText, artifactProgress, turns, globalMaxTurns }
7122
- );
7123
- if (toolResult.flow !== "continue") endTurnEarly = true;
7124
- if (toolResult.flow === "halt") continueLoop = false;
7125
- break;
7126
- }
7127
- case "error":
7128
- throw new Error(event.message);
7129
- case "done":
7130
- if (event.usage) lastUsage = event.usage;
7131
- break;
7132
- default: {
7133
- const exhaustive = event;
7134
- void exhaustive;
7135
- this._warnUnknownStreamEvent(event);
7136
- break;
7137
- }
7138
- }
7139
- if (endTurnEarly) break;
7140
- }
7141
- finalizeXmlArtifact(xmlCtx, { targetElement, messageId, artifactProgress, artifactXmlHint, textParser, streamingSegmentIds });
7142
- const finals = textParser.finalize();
7143
- if (artifactRawHint && rawSegId) {
7144
- const lineCount = rawContent.split("\n").length;
7145
- const isInline = lineCount < 15;
7146
- targetElement.updateSegment?.(rawSegId, { content: rawContent, inline: isInline });
7147
- dispatchArtifactLifecycle(targetElement, messageId, {
7148
- id: rawSegId,
7149
- type: "artifact",
7150
- mimeType: artifactRawHint.mimeType,
7151
- artifactType: artifactRawHint.kind,
7152
- title: artifactRawHint.kind,
7153
- content: rawContent,
7154
- inline: isInline
7155
- }, artifactProgress, true);
7156
- }
7157
- if (artifactHint && !artifactPromoted) {
7158
- const codeIdx = finals.findIndex((s) => s.type === "code");
7159
- if (codeIdx !== -1) {
7160
- const codeSeg = finals[codeIdx];
7161
- const promoted = {
7162
- id: codeSeg.id,
7163
- type: "artifact",
7164
- mimeType: artifactHint.mimeType,
7165
- artifactType: artifactHint.kind,
7166
- title: codeSeg.filename ?? artifactHint.kind,
7167
- content: codeSeg.content
7168
- };
7169
- finals[codeIdx] = promoted;
7170
- artifactPromoted = true;
7171
- if (streamingSegmentIds.has(promoted.id)) {
7172
- targetElement.updateSegment?.(promoted.id, promoted);
7173
- }
7174
- }
7175
- }
7176
- for (const s of finals) {
7177
- if (!streamingSegmentIds.has(s.id)) {
7178
- targetElement.addSegment?.(s);
7179
- } else if ("content" in s) {
7180
- targetElement.updateSegment?.(s.id, segmentContentUpdate(s));
7181
- }
7182
- if (s.type === "artifact") {
7183
- dispatchArtifactLifecycle(targetElement, messageId, s, artifactProgress, true);
7184
- }
7185
- }
7186
- if (toolCallsThisTurn.length === 0) {
7187
- if (pipeline && pipelineIndex < pipeline.length - 1) {
7188
- if (precedingText.trim()) {
7189
- messages.push({ role: "assistant", content: precedingText.trim() });
7190
- }
7191
- pipelineIndex++;
7192
- const pwId = `pw-${uuid()}`;
7193
- targetElement.addSegment?.({ id: pwId, type: "pipeline-waiting" });
7194
- } else {
7195
- continueLoop = false;
7196
- }
7197
- }
7198
- } finally {
7199
- if (!streamDrained) {
7200
- try {
7201
- await reader.cancel();
7202
- } catch {
7203
- }
7204
- }
7205
- reader.releaseLock();
7206
- }
7207
- }
7208
- this._updateMessage(targetElement, messageId, { status: "completed" });
7209
- if (lastUsage) {
7210
- try {
7211
- targetElement.setUsage?.(messageId, lastUsage);
7212
- } catch {
7213
- }
7214
- }
7215
- return lastUsage;
6379
+ const runner = this.options.streamRunner ?? runStreamAgent;
6380
+ return this._runViaStreamRunner(runner, targetElement, messageId, provider, baseRequest, authConfig, streamController);
7216
6381
  }
7217
6382
  /**
7218
- * Delegate `_streamLoop`'s agentic loop to an injected {@link AparteStreamRunner},
7219
- * rendering its DOM-free events through {@link createStreamAdapter}.
7220
- * Builds the runner's dependencies from this client's config / provider /
7221
- * transport; the adapter reproduces the inline loop's `targetElement.*` calls
7222
- * (validated by the engine `stream-parity` suite). Leading writes (status
7223
- * streaming, prefix segments) mirror the inline path; the runner's `run-start`
7224
- * re-asserts `streaming` idempotently.
6383
+ * Run the loop through a {@link AparteStreamRunner} — the engine's by default —
6384
+ * rendering its DOM-free events through {@link createStreamAdapter}. Builds the
6385
+ * runner's dependencies from this client's config / provider / transport. The
6386
+ * leading writes (status streaming, prefix segments) happen here; the runner's
6387
+ * `run-start` re-asserts `streaming` idempotently. The call sequence this
6388
+ * produces is pinned by the `stream-parity` suite's snapshots, recorded while
6389
+ * the inline loop it replaced still ran beside it.
7225
6390
  */
7226
6391
  async _runViaStreamRunner(streamRunner, targetElement, messageId, provider, baseRequest, authConfig, streamController) {
7227
6392
  const signal = streamController.signal;
7228
- this._updateMessage(targetElement, messageId, { status: "streaming" });
7229
- const prefixSegments = baseRequest._meta?.["prefixSegments"];
7230
- if (prefixSegments?.length) {
7231
- for (const seg of prefixSegments) targetElement.addSegment?.(seg);
7232
- }
7233
6393
  const artifactHint = baseRequest._meta?.artifactHint;
7234
- const emitter = createStreamAdapter({
6394
+ const adapter = createStreamAdapter({
7235
6395
  target: targetElement,
7236
6396
  config: this._config,
7237
6397
  messageId,
7238
6398
  artifactHint
7239
6399
  });
6400
+ const prefixSegments = baseRequest._meta?.["prefixSegments"];
6401
+ const emitter = (event) => {
6402
+ adapter(event);
6403
+ if (event.type === "run-start" && prefixSegments?.length) {
6404
+ for (const seg of prefixSegments) targetElement.addSegment?.(seg);
6405
+ }
6406
+ };
7240
6407
  const transportCall = async (request) => {
7241
6408
  const response = await this._config.getTransport().chat(provider, request, authConfig, { providerId: provider.id, signal });
7242
6409
  return typeof response === "string" ? response : readableToAsyncIterable(response, signal);
@@ -7267,7 +6434,7 @@ ${content}
7267
6434
  // inline path, so setting it was silently ignored here. Exactly the
7268
6435
  // asymmetry the option was added to remove.
7269
6436
  toolTimeoutMs: this.options.toolTimeoutMs,
7270
- // Match the inline loop's id conventions: prefixed artifact ids, but a
6437
+ // The id conventions the loop always had: prefixed artifact ids, but a
7271
6438
  // BARE uuid for the synthetic tool (the adapter renders `tool-<id>`).
7272
6439
  idGen: (prefix) => prefix === "synthetic-tool" ? uuid() : `${prefix}-${uuid()}`
7273
6440
  });
@@ -7308,16 +6475,6 @@ ${content}
7308
6475
  }
7309
6476
  dispatchLifecycleEvent(target, "aparte-message-error", { messageId, error });
7310
6477
  }
7311
- /** Warn once per client for an unrecognised stream event, then carry on. */
7312
- _warnedUnknownEvents = /* @__PURE__ */ new Set();
7313
- _warnUnknownStreamEvent(event) {
7314
- const type = String(event?.type ?? "undefined");
7315
- if (this._warnedUnknownEvents.has(type)) return;
7316
- this._warnedUnknownEvents.add(type);
7317
- console.warn(
7318
- `[AparteClient] Ignoring unrecognised stream event "${type}". The reply is unaffected; this usually means the provider or its SDK emits a part this version of aparté does not map yet.`
7319
- );
7320
- }
7321
6478
  }
7322
6479
  function findHead(node) {
7323
6480
  if (node.next) return findHead(node.next);
@@ -7489,6 +6646,12 @@ class AparteMessageRepository {
7489
6646
  import(exported) {
7490
6647
  this.clear();
7491
6648
  for (const { message, parentId } of exported.messages) {
6649
+ if (parentId !== null && !this._nodes.has(parentId)) {
6650
+ console.warn(
6651
+ `[AparteMessageRepository] import: message "${message.id}" names a parent "${parentId}" the snapshot does not hold before it — skipped.`
6652
+ );
6653
+ continue;
6654
+ }
7492
6655
  this.addOrUpdateMessage(parentId, message);
7493
6656
  }
7494
6657
  if (exported.headId) {
@@ -7498,15 +6661,25 @@ class AparteMessageRepository {
7498
6661
  /**
7499
6662
  * Export the full tree for serialisation or debugging.
7500
6663
  * Compatible with the assistant-ui ExportedMessageRepository format.
6664
+ *
6665
+ * Walked from the root through each node's `children`, so a parent is always
6666
+ * written before its children. It used to iterate `_nodes` in insertion order and
6667
+ * call that topological — which holds until `_relink` re-parents an EXISTING node
6668
+ * under one inserted after it (`addOrUpdateMessage` with a known id and a newer
6669
+ * parent). The snapshot then listed the child first, and `import()` threw "parent
6670
+ * not found" on the very tree this method had just produced.
7501
6671
  */
7502
6672
  export() {
7503
6673
  const messages = [];
7504
- this._nodes.forEach((node) => {
7505
- messages.push({
7506
- message: node.current,
7507
- parentId: node.prev?.current.id ?? null
7508
- });
7509
- });
6674
+ const walk = (children, parentId) => {
6675
+ for (const id of children) {
6676
+ const node = this._nodes.get(id);
6677
+ if (!node) continue;
6678
+ messages.push({ message: node.current, parentId });
6679
+ walk(node.children, id);
6680
+ }
6681
+ };
6682
+ walk(this._root.children, null);
7510
6683
  return { headId: this.headId, messages };
7511
6684
  }
7512
6685
  // ─── Private helpers ────────────────────────────────────────────────────
@@ -7612,75 +6785,73 @@ function isAwaitingReply(message) {
7612
6785
  return message.status === void 0 && message.role === "assistant" && !message.content?.trim() && !message.segments?.length;
7613
6786
  }
7614
6787
  export {
7615
- resolveConfig as $,
6788
+ runWithConfig as $,
7616
6789
  APARTE_CONFIG_CHANGE as A,
7617
- contentToText as B,
7618
- contextConfig as C,
7619
- createAparteChatHandler as D,
7620
- createStreamAdapter as E,
7621
- cssEscape as F,
7622
- declineDefaultRenderers as G,
7623
- defaultSanitizer as H,
7624
- deriveArtifactKind as I,
7625
- detachConfig as J,
7626
- escapeAttr as K,
7627
- escapeHtml as L,
7628
- filesToAttachments as M,
7629
- getAllRenderers as N,
7630
- getSegmentRenderer as O,
7631
- installDefaultRenderersOnce as P,
7632
- isAwaitingReply as Q,
7633
- isFormatAdapter as R,
7634
- isSafeUrl as S,
7635
- isSegmentSettled as T,
7636
- parseAparteEventStream as U,
7637
- parseMarkdownToSegments as V,
7638
- populateBubbleFromMessage as W,
7639
- readableToAsyncIterable as X,
7640
- registerDefaultRenderers as Y,
7641
- registerSegmentRenderer as Z,
7642
- requestUserInput as _,
6790
+ contextConfig as B,
6791
+ createAparteChatHandler as C,
6792
+ createStreamAdapter as D,
6793
+ cssEscape as E,
6794
+ declineDefaultRenderers as F,
6795
+ defaultSanitizer as G,
6796
+ detachConfig as H,
6797
+ escapeAttr as I,
6798
+ escapeHtml as J,
6799
+ filesToAttachments as K,
6800
+ getAllRenderers as L,
6801
+ getSegmentRenderer as M,
6802
+ installDefaultRenderersOnce as N,
6803
+ isAwaitingReply as O,
6804
+ isFormatAdapter as P,
6805
+ isSafeUrl as Q,
6806
+ isSegmentSettled as R,
6807
+ parseAparteEventStream as S,
6808
+ parseMarkdownToSegments as T,
6809
+ populateBubbleFromMessage as U,
6810
+ readableToAsyncIterable as V,
6811
+ registerDefaultRenderers as W,
6812
+ registerSegmentRenderer as X,
6813
+ requestUserInput as Y,
6814
+ resolveConfig as Z,
6815
+ revokeAttachmentUrls as _,
7643
6816
  APARTE_CONVERSATION_SCHEMA_VERSION as a,
7644
- revokeAttachmentUrls as a0,
7645
- runWithConfig as a1,
7646
- segmentDuration as a2,
7647
- segmentTiming as a3,
7648
- subscribeConfigChange as a4,
7649
- unregisterSegmentRenderer as a5,
7650
- uuid as a6,
7651
- mergeSegmentUpdate as a7,
7652
- writeStreamedMarkdown as a8,
7653
- stampSegmentActivity as a9,
7654
- stampSegmentOnInsert as aa,
7655
- stampSegmentOnUpdate as ab,
7656
- renumberSegments as ac,
7657
- openSegmentIds as ad,
7658
- isTerminalStatus as ae,
7659
- adoptMessageSegments as af,
6817
+ segmentDuration as a0,
6818
+ segmentTiming as a1,
6819
+ subscribeConfigChange as a2,
6820
+ unregisterSegmentRenderer as a3,
6821
+ uuid as a4,
6822
+ mergeSegmentUpdate as a5,
6823
+ writeStreamedMarkdown as a6,
6824
+ stampSegmentActivity as a7,
6825
+ stampSegmentOnInsert as a8,
6826
+ stampSegmentOnUpdate as a9,
6827
+ renumberSegments as aa,
6828
+ openSegmentIds as ab,
6829
+ isTerminalStatus as ac,
6830
+ adoptMessageSegments as ad,
7660
6831
  APARTE_DEFAULT_BUBBLE_ACTIONS as b,
7661
6832
  APARTE_DEFAULT_HOST_HANDLERS as c,
7662
6833
  APARTE_DEFAULT_ICON_FALLBACKS as d,
7663
6834
  APARTE_DEFAULT_LOCALE as e,
7664
- APARTE_DEFAULT_SKELETON_FALLBACKS as f,
7665
- APARTE_DEFAULT_UI_EVENTS as g,
7666
- APARTE_HOST_ATTR as h,
7667
- AparteBackendTransport as i,
7668
- AparteChatHost as j,
7669
- AparteClient as k,
7670
- AparteConfig as l,
7671
- AparteConversationController as m,
7672
- AparteConversationManager as n,
7673
- AparteDirectTransport as o,
7674
- AparteElicitationAbortError as p,
7675
- AparteError as q,
7676
- AparteErrorCode as r,
7677
- AparteMessageRepository as s,
7678
- AparteStreamParser as t,
7679
- aparteGlobalConfig as u,
7680
- applyElementProps as v,
7681
- attachConfig as w,
7682
- buildApprovalPanel as x,
7683
- buildElicitationPanel as y,
7684
- collectRendererStyles as z
6835
+ APARTE_DEFAULT_UI_EVENTS as f,
6836
+ APARTE_HOST_ATTR as g,
6837
+ AparteBackendTransport as h,
6838
+ AparteChatHost as i,
6839
+ AparteClient as j,
6840
+ AparteConfig as k,
6841
+ AparteConversationController as l,
6842
+ AparteConversationManager as m,
6843
+ AparteDirectTransport as n,
6844
+ AparteElicitationAbortError as o,
6845
+ AparteError as p,
6846
+ AparteErrorCode as q,
6847
+ AparteMessageRepository as r,
6848
+ AparteStreamParser as s,
6849
+ aparteGlobalConfig as t,
6850
+ applyElementProps as u,
6851
+ attachConfig as v,
6852
+ buildApprovalPanel as w,
6853
+ buildElicitationPanel as x,
6854
+ collectRendererStyles as y,
6855
+ contentToText as z
7685
6856
  };
7686
- //# sourceMappingURL=is-awaiting-reply-DrO_oR32.js.map
6857
+ //# sourceMappingURL=is-awaiting-reply-BZ_iJ3s3.js.map