@aparte/core 0.16.8 → 0.16.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/aparte-client.d.ts +57 -2
- package/dist/client/aparte-client.d.ts.map +1 -1
- package/dist/client/lifecycle-events.d.ts +8 -1
- package/dist/client/lifecycle-events.d.ts.map +1 -1
- package/dist/client/stream-adapter.d.ts +8 -0
- package/dist/client/stream-adapter.d.ts.map +1 -1
- package/dist/components/bubble/aparte-chat-bubble.d.ts +6 -0
- package/dist/components/bubble/aparte-chat-bubble.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer-attachments.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer-input.d.ts +1 -1
- package/dist/components/composer/aparte-composer.d.ts.map +1 -1
- package/dist/components/conversation-list/aparte-conversation-list.d.ts +5 -0
- package/dist/components/conversation-list/aparte-conversation-list.d.ts.map +1 -1
- package/dist/components/scroll-rail/aparte-scroll-rail.d.ts +3 -2
- package/dist/components/scroll-rail/aparte-scroll-rail.d.ts.map +1 -1
- package/dist/components/status/aparte-chat-status.d.ts +41 -3
- package/dist/components/status/aparte-chat-status.d.ts.map +1 -1
- package/dist/custom-elements.json +12484 -12344
- package/dist/{dialog-triggers-D8wNICrT.js → dialog-triggers-CApC7K1I.js} +96 -26
- package/dist/{dialog-triggers-D8wNICrT.js.map → dialog-triggers-CApC7K1I.js.map} +1 -1
- package/dist/elicitation/panel.d.ts.map +1 -1
- package/dist/index.css +119 -31
- package/dist/index.js +157 -47
- package/dist/index.js.map +1 -1
- package/dist/index.node.js +1 -1
- package/dist/interop/element-props.d.ts.map +1 -1
- package/dist/primitives/select/aparte-select.d.ts +25 -3
- package/dist/primitives/select/aparte-select.d.ts.map +1 -1
- package/dist/types/events.d.ts +4 -2
- package/dist/types/events.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -4385,13 +4385,13 @@ class AparteChatHost {
|
|
|
4385
4385
|
});
|
|
4386
4386
|
}
|
|
4387
4387
|
}
|
|
4388
|
-
function dispatchLifecycleEvent(target, name, detail) {
|
|
4389
|
-
const id = target.id || void 0;
|
|
4388
|
+
function dispatchLifecycleEvent(target, name, detail, targetId) {
|
|
4389
|
+
const id = targetId ?? (target.id || void 0);
|
|
4390
4390
|
const stamped = detail && typeof detail === "object" ? { targetId: id, ...detail } : detail;
|
|
4391
4391
|
target.dispatchEvent(new CustomEvent(name, { bubbles: true, composed: true, detail: stamped }));
|
|
4392
4392
|
}
|
|
4393
4393
|
function createStreamAdapter(opts) {
|
|
4394
|
-
const { target, config, messageId } = opts;
|
|
4394
|
+
const { target, config, messageId, targetId } = opts;
|
|
4395
4395
|
let parser = new AparteStreamParser({ blocks: config.getStreamBlocks() });
|
|
4396
4396
|
let streaming = /* @__PURE__ */ new Set();
|
|
4397
4397
|
let thinkingId = null;
|
|
@@ -4479,7 +4479,7 @@ function createStreamAdapter(opts) {
|
|
|
4479
4479
|
}
|
|
4480
4480
|
case "tool-awaiting-approval":
|
|
4481
4481
|
target.updateSegment?.(`tool-${e.toolCallId}`, { status: "awaiting-approval" });
|
|
4482
|
-
dispatchLifecycleEvent(target, "aparte-tool-approval-request", { toolCallId: e.toolCallId, toolName: e.name, input: e.input });
|
|
4482
|
+
dispatchLifecycleEvent(target, "aparte-tool-approval-request", { toolCallId: e.toolCallId, toolName: e.name, input: e.input }, targetId);
|
|
4483
4483
|
break;
|
|
4484
4484
|
case "tool-approved":
|
|
4485
4485
|
target.updateSegment?.(`tool-${e.toolCallId}`, { status: "pending" });
|
|
@@ -4513,7 +4513,7 @@ function createStreamAdapter(opts) {
|
|
|
4513
4513
|
}
|
|
4514
4514
|
break;
|
|
4515
4515
|
case "run-aborted":
|
|
4516
|
-
dispatchLifecycleEvent(target, "aparte-message-aborted", { messageId });
|
|
4516
|
+
dispatchLifecycleEvent(target, "aparte-message-aborted", { messageId }, targetId);
|
|
4517
4517
|
break;
|
|
4518
4518
|
case "run-done":
|
|
4519
4519
|
target.updateMessage?.(messageId, { status: "completed" });
|
|
@@ -4690,12 +4690,20 @@ function buildEnumField(field, onChange, fallbackLabel, settle) {
|
|
|
4690
4690
|
wrap.appendChild(list);
|
|
4691
4691
|
const controls = () => Array.from(list.querySelectorAll(".aparte-elic-control"));
|
|
4692
4692
|
const other = () => list.querySelector('input[value="__other__"]');
|
|
4693
|
+
let arrowed = false;
|
|
4694
|
+
list.addEventListener("keydown", (e) => {
|
|
4695
|
+
arrowed = e.key.startsWith("Arrow");
|
|
4696
|
+
});
|
|
4697
|
+
list.addEventListener("click", () => {
|
|
4698
|
+
arrowed = false;
|
|
4699
|
+
});
|
|
4693
4700
|
list.addEventListener("change", () => {
|
|
4694
4701
|
const o = other();
|
|
4695
4702
|
if (o && otherText) {
|
|
4696
4703
|
otherText.style.display = o.checked ? "" : "none";
|
|
4697
|
-
if (o.checked) otherText.focus();
|
|
4704
|
+
if (o.checked && !arrowed) otherText.focus();
|
|
4698
4705
|
}
|
|
4706
|
+
arrowed = false;
|
|
4699
4707
|
onChange();
|
|
4700
4708
|
});
|
|
4701
4709
|
otherText?.addEventListener("input", onChange);
|
|
@@ -5313,6 +5321,7 @@ class AparteClient {
|
|
|
5313
5321
|
* sequence, so it can't drift between the three entry points.
|
|
5314
5322
|
*/
|
|
5315
5323
|
async _streamTurn(targetElement, messageId, provider, messages, modelId, authConfig, opts) {
|
|
5324
|
+
const chatId = this._targetIdOf(targetElement);
|
|
5316
5325
|
const registeredTools = this._toolsForCurrentModel();
|
|
5317
5326
|
let baseRequest = {
|
|
5318
5327
|
messages,
|
|
@@ -5328,22 +5337,22 @@ class AparteClient {
|
|
|
5328
5337
|
baseRequest = { ...baseRequest, tools: void 0 };
|
|
5329
5338
|
}
|
|
5330
5339
|
if (this._isAborted) {
|
|
5331
|
-
dispatchLifecycleEvent(targetElement, "aparte-message-aborted", { messageId });
|
|
5340
|
+
dispatchLifecycleEvent(targetElement, "aparte-message-aborted", { messageId }, chatId);
|
|
5332
5341
|
this._updateMessage(targetElement, messageId, { status: "completed" });
|
|
5333
5342
|
return;
|
|
5334
5343
|
}
|
|
5335
|
-
dispatchLifecycleEvent(targetElement, "aparte-message-start", { messageId, role: "assistant" });
|
|
5344
|
+
dispatchLifecycleEvent(targetElement, "aparte-message-start", { messageId, role: "assistant" }, chatId);
|
|
5336
5345
|
const streamController = new AbortController();
|
|
5337
5346
|
try {
|
|
5338
5347
|
const usage = await this._streamLoop(targetElement, messageId, provider, baseRequest, authConfig, streamController);
|
|
5339
5348
|
if (this._isAborted || streamController.signal.aborted) {
|
|
5340
5349
|
this._updateMessage(targetElement, messageId, { status: "completed" });
|
|
5341
5350
|
} else {
|
|
5342
|
-
dispatchLifecycleEvent(targetElement, "aparte-message-done", { messageId, role: "assistant", usage });
|
|
5351
|
+
dispatchLifecycleEvent(targetElement, "aparte-message-done", { messageId, role: "assistant", usage }, chatId);
|
|
5343
5352
|
}
|
|
5344
5353
|
} catch (error) {
|
|
5345
5354
|
if (this._isAborted || streamController.signal.aborted) {
|
|
5346
|
-
dispatchLifecycleEvent(targetElement, "aparte-message-aborted", { messageId });
|
|
5355
|
+
dispatchLifecycleEvent(targetElement, "aparte-message-aborted", { messageId }, chatId);
|
|
5347
5356
|
this._updateMessage(targetElement, messageId, { status: "completed" });
|
|
5348
5357
|
return;
|
|
5349
5358
|
}
|
|
@@ -5531,13 +5540,64 @@ class AparteClient {
|
|
|
5531
5540
|
return null;
|
|
5532
5541
|
}
|
|
5533
5542
|
/**
|
|
5534
|
-
*
|
|
5543
|
+
* Which chat a render target BELONGS to — the id the lifecycle events must carry.
|
|
5544
|
+
*
|
|
5545
|
+
* Not `target.id`: the target is whatever renders, and an `<aparte-chat>` shell
|
|
5546
|
+
* delegates that to its `.viewport`, which has no id. So every lifecycle event on
|
|
5547
|
+
* a shell-shaped chat went out unstamped, and the receive side reads a missing id
|
|
5548
|
+
* as "for every chat on the page" — deliberately, so a single-chat page needs no
|
|
5549
|
+
* wiring. On a two-chat page one chat's turn therefore drove every composer.
|
|
5550
|
+
*
|
|
5551
|
+
* The climb is the same rule `aparte-composer._ownTargetId` uses on the receive
|
|
5552
|
+
* side, and it has to be, or the two would disagree about which chat is which:
|
|
5553
|
+
* the nearest `aparte-chat` / `[data-aparte-chat]` ancestor (or the element
|
|
5554
|
+
* itself) that HAS an id. Deliberately not `chatBoundaryOf` — that returns the
|
|
5555
|
+
* boundary whether or not it is identified, and an id-less boundary must fall
|
|
5556
|
+
* through to the next one up rather than answer "no id".
|
|
5557
|
+
*/
|
|
5558
|
+
_targetIdOf(el2) {
|
|
5559
|
+
let node = el2 ?? null;
|
|
5560
|
+
while (node) {
|
|
5561
|
+
const tag = node.tagName?.toLowerCase();
|
|
5562
|
+
const isHost = tag === "aparte-chat" || node.hasAttribute?.("data-aparte-chat");
|
|
5563
|
+
if (isHost && node.id) return node.id;
|
|
5564
|
+
node = node.parentElement;
|
|
5565
|
+
}
|
|
5566
|
+
return el2?.id || void 0;
|
|
5567
|
+
}
|
|
5568
|
+
/**
|
|
5569
|
+
* Convert AparteMessage[] to AparteChatMessage[] for re-submission (retry, edit).
|
|
5570
|
+
*
|
|
5571
|
+
* The SLICE is each caller's own business — retry cuts before the reply it is
|
|
5572
|
+
* regenerating, edit cuts after the message being reworded, send cuts at the last
|
|
5573
|
+
* answered turn. What a message CONTRIBUTES to the wire is not: send, retry and
|
|
5574
|
+
* edit must not disagree about it, and they did. This one had no filter at all,
|
|
5575
|
+
* so a failed turn and a turn stopped before its first token both went out as
|
|
5576
|
+
* `{ role: 'assistant', content: '' }` — a claim the assistant said nothing, which
|
|
5577
|
+
* some providers reject outright and the rest read as an empty reply worth
|
|
5578
|
+
* imitating.
|
|
5535
5579
|
*/
|
|
5536
5580
|
_messagesToChatMessages(messages) {
|
|
5537
|
-
return
|
|
5581
|
+
return this._wireHistory(messages);
|
|
5582
|
+
}
|
|
5583
|
+
/**
|
|
5584
|
+
* The one rule for what a message contributes to the wire.
|
|
5585
|
+
*
|
|
5586
|
+
* Not the slice — the contribution: a role the wire has (`user`/`assistant`), a
|
|
5587
|
+
* turn that neither failed nor is still in flight, and text that is not empty.
|
|
5588
|
+
*
|
|
5589
|
+
* `_wireText` (not `m.content`): assistant replies stream their text into
|
|
5590
|
+
* `segments`, leaving `content` as '', so without flattening every reply would be
|
|
5591
|
+
* empty here and the drop below would swallow the whole transcript.
|
|
5592
|
+
*/
|
|
5593
|
+
_wireHistory(messages) {
|
|
5594
|
+
return messages.filter((m) => {
|
|
5595
|
+
if (m.role !== "user" && m.role !== "assistant") return false;
|
|
5596
|
+
return m.status !== "error" && !AparteClient._inFlight(m);
|
|
5597
|
+
}).map((m) => ({
|
|
5538
5598
|
role: m.role,
|
|
5539
5599
|
content: this._wireText(m)
|
|
5540
|
-
}));
|
|
5600
|
+
})).filter((m) => m.content.length > 0);
|
|
5541
5601
|
}
|
|
5542
5602
|
/** The "no model selected" warning is said once per client, not once per dropped send. */
|
|
5543
5603
|
_warnedNoModel = false;
|
|
@@ -5671,22 +5731,30 @@ class AparteClient {
|
|
|
5671
5731
|
messages.push({ role: "user", content: userMsg });
|
|
5672
5732
|
return messages;
|
|
5673
5733
|
}
|
|
5734
|
+
/**
|
|
5735
|
+
* A turn that has not finished yet. `status` is OPTIONAL on `AparteMessage`, so
|
|
5736
|
+
* the question the history gates can ask is "is this still in flight?", never
|
|
5737
|
+
* "is this completed?".
|
|
5738
|
+
*
|
|
5739
|
+
* The gates below used to ask the second one, and a host that SEEDS a transcript
|
|
5740
|
+
* — restoring a saved conversation, hydrating a server-rendered one — has no
|
|
5741
|
+
* reason to invent a status for turns that are already over. With none, the
|
|
5742
|
+
* cutoff never advanced past 0 and the whole transcript was sliced away: the
|
|
5743
|
+
* model got the new question with no context, and nothing in the UI said so,
|
|
5744
|
+
* because the viewport still showed every message.
|
|
5745
|
+
*/
|
|
5746
|
+
static _inFlight(m) {
|
|
5747
|
+
return m.status === "streaming" || m.status === "pending";
|
|
5748
|
+
}
|
|
5674
5749
|
_toHistoryMessages(messages) {
|
|
5675
5750
|
let cutoff = 0;
|
|
5676
5751
|
for (let i = 0; i < messages.length; i++) {
|
|
5677
5752
|
const m = messages[i];
|
|
5678
|
-
if (m.role === "assistant" && m.status
|
|
5753
|
+
if (m.role === "assistant" && m.status !== "error" && !AparteClient._inFlight(m)) {
|
|
5679
5754
|
cutoff = i + 1;
|
|
5680
5755
|
}
|
|
5681
5756
|
}
|
|
5682
|
-
return messages.slice(0, cutoff)
|
|
5683
|
-
if (m.role === "user") return m.status !== "error";
|
|
5684
|
-
if (m.role === "assistant") return m.status === "completed";
|
|
5685
|
-
return false;
|
|
5686
|
-
}).map((m) => ({
|
|
5687
|
-
role: m.role,
|
|
5688
|
-
content: this._wireText(m)
|
|
5689
|
-
})).filter((m) => m.content.length > 0);
|
|
5757
|
+
return this._wireHistory(messages.slice(0, cutoff));
|
|
5690
5758
|
}
|
|
5691
5759
|
/**
|
|
5692
5760
|
* A message's text as the MODEL should read it — the one mapper both history
|
|
@@ -5859,10 +5927,12 @@ ${content}
|
|
|
5859
5927
|
*/
|
|
5860
5928
|
async _runViaStreamRunner(streamRunner, targetElement, messageId, provider, baseRequest, authConfig, streamController) {
|
|
5861
5929
|
const signal = streamController.signal;
|
|
5930
|
+
const chatId = this._targetIdOf(targetElement);
|
|
5862
5931
|
const adapter = createStreamAdapter({
|
|
5863
5932
|
target: targetElement,
|
|
5864
5933
|
config: this._config,
|
|
5865
|
-
messageId
|
|
5934
|
+
messageId,
|
|
5935
|
+
...chatId ? { targetId: chatId } : {}
|
|
5866
5936
|
});
|
|
5867
5937
|
const prefixSegments = baseRequest._meta?.["prefixSegments"];
|
|
5868
5938
|
const emitter = (event) => {
|
|
@@ -5955,7 +6025,7 @@ ${content}
|
|
|
5955
6025
|
segments: [errorSegment]
|
|
5956
6026
|
});
|
|
5957
6027
|
}
|
|
5958
|
-
dispatchLifecycleEvent(target, "aparte-message-error", { messageId, error });
|
|
6028
|
+
dispatchLifecycleEvent(target, "aparte-message-error", { messageId, error }, this._targetIdOf(target));
|
|
5959
6029
|
}
|
|
5960
6030
|
}
|
|
5961
6031
|
function findHead(node) {
|
|
@@ -6269,7 +6339,7 @@ function applyElementProps(el2, props, transformValue = (value) => value) {
|
|
|
6269
6339
|
if (value === null || value === void 0) el2.style.removeProperty(key);
|
|
6270
6340
|
else if (typeof value === "object" || typeof value === "function") el2.style.removeProperty(key);
|
|
6271
6341
|
else el2.style.setProperty(key, String(value));
|
|
6272
|
-
} else if (key.startsWith("on")) {
|
|
6342
|
+
} else if (key.toLowerCase().startsWith("on")) {
|
|
6273
6343
|
el2.removeAttribute(key);
|
|
6274
6344
|
} else if (value === null || value === void 0 || value === false) {
|
|
6275
6345
|
el2.removeAttribute(key);
|
|
@@ -6407,4 +6477,4 @@ export {
|
|
|
6407
6477
|
collectRendererStyles as y,
|
|
6408
6478
|
contentToText as z
|
|
6409
6479
|
};
|
|
6410
|
-
//# sourceMappingURL=dialog-triggers-
|
|
6480
|
+
//# sourceMappingURL=dialog-triggers-CApC7K1I.js.map
|