@artooi/ag-ui-web-component 0.2.0 → 0.2.1
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/CHANGELOG.md +24 -1
- package/README.md +7 -3
- package/dist/ag-ui-web-component.bundle.js +7 -7
- package/dist/ag-ui-web-component.bundle.js.map +3 -3
- package/dist/core/ag_ui_chat.d.ts.map +1 -1
- package/dist/core/agui_client.d.ts +13 -0
- package/dist/core/agui_client.d.ts.map +1 -1
- package/dist/index.js +72 -9
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
- package/src/core/ag_ui_chat.ts +73 -8
- package/src/core/agui_client.ts +18 -0
- package/src/version.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@artooi/ag-ui-web-component",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Framework-free <ag-ui-chat> Web Component over the AG-UI protocol. Drop-in chat sidebar with a pluggable client-side tool registry, DOM driver primitives, animations, and destructive-action confirmation modal.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
package/src/core/ag_ui_chat.ts
CHANGED
|
@@ -152,6 +152,12 @@ export class AgUiChat extends HTMLElement {
|
|
|
152
152
|
readonly #toolRegistry = new ClientToolRegistry();
|
|
153
153
|
/** Tool-call cards awaiting execution, keyed by call id. */
|
|
154
154
|
readonly #toolCards = new Map<string, ToolCallCard>();
|
|
155
|
+
/**
|
|
156
|
+
* Call ids whose card was already settled from a streamed server-side result
|
|
157
|
+
* (`TOOL_CALL_RESULT`), so the post-run executeTool sweep doesn't overwrite
|
|
158
|
+
* the real output with the generic "executed on the server" fallback.
|
|
159
|
+
*/
|
|
160
|
+
readonly #serverSettled = new Set<string>();
|
|
155
161
|
readonly #root: ShadowRoot;
|
|
156
162
|
readonly #chat: HTMLDivElement;
|
|
157
163
|
readonly #messages: HTMLDivElement;
|
|
@@ -410,6 +416,7 @@ export class AgUiChat extends HTMLElement {
|
|
|
410
416
|
this.#streamingBubble = null;
|
|
411
417
|
this.#hidePending();
|
|
412
418
|
this.#toolCards.clear();
|
|
419
|
+
this.#serverSettled.clear();
|
|
413
420
|
this.#initialMessages = [];
|
|
414
421
|
this.#messages.replaceChildren();
|
|
415
422
|
this.#threadId = this.conversationStore.threadId();
|
|
@@ -435,18 +442,57 @@ export class AgUiChat extends HTMLElement {
|
|
|
435
442
|
}
|
|
436
443
|
}
|
|
437
444
|
|
|
438
|
-
/**
|
|
445
|
+
/**
|
|
446
|
+
* Replay a restored message: text bubbles *and* tool activity. An assistant
|
|
447
|
+
* turn may carry `toolCalls` (rendered as cards) and/or text; a `tool` turn
|
|
448
|
+
* carries a result that settles the matching card. So a refreshed page shows
|
|
449
|
+
* the full transcript — tool calls and their results — not just the prose.
|
|
450
|
+
*/
|
|
439
451
|
#renderHistoricMessage(message: Message): void {
|
|
440
|
-
|
|
452
|
+
const text = typeof message.content === "string" ? message.content : "";
|
|
453
|
+
if (message.role === MESSAGE_ROLE.USER) {
|
|
454
|
+
if (text !== "") {
|
|
455
|
+
this.appendMessage(MESSAGE_ROLE.USER, text);
|
|
456
|
+
}
|
|
441
457
|
return;
|
|
442
458
|
}
|
|
443
|
-
if (message.role === MESSAGE_ROLE.
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
459
|
+
if (message.role === MESSAGE_ROLE.ASSISTANT) {
|
|
460
|
+
if (text !== "") {
|
|
461
|
+
this.#revealWords(this.appendMessage(MESSAGE_ROLE.ASSISTANT, text));
|
|
462
|
+
}
|
|
463
|
+
const toolCalls = message.toolCalls;
|
|
464
|
+
if (toolCalls !== undefined) {
|
|
465
|
+
for (const call of toolCalls) {
|
|
466
|
+
this.#cardFor({
|
|
467
|
+
id: call.id,
|
|
468
|
+
name: call.function.name,
|
|
469
|
+
args: this.#parseArgs(call.function.arguments),
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
if (message.role === "tool") {
|
|
476
|
+
const card = this.#toolCards.get(message.toolCallId);
|
|
477
|
+
if (card !== undefined) {
|
|
478
|
+
card.settle(TOOL_CALL_STATUS.DONE, message.content);
|
|
479
|
+
}
|
|
447
480
|
}
|
|
448
481
|
}
|
|
449
482
|
|
|
483
|
+
/** Parse a tool call's JSON `arguments` string from history into an object. */
|
|
484
|
+
#parseArgs(raw: string): Record<string, unknown> {
|
|
485
|
+
try {
|
|
486
|
+
const parsed: unknown = JSON.parse(raw);
|
|
487
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
488
|
+
return parsed as Record<string, unknown>;
|
|
489
|
+
}
|
|
490
|
+
} catch {
|
|
491
|
+
// Malformed history — fall back to empty args rather than failing replay.
|
|
492
|
+
}
|
|
493
|
+
return {};
|
|
494
|
+
}
|
|
495
|
+
|
|
450
496
|
/**
|
|
451
497
|
* Word-by-word reveal for the `word` text-animation mode, applied to a
|
|
452
498
|
* completed assistant bubble. `fade` is pure CSS (no JS); `none` is a no-op.
|
|
@@ -639,8 +685,13 @@ export class AgUiChat extends HTMLElement {
|
|
|
639
685
|
const tool = this.#resolveTool(call.name);
|
|
640
686
|
if (tool === null) {
|
|
641
687
|
// A server-side tool the server already executed — not ours to re-run.
|
|
642
|
-
|
|
643
|
-
|
|
688
|
+
// Its real output usually arrived via `onToolResult` (TOOL_CALL_RESULT)
|
|
689
|
+
// and already settled the card; only fall back when it didn't. We do NOT
|
|
690
|
+
// show the pending indicator here: a server tool never triggers another
|
|
691
|
+
// client round, so showing it would leave it stuck after the run ended.
|
|
692
|
+
if (!this.#serverSettled.has(call.id)) {
|
|
693
|
+
card.settle(TOOL_CALL_STATUS.DONE, "Executed on the server.");
|
|
694
|
+
}
|
|
644
695
|
return null;
|
|
645
696
|
}
|
|
646
697
|
if (await this.#needsConfirmation(call, tool)) {
|
|
@@ -708,6 +759,14 @@ export class AgUiChat extends HTMLElement {
|
|
|
708
759
|
this.#hidePending();
|
|
709
760
|
this.#cardFor(call);
|
|
710
761
|
},
|
|
762
|
+
onToolResult: (toolCallId, content) => {
|
|
763
|
+
const card = this.#toolCards.get(toolCallId);
|
|
764
|
+
if (card === undefined) {
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
card.settle(TOOL_CALL_STATUS.DONE, content);
|
|
768
|
+
this.#serverSettled.add(toolCallId);
|
|
769
|
+
},
|
|
711
770
|
onRunEnd: () => {
|
|
712
771
|
this.#hidePending();
|
|
713
772
|
this.#send.disabled = false;
|
|
@@ -719,6 +778,12 @@ export class AgUiChat extends HTMLElement {
|
|
|
719
778
|
this.#send.disabled = false;
|
|
720
779
|
this.#streamingBubble = null;
|
|
721
780
|
},
|
|
781
|
+
onSettled: () => {
|
|
782
|
+
// Terminal guarantee: whatever path ended the run, return to rest.
|
|
783
|
+
this.#hidePending();
|
|
784
|
+
this.#send.disabled = false;
|
|
785
|
+
this.#streamingBubble = null;
|
|
786
|
+
},
|
|
722
787
|
};
|
|
723
788
|
}
|
|
724
789
|
|
package/src/core/agui_client.ts
CHANGED
|
@@ -45,8 +45,21 @@ export interface AgUiClientHandlers {
|
|
|
45
45
|
onTextEnd(buffer: string): void;
|
|
46
46
|
/** Fired when the agent finishes calling a tool (server- or frontend-side). */
|
|
47
47
|
onToolCall(call: AgUiToolCall): void;
|
|
48
|
+
/**
|
|
49
|
+
* Fired when a server-side tool's result streams back (AG-UI's
|
|
50
|
+
* `TOOL_CALL_RESULT`). Frontend tools don't emit this — the client supplies
|
|
51
|
+
* their result itself — so this is the channel for server-executed output.
|
|
52
|
+
*/
|
|
53
|
+
onToolResult(toolCallId: string, content: string): void;
|
|
48
54
|
onRunEnd(): void;
|
|
49
55
|
onError(message: string): void;
|
|
56
|
+
/**
|
|
57
|
+
* Fired exactly once when the whole interaction settles — after the run loop
|
|
58
|
+
* ends for any reason (a server-only round, frontend-tool rounds exhausted,
|
|
59
|
+
* or an error). The terminal guarantee that the UI returns to rest (pending
|
|
60
|
+
* indicator cleared, input re-enabled) no matter how the run finished.
|
|
61
|
+
*/
|
|
62
|
+
onSettled(): void;
|
|
50
63
|
}
|
|
51
64
|
|
|
52
65
|
/**
|
|
@@ -140,6 +153,8 @@ export class AgUiClient {
|
|
|
140
153
|
await this.#runLoop();
|
|
141
154
|
} catch (error) {
|
|
142
155
|
this.#handlers.onError(error instanceof Error ? error.message : String(error));
|
|
156
|
+
} finally {
|
|
157
|
+
this.#handlers.onSettled();
|
|
143
158
|
}
|
|
144
159
|
}
|
|
145
160
|
|
|
@@ -201,6 +216,9 @@ export class AgUiClient {
|
|
|
201
216
|
pending.push(call);
|
|
202
217
|
h.onToolCall(call);
|
|
203
218
|
},
|
|
219
|
+
onToolCallResultEvent({ event }) {
|
|
220
|
+
h.onToolResult(event.toolCallId, event.content);
|
|
221
|
+
},
|
|
204
222
|
onRunErrorEvent({ event }) {
|
|
205
223
|
h.onError(event.message);
|
|
206
224
|
},
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION: string = "0.2.
|
|
1
|
+
export const VERSION: string = "0.2.1";
|