@artooi/ag-ui-web-component 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +86 -1
- package/README.md +98 -5
- package/dist/ag-ui-web-component.bundle.js +126 -50
- package/dist/ag-ui-web-component.bundle.js.map +4 -4
- package/dist/constants.d.ts +9 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/core/ag_ui_chat.d.ts +29 -4
- package/dist/core/ag_ui_chat.d.ts.map +1 -1
- package/dist/core/agui_client.d.ts +11 -0
- package/dist/core/agui_client.d.ts.map +1 -1
- package/dist/core/create_http_agent.d.ts +6 -0
- package/dist/core/create_http_agent.d.ts.map +1 -1
- package/dist/core/run_index.d.ts +50 -0
- package/dist/core/run_index.d.ts.map +1 -0
- package/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +453 -63
- package/dist/index.js.map +3 -3
- package/dist/tools/page_state.d.ts +40 -0
- package/dist/tools/page_state.d.ts.map +1 -0
- package/dist/ui/checkpoint_menu.d.ts +32 -0
- package/dist/ui/checkpoint_menu.d.ts.map +1 -0
- package/dist/ui/styles.d.ts +1 -1
- package/dist/ui/styles.d.ts.map +1 -1
- package/dist/ui/ui_strings.d.ts +10 -0
- package/dist/ui/ui_strings.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +10 -0
- package/src/core/ag_ui_chat.ts +153 -6
- package/src/core/agui_client.ts +28 -0
- package/src/core/create_http_agent.ts +7 -0
- package/src/core/run_index.ts +91 -0
- package/src/index.ts +14 -1
- package/src/tools/page_state.ts +72 -0
- package/src/ui/checkpoint_menu.ts +153 -0
- package/src/ui/styles.ts +76 -0
- package/src/ui/ui_strings.ts +15 -0
- package/src/version.ts +1 -1
- package/dist/tools/state_hook.d.ts +0 -23
- package/dist/tools/state_hook.d.ts.map +0 -1
- package/src/tools/state_hook.ts +0 -53
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
var ELEMENT_TAG = "ag-ui-chat";
|
|
3
3
|
var SUBMIT_EVENT = "ag-ui-submit";
|
|
4
4
|
var TOGGLE_EVENT = "ag-ui-toggle";
|
|
5
|
+
var STATE_EVENT = "ag-ui-state";
|
|
5
6
|
var MESSAGE_ROLE = {
|
|
6
7
|
USER: "user",
|
|
7
8
|
ASSISTANT: "assistant"
|
|
@@ -318,6 +319,38 @@ function createPageMapContext(getPageMap, autoInject) {
|
|
|
318
319
|
return [{ description: "page_map", value: JSON.stringify(getPageMap()) }];
|
|
319
320
|
}
|
|
320
321
|
|
|
322
|
+
// src/tools/page_state.ts
|
|
323
|
+
function createPageStateTools(binding) {
|
|
324
|
+
const tools = [
|
|
325
|
+
{
|
|
326
|
+
name: `read_${binding.name}`,
|
|
327
|
+
description: `Read the "${binding.name}" state.`,
|
|
328
|
+
parameters: {
|
|
329
|
+
type: "object",
|
|
330
|
+
properties: {},
|
|
331
|
+
required: [],
|
|
332
|
+
[X_SUMMARY_KEY]: `Read ${binding.name}`
|
|
333
|
+
},
|
|
334
|
+
handler: () => binding.read()
|
|
335
|
+
}
|
|
336
|
+
];
|
|
337
|
+
const write = binding.write;
|
|
338
|
+
if (write !== void 0) {
|
|
339
|
+
tools.push({
|
|
340
|
+
name: `set_${binding.name}`,
|
|
341
|
+
description: `Update the "${binding.name}" state.`,
|
|
342
|
+
parameters: {
|
|
343
|
+
...binding.schema ?? { type: "object" },
|
|
344
|
+
[X_DESTRUCTIVE_KEY]: true,
|
|
345
|
+
[X_SUMMARY_KEY]: `Update ${binding.name}`
|
|
346
|
+
},
|
|
347
|
+
handler: (args) => write(args)
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
return tools;
|
|
351
|
+
}
|
|
352
|
+
var createStateHookTools = createPageStateTools;
|
|
353
|
+
|
|
321
354
|
// src/tools/parse_tool_catalog.ts
|
|
322
355
|
function parseToolCatalog(data) {
|
|
323
356
|
const out = {};
|
|
@@ -412,37 +445,6 @@ function createRouteTools(getRouteMap, getNavigate) {
|
|
|
412
445
|
];
|
|
413
446
|
}
|
|
414
447
|
|
|
415
|
-
// src/tools/state_hook.ts
|
|
416
|
-
function createStateHookTools(hook) {
|
|
417
|
-
const tools = [
|
|
418
|
-
{
|
|
419
|
-
name: `read_${hook.name}`,
|
|
420
|
-
description: `Read the "${hook.name}" state.`,
|
|
421
|
-
parameters: {
|
|
422
|
-
type: "object",
|
|
423
|
-
properties: {},
|
|
424
|
-
required: [],
|
|
425
|
-
[X_SUMMARY_KEY]: `Read ${hook.name}`
|
|
426
|
-
},
|
|
427
|
-
handler: () => hook.read()
|
|
428
|
-
}
|
|
429
|
-
];
|
|
430
|
-
const write = hook.write;
|
|
431
|
-
if (write !== void 0) {
|
|
432
|
-
tools.push({
|
|
433
|
-
name: `set_${hook.name}`,
|
|
434
|
-
description: `Update the "${hook.name}" state.`,
|
|
435
|
-
parameters: {
|
|
436
|
-
...hook.schema ?? { type: "object" },
|
|
437
|
-
[X_DESTRUCTIVE_KEY]: true,
|
|
438
|
-
[X_SUMMARY_KEY]: `Update ${hook.name}`
|
|
439
|
-
},
|
|
440
|
-
handler: (args) => write(args)
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
return tools;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
448
|
// src/ui/ui_strings.ts
|
|
447
449
|
var DEFAULT_UI_STRINGS = {
|
|
448
450
|
title: "Assistant",
|
|
@@ -451,6 +453,11 @@ var DEFAULT_UI_STRINGS = {
|
|
|
451
453
|
collapse: "Collapse",
|
|
452
454
|
expand: "Expand",
|
|
453
455
|
toggleTheme: "Toggle theme",
|
|
456
|
+
checkpoints: "Continue a run",
|
|
457
|
+
noCheckpoints: "Nothing to continue yet.",
|
|
458
|
+
resumeRun: "Resume",
|
|
459
|
+
forkRun: "Fork",
|
|
460
|
+
forkedRun: "branched",
|
|
454
461
|
conversation: "Conversation",
|
|
455
462
|
thinking: "Assistant is thinking\u2026",
|
|
456
463
|
thoughts: "Thoughts",
|
|
@@ -841,6 +848,138 @@ function accepts(accept, file) {
|
|
|
841
848
|
});
|
|
842
849
|
}
|
|
843
850
|
|
|
851
|
+
// src/ui/relative_time.ts
|
|
852
|
+
function relativeTime(timestamp, now = Date.now(), strings = DEFAULT_UI_STRINGS) {
|
|
853
|
+
if (!Number.isFinite(timestamp)) {
|
|
854
|
+
return strings.justNow;
|
|
855
|
+
}
|
|
856
|
+
const seconds = Math.round((now - timestamp) / 1e3);
|
|
857
|
+
if (seconds < 60) {
|
|
858
|
+
return strings.justNow;
|
|
859
|
+
}
|
|
860
|
+
const minutes = Math.round(seconds / 60);
|
|
861
|
+
if (minutes < 60) {
|
|
862
|
+
return strings.minutesAgo.replace("{n}", String(minutes));
|
|
863
|
+
}
|
|
864
|
+
const hours = Math.round(minutes / 60);
|
|
865
|
+
if (hours < 24) {
|
|
866
|
+
return strings.hoursAgo.replace("{n}", String(hours));
|
|
867
|
+
}
|
|
868
|
+
const days = Math.round(hours / 24);
|
|
869
|
+
if (days < 7) {
|
|
870
|
+
return strings.daysAgo.replace("{n}", String(days));
|
|
871
|
+
}
|
|
872
|
+
return strings.weeksAgo.replace("{n}", String(Math.round(days / 7)));
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// src/ui/checkpoint_menu.ts
|
|
876
|
+
var CheckpointMenu = class {
|
|
877
|
+
/** The panel root. Append to the chat shell; hidden until opened. */
|
|
878
|
+
element;
|
|
879
|
+
#onPick;
|
|
880
|
+
#list;
|
|
881
|
+
#heading;
|
|
882
|
+
#strings;
|
|
883
|
+
#runs = [];
|
|
884
|
+
constructor(onPick, strings = DEFAULT_UI_STRINGS) {
|
|
885
|
+
this.#onPick = onPick;
|
|
886
|
+
this.#strings = strings;
|
|
887
|
+
this.element = document.createElement("div");
|
|
888
|
+
this.element.className = "checkpoints";
|
|
889
|
+
this.element.setAttribute("part", "checkpoints");
|
|
890
|
+
this.element.setAttribute("role", "dialog");
|
|
891
|
+
this.element.setAttribute("aria-label", strings.checkpoints);
|
|
892
|
+
this.element.hidden = true;
|
|
893
|
+
const header = document.createElement("div");
|
|
894
|
+
header.className = "checkpoints-header";
|
|
895
|
+
header.setAttribute("part", "checkpoints-header");
|
|
896
|
+
this.#heading = document.createElement("span");
|
|
897
|
+
this.#heading.className = "checkpoints-title";
|
|
898
|
+
this.#heading.textContent = strings.checkpoints;
|
|
899
|
+
header.append(this.#heading);
|
|
900
|
+
this.#list = document.createElement("div");
|
|
901
|
+
this.#list.className = "checkpoints-list";
|
|
902
|
+
this.#list.setAttribute("part", "checkpoints-list");
|
|
903
|
+
this.element.append(header, this.#list);
|
|
904
|
+
this.element.addEventListener("keydown", (event) => {
|
|
905
|
+
if (event.key === "Escape") {
|
|
906
|
+
event.stopPropagation();
|
|
907
|
+
this.close();
|
|
908
|
+
}
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
/** Replace the rows. The host passes only `continuable` runs. */
|
|
912
|
+
setRuns(runs) {
|
|
913
|
+
this.#runs = runs;
|
|
914
|
+
this.#render();
|
|
915
|
+
}
|
|
916
|
+
/** Re-localize a panel built before the host's strings resolved. */
|
|
917
|
+
setStrings(strings) {
|
|
918
|
+
this.#strings = strings;
|
|
919
|
+
this.element.setAttribute("aria-label", strings.checkpoints);
|
|
920
|
+
this.#heading.textContent = strings.checkpoints;
|
|
921
|
+
this.#render();
|
|
922
|
+
}
|
|
923
|
+
open() {
|
|
924
|
+
this.element.hidden = false;
|
|
925
|
+
}
|
|
926
|
+
close() {
|
|
927
|
+
this.element.hidden = true;
|
|
928
|
+
}
|
|
929
|
+
get open_() {
|
|
930
|
+
return !this.element.hidden;
|
|
931
|
+
}
|
|
932
|
+
#render() {
|
|
933
|
+
this.#list.replaceChildren();
|
|
934
|
+
if (this.#runs.length === 0) {
|
|
935
|
+
const empty = document.createElement("div");
|
|
936
|
+
empty.className = "checkpoints-empty";
|
|
937
|
+
empty.setAttribute("part", "checkpoints-empty");
|
|
938
|
+
empty.textContent = this.#strings.noCheckpoints;
|
|
939
|
+
this.#list.append(empty);
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
for (const run of this.#runs) {
|
|
943
|
+
this.#list.append(this.#row(run));
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
#row(run) {
|
|
947
|
+
const row = document.createElement("div");
|
|
948
|
+
row.className = "checkpoint-row";
|
|
949
|
+
row.setAttribute("part", "checkpoint-row");
|
|
950
|
+
const label = document.createElement("span");
|
|
951
|
+
label.className = "checkpoint-label";
|
|
952
|
+
label.textContent = run.started_at === null ? run.run_id : relativeTime(Date.parse(run.started_at), Date.now(), this.#strings);
|
|
953
|
+
label.title = run.run_id;
|
|
954
|
+
row.append(label);
|
|
955
|
+
if (run.parent_run_id !== null) {
|
|
956
|
+
const branch = document.createElement("span");
|
|
957
|
+
branch.className = "checkpoint-branch";
|
|
958
|
+
branch.setAttribute("part", "checkpoint-branch");
|
|
959
|
+
branch.textContent = this.#strings.forkedRun;
|
|
960
|
+
branch.title = run.parent_run_id;
|
|
961
|
+
row.append(branch);
|
|
962
|
+
}
|
|
963
|
+
row.append(
|
|
964
|
+
this.#action(run.run_id, "resume", this.#strings.resumeRun),
|
|
965
|
+
this.#action(run.run_id, "fork", this.#strings.forkRun)
|
|
966
|
+
);
|
|
967
|
+
return row;
|
|
968
|
+
}
|
|
969
|
+
#action(runId, verb, label) {
|
|
970
|
+
const button = document.createElement("button");
|
|
971
|
+
button.type = "button";
|
|
972
|
+
button.className = `checkpoint-action checkpoint-${verb}`;
|
|
973
|
+
button.setAttribute("part", `checkpoint-action checkpoint-${verb}`);
|
|
974
|
+
button.textContent = label;
|
|
975
|
+
button.addEventListener("click", () => {
|
|
976
|
+
this.close();
|
|
977
|
+
this.#onPick(runId, verb);
|
|
978
|
+
});
|
|
979
|
+
return button;
|
|
980
|
+
}
|
|
981
|
+
};
|
|
982
|
+
|
|
844
983
|
// src/ui/confirmation_card.ts
|
|
845
984
|
function actionButton2(modifier, label) {
|
|
846
985
|
const button = document.createElement("button");
|
|
@@ -4952,6 +5091,82 @@ var STYLES = `
|
|
|
4952
5091
|
display: none;
|
|
4953
5092
|
}
|
|
4954
5093
|
|
|
5094
|
+
.checkpoints {
|
|
5095
|
+
position: absolute;
|
|
5096
|
+
inset-block-start: 3rem;
|
|
5097
|
+
inset-inline: 0.75rem;
|
|
5098
|
+
z-index: 6;
|
|
5099
|
+
display: flex;
|
|
5100
|
+
flex-direction: column;
|
|
5101
|
+
gap: 0.25rem;
|
|
5102
|
+
padding: 0.5rem;
|
|
5103
|
+
border: 1px solid var(--agui-border, #d7d7dc);
|
|
5104
|
+
border-radius: 0.5rem;
|
|
5105
|
+
background: var(--agui-surface, #fff);
|
|
5106
|
+
box-shadow: 0 6px 24px rgb(0 0 0 / 12%);
|
|
5107
|
+
max-height: 60%;
|
|
5108
|
+
overflow-y: auto;
|
|
5109
|
+
}
|
|
5110
|
+
|
|
5111
|
+
.checkpoints[hidden] {
|
|
5112
|
+
display: none;
|
|
5113
|
+
}
|
|
5114
|
+
|
|
5115
|
+
.checkpoints-title {
|
|
5116
|
+
font-size: 0.75rem;
|
|
5117
|
+
font-weight: 600;
|
|
5118
|
+
opacity: 0.7;
|
|
5119
|
+
}
|
|
5120
|
+
|
|
5121
|
+
.checkpoints-empty {
|
|
5122
|
+
padding: 0.5rem 0.25rem;
|
|
5123
|
+
font-size: 0.8125rem;
|
|
5124
|
+
opacity: 0.7;
|
|
5125
|
+
}
|
|
5126
|
+
|
|
5127
|
+
.checkpoint-row {
|
|
5128
|
+
display: flex;
|
|
5129
|
+
align-items: center;
|
|
5130
|
+
gap: 0.5rem;
|
|
5131
|
+
padding: 0.25rem;
|
|
5132
|
+
border-radius: 0.375rem;
|
|
5133
|
+
}
|
|
5134
|
+
|
|
5135
|
+
.checkpoint-row:hover {
|
|
5136
|
+
background: var(--agui-hover, #f3f3f5);
|
|
5137
|
+
}
|
|
5138
|
+
|
|
5139
|
+
.checkpoint-label {
|
|
5140
|
+
flex: 1;
|
|
5141
|
+
font-size: 0.8125rem;
|
|
5142
|
+
white-space: nowrap;
|
|
5143
|
+
overflow: hidden;
|
|
5144
|
+
text-overflow: ellipsis;
|
|
5145
|
+
}
|
|
5146
|
+
|
|
5147
|
+
.checkpoint-branch {
|
|
5148
|
+
font-size: 0.6875rem;
|
|
5149
|
+
padding: 0 0.375rem;
|
|
5150
|
+
border-radius: 999px;
|
|
5151
|
+
background: var(--agui-hover, #f3f3f5);
|
|
5152
|
+
opacity: 0.8;
|
|
5153
|
+
}
|
|
5154
|
+
|
|
5155
|
+
.checkpoint-action {
|
|
5156
|
+
font: inherit;
|
|
5157
|
+
font-size: 0.75rem;
|
|
5158
|
+
cursor: pointer;
|
|
5159
|
+
padding: 0.125rem 0.5rem;
|
|
5160
|
+
border: 1px solid var(--agui-border, #d7d7dc);
|
|
5161
|
+
border-radius: 0.375rem;
|
|
5162
|
+
background: transparent;
|
|
5163
|
+
color: inherit;
|
|
5164
|
+
}
|
|
5165
|
+
|
|
5166
|
+
.checkpoint-action:hover {
|
|
5167
|
+
background: var(--agui-hover, #f3f3f5);
|
|
5168
|
+
}
|
|
5169
|
+
|
|
4955
5170
|
.drawer-backdrop {
|
|
4956
5171
|
position: absolute;
|
|
4957
5172
|
inset: 0;
|
|
@@ -5182,30 +5397,6 @@ var ThoughtsBlock = class {
|
|
|
5182
5397
|
}
|
|
5183
5398
|
};
|
|
5184
5399
|
|
|
5185
|
-
// src/ui/relative_time.ts
|
|
5186
|
-
function relativeTime(timestamp, now = Date.now(), strings = DEFAULT_UI_STRINGS) {
|
|
5187
|
-
if (!Number.isFinite(timestamp)) {
|
|
5188
|
-
return strings.justNow;
|
|
5189
|
-
}
|
|
5190
|
-
const seconds = Math.round((now - timestamp) / 1e3);
|
|
5191
|
-
if (seconds < 60) {
|
|
5192
|
-
return strings.justNow;
|
|
5193
|
-
}
|
|
5194
|
-
const minutes = Math.round(seconds / 60);
|
|
5195
|
-
if (minutes < 60) {
|
|
5196
|
-
return strings.minutesAgo.replace("{n}", String(minutes));
|
|
5197
|
-
}
|
|
5198
|
-
const hours = Math.round(minutes / 60);
|
|
5199
|
-
if (hours < 24) {
|
|
5200
|
-
return strings.hoursAgo.replace("{n}", String(hours));
|
|
5201
|
-
}
|
|
5202
|
-
const days = Math.round(hours / 24);
|
|
5203
|
-
if (days < 7) {
|
|
5204
|
-
return strings.daysAgo.replace("{n}", String(days));
|
|
5205
|
-
}
|
|
5206
|
-
return strings.weeksAgo.replace("{n}", String(Math.round(days / 7)));
|
|
5207
|
-
}
|
|
5208
|
-
|
|
5209
5400
|
// src/ui/thread_drawer.ts
|
|
5210
5401
|
var ThreadDrawer = class {
|
|
5211
5402
|
/** The drawer root (backdrop + panel). Append to the chat shell; hidden until opened. */
|
|
@@ -5728,6 +5919,22 @@ var AgUiClient = class {
|
|
|
5728
5919
|
this.#onPersist = config.onPersist ?? (() => {
|
|
5729
5920
|
});
|
|
5730
5921
|
this.#connectionLostMessage = config.connectionLostMessage ?? "Connection lost";
|
|
5922
|
+
const onStateChanged = config.onStateChanged;
|
|
5923
|
+
if (onStateChanged !== void 0) {
|
|
5924
|
+
this.#agent.subscribe({
|
|
5925
|
+
onStateChanged: ({ state }) => {
|
|
5926
|
+
onStateChanged(state);
|
|
5927
|
+
}
|
|
5928
|
+
});
|
|
5929
|
+
}
|
|
5930
|
+
}
|
|
5931
|
+
/** The current AG-UI shared state, as the agent last applied it. */
|
|
5932
|
+
get state() {
|
|
5933
|
+
return this.#agent.state;
|
|
5934
|
+
}
|
|
5935
|
+
/** Replace the shared state; the next run sends it as `RunAgentInput.state`. */
|
|
5936
|
+
setState(state) {
|
|
5937
|
+
this.#agent.setState({ ...state });
|
|
5731
5938
|
}
|
|
5732
5939
|
/** Whether a run is currently in flight. */
|
|
5733
5940
|
get running() {
|
|
@@ -6125,6 +6332,7 @@ function createHttpAgent(options) {
|
|
|
6125
6332
|
return new HttpAgent({
|
|
6126
6333
|
url: options.endpoint,
|
|
6127
6334
|
headers: options.headers ?? {},
|
|
6335
|
+
initialState: { ...options.initialState ?? {} },
|
|
6128
6336
|
// HttpAgent invokes its configured fetch as a method (`this.fetch(...)`),
|
|
6129
6337
|
// which would rebind the global `fetch` to the agent instance and trigger
|
|
6130
6338
|
// "Illegal invocation" in browsers. Wrap it so `fetch` is always called as
|
|
@@ -6256,6 +6464,60 @@ var RemoteConversationStore = class {
|
|
|
6256
6464
|
}
|
|
6257
6465
|
};
|
|
6258
6466
|
|
|
6467
|
+
// src/core/run_index.ts
|
|
6468
|
+
var RunIndex = class {
|
|
6469
|
+
#url;
|
|
6470
|
+
#headers;
|
|
6471
|
+
constructor(url, headers = () => ({})) {
|
|
6472
|
+
this.#url = url.endsWith("/") ? url : `${url}/`;
|
|
6473
|
+
this.#headers = headers;
|
|
6474
|
+
}
|
|
6475
|
+
/**
|
|
6476
|
+
* The user's runs, or `[]` when the endpoint is unreachable or answers with
|
|
6477
|
+
* an error. A history affordance that cannot load is empty, never broken:
|
|
6478
|
+
* the caller renders its empty state rather than surfacing a transport fault
|
|
6479
|
+
* the user can do nothing about.
|
|
6480
|
+
*/
|
|
6481
|
+
async list() {
|
|
6482
|
+
try {
|
|
6483
|
+
const response = await fetch(this.#url, {
|
|
6484
|
+
method: "GET",
|
|
6485
|
+
headers: { Accept: "application/json", ...this.#headers() }
|
|
6486
|
+
});
|
|
6487
|
+
if (!response.ok) {
|
|
6488
|
+
return [];
|
|
6489
|
+
}
|
|
6490
|
+
const body = await response.json();
|
|
6491
|
+
return body.runs ?? [];
|
|
6492
|
+
} catch {
|
|
6493
|
+
return [];
|
|
6494
|
+
}
|
|
6495
|
+
}
|
|
6496
|
+
/** The rows a client may actually continue. */
|
|
6497
|
+
async continuable() {
|
|
6498
|
+
return (await this.list()).filter((run) => run.continuable);
|
|
6499
|
+
}
|
|
6500
|
+
/** The endpoint that continues `runId` as a new run. */
|
|
6501
|
+
resumeUrl(runId) {
|
|
6502
|
+
return this.#sibling("resume", runId);
|
|
6503
|
+
}
|
|
6504
|
+
/** The endpoint that branches `runId` into a new run, leaving the source untouched. */
|
|
6505
|
+
forkUrl(runId) {
|
|
6506
|
+
return this.#sibling("fork", runId);
|
|
6507
|
+
}
|
|
6508
|
+
/**
|
|
6509
|
+
* `<mount>/<verb>/<runId>/`, derived from the index URL's own prefix.
|
|
6510
|
+
*
|
|
6511
|
+
* Built by string surgery on the trailing `runs/` rather than with `new URL`,
|
|
6512
|
+
* because the configured value may be root-relative (`/agent/runs/`) — the
|
|
6513
|
+
* common case in a Django template — and `new URL` needs an absolute base.
|
|
6514
|
+
*/
|
|
6515
|
+
#sibling(verb, runId) {
|
|
6516
|
+
const prefix = this.#url.slice(0, -"runs/".length);
|
|
6517
|
+
return `${prefix}${verb}/${encodeURIComponent(runId)}/`;
|
|
6518
|
+
}
|
|
6519
|
+
};
|
|
6520
|
+
|
|
6259
6521
|
// src/core/transcribe_audio.ts
|
|
6260
6522
|
async function transcribeAudio(audio, options) {
|
|
6261
6523
|
const form = new FormData();
|
|
@@ -6404,7 +6666,7 @@ var AgUiChat = class extends HTMLElement {
|
|
|
6404
6666
|
/**
|
|
6405
6667
|
* Per-run frontend tool catalog provider. Defaults to the built-in
|
|
6406
6668
|
* `route.*` tools (when a {@link routeMap} is set) plus the tools registered
|
|
6407
|
-
* via {@link registerTool} / {@link
|
|
6669
|
+
* via {@link registerTool} / {@link registerPageState}; override to supply a
|
|
6408
6670
|
* fully custom catalog.
|
|
6409
6671
|
*/
|
|
6410
6672
|
getTools = () => [
|
|
@@ -6528,6 +6790,10 @@ var AgUiChat = class extends HTMLElement {
|
|
|
6528
6790
|
#title;
|
|
6529
6791
|
#skillsMenu;
|
|
6530
6792
|
#drawer;
|
|
6793
|
+
/** Checkpoint panel; rows load only when `data-runs-url` is set. */
|
|
6794
|
+
#checkpoints;
|
|
6795
|
+
/** Built lazily from `data-runs-url`; `null` when the host didn't opt in. */
|
|
6796
|
+
#runIndex = null;
|
|
6531
6797
|
#skillHint;
|
|
6532
6798
|
/** File-picker button + hidden input + tray slot; the tray mounts on connect. */
|
|
6533
6799
|
#attachButton;
|
|
@@ -6548,6 +6814,10 @@ var AgUiChat = class extends HTMLElement {
|
|
|
6548
6814
|
/** Refs attached to the message currently being sent (the context manifest). */
|
|
6549
6815
|
#runAttachments = [];
|
|
6550
6816
|
#client = null;
|
|
6817
|
+
// Seed for the next client. Once one exists it owns the live value (the
|
|
6818
|
+
// agent applies STATE_SNAPSHOT / STATE_DELTA into it), so this is only the
|
|
6819
|
+
// starting point — `sharedState` reads through to the client when present.
|
|
6820
|
+
#sharedState = {};
|
|
6551
6821
|
// Whether an interaction is in flight (first onRunStart → onSettled). Drives
|
|
6552
6822
|
// the Send⇄Stop button: `agent.isRunning` is false between frontend-tool
|
|
6553
6823
|
// rounds, but the user must still be able to stop there.
|
|
@@ -6618,6 +6888,70 @@ var AgUiChat = class extends HTMLElement {
|
|
|
6618
6888
|
this.#deleteThread(threadId);
|
|
6619
6889
|
}
|
|
6620
6890
|
});
|
|
6891
|
+
this.#checkpoints = new CheckpointMenu((runId, verb) => {
|
|
6892
|
+
void this.#continueRun(runId, verb);
|
|
6893
|
+
});
|
|
6894
|
+
}
|
|
6895
|
+
/** The run index, built once from `data-runs-url`; `null` when unset. */
|
|
6896
|
+
#runs() {
|
|
6897
|
+
const url = this.getAttribute("data-runs-url");
|
|
6898
|
+
if (url === null || url === "") {
|
|
6899
|
+
return null;
|
|
6900
|
+
}
|
|
6901
|
+
if (this.#runIndex === null) {
|
|
6902
|
+
this.#runIndex = new RunIndex(url, () => this.headers);
|
|
6903
|
+
}
|
|
6904
|
+
return this.#runIndex;
|
|
6905
|
+
}
|
|
6906
|
+
/**
|
|
6907
|
+
* Continue `runId` as a **new** run, seeded server-side from its snapshot.
|
|
6908
|
+
*
|
|
6909
|
+
* Uses a short-lived agent pointed at the resume / fork endpoint and seeded
|
|
6910
|
+
* with **no** history, so the request carries only the turn typed here — the
|
|
6911
|
+
* contract those endpoints assume, since the server supplies the prior turns
|
|
6912
|
+
* from the snapshot and re-sending them would duplicate. Building a separate
|
|
6913
|
+
* agent makes that structural: the main agent keeps its own history, and
|
|
6914
|
+
* "only the new turn" can't be got wrong by forgetting to clear it. The
|
|
6915
|
+
* fresh `run_id` the endpoints also require comes free — a new agent mints
|
|
6916
|
+
* one per run.
|
|
6917
|
+
*
|
|
6918
|
+
* Handlers are the element's own, so the continuation streams into the same
|
|
6919
|
+
* transcript the user is looking at.
|
|
6920
|
+
*/
|
|
6921
|
+
async #continueRun(runId, verb) {
|
|
6922
|
+
const index = this.#runs();
|
|
6923
|
+
if (index === null) {
|
|
6924
|
+
return;
|
|
6925
|
+
}
|
|
6926
|
+
const content = this.#input.value.trim();
|
|
6927
|
+
if (content === "") {
|
|
6928
|
+
return;
|
|
6929
|
+
}
|
|
6930
|
+
this.#input.value = "";
|
|
6931
|
+
const endpoint = verb === "resume" ? index.resumeUrl(runId) : index.forkUrl(runId);
|
|
6932
|
+
const agent = this.agentFactory({
|
|
6933
|
+
endpoint,
|
|
6934
|
+
headers: this.headers,
|
|
6935
|
+
getHeaders: () => this.headers,
|
|
6936
|
+
threadId: this.#threadId,
|
|
6937
|
+
// The seed the endpoints assume: nothing. The snapshot is the history.
|
|
6938
|
+
initialMessages: []
|
|
6939
|
+
});
|
|
6940
|
+
const client = new AgUiClient({
|
|
6941
|
+
agent,
|
|
6942
|
+
handlers: this.#handlers(),
|
|
6943
|
+
getTools: () => this.getTools(),
|
|
6944
|
+
getContext: () => this.getContext(),
|
|
6945
|
+
executeTool: (call) => this.#executeTool(call),
|
|
6946
|
+
resolveInterrupts: (interrupts) => this.#resolveInterrupts(interrupts),
|
|
6947
|
+
connectionLostMessage: this.#strings.connectionLost
|
|
6948
|
+
});
|
|
6949
|
+
await client.send(content);
|
|
6950
|
+
}
|
|
6951
|
+
/** Load the checkpoint panel with the runs that can actually be continued. */
|
|
6952
|
+
async #refreshCheckpoints() {
|
|
6953
|
+
const index = this.#runs();
|
|
6954
|
+
this.#checkpoints.setRuns(index === null ? [] : await index.continuable());
|
|
6621
6955
|
}
|
|
6622
6956
|
/** Attributes whose late changes must reflect in already-rendered chrome. */
|
|
6623
6957
|
static get observedAttributes() {
|
|
@@ -6630,12 +6964,40 @@ var AgUiChat = class extends HTMLElement {
|
|
|
6630
6964
|
registerTool(tool) {
|
|
6631
6965
|
this.#toolRegistry.register(tool);
|
|
6632
6966
|
}
|
|
6633
|
-
/**
|
|
6634
|
-
|
|
6635
|
-
|
|
6967
|
+
/**
|
|
6968
|
+
* AG-UI **shared state** for this conversation — the protocol's own state
|
|
6969
|
+
* channel, sent as `RunAgentInput.state` on every run and replaced in place
|
|
6970
|
+
* when the server streams `STATE_SNAPSHOT` / `STATE_DELTA`. Assigning seeds
|
|
6971
|
+
* the next run; reading returns whatever the agent last applied.
|
|
6972
|
+
*
|
|
6973
|
+
* Listen for {@link STATE_EVENT} to react to server-driven changes.
|
|
6974
|
+
*
|
|
6975
|
+
* Not to be confused with {@link registerPageState}, which exposes host
|
|
6976
|
+
* state to the agent as ordinary *tools*.
|
|
6977
|
+
*/
|
|
6978
|
+
get sharedState() {
|
|
6979
|
+
return this.#client?.state ?? this.#sharedState;
|
|
6980
|
+
}
|
|
6981
|
+
set sharedState(state) {
|
|
6982
|
+
this.#sharedState = { ...state };
|
|
6983
|
+
this.#client?.setState(this.#sharedState);
|
|
6984
|
+
}
|
|
6985
|
+
/** Bind a piece of host page state to `read_<name>` / `set_<name>` tools. */
|
|
6986
|
+
registerPageState(binding) {
|
|
6987
|
+
for (const tool of createPageStateTools(binding)) {
|
|
6636
6988
|
this.#toolRegistry.register(tool);
|
|
6637
6989
|
}
|
|
6638
6990
|
}
|
|
6991
|
+
/**
|
|
6992
|
+
* @deprecated Renamed to {@link registerPageState}. The old name read as
|
|
6993
|
+
* AG-UI shared-state sync (`STATE_SNAPSHOT` / `STATE_DELTA`), which this
|
|
6994
|
+
* component does not implement — these are ordinary client tools over host
|
|
6995
|
+
* page state. Behaviour is unchanged; this alias will be removed in a future
|
|
6996
|
+
* major.
|
|
6997
|
+
*/
|
|
6998
|
+
registerStateHook(binding) {
|
|
6999
|
+
this.registerPageState(binding);
|
|
7000
|
+
}
|
|
6639
7001
|
/** The built-in `route.*` tools, present only when a route map is set. */
|
|
6640
7002
|
#routeTools() {
|
|
6641
7003
|
if (this.routeMap.length === 0) {
|
|
@@ -6797,6 +7159,7 @@ var AgUiChat = class extends HTMLElement {
|
|
|
6797
7159
|
}
|
|
6798
7160
|
this.#render();
|
|
6799
7161
|
this.#drawer.setStrings(this.#strings);
|
|
7162
|
+
this.#checkpoints.setStrings(this.#strings);
|
|
6800
7163
|
if (this.#readScopedItem(COLLAPSED_KEY) === "1") {
|
|
6801
7164
|
this.setAttribute("collapsed", "");
|
|
6802
7165
|
}
|
|
@@ -7338,11 +7701,20 @@ Use the read_attachment tool with an id to read a file's contents.`
|
|
|
7338
7701
|
void this.#refreshDrawer();
|
|
7339
7702
|
this.#drawer.open();
|
|
7340
7703
|
});
|
|
7704
|
+
const checkpoints = this.#headerButton("checkpoints", this.#strings.checkpoints, "\u2B6F");
|
|
7705
|
+
checkpoints.addEventListener("click", () => {
|
|
7706
|
+
void this.#refreshCheckpoints();
|
|
7707
|
+
this.#checkpoints.open();
|
|
7708
|
+
});
|
|
7341
7709
|
const newChat = this.#headerButton("new", this.#strings.newChat, "\u271A");
|
|
7342
7710
|
newChat.addEventListener("click", () => this.newChat());
|
|
7343
7711
|
const collapse = this.#headerButton("collapse", this.#strings.collapse, "\u2014");
|
|
7344
7712
|
collapse.addEventListener("click", () => this.toggleCollapsed());
|
|
7345
|
-
|
|
7713
|
+
if (this.#runs() !== null) {
|
|
7714
|
+
controls.append(history, checkpoints, newChat);
|
|
7715
|
+
} else {
|
|
7716
|
+
controls.append(history, newChat);
|
|
7717
|
+
}
|
|
7346
7718
|
if (this.getAttribute("data-theme-toggle") !== null) {
|
|
7347
7719
|
this.#themeToggle.type = "button";
|
|
7348
7720
|
this.#themeToggle.className = "header-btn header-btn--theme";
|
|
@@ -7419,7 +7791,8 @@ Use the read_attachment tool with an id to read a file's contents.`
|
|
|
7419
7791
|
this.#attachSlot,
|
|
7420
7792
|
inputRow,
|
|
7421
7793
|
footer,
|
|
7422
|
-
this.#drawer.element
|
|
7794
|
+
this.#drawer.element,
|
|
7795
|
+
this.#checkpoints.element
|
|
7423
7796
|
);
|
|
7424
7797
|
this.#rail.className = "rail";
|
|
7425
7798
|
this.#rail.type = "button";
|
|
@@ -7550,7 +7923,8 @@ Use the read_attachment tool with an id to read a file's contents.`
|
|
|
7550
7923
|
// re-reads this on each call.
|
|
7551
7924
|
getHeaders: () => this.headers,
|
|
7552
7925
|
threadId: this.#threadId,
|
|
7553
|
-
initialMessages: this.#initialMessages
|
|
7926
|
+
initialMessages: this.#initialMessages,
|
|
7927
|
+
initialState: this.#sharedState
|
|
7554
7928
|
});
|
|
7555
7929
|
this.#client = new AgUiClient({
|
|
7556
7930
|
agent,
|
|
@@ -7560,11 +7934,23 @@ Use the read_attachment tool with an id to read a file's contents.`
|
|
|
7560
7934
|
executeTool: (call) => this.#executeTool(call),
|
|
7561
7935
|
resolveInterrupts: (interrupts) => this.#resolveInterrupts(interrupts),
|
|
7562
7936
|
onPersist: (messages) => this.conversationStore.saveMessages(this.#threadId, messages),
|
|
7937
|
+
onStateChanged: (state) => this.#onSharedStateChanged(state),
|
|
7563
7938
|
connectionLostMessage: this.#strings.connectionLost
|
|
7564
7939
|
});
|
|
7565
7940
|
}
|
|
7566
7941
|
return this.#client;
|
|
7567
7942
|
}
|
|
7943
|
+
/** Mirror the agent's applied state and tell the host it moved. */
|
|
7944
|
+
#onSharedStateChanged(state) {
|
|
7945
|
+
this.#sharedState = { ...state };
|
|
7946
|
+
this.dispatchEvent(
|
|
7947
|
+
new CustomEvent(STATE_EVENT, {
|
|
7948
|
+
detail: { state: this.#sharedState },
|
|
7949
|
+
bubbles: true,
|
|
7950
|
+
composed: true
|
|
7951
|
+
})
|
|
7952
|
+
);
|
|
7953
|
+
}
|
|
7568
7954
|
/** Whether ``call`` should be gated behind the confirmation card. */
|
|
7569
7955
|
async #needsConfirmation(call, tool) {
|
|
7570
7956
|
if (this.autoConfirm) {
|
|
@@ -7870,10 +8256,11 @@ function setControlValue(el, value) {
|
|
|
7870
8256
|
}
|
|
7871
8257
|
|
|
7872
8258
|
// src/version.ts
|
|
7873
|
-
var VERSION = "0.
|
|
8259
|
+
var VERSION = "0.13.0";
|
|
7874
8260
|
export {
|
|
7875
8261
|
AgUiChat,
|
|
7876
8262
|
AgUiClient,
|
|
8263
|
+
CheckpointMenu,
|
|
7877
8264
|
ClientToolRegistry,
|
|
7878
8265
|
ConnectionLostError,
|
|
7879
8266
|
DEFAULT_UI_STRINGS,
|
|
@@ -7882,6 +8269,8 @@ export {
|
|
|
7882
8269
|
MESSAGE_ROLE,
|
|
7883
8270
|
PAGE_ACTIONS,
|
|
7884
8271
|
RemoteConversationStore,
|
|
8272
|
+
RunIndex,
|
|
8273
|
+
STATE_EVENT,
|
|
7885
8274
|
SUBMIT_EVENT,
|
|
7886
8275
|
SessionStorageStore,
|
|
7887
8276
|
TOGGLE_EVENT,
|
|
@@ -7897,6 +8286,7 @@ export {
|
|
|
7897
8286
|
createHttpAgent,
|
|
7898
8287
|
createPageActionTools,
|
|
7899
8288
|
createPageMapContext,
|
|
8289
|
+
createPageStateTools,
|
|
7900
8290
|
createRouteTools,
|
|
7901
8291
|
createStateHookTools,
|
|
7902
8292
|
defineAgUiChat,
|