@aparte/core 0.13.1 → 0.15.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/index.d.ts +4 -0
  19. package/dist/components/index.d.ts.map +1 -1
  20. package/dist/components/suggestions/aparte-suggestions.d.ts +95 -0
  21. package/dist/components/suggestions/aparte-suggestions.d.ts.map +1 -0
  22. package/dist/components/suggestions/index.d.ts +3 -0
  23. package/dist/components/suggestions/index.d.ts.map +1 -0
  24. package/dist/components/viewport/aparte-chat-viewport.d.ts +35 -7
  25. package/dist/components/viewport/aparte-chat-viewport.d.ts.map +1 -1
  26. package/dist/config/aparte-config.d.ts +1 -21
  27. package/dist/config/aparte-config.d.ts.map +1 -1
  28. package/dist/config/index.d.ts +0 -2
  29. package/dist/config/index.d.ts.map +1 -1
  30. package/dist/config/locale.d.ts +30 -1
  31. package/dist/config/locale.d.ts.map +1 -1
  32. package/dist/conversations/index.d.ts +1 -1
  33. package/dist/conversations/index.d.ts.map +1 -1
  34. package/dist/conversations/types.d.ts +9 -57
  35. package/dist/conversations/types.d.ts.map +1 -1
  36. package/dist/custom-elements.json +5543 -5196
  37. package/dist/generated/element-attributes.d.ts +34 -6
  38. package/dist/generated/element-attributes.d.ts.map +1 -1
  39. package/dist/{glyphs-QXfbL3n_.js → glyphs-B3BZ2TIb.js} +21 -21
  40. package/dist/glyphs-B3BZ2TIb.js.map +1 -0
  41. package/dist/host/aparte-chat-host.d.ts +1 -1
  42. package/dist/host/aparte-chat-host.d.ts.map +1 -1
  43. package/dist/icons/extended.d.ts +87 -85
  44. package/dist/icons/extended.d.ts.map +1 -1
  45. package/dist/icons/glyphs.d.ts +53 -50
  46. package/dist/icons/glyphs.d.ts.map +1 -1
  47. package/dist/icons.js +41 -41
  48. package/dist/icons.js.map +1 -1
  49. package/dist/index.css +532 -171
  50. package/dist/index.d.ts +10 -4
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +593 -183
  53. package/dist/index.js.map +1 -1
  54. package/dist/index.node.d.ts +6 -4
  55. package/dist/index.node.d.ts.map +1 -1
  56. package/dist/index.node.js +56 -56
  57. package/dist/index.node.js.map +1 -1
  58. package/dist/{is-awaiting-reply-CWp1ffHh.js → is-awaiting-reply-BZ_iJ3s3.js} +175 -994
  59. package/dist/is-awaiting-reply-BZ_iJ3s3.js.map +1 -0
  60. package/dist/parsers/aparte-stream-parser.d.ts +6 -17
  61. package/dist/parsers/aparte-stream-parser.d.ts.map +1 -1
  62. package/dist/primitives/select/aparte-select.d.ts +2 -2
  63. package/dist/primitives/select/aparte-select.d.ts.map +1 -1
  64. package/dist/renderers/segment-renderers.d.ts.map +1 -1
  65. package/dist/renderers/segments/tool-call.d.ts.map +1 -1
  66. package/dist/runtime/message-repository.d.ts +7 -0
  67. package/dist/runtime/message-repository.d.ts.map +1 -1
  68. package/dist/transport/types.d.ts +8 -0
  69. package/dist/transport/types.d.ts.map +1 -1
  70. package/dist/types/chat.d.ts +32 -38
  71. package/dist/types/chat.d.ts.map +1 -1
  72. package/dist/types/errors.d.ts +16 -1
  73. package/dist/types/errors.d.ts.map +1 -1
  74. package/dist/types/event-map.d.ts +4 -0
  75. package/dist/types/event-map.d.ts.map +1 -1
  76. package/dist/types/events.d.ts +15 -0
  77. package/dist/types/events.d.ts.map +1 -1
  78. package/dist/types/imperative-api.d.ts +10 -1
  79. package/dist/types/imperative-api.d.ts.map +1 -1
  80. package/dist/types/index.d.ts +2 -2
  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-CWp1ffHh.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) {
@@ -2636,44 +2617,6 @@ const toolCallRenderer = {
2636
2617
  // has no other way onto the page.
2637
2618
  getStyles: () => ""
2638
2619
  };
2639
- const waitingLabel = () => contextConfig().t("generating");
2640
- const pipelineWaitingRenderer = {
2641
- type: "pipeline-waiting",
2642
- render: (segment) => {
2643
- return `
2644
- <div class="aparte-segment aparte-segment-pipeline-waiting" data-segment-id="${escapeHtml(segment.id)}" aria-label="${escapeAttr(waitingLabel())}" role="status">
2645
- <span class="aparte-dot aparte-pw-dot"></span>
2646
- <span class="aparte-dot aparte-pw-dot"></span>
2647
- <span class="aparte-dot aparte-pw-dot"></span>
2648
- </div>`;
2649
- },
2650
- update: () => {
2651
- },
2652
- /**
2653
- * Three dots and an accessible name. The dots are CSS; the name is the whole
2654
- * content as far as a screen reader is concerned, and it was hardcoded English in
2655
- * every locale — the one string in this library where being untranslated is
2656
- * invisible to everyone who can see the screen.
2657
- */
2658
- relabel: (element) => {
2659
- element.setAttribute("aria-label", waitingLabel());
2660
- },
2661
- setup: (el2) => {
2662
- const parent = el2.parentElement;
2663
- if (!parent) return;
2664
- const observer = new MutationObserver(() => {
2665
- if (el2.nextElementSibling) {
2666
- observer.disconnect();
2667
- el2.remove();
2668
- }
2669
- });
2670
- observer.observe(parent, { childList: true });
2671
- },
2672
- // CSS lives in styles/segment/ — see that folder for why a
2673
- // built-in's rules belong there. `getStyles` stays for a CONSUMER's renderer, which
2674
- // has no other way onto the page.
2675
- getStyles: () => ""
2676
- };
2677
2620
  function stripCodeFences(content) {
2678
2621
  let s = content;
2679
2622
  if (s.startsWith("```") || s.startsWith("~~~")) {
@@ -3467,8 +3410,7 @@ const DEFAULT_RENDERERS = [
3467
3410
  codeRenderer,
3468
3411
  errorRenderer,
3469
3412
  toolCallRenderer,
3470
- artifactRenderer,
3471
- pipelineWaitingRenderer
3413
+ artifactRenderer
3472
3414
  ];
3473
3415
  const OPEN_TOOL_STATUSES = /* @__PURE__ */ new Set(["pending", "awaiting-approval"]);
3474
3416
  const ACTIVITY_KEYS = ["content", "output", "result"];
@@ -4428,7 +4370,7 @@ class AparteChatHost {
4428
4370
  this.binding.setMessages(next);
4429
4371
  this.binding.onMessagesChange?.(next);
4430
4372
  }
4431
- /** Remove a transient segment (e.g. pipeline-waiting indicator). */
4373
+ /** Remove a transient segment (a status row the host added itself). */
4432
4374
  removeSegment(segmentId) {
4433
4375
  this._flushStreamState();
4434
4376
  this._lastBubble()?.removeSegment?.(segmentId);
@@ -4607,7 +4549,7 @@ class AparteChatHost {
4607
4549
  this._bufferToken(messageId, res.value);
4608
4550
  }
4609
4551
  this._flushStreamState();
4610
- if (!ac.signal.aborted) this._vp()?.completeMessage?.(messageId);
4552
+ this._vp()?.completeMessage?.(messageId);
4611
4553
  this._setStreamingId(null);
4612
4554
  } catch (err) {
4613
4555
  this._flushStreamState();
@@ -4864,176 +4806,6 @@ function dispatchArtifactLifecycle(target, messageId, segment, progress, isFinal
4864
4806
  }));
4865
4807
  }
4866
4808
  }
4867
- const INLINE_MAX_LINES = 15;
4868
- const XML_OPEN_TAG = "<artifact";
4869
- function partialXmlOpenTagLength(text) {
4870
- const max = Math.min(text.length, XML_OPEN_TAG.length - 1);
4871
- for (let k = max; k > 0; k--) {
4872
- if (text.endsWith(XML_OPEN_TAG.slice(0, k))) return k;
4873
- }
4874
- return 0;
4875
- }
4876
- function feedXmlArtifactDelta(delta, xml, ctx) {
4877
- const { targetElement, messageId, textParser, streamingSegmentIds, artifactProgress, artifactXmlHint } = ctx;
4878
- let remaining = delta;
4879
- const emitChatText = (text, syncActive = true) => {
4880
- if (!text) return;
4881
- const r = textParser.parse(text);
4882
- for (const seg of r.segments) {
4883
- if (!streamingSegmentIds.has(seg.id)) {
4884
- targetElement.addSegment?.(seg);
4885
- streamingSegmentIds.add(seg.id);
4886
- } else if ("content" in seg) {
4887
- targetElement.updateSegment?.(seg.id, segmentContentUpdate(seg));
4888
- }
4889
- }
4890
- const active = textParser.getState().activeSegment;
4891
- if (syncActive && active) {
4892
- if (!streamingSegmentIds.has(active.id)) {
4893
- targetElement.addSegment?.(active);
4894
- streamingSegmentIds.add(active.id);
4895
- } else {
4896
- targetElement.updateSegment?.(active.id, segmentContentUpdate(active));
4897
- }
4898
- } else if (!r.segments.length && !active) ;
4899
- };
4900
- while (remaining.length > 0) {
4901
- if (xml.state === "normal") {
4902
- const tagStart = remaining.indexOf(XML_OPEN_TAG);
4903
- if (tagStart === -1) {
4904
- const held = partialXmlOpenTagLength(remaining);
4905
- if (held > 0) {
4906
- emitChatText(remaining.slice(0, remaining.length - held), false);
4907
- xml.scanBuf = remaining.slice(remaining.length - held);
4908
- xml.state = "scanning";
4909
- } else {
4910
- emitChatText(remaining);
4911
- }
4912
- remaining = "";
4913
- } else {
4914
- emitChatText(remaining.slice(0, tagStart), false);
4915
- xml.scanBuf = "";
4916
- remaining = remaining.slice(tagStart);
4917
- xml.state = "scanning";
4918
- }
4919
- } else if (xml.state === "scanning") {
4920
- xml.scanBuf += remaining;
4921
- remaining = "";
4922
- const cmp = Math.min(xml.scanBuf.length, XML_OPEN_TAG.length);
4923
- if (xml.scanBuf.slice(0, cmp) !== XML_OPEN_TAG.slice(0, cmp)) {
4924
- emitChatText(xml.scanBuf);
4925
- xml.scanBuf = "";
4926
- xml.state = "normal";
4927
- continue;
4928
- }
4929
- const gtIdx = xml.scanBuf.indexOf(">");
4930
- if (gtIdx !== -1) {
4931
- const tag = xml.scanBuf.slice(0, gtIdx + 1);
4932
- const mimeMatch = /mimeType=['"]([^'"]+)['"]/.exec(tag);
4933
- const titleMatch = /title=['"]([^'"]+)['"]/.exec(tag);
4934
- xml.mime = mimeMatch?.[1] ?? artifactXmlHint.mimeType;
4935
- xml.title = titleMatch?.[1] ?? artifactXmlHint.kind;
4936
- xml.kind = deriveArtifactKind(xml.mime, artifactXmlHint.kind);
4937
- xml.segId = `artifact-xml-${uuid()}`;
4938
- xml.content = "";
4939
- const openSeg = {
4940
- id: xml.segId,
4941
- type: "artifact",
4942
- mimeType: xml.mime,
4943
- artifactType: xml.kind,
4944
- title: xml.title,
4945
- content: ""
4946
- };
4947
- targetElement.addSegment?.(openSeg);
4948
- streamingSegmentIds.add(xml.segId);
4949
- dispatchArtifactLifecycle(targetElement, messageId, openSeg, artifactProgress, false);
4950
- xml.state = "in-artifact";
4951
- remaining = xml.scanBuf.slice(gtIdx + 1);
4952
- xml.scanBuf = "";
4953
- }
4954
- } else {
4955
- const CLOSE = "</artifact>";
4956
- const combined = xml.closeBuf + remaining;
4957
- const closeIdx = combined.indexOf(CLOSE);
4958
- if (closeIdx !== -1) {
4959
- xml.content += combined.slice(0, closeIdx);
4960
- const lineCount = xml.content.split("\n").length;
4961
- const isInline = lineCount < INLINE_MAX_LINES;
4962
- const finalSeg = {
4963
- id: xml.segId,
4964
- mimeType: xml.mime,
4965
- artifactType: xml.kind,
4966
- title: xml.title,
4967
- content: xml.content
4968
- };
4969
- targetElement.updateSegment?.(xml.segId, { content: xml.content, inline: isInline });
4970
- dispatchArtifactLifecycle(targetElement, messageId, finalSeg, artifactProgress, true);
4971
- xml.state = "normal";
4972
- xml.closeBuf = "";
4973
- remaining = combined.slice(closeIdx + CLOSE.length);
4974
- } else {
4975
- const safeLen = Math.max(0, combined.length - CLOSE.length + 1);
4976
- const safe = combined.slice(0, safeLen);
4977
- xml.content += safe;
4978
- xml.closeBuf = combined.slice(safeLen);
4979
- remaining = "";
4980
- if (xml.segId) {
4981
- targetElement.updateSegment?.(xml.segId, { content: xml.content });
4982
- dispatchArtifactLifecycle(targetElement, messageId, {
4983
- id: xml.segId,
4984
- mimeType: xml.mime,
4985
- artifactType: xml.kind,
4986
- title: xml.title,
4987
- content: xml.content
4988
- }, artifactProgress, false);
4989
- }
4990
- }
4991
- }
4992
- }
4993
- }
4994
- function finalizeXmlArtifact(xml, ctx) {
4995
- const { targetElement, messageId, artifactProgress, artifactXmlHint } = ctx;
4996
- if (xml.state === "scanning") {
4997
- const held = xml.scanBuf;
4998
- xml.scanBuf = "";
4999
- xml.state = "normal";
5000
- if (held) {
5001
- const r = ctx.textParser.parse(held);
5002
- for (const seg of r.segments) {
5003
- if (!ctx.streamingSegmentIds.has(seg.id)) {
5004
- targetElement.addSegment?.(seg);
5005
- ctx.streamingSegmentIds.add(seg.id);
5006
- } else if ("content" in seg) {
5007
- targetElement.updateSegment?.(seg.id, segmentContentUpdate(seg));
5008
- }
5009
- }
5010
- const active = ctx.textParser.getState().activeSegment;
5011
- if (active) {
5012
- if (!ctx.streamingSegmentIds.has(active.id)) {
5013
- targetElement.addSegment?.(active);
5014
- ctx.streamingSegmentIds.add(active.id);
5015
- } else {
5016
- targetElement.updateSegment?.(active.id, segmentContentUpdate(active));
5017
- }
5018
- }
5019
- }
5020
- return;
5021
- }
5022
- if (artifactXmlHint && xml.state === "in-artifact" && xml.segId) {
5023
- xml.content += xml.closeBuf;
5024
- const lineCount = xml.content.split("\n").length;
5025
- const isInline = lineCount < INLINE_MAX_LINES;
5026
- targetElement.updateSegment?.(xml.segId, { content: xml.content, inline: isInline });
5027
- dispatchArtifactLifecycle(targetElement, messageId, {
5028
- id: xml.segId,
5029
- mimeType: xml.mime,
5030
- artifactType: xml.kind,
5031
- title: xml.title,
5032
- content: xml.content
5033
- }, artifactProgress, true);
5034
- console.warn("[AparteClient] XML artifact finalized without closing tag — content may be partial.");
5035
- }
5036
- }
5037
4809
  function createStreamAdapter(opts) {
5038
4810
  const { target, config, messageId, artifactHint } = opts;
5039
4811
  let parser = new AparteStreamParser();
@@ -5043,7 +4815,6 @@ function createStreamAdapter(opts) {
5043
4815
  let thinkingCollapsed = false;
5044
4816
  let artifactProgress = /* @__PURE__ */ new Map();
5045
4817
  let artifactPromoted = false;
5046
- let artifactMeta = /* @__PURE__ */ new Map();
5047
4818
  return (e) => {
5048
4819
  switch (e.type) {
5049
4820
  case "run-start":
@@ -5057,7 +4828,6 @@ function createStreamAdapter(opts) {
5057
4828
  thinkingCollapsed = false;
5058
4829
  artifactProgress = /* @__PURE__ */ new Map();
5059
4830
  artifactPromoted = false;
5060
- artifactMeta = /* @__PURE__ */ new Map();
5061
4831
  break;
5062
4832
  case "thinking-delta": {
5063
4833
  thinkingContent += e.delta;
@@ -5082,18 +4852,6 @@ function createStreamAdapter(opts) {
5082
4852
  target.updateSegment?.(thinkingId, { collapsed: true, isStreaming: false });
5083
4853
  thinkingCollapsed = true;
5084
4854
  }
5085
- if (e.reduced) {
5086
- const r = parser.parse(e.delta);
5087
- for (const segment of r.segments) {
5088
- if (!streaming.has(segment.id)) {
5089
- target.addSegment?.(segment);
5090
- streaming.add(segment.id);
5091
- } else if ("content" in segment) {
5092
- target.updateSegment?.(segment.id, segmentContentUpdate(segment));
5093
- }
5094
- }
5095
- break;
5096
- }
5097
4855
  const result = parser.parse(e.delta);
5098
4856
  for (let segment of result.segments) {
5099
4857
  if (artifactHint && !artifactPromoted && segment.type === "code") {
@@ -5166,33 +4924,6 @@ function createStreamAdapter(opts) {
5166
4924
  }
5167
4925
  break;
5168
4926
  }
5169
- case "artifact-open": {
5170
- const seg = {
5171
- id: e.id,
5172
- type: "artifact",
5173
- mimeType: e.mimeType,
5174
- artifactType: e.kind,
5175
- title: e.title,
5176
- content: ""
5177
- };
5178
- target.addSegment?.(seg);
5179
- streaming.add(e.id);
5180
- artifactMeta.set(e.id, { mimeType: e.mimeType, artifactType: e.kind, title: e.title });
5181
- dispatchArtifactLifecycle(target, messageId, seg, artifactProgress, false);
5182
- break;
5183
- }
5184
- case "artifact-chunk": {
5185
- const meta = artifactMeta.get(e.id);
5186
- target.updateSegment?.(e.id, { content: e.content });
5187
- dispatchArtifactLifecycle(target, messageId, { id: e.id, content: e.content, ...meta }, artifactProgress, false);
5188
- break;
5189
- }
5190
- case "artifact-close": {
5191
- const meta = artifactMeta.get(e.id);
5192
- target.updateSegment?.(e.id, { content: e.content, inline: e.inline });
5193
- dispatchArtifactLifecycle(target, messageId, { id: e.id, content: e.content, ...meta }, artifactProgress, true);
5194
- break;
5195
- }
5196
4927
  case "artifact-ready": {
5197
4928
  const seg = {
5198
4929
  id: e.id,
@@ -5251,9 +4982,6 @@ function createStreamAdapter(opts) {
5251
4982
  target.updateSegment?.(`tool-${e.toolCallId}`, { status: "aborted" });
5252
4983
  }
5253
4984
  break;
5254
- case "phase-advance":
5255
- target.addSegment?.({ id: `pw-${uuid()}`, type: "pipeline-waiting" });
5256
- break;
5257
4985
  case "run-aborted":
5258
4986
  dispatchLifecycleEvent(target, "aparte-message-aborted", { messageId });
5259
4987
  break;
@@ -5791,20 +5519,6 @@ function buildApprovalPanel(message, options, onChange) {
5791
5519
  function requestUserInput(request) {
5792
5520
  return resolveConfig(request.target ?? null).requestUserInput(request);
5793
5521
  }
5794
- const DEFAULT_TOOL_HANDLER_TIMEOUT_MS = 5 * 60 * 1e3;
5795
- function withToolTimeout(work, controller, timeoutMs) {
5796
- let timer;
5797
- const timedOut = new Promise((_resolve, reject) => {
5798
- timer = setTimeout(() => {
5799
- controller.abort();
5800
- reject(Object.assign(new Error(`tool handler exceeded ${timeoutMs}ms`), { name: "AbortError" }));
5801
- }, timeoutMs);
5802
- });
5803
- return {
5804
- raced: Promise.race([work(controller.signal), timedOut]),
5805
- done: () => clearTimeout(timer)
5806
- };
5807
- }
5808
5522
  class AparteClient {
5809
5523
  _boundHandler = null;
5810
5524
  _boundAbortHandler = null;
@@ -5839,6 +5553,8 @@ class AparteClient {
5839
5553
  }
5840
5554
  /** Aborts the in-flight vendor/transport fetch when the user stops a stream. */
5841
5555
  _streamController = null;
5556
+ /** Aborts an in-flight `compact()` summarisation — its own slot, see `compact`. */
5557
+ _compactController = null;
5842
5558
  options;
5843
5559
  /** Config read by this client — an instance config, or the global default. */
5844
5560
  _config;
@@ -5964,6 +5680,7 @@ class AparteClient {
5964
5680
  abort() {
5965
5681
  this._isAborted = true;
5966
5682
  this._streamController?.abort();
5683
+ this._compactController?.abort();
5967
5684
  for (const controller of this._activeToolControllers) {
5968
5685
  controller.abort();
5969
5686
  }
@@ -6071,6 +5788,7 @@ class AparteClient {
6071
5788
  }
6072
5789
  if (this._isAborted) {
6073
5790
  dispatchLifecycleEvent(targetElement, "aparte-message-aborted", { messageId });
5791
+ this._updateMessage(targetElement, messageId, { status: "completed" });
6074
5792
  return;
6075
5793
  }
6076
5794
  dispatchLifecycleEvent(targetElement, "aparte-message-start", { messageId, role: "assistant" });
@@ -6150,7 +5868,7 @@ class AparteClient {
6150
5868
  stream: false
6151
5869
  };
6152
5870
  const compactController = new AbortController();
6153
- this._streamController = compactController;
5871
+ this._compactController = compactController;
6154
5872
  const response = await this._config.getTransport().chat(
6155
5873
  provider,
6156
5874
  summarizeRequest,
@@ -6181,7 +5899,11 @@ class AparteClient {
6181
5899
  target.appendMessage?.({
6182
5900
  id: uuid(),
6183
5901
  role: "assistant",
6184
- 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")}**
6185
5907
 
6186
5908
  ${summary}`,
6187
5909
  timestamp: Date.now(),
@@ -6635,67 +6357,6 @@ ${content}
6635
6357
  );
6636
6358
  return parts.filter((p) => p !== null);
6637
6359
  }
6638
- /**
6639
- * Turn-1 forced tool call. When `toolChoice = { name, input }`
6640
- * (orchestrator-driven), execute the handler directly instead of consulting
6641
- * the LLM, render the tool segment, inject the result as `tool_result`, and
6642
- * strip `toolChoice` for the follow-up turn. Returns the (possibly-updated)
6643
- * request and whether the loop should skip to the next turn (handler missing
6644
- * or aborted). Extracted from `_streamLoop`. `messages` is mutated in place.
6645
- */
6646
- async _maybeRunSyntheticTool(baseRequest, turns, messages, targetElement) {
6647
- const toolChoice = baseRequest.toolChoice;
6648
- if (!(turns === 1 && toolChoice && typeof toolChoice === "object" && toolChoice.input !== void 0)) {
6649
- return { baseRequest, skip: false };
6650
- }
6651
- const syntheticId = uuid();
6652
- const syntheticCall = { id: syntheticId, name: toolChoice.name, input: toolChoice.input };
6653
- const toolSeg = {
6654
- id: `tool-${syntheticId}`,
6655
- type: "tool_call",
6656
- toolCall: syntheticCall,
6657
- status: "pending"
6658
- };
6659
- const toolRenderer = this._config.getToolRenderer(toolChoice.name);
6660
- if (toolRenderer) {
6661
- const html = toolRenderer.render(toolSeg);
6662
- if (html) targetElement.addSegment?.(toolSeg);
6663
- } else {
6664
- targetElement.addSegment?.(toolSeg);
6665
- }
6666
- const handler = this._config.getToolHandler(toolChoice.name);
6667
- if (!handler) {
6668
- console.warn(`[AparteClient] No handler for synthetic tool "${toolChoice.name}"`);
6669
- targetElement.updateSegment?.(toolSeg.id, { status: "aborted" });
6670
- return { baseRequest, skip: true };
6671
- }
6672
- const controller = new AbortController();
6673
- this._activeToolControllers.add(controller);
6674
- const { raced, done: clearToolTimeout } = withToolTimeout(
6675
- (sig) => handler(syntheticCall, sig, {
6676
- target: targetElement,
6677
- config: this._config
6678
- }),
6679
- controller,
6680
- this.options.toolTimeoutMs ?? DEFAULT_TOOL_HANDLER_TIMEOUT_MS
6681
- );
6682
- try {
6683
- const result = await raced;
6684
- targetElement.updateSegment?.(toolSeg.id, { status: "resolved", result: result.content });
6685
- messages.push({ role: "tool_call", content: "", toolCalls: [syntheticCall] });
6686
- messages.push({ role: "tool_result", content: result.content, toolCallId: syntheticId });
6687
- return { baseRequest: { ...baseRequest, toolChoice: "none", tools: void 0 }, skip: false };
6688
- } catch (err) {
6689
- if (err?.name === "AbortError") {
6690
- targetElement.updateSegment?.(toolSeg.id, { status: "aborted" });
6691
- return { baseRequest, skip: true };
6692
- }
6693
- throw err;
6694
- } finally {
6695
- clearToolTimeout();
6696
- this._activeToolControllers.delete(controller);
6697
- }
6698
- }
6699
6360
  /**
6700
6361
  * What the loop does after one tool call. THREE outcomes, because a boolean
6701
6362
  * conflated two questions that a refusal answers differently.
@@ -6708,525 +6369,41 @@ ${content}
6708
6369
  * - `'halt'` — the run is over: an abort, a turn limit, a missing handler.
6709
6370
  */
6710
6371
  /**
6711
- * Handle one `tool_use` stream event from {@link _streamLoop}: the built-in
6712
- * `create_artifact`, per-tool renderer selection, the human-in-the-loop
6713
- * approval gate, and running the registered handler (timeout / abort).
6714
- * Mutates the shared `messages` / `toolCallsThisTurn` history in place and
6715
- * returns what the loop should do next.
6716
- */
6717
- async _handleToolUseEvent(event, ctx) {
6718
- const { targetElement, messageId, messages, toolCallsThisTurn, precedingText, artifactProgress, turns, globalMaxTurns } = ctx;
6719
- let flow = "continue";
6720
- toolCallsThisTurn.push({ id: event.id, name: event.name, input: event.input });
6721
- if (event.name === "create_artifact") {
6722
- const input = event.input;
6723
- const mimeType = input.mimeType ?? "text/plain";
6724
- const kind = deriveArtifactKind(mimeType, "text");
6725
- const artifactSeg = {
6726
- id: `artifact-${event.id}`,
6727
- type: "artifact",
6728
- mimeType,
6729
- artifactType: kind,
6730
- title: input.title ?? kind,
6731
- content: input.content ?? ""
6732
- };
6733
- targetElement.addSegment?.(artifactSeg);
6734
- dispatchArtifactLifecycle(targetElement, messageId, artifactSeg, artifactProgress, true);
6735
- messages.push({
6736
- role: "tool_call",
6737
- content: "",
6738
- toolCalls: [{ id: event.id, name: event.name, input: event.input }]
6739
- });
6740
- messages.push({
6741
- role: "tool_result",
6742
- content: "Artifact created successfully.",
6743
- toolCallId: event.id
6744
- });
6745
- return { flow: "continue" };
6746
- }
6747
- const toolSeg = {
6748
- id: `tool-${event.id}`,
6749
- type: "tool_call",
6750
- toolCall: { id: event.id, name: event.name, input: event.input },
6751
- status: "pending"
6752
- };
6753
- const toolRenderer = this._config.getToolRenderer(event.name);
6754
- if (toolRenderer) {
6755
- injectToolRendererStyles(event.name, toolRenderer);
6756
- const html = toolRenderer.render(toolSeg);
6757
- if (html) {
6758
- targetElement.addSegment?.(toolSeg);
6759
- }
6760
- } else {
6761
- targetElement.addSegment?.(toolSeg);
6762
- }
6763
- const toolDef = this._config.getTools().find((t) => t.name === event.name);
6764
- const effectiveMaxTurns = toolDef?.maxTurns ?? globalMaxTurns;
6765
- if (turns >= effectiveMaxTurns) {
6766
- console.warn(`[AparteClient] Tool "${event.name}" maxTurns (${effectiveMaxTurns}) reached.`);
6767
- targetElement.updateSegment?.(toolSeg.id, { status: "aborted" });
6768
- return { flow: "halt" };
6769
- }
6770
- const handler = this._config.getToolHandler(event.name);
6771
- if (handler) {
6772
- let effectiveInput = event.input;
6773
- if (toolDef?.needsApproval) {
6774
- const approvalController = new AbortController();
6775
- this._activeToolControllers.add(approvalController);
6776
- targetElement.updateSegment?.(toolSeg.id, { status: "awaiting-approval" });
6777
- dispatchLifecycleEvent(targetElement, "aparte-tool-approval-request", {
6778
- toolCallId: event.id,
6779
- toolName: event.name,
6780
- input: event.input
6781
- });
6782
- let decision;
6783
- const resolveApproval = this.options.approvalResolver ?? ((call, sig) => this._askForApproval(call, sig, targetElement));
6784
- try {
6785
- decision = await resolveApproval(event, approvalController.signal);
6786
- } catch (err) {
6787
- if (err?.name === "AbortError") {
6788
- targetElement.updateSegment?.(toolSeg.id, { status: "aborted" });
6789
- return { flow: "halt" };
6790
- }
6791
- throw err;
6792
- } finally {
6793
- this._activeToolControllers.delete(approvalController);
6794
- }
6795
- if (approvalController.signal.aborted) {
6796
- targetElement.updateSegment?.(toolSeg.id, { status: "aborted" });
6797
- return { flow: "halt" };
6798
- }
6799
- if (!decision.approved) {
6800
- const rejection = decision.instruction ? `The user rejected this tool call and said: ${decision.instruction}` : "Tool execution was rejected by the user.";
6801
- targetElement.updateSegment?.(toolSeg.id, { status: "rejected", result: rejection });
6802
- const existingToolCallMsg = messages.find(
6803
- (m) => m.role === "tool_call" && m.toolCalls?.some((tc) => tc.id === event.id)
6804
- );
6805
- if (!existingToolCallMsg) {
6806
- messages.push({
6807
- role: "tool_call",
6808
- content: "",
6809
- toolCalls: toolCallsThisTurn,
6810
- precedingText: precedingText.trim() || void 0
6811
- });
6812
- }
6813
- messages.push({ role: "tool_result", content: rejection, toolCallId: event.id });
6814
- return { flow: "respond" };
6815
- }
6816
- if (decision.payload && typeof decision.payload === "object" && !Array.isArray(decision.payload)) {
6817
- effectiveInput = { ...event.input, ...decision.payload };
6818
- }
6819
- targetElement.updateSegment?.(toolSeg.id, { status: "pending" });
6820
- }
6821
- const controller = new AbortController();
6822
- this._activeToolControllers.add(controller);
6823
- const { raced, done: clearToolTimeout } = withToolTimeout(
6824
- (sig) => handler(
6825
- { id: event.id, name: event.name, input: effectiveInput },
6826
- sig,
6827
- { target: targetElement, config: this._config }
6828
- ),
6829
- controller,
6830
- this.options.toolTimeoutMs ?? DEFAULT_TOOL_HANDLER_TIMEOUT_MS
6831
- );
6832
- try {
6833
- const result = await raced;
6834
- targetElement.updateSegment?.(toolSeg.id, { status: "resolved", result: result.content });
6835
- const existingToolCallMsg = messages.find(
6836
- (m) => m.role === "tool_call" && m.toolCalls?.some((tc) => tc.id === event.id)
6837
- );
6838
- if (!existingToolCallMsg) {
6839
- messages.push({
6840
- role: "tool_call",
6841
- content: "",
6842
- toolCalls: toolCallsThisTurn,
6843
- precedingText: precedingText.trim() || void 0
6844
- });
6845
- }
6846
- messages.push({
6847
- role: "tool_result",
6848
- content: result.content,
6849
- toolCallId: event.id
6850
- });
6851
- } catch (err) {
6852
- if (err?.name === "AbortError") {
6853
- targetElement.updateSegment?.(toolSeg.id, { status: "aborted" });
6854
- flow = "halt";
6855
- } else {
6856
- throw err;
6857
- }
6858
- } finally {
6859
- clearToolTimeout();
6860
- this._activeToolControllers.delete(controller);
6861
- }
6862
- } else {
6863
- console.warn(`[AparteClient] No handler registered for tool "${event.name}"`);
6864
- targetElement.updateSegment?.(toolSeg.id, { status: "aborted" });
6865
- flow = "halt";
6866
- }
6867
- return { flow };
6868
- }
6869
- /**
6870
- * Stream loop: runs one provider.chat() call and repeats if a tool was called.
6871
- * 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.
6872
6374
  */
6873
6375
  async _streamLoop(targetElement, messageId, provider, baseRequest, authConfig) {
6874
6376
  this._streamController?.abort();
6875
6377
  const streamController = new AbortController();
6876
6378
  this._streamController = streamController;
6877
- if (this.options.streamRunner) {
6878
- return this._runViaStreamRunner(this.options.streamRunner, targetElement, messageId, provider, baseRequest, authConfig, streamController);
6879
- }
6880
- const messages = [...baseRequest.messages];
6881
- let continueLoop = true;
6882
- let turns = 0;
6883
- const globalMaxTurns = this.options.maxTurns ?? 10;
6884
- let lastUsage;
6885
- const pipeline = baseRequest._meta?.pipeline;
6886
- let pipelineIndex = 0;
6887
- this._updateMessage(targetElement, messageId, { status: "streaming" });
6888
- for (const seg of baseRequest._meta?.prefixSegments ?? []) {
6889
- targetElement.addSegment?.(seg);
6890
- }
6891
- while (continueLoop) {
6892
- let endTurnEarly = false;
6893
- if (this._isAborted) {
6894
- dispatchLifecycleEvent(targetElement, "aparte-message-aborted", { messageId });
6895
- break;
6896
- }
6897
- turns++;
6898
- if (turns > globalMaxTurns) {
6899
- console.warn(`[AparteClient] maxTurns (${globalMaxTurns}) exceeded — stopping loop.`);
6900
- targetElement.addSegment?.({
6901
- id: `max-turns-${uuid()}`,
6902
- type: "error",
6903
- content: `Stopped after ${globalMaxTurns} tool calls to prevent an infinite loop.`,
6904
- details: "MAX_TURNS_EXCEEDED"
6905
- });
6906
- break;
6907
- }
6908
- const synthetic = await this._maybeRunSyntheticTool(baseRequest, turns, messages, targetElement);
6909
- baseRequest = synthetic.baseRequest;
6910
- if (synthetic.skip) {
6911
- continueLoop = false;
6912
- continue;
6913
- }
6914
- let phaseMessages = messages;
6915
- let phaseMeta = baseRequest._meta;
6916
- if (pipeline && pipelineIndex < pipeline.length) {
6917
- const phase = pipeline[pipelineIndex];
6918
- phaseMessages = [{ role: "system", content: phase.system }, ...messages];
6919
- if (phase.mode === "artifact") {
6920
- phaseMeta = { ...phaseMeta, artifactRaw: { mimeType: phase.mimeType, kind: phase.kind } };
6921
- } else {
6922
- const { artifactRaw: _dropped, pipeline: _p, ...restMeta } = phaseMeta ?? {};
6923
- phaseMeta = restMeta;
6924
- }
6925
- }
6926
- const request = { ...baseRequest, messages: phaseMessages, _meta: phaseMeta };
6927
- const response = await this._config.getTransport().chat(provider, request, authConfig, { providerId: provider.id, signal: streamController.signal });
6928
- if (typeof response === "string") {
6929
- const wholeParser = new AparteStreamParser();
6930
- const parsed = [
6931
- ...wholeParser.parse(response).segments,
6932
- ...wholeParser.finalize()
6933
- ];
6934
- const stringHint = baseRequest._meta?.artifactHint;
6935
- if (stringHint) {
6936
- const codeIdx = parsed.findIndex((s) => s.type === "code");
6937
- if (codeIdx !== -1) {
6938
- const codeSeg = parsed[codeIdx];
6939
- parsed[codeIdx] = {
6940
- id: codeSeg.id,
6941
- type: "artifact",
6942
- mimeType: stringHint.mimeType,
6943
- artifactType: stringHint.kind,
6944
- title: codeSeg.filename ?? stringHint.kind,
6945
- content: codeSeg.content
6946
- };
6947
- }
6948
- }
6949
- if (parsed.length > 0) {
6950
- for (const segment of parsed) targetElement.addSegment?.(segment);
6951
- this._updateMessage(targetElement, messageId, { status: "completed" });
6952
- } else {
6953
- this._updateMessage(targetElement, messageId, { content: response, status: "completed" });
6954
- }
6955
- return void 0;
6956
- }
6957
- const reader = response.getReader();
6958
- const textParser = new AparteStreamParser();
6959
- const streamingSegmentIds = /* @__PURE__ */ new Set();
6960
- const artifactProgress = /* @__PURE__ */ new Map();
6961
- let thinkingSegmentId = null;
6962
- let thinkingContent = "";
6963
- let thinkingCollapsed = false;
6964
- const artifactHint = baseRequest._meta?.artifactHint;
6965
- let artifactPromoted = false;
6966
- const artifactRawHint = request._meta?.artifactRaw;
6967
- let rawSegId = null;
6968
- let rawContent = "";
6969
- if (artifactRawHint) {
6970
- rawSegId = `artifact-raw-${uuid()}`;
6971
- const rawSeg = {
6972
- id: rawSegId,
6973
- type: "artifact",
6974
- mimeType: artifactRawHint.mimeType,
6975
- artifactType: artifactRawHint.kind,
6976
- title: artifactRawHint.kind,
6977
- content: ""
6978
- };
6979
- targetElement.addSegment?.(rawSeg);
6980
- streamingSegmentIds.add(rawSegId);
6981
- dispatchArtifactLifecycle(targetElement, messageId, rawSeg, artifactProgress, false);
6982
- }
6983
- const artifactXmlHint = baseRequest._meta?.artifactXml;
6984
- const xmlCtx = {
6985
- state: "normal",
6986
- scanBuf: "",
6987
- closeBuf: "",
6988
- segId: null,
6989
- content: "",
6990
- mime: "",
6991
- kind: "",
6992
- title: ""
6993
- };
6994
- let precedingText = "";
6995
- const toolCallsThisTurn = [];
6996
- const bailOnAbort = () => {
6997
- if (!this._isAborted) return false;
6998
- try {
6999
- void reader.cancel();
7000
- } catch {
7001
- }
7002
- dispatchLifecycleEvent(targetElement, "aparte-message-aborted", { messageId });
7003
- continueLoop = false;
7004
- return true;
7005
- };
7006
- let streamDrained = false;
7007
- try {
7008
- while (true) {
7009
- if (bailOnAbort()) break;
7010
- const { done, value: event } = await reader.read();
7011
- if (bailOnAbort()) break;
7012
- if (done) {
7013
- streamDrained = true;
7014
- break;
7015
- }
7016
- switch (event.type) {
7017
- case "thinking": {
7018
- thinkingContent += event.delta;
7019
- if (!thinkingSegmentId) {
7020
- const seg = {
7021
- id: `think-${uuid()}`,
7022
- type: "thinking",
7023
- content: thinkingContent,
7024
- collapsed: true,
7025
- label: "Thinking"
7026
- };
7027
- thinkingSegmentId = seg.id;
7028
- streamingSegmentIds.add(seg.id);
7029
- targetElement.addSegment?.(seg);
7030
- } else {
7031
- targetElement.updateSegment?.(thinkingSegmentId, { content: thinkingContent });
7032
- }
7033
- break;
7034
- }
7035
- case "text": {
7036
- if (thinkingSegmentId && !thinkingCollapsed) {
7037
- targetElement.updateSegment?.(thinkingSegmentId, { collapsed: true, isStreaming: false });
7038
- thinkingCollapsed = true;
7039
- }
7040
- precedingText += event.delta;
7041
- if (artifactRawHint && rawSegId) {
7042
- rawContent += event.delta;
7043
- targetElement.updateSegment?.(rawSegId, { content: rawContent });
7044
- dispatchArtifactLifecycle(targetElement, messageId, {
7045
- id: rawSegId,
7046
- type: "artifact",
7047
- mimeType: artifactRawHint.mimeType,
7048
- artifactType: artifactRawHint.kind,
7049
- title: artifactRawHint.kind,
7050
- content: rawContent
7051
- }, artifactProgress, false);
7052
- break;
7053
- }
7054
- if (artifactXmlHint) {
7055
- feedXmlArtifactDelta(event.delta, xmlCtx, {
7056
- targetElement,
7057
- messageId,
7058
- textParser,
7059
- streamingSegmentIds,
7060
- artifactProgress,
7061
- artifactXmlHint
7062
- });
7063
- break;
7064
- }
7065
- const result = textParser.parse(event.delta);
7066
- for (let segment of result.segments) {
7067
- if (artifactHint && !artifactPromoted && segment.type === "code") {
7068
- const codeSeg = segment;
7069
- const promoted = {
7070
- id: codeSeg.id,
7071
- type: "artifact",
7072
- mimeType: artifactHint.mimeType,
7073
- artifactType: artifactHint.kind,
7074
- title: codeSeg.filename ?? artifactHint.kind,
7075
- content: codeSeg.content
7076
- };
7077
- segment = promoted;
7078
- artifactPromoted = true;
7079
- }
7080
- if (!streamingSegmentIds.has(segment.id)) {
7081
- targetElement.addSegment?.(segment);
7082
- streamingSegmentIds.add(segment.id);
7083
- } else if ("content" in segment) {
7084
- targetElement.updateSegment?.(segment.id, segmentContentUpdate(segment));
7085
- }
7086
- if (segment.type === "artifact") {
7087
- dispatchArtifactLifecycle(targetElement, messageId, segment, artifactProgress, true);
7088
- }
7089
- }
7090
- const active = textParser.getState().activeSegment;
7091
- if (active) {
7092
- if (!streamingSegmentIds.has(active.id)) {
7093
- targetElement.addSegment?.(active);
7094
- streamingSegmentIds.add(active.id);
7095
- if (active.type === "artifact") {
7096
- dispatchArtifactLifecycle(targetElement, messageId, active, artifactProgress, false);
7097
- }
7098
- } else {
7099
- targetElement.updateSegment?.(active.id, segmentContentUpdate(active));
7100
- if (active.type === "artifact") {
7101
- dispatchArtifactLifecycle(targetElement, messageId, active, artifactProgress, false);
7102
- }
7103
- }
7104
- } else if (result.segments.length === 0) {
7105
- }
7106
- break;
7107
- }
7108
- case "tool_use": {
7109
- const toolResult = await this._handleToolUseEvent(
7110
- { id: event.id, name: event.name, input: event.input },
7111
- { targetElement, messageId, messages, toolCallsThisTurn, precedingText, artifactProgress, turns, globalMaxTurns }
7112
- );
7113
- if (toolResult.flow !== "continue") endTurnEarly = true;
7114
- if (toolResult.flow === "halt") continueLoop = false;
7115
- break;
7116
- }
7117
- case "error":
7118
- throw new Error(event.message);
7119
- case "done":
7120
- if (event.usage) lastUsage = event.usage;
7121
- break;
7122
- default: {
7123
- const exhaustive = event;
7124
- void exhaustive;
7125
- this._warnUnknownStreamEvent(event);
7126
- break;
7127
- }
7128
- }
7129
- if (endTurnEarly) break;
7130
- }
7131
- finalizeXmlArtifact(xmlCtx, { targetElement, messageId, artifactProgress, artifactXmlHint, textParser, streamingSegmentIds });
7132
- const finals = textParser.finalize();
7133
- if (artifactRawHint && rawSegId) {
7134
- const lineCount = rawContent.split("\n").length;
7135
- const isInline = lineCount < 15;
7136
- targetElement.updateSegment?.(rawSegId, { content: rawContent, inline: isInline });
7137
- dispatchArtifactLifecycle(targetElement, messageId, {
7138
- id: rawSegId,
7139
- type: "artifact",
7140
- mimeType: artifactRawHint.mimeType,
7141
- artifactType: artifactRawHint.kind,
7142
- title: artifactRawHint.kind,
7143
- content: rawContent,
7144
- inline: isInline
7145
- }, artifactProgress, true);
7146
- }
7147
- if (artifactHint && !artifactPromoted) {
7148
- const codeIdx = finals.findIndex((s) => s.type === "code");
7149
- if (codeIdx !== -1) {
7150
- const codeSeg = finals[codeIdx];
7151
- const promoted = {
7152
- id: codeSeg.id,
7153
- type: "artifact",
7154
- mimeType: artifactHint.mimeType,
7155
- artifactType: artifactHint.kind,
7156
- title: codeSeg.filename ?? artifactHint.kind,
7157
- content: codeSeg.content
7158
- };
7159
- finals[codeIdx] = promoted;
7160
- artifactPromoted = true;
7161
- if (streamingSegmentIds.has(promoted.id)) {
7162
- targetElement.updateSegment?.(promoted.id, promoted);
7163
- }
7164
- }
7165
- }
7166
- for (const s of finals) {
7167
- if (!streamingSegmentIds.has(s.id)) {
7168
- targetElement.addSegment?.(s);
7169
- } else if ("content" in s) {
7170
- targetElement.updateSegment?.(s.id, segmentContentUpdate(s));
7171
- }
7172
- if (s.type === "artifact") {
7173
- dispatchArtifactLifecycle(targetElement, messageId, s, artifactProgress, true);
7174
- }
7175
- }
7176
- if (toolCallsThisTurn.length === 0) {
7177
- if (pipeline && pipelineIndex < pipeline.length - 1) {
7178
- if (precedingText.trim()) {
7179
- messages.push({ role: "assistant", content: precedingText.trim() });
7180
- }
7181
- pipelineIndex++;
7182
- const pwId = `pw-${uuid()}`;
7183
- targetElement.addSegment?.({ id: pwId, type: "pipeline-waiting" });
7184
- } else {
7185
- continueLoop = false;
7186
- }
7187
- }
7188
- } finally {
7189
- if (!streamDrained) {
7190
- try {
7191
- await reader.cancel();
7192
- } catch {
7193
- }
7194
- }
7195
- reader.releaseLock();
7196
- }
7197
- }
7198
- this._updateMessage(targetElement, messageId, { status: "completed" });
7199
- if (lastUsage) {
7200
- try {
7201
- targetElement.setUsage?.(messageId, lastUsage);
7202
- } catch {
7203
- }
7204
- }
7205
- return lastUsage;
6379
+ const runner = this.options.streamRunner ?? runStreamAgent;
6380
+ return this._runViaStreamRunner(runner, targetElement, messageId, provider, baseRequest, authConfig, streamController);
7206
6381
  }
7207
6382
  /**
7208
- * Delegate `_streamLoop`'s agentic loop to an injected {@link AparteStreamRunner},
7209
- * rendering its DOM-free events through {@link createStreamAdapter}.
7210
- * Builds the runner's dependencies from this client's config / provider /
7211
- * transport; the adapter reproduces the inline loop's `targetElement.*` calls
7212
- * (validated by the engine `stream-parity` suite). Leading writes (status
7213
- * streaming, prefix segments) mirror the inline path; the runner's `run-start`
7214
- * 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.
7215
6390
  */
7216
6391
  async _runViaStreamRunner(streamRunner, targetElement, messageId, provider, baseRequest, authConfig, streamController) {
7217
6392
  const signal = streamController.signal;
7218
- this._updateMessage(targetElement, messageId, { status: "streaming" });
7219
- const prefixSegments = baseRequest._meta?.["prefixSegments"];
7220
- if (prefixSegments?.length) {
7221
- for (const seg of prefixSegments) targetElement.addSegment?.(seg);
7222
- }
7223
6393
  const artifactHint = baseRequest._meta?.artifactHint;
7224
- const emitter = createStreamAdapter({
6394
+ const adapter = createStreamAdapter({
7225
6395
  target: targetElement,
7226
6396
  config: this._config,
7227
6397
  messageId,
7228
6398
  artifactHint
7229
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
+ };
7230
6407
  const transportCall = async (request) => {
7231
6408
  const response = await this._config.getTransport().chat(provider, request, authConfig, { providerId: provider.id, signal });
7232
6409
  return typeof response === "string" ? response : readableToAsyncIterable(response, signal);
@@ -7257,7 +6434,7 @@ ${content}
7257
6434
  // inline path, so setting it was silently ignored here. Exactly the
7258
6435
  // asymmetry the option was added to remove.
7259
6436
  toolTimeoutMs: this.options.toolTimeoutMs,
7260
- // 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
7261
6438
  // BARE uuid for the synthetic tool (the adapter renders `tool-<id>`).
7262
6439
  idGen: (prefix) => prefix === "synthetic-tool" ? uuid() : `${prefix}-${uuid()}`
7263
6440
  });
@@ -7298,16 +6475,6 @@ ${content}
7298
6475
  }
7299
6476
  dispatchLifecycleEvent(target, "aparte-message-error", { messageId, error });
7300
6477
  }
7301
- /** Warn once per client for an unrecognised stream event, then carry on. */
7302
- _warnedUnknownEvents = /* @__PURE__ */ new Set();
7303
- _warnUnknownStreamEvent(event) {
7304
- const type = String(event?.type ?? "undefined");
7305
- if (this._warnedUnknownEvents.has(type)) return;
7306
- this._warnedUnknownEvents.add(type);
7307
- console.warn(
7308
- `[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.`
7309
- );
7310
- }
7311
6478
  }
7312
6479
  function findHead(node) {
7313
6480
  if (node.next) return findHead(node.next);
@@ -7479,6 +6646,12 @@ class AparteMessageRepository {
7479
6646
  import(exported) {
7480
6647
  this.clear();
7481
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
+ }
7482
6655
  this.addOrUpdateMessage(parentId, message);
7483
6656
  }
7484
6657
  if (exported.headId) {
@@ -7488,15 +6661,25 @@ class AparteMessageRepository {
7488
6661
  /**
7489
6662
  * Export the full tree for serialisation or debugging.
7490
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.
7491
6671
  */
7492
6672
  export() {
7493
6673
  const messages = [];
7494
- this._nodes.forEach((node) => {
7495
- messages.push({
7496
- message: node.current,
7497
- parentId: node.prev?.current.id ?? null
7498
- });
7499
- });
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);
7500
6683
  return { headId: this.headId, messages };
7501
6684
  }
7502
6685
  // ─── Private helpers ────────────────────────────────────────────────────
@@ -7602,75 +6785,73 @@ function isAwaitingReply(message) {
7602
6785
  return message.status === void 0 && message.role === "assistant" && !message.content?.trim() && !message.segments?.length;
7603
6786
  }
7604
6787
  export {
7605
- resolveConfig as $,
6788
+ runWithConfig as $,
7606
6789
  APARTE_CONFIG_CHANGE as A,
7607
- contentToText as B,
7608
- contextConfig as C,
7609
- createAparteChatHandler as D,
7610
- createStreamAdapter as E,
7611
- cssEscape as F,
7612
- declineDefaultRenderers as G,
7613
- defaultSanitizer as H,
7614
- deriveArtifactKind as I,
7615
- detachConfig as J,
7616
- escapeAttr as K,
7617
- escapeHtml as L,
7618
- filesToAttachments as M,
7619
- getAllRenderers as N,
7620
- getSegmentRenderer as O,
7621
- installDefaultRenderersOnce as P,
7622
- isAwaitingReply as Q,
7623
- isFormatAdapter as R,
7624
- isSafeUrl as S,
7625
- isSegmentSettled as T,
7626
- parseAparteEventStream as U,
7627
- parseMarkdownToSegments as V,
7628
- populateBubbleFromMessage as W,
7629
- readableToAsyncIterable as X,
7630
- registerDefaultRenderers as Y,
7631
- registerSegmentRenderer as Z,
7632
- 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 _,
7633
6816
  APARTE_CONVERSATION_SCHEMA_VERSION as a,
7634
- revokeAttachmentUrls as a0,
7635
- runWithConfig as a1,
7636
- segmentDuration as a2,
7637
- segmentTiming as a3,
7638
- subscribeConfigChange as a4,
7639
- unregisterSegmentRenderer as a5,
7640
- uuid as a6,
7641
- mergeSegmentUpdate as a7,
7642
- writeStreamedMarkdown as a8,
7643
- stampSegmentActivity as a9,
7644
- stampSegmentOnInsert as aa,
7645
- stampSegmentOnUpdate as ab,
7646
- renumberSegments as ac,
7647
- openSegmentIds as ad,
7648
- isTerminalStatus as ae,
7649
- 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,
7650
6831
  APARTE_DEFAULT_BUBBLE_ACTIONS as b,
7651
6832
  APARTE_DEFAULT_HOST_HANDLERS as c,
7652
6833
  APARTE_DEFAULT_ICON_FALLBACKS as d,
7653
6834
  APARTE_DEFAULT_LOCALE as e,
7654
- APARTE_DEFAULT_SKELETON_FALLBACKS as f,
7655
- APARTE_DEFAULT_UI_EVENTS as g,
7656
- APARTE_HOST_ATTR as h,
7657
- AparteBackendTransport as i,
7658
- AparteChatHost as j,
7659
- AparteClient as k,
7660
- AparteConfig as l,
7661
- AparteConversationController as m,
7662
- AparteConversationManager as n,
7663
- AparteDirectTransport as o,
7664
- AparteElicitationAbortError as p,
7665
- AparteError as q,
7666
- AparteErrorCode as r,
7667
- AparteMessageRepository as s,
7668
- AparteStreamParser as t,
7669
- aparteGlobalConfig as u,
7670
- applyElementProps as v,
7671
- attachConfig as w,
7672
- buildApprovalPanel as x,
7673
- buildElicitationPanel as y,
7674
- 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
7675
6856
  };
7676
- //# sourceMappingURL=is-awaiting-reply-CWp1ffHh.js.map
6857
+ //# sourceMappingURL=is-awaiting-reply-BZ_iJ3s3.js.map