@artooi/ag-ui-web-component 0.39.0 → 0.40.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 +155 -1
- package/README.md +70 -12
- package/dist/ag-ui-web-component.bundle.js +168 -45
- package/dist/ag-ui-web-component.bundle.js.map +3 -3
- package/dist/core/ag_ui_chat.d.ts +7 -3
- package/dist/core/ag_ui_chat.d.ts.map +1 -1
- package/dist/core/agui_client.d.ts +12 -0
- package/dist/core/agui_client.d.ts.map +1 -1
- package/dist/core/client_seed.d.ts +14 -2
- package/dist/core/client_seed.d.ts.map +1 -1
- package/dist/index.js +636 -231
- package/dist/index.js.map +2 -2
- package/dist/tools/tool_catalog.d.ts.map +1 -1
- package/dist/ui/composer/composer_attachments.d.ts +6 -1
- package/dist/ui/composer/composer_attachments.d.ts.map +1 -1
- package/dist/ui/composer/composer_voice.d.ts +3 -0
- package/dist/ui/composer/composer_voice.d.ts.map +1 -1
- package/dist/ui/excerpts/transcript_quote_offer.d.ts +7 -2
- package/dist/ui/excerpts/transcript_quote_offer.d.ts.map +1 -1
- package/dist/ui/history/conversation_history.d.ts +29 -3
- package/dist/ui/history/conversation_history.d.ts.map +1 -1
- package/dist/ui/placement/launcher_drag.d.ts +6 -0
- package/dist/ui/placement/launcher_drag.d.ts.map +1 -1
- package/dist/ui/placement/panel_placement.d.ts +1 -1
- package/dist/ui/placement/panel_placement.d.ts.map +1 -1
- package/dist/ui/styles.d.ts +1 -1
- package/dist/ui/styles.d.ts.map +1 -1
- package/dist/ui/transcript/transcript.d.ts +3 -2
- package/dist/ui/transcript/transcript.d.ts.map +1 -1
- package/dist/ui/ui_strings.d.ts +2 -0
- package/dist/ui/ui_strings.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/ag_ui_chat.ts +217 -64
- package/src/core/agui_client.ts +26 -14
- package/src/core/client_seed.ts +14 -2
- package/src/tools/tool_catalog.ts +63 -12
- package/src/ui/composer/composer_attachments.ts +64 -41
- package/src/ui/composer/composer_voice.ts +5 -0
- package/src/ui/excerpts/transcript_quote_offer.ts +26 -13
- package/src/ui/history/conversation_history.ts +125 -17
- package/src/ui/placement/launcher_drag.ts +104 -89
- package/src/ui/placement/panel_placement.ts +27 -3
- package/src/ui/styles.ts +137 -14
- package/src/ui/transcript/transcript.ts +10 -5
- package/src/ui/ui_strings.ts +3 -0
- package/src/version.ts +1 -1
package/dist/ui/styles.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../src/ui/styles.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../src/ui/styles.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,MAAM,or8HAyrHlB,CAAC"}
|
|
@@ -42,9 +42,10 @@ export declare class Transcript {
|
|
|
42
42
|
/**
|
|
43
43
|
* Start following the foot of the list, and wire the jump button to it.
|
|
44
44
|
* Called while rendering rather than at construction: the viewport has to
|
|
45
|
-
* exist and the observer has to have something to observe.
|
|
45
|
+
* exist and the observer has to have something to observe. The button
|
|
46
|
+
* outlives a connection, so its listener goes under `signal`.
|
|
46
47
|
*/
|
|
47
|
-
mountScroller(jumpButton: HTMLButtonElement): void;
|
|
48
|
+
mountScroller(jumpButton: HTMLButtonElement, signal: AbortSignal): void;
|
|
48
49
|
/** Stop following, when the element leaves the document. */
|
|
49
50
|
disposeScroller(): void;
|
|
50
51
|
/** Keep the list at its foot, if the reader is there. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transcript.d.ts","sourceRoot":"","sources":["../../../src/ui/transcript/transcript.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE9D,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AAKrF,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC/E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAMlD,+DAA+D;AAC/D,MAAM,WAAW,cAAc;IAC7B,oFAAoF;IACpF,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,kCAAkC;IAClC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,qDAAqD;IACrD,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,iCAAiC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,SAAS,CAAC;IAClC,oDAAoD;IACpD,QAAQ,CAAC,WAAW,EAAE,MAAM,OAAO,CAAC;IACpC,4DAA4D;IAC5D,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,UAAU,GAAG,IAAI,CAAC;IAC1D,oDAAoD;IACpD,QAAQ,CAAC,aAAa,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrD,6DAA6D;IAC7D,QAAQ,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC7D,qEAAqE;IACrE,QAAQ,CAAC,iBAAiB,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;CAC5E;AAED;;;;;;;;;;GAUG;AACH,qBAAa,UAAU;;IA2BrB,YAAY,IAAI,EAAE,cAAc,EAE/B;IAED
|
|
1
|
+
{"version":3,"file":"transcript.d.ts","sourceRoot":"","sources":["../../../src/ui/transcript/transcript.ts"],"names":[],"mappings":"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"}
|
package/dist/ui/ui_strings.d.ts
CHANGED
|
@@ -115,6 +115,8 @@ export interface UiStrings {
|
|
|
115
115
|
notConnected: string;
|
|
116
116
|
/** Composer hint when a run continuation was picked with an empty composer. */
|
|
117
117
|
continueNeedsTurn: string;
|
|
118
|
+
/** Composer hint when a run continuation was picked while a run is in flight. */
|
|
119
|
+
continueWhileRunning: string;
|
|
118
120
|
/** `aria-label` of the message textarea. */
|
|
119
121
|
message: string;
|
|
120
122
|
/** Placeholder of the message textarea. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui_strings.d.ts","sourceRoot":"","sources":["../../src/ui/ui_strings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,SAAS;IAExB,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,YAAY,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,SAAS,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,aAAa,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,uBAAuB;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IAGpB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB,oFAAoF;IACpF,cAAc,EAAE,MAAM,CAAC;IACvB,iDAAiD;IACjD,YAAY,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,YAAY,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,kBAAkB,EAAE,MAAM,CAAC;IAC3B,2EAA2E;IAC3E,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kFAAkF;IAClF,wBAAwB,EAAE,MAAM,CAAC;IACjC,mEAAmE;IACnE,eAAe,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,cAAc,EAAE,MAAM,CAAC;IACvB;oDACgD;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,cAAc,EAAE,MAAM,CAAC;IACvB,kGAAkG;IAClG,kBAAkB,EAAE,MAAM,CAAC;IAC3B,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,eAAe,EAAE,MAAM,CAAC;IACxB,2EAA2E;IAC3E,eAAe,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,UAAU,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,gBAAgB,EAAE,MAAM,CAAC;IACzB,yEAAyE;IACzE,UAAU,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,cAAc,EAAE,MAAM,CAAC;IACvB,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,yBAAyB,EAAE,MAAM,CAAC;IAClC,8EAA8E;IAC9E,YAAY,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,iBAAiB,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"ui_strings.d.ts","sourceRoot":"","sources":["../../src/ui/ui_strings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,SAAS;IAExB,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,YAAY,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,SAAS,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,aAAa,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,uBAAuB;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IAGpB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB,oFAAoF;IACpF,cAAc,EAAE,MAAM,CAAC;IACvB,iDAAiD;IACjD,YAAY,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,YAAY,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,kBAAkB,EAAE,MAAM,CAAC;IAC3B,2EAA2E;IAC3E,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kFAAkF;IAClF,wBAAwB,EAAE,MAAM,CAAC;IACjC,mEAAmE;IACnE,eAAe,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,cAAc,EAAE,MAAM,CAAC;IACvB;oDACgD;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,cAAc,EAAE,MAAM,CAAC;IACvB,kGAAkG;IAClG,kBAAkB,EAAE,MAAM,CAAC;IAC3B,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,eAAe,EAAE,MAAM,CAAC;IACxB,2EAA2E;IAC3E,eAAe,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,UAAU,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,gBAAgB,EAAE,MAAM,CAAC;IACzB,yEAAyE;IACzE,UAAU,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,cAAc,EAAE,MAAM,CAAC;IACvB,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,yBAAyB,EAAE,MAAM,CAAC;IAClC,8EAA8E;IAC9E,YAAY,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iFAAiF;IACjF,oBAAoB,EAAE,MAAM,CAAC;IAG7B,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,gBAAgB,EAAE,MAAM,CAAC;IACzB,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,2BAA2B;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,aAAa,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,YAAY,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,cAAc,EAAE,MAAM,CAAC;IAGvB,uCAAuC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,YAAY,EAAE,MAAM,CAAC;IACrB,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,eAAe,EAAE,MAAM,CAAC;IACxB,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,gBAAgB,EAAE,MAAM,CAAC;IACzB,uDAAuD;IACvD,gBAAgB,EAAE,MAAM,CAAC;IACzB,mDAAmD;IACnD,cAAc,EAAE,MAAM,CAAC;IACvB,wEAAwE;IACxE,WAAW,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,UAAU,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,aAAa,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,gBAAgB,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;IAGhB;;;;;;OAMG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB;;;;;;;OAOG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oEAAoE;IACpE,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;;;;OAMG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,aAAa,EAAE,MAAM,CAAC;IAGtB,wEAAwE;IACxE,gBAAgB,EAAE,MAAM,CAAC;IACzB,0DAA0D;IAC1D,mBAAmB,EAAE,MAAM,CAAC;IAC5B,uEAAuE;IACvE,uBAAuB,EAAE,MAAM,CAAC;IAChC,0DAA0D;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,cAAc,EAAE,MAAM,CAAC;IACvB,kEAAkE;IAClE,cAAc,EAAE,MAAM,CAAC;IACvB;;8CAE0C;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,YAAY,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,oDAAoD;IACpD,aAAa,EAAE,MAAM,CAAC;IACtB,qFAAqF;IACrF,aAAa,EAAE,MAAM,CAAC;IACtB,mFAAmF;IACnF,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IAGf,iEAAiE;IACjE,aAAa,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,cAAc,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IAGb,gDAAgD;IAChD,aAAa,EAAE,MAAM,CAAC;IACtB,sFAAsF;IACtF,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IAGf,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,eAAe,EAAE,MAAM,CAAC;IACxB,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oCAAoC;IACpC,YAAY,EAAE,MAAM,CAAC;IAGrB,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,iCAAiC;IACjC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,gBAAgB,EAAE,MAAM,CAAC;IAGzB,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,UAAU,EAAE,MAAM,CAAC;IAGnB,qCAAqC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,aAAa,EAAE,MAAM,CAAC;IACtB,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAGlB,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,8EAA8E;AAC9E,eAAO,MAAM,kBAAkB,EAAE,SA4IhC,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS,CASvE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@artooi/ag-ui-web-component",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Context,
|
|
1
|
+
import type { Context, Tool } from "@ag-ui/core";
|
|
2
2
|
import {
|
|
3
3
|
ATTACHMENT_EVENT,
|
|
4
4
|
CHART_ACTIVITY_TYPE,
|
|
@@ -242,6 +242,8 @@ export class AgUiChat extends HTMLElement {
|
|
|
242
242
|
* Optional full replacement for the `ask_user` question UI, resolving with
|
|
243
243
|
* the answer; the same seam as {@link approvalRenderer}, styled via `strings`
|
|
244
244
|
* and the `question*` `::part()`s when left unset. Requires {@link askUser}.
|
|
245
|
+
* A renderer that throws or rejects hands the question to the built-in card,
|
|
246
|
+
* as a failing {@link approvalRenderer} does, unless Stop already fired.
|
|
245
247
|
*/
|
|
246
248
|
askUserRenderer: QuestionRenderer | null = null;
|
|
247
249
|
|
|
@@ -585,11 +587,38 @@ export class AgUiChat extends HTMLElement {
|
|
|
585
587
|
readonly #voice: ComposerVoice;
|
|
586
588
|
/** Whether the element is currently in the DOM; gates the connect-time warning. */
|
|
587
589
|
#connected = false;
|
|
590
|
+
/**
|
|
591
|
+
* Aborted when the element leaves the document, and replaced when it comes
|
|
592
|
+
* back.
|
|
593
|
+
*
|
|
594
|
+
* Every listener connecting adds to an element that outlives the connection
|
|
595
|
+
* -- the composer, Send, the launcher, the shell -- is added under this
|
|
596
|
+
* signal. Connecting runs again on every insertion, so a listener added
|
|
597
|
+
* without it is a second listener the next time: one click on Stop stopped
|
|
598
|
+
* the run three times, and one press of the built-in theme toggle flipped it
|
|
599
|
+
* twice, back to where it started.
|
|
600
|
+
*/
|
|
601
|
+
#connection = new AbortController();
|
|
602
|
+
/**
|
|
603
|
+
* Whether the element has connected before, which makes this connection a
|
|
604
|
+
* re-insertion: there is a transcript on screen from last time, and the
|
|
605
|
+
* history replay would draw the conversation a second time beneath it.
|
|
606
|
+
*/
|
|
607
|
+
#connectedBefore = false;
|
|
608
|
+
/**
|
|
609
|
+
* The remote store `data-threads-url` wrapped around the conversation store,
|
|
610
|
+
* and the store inside it, so connecting again can wrap that store rather
|
|
611
|
+
* than the wrapper.
|
|
612
|
+
*/
|
|
613
|
+
#threadStore: { remote: RemoteConversationStore; inner: ClientConversationStore } | null = null;
|
|
588
614
|
|
|
589
615
|
#client: AgUiClient | null = null;
|
|
590
616
|
// Seed for the next client. Once one exists it owns the live value (the
|
|
591
617
|
// agent applies STATE_SNAPSHOT / STATE_DELTA into it), so this is only the
|
|
592
|
-
// starting point — `sharedState` reads through to the client when present
|
|
618
|
+
// starting point — `sharedState` reads through to the client when present,
|
|
619
|
+
// and to a running checkpoint continuation before that. Every client mirrors
|
|
620
|
+
// its changes here, which is what the client built after a continuation is
|
|
621
|
+
// seeded from.
|
|
593
622
|
#sharedState: Record<string, unknown> = {};
|
|
594
623
|
// Whether an interaction is in flight (first onRunStart → onSettled). Drives
|
|
595
624
|
// the Send⇄Stop button: `agent.isRunning` is false between frontend-tool
|
|
@@ -807,7 +836,12 @@ export class AgUiChat extends HTMLElement {
|
|
|
807
836
|
},
|
|
808
837
|
});
|
|
809
838
|
this.#checkpoints = new CheckpointMenu((runId, verb) => {
|
|
810
|
-
|
|
839
|
+
// Reported rather than dropped: the send fails when the host's store
|
|
840
|
+
// refuses the first save, and `continueRun` has already let go of the
|
|
841
|
+
// continuation by then, so the panel takes another pick.
|
|
842
|
+
void this.#history.continueRun(runId, verb).catch((error: unknown) => {
|
|
843
|
+
console.warn("<ag-ui-chat>: continuing a run failed", error);
|
|
844
|
+
});
|
|
811
845
|
});
|
|
812
846
|
this.#history = new ConversationHistory({
|
|
813
847
|
element: this,
|
|
@@ -827,8 +861,14 @@ export class AgUiChat extends HTMLElement {
|
|
|
827
861
|
requestCredentials: () => this.#requestCredentials(),
|
|
828
862
|
appendMessage: (role, content) => this.appendMessage(role, content),
|
|
829
863
|
autoGrow: () => autoGrow(this.#input),
|
|
864
|
+
continuationEnded: () => this.#flushQueued(),
|
|
865
|
+
client: () => this.#client,
|
|
830
866
|
ensureClient: () => this.#ensureClient(),
|
|
831
867
|
buildClient: (seed) => this.#buildClient(seed),
|
|
868
|
+
releaseClient: () => {
|
|
869
|
+
this.#client = null;
|
|
870
|
+
},
|
|
871
|
+
running: () => this.#running,
|
|
832
872
|
cancelRun: () => this.#cancelRun(),
|
|
833
873
|
resetState: () => this.#resetState(),
|
|
834
874
|
setRunning: (running) => this.#setRunning(running),
|
|
@@ -952,14 +992,38 @@ export class AgUiChat extends HTMLElement {
|
|
|
952
992
|
* from {@link registerPageState}, which exposes host state as ordinary tools.
|
|
953
993
|
*/
|
|
954
994
|
get sharedState(): Readonly<Record<string, unknown>> {
|
|
955
|
-
return this.#
|
|
995
|
+
return this.#liveClient()?.state ?? this.#sharedState;
|
|
956
996
|
}
|
|
957
997
|
|
|
958
998
|
set sharedState(state: Readonly<Record<string, unknown>>) {
|
|
959
999
|
this.#sharedState = { ...state };
|
|
960
1000
|
// A client already exists for this conversation — push it through so the
|
|
961
1001
|
// next run sends it, rather than silently waiting for a new conversation.
|
|
962
|
-
|
|
1002
|
+
// A continuation in flight is that client while it runs: its next round
|
|
1003
|
+
// sends what it holds, and the getter reads it back from there.
|
|
1004
|
+
this.#liveClient()?.setState(this.#sharedState);
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
/**
|
|
1008
|
+
* The client holding the conversation's live shared state: a checkpoint
|
|
1009
|
+
* continuation while one runs, otherwise the conversation's own client, and
|
|
1010
|
+
* `null` before either exists.
|
|
1011
|
+
*
|
|
1012
|
+
* The continuation first, because the state it streams is applied to its own
|
|
1013
|
+
* agent. Reading the conversation's client alone returned what the state was
|
|
1014
|
+
* before the continuation began, for as long as that client lived.
|
|
1015
|
+
*
|
|
1016
|
+
* It now lives no longer than the continuation's first save, which hands the
|
|
1017
|
+
* conversation to the next client built, seeded from the mirror every client
|
|
1018
|
+
* writes -- so the next run sends what the continuation left. That release
|
|
1019
|
+
* alone makes the getter's fallback give the same answer, and no test can
|
|
1020
|
+
* tell the two apart; reading the continuation keeps the getter right
|
|
1021
|
+
* without depending on when the other client is let go. The setter has no
|
|
1022
|
+
* such fallback, and is held by "reads the shared state it changed while it
|
|
1023
|
+
* runs, not the conversation's" in `ag_ui_chat_checkpoints.test.ts`.
|
|
1024
|
+
*/
|
|
1025
|
+
#liveClient(): AgUiClient | null {
|
|
1026
|
+
return this.#history.continuation ?? this.#client;
|
|
963
1027
|
}
|
|
964
1028
|
|
|
965
1029
|
/** Bind a piece of host page state to `read_<name>` / `set_<name>` tools. */
|
|
@@ -1164,6 +1228,18 @@ export class AgUiChat extends HTMLElement {
|
|
|
1164
1228
|
}
|
|
1165
1229
|
|
|
1166
1230
|
connectedCallback(): void {
|
|
1231
|
+
// A fresh signal per connection: disconnecting aborted the last one, and
|
|
1232
|
+
// with it every listener the last connection added.
|
|
1233
|
+
this.#connection = new AbortController();
|
|
1234
|
+
// Coming back is a reload, not a merge, for the reason `reload()` is: the
|
|
1235
|
+
// run was cancelled on the way out, and the transcript is rebuilt below
|
|
1236
|
+
// from persisted history -- which could only be appended to what is still
|
|
1237
|
+
// showing. The composer's recall history is kept, because a move is not a
|
|
1238
|
+
// farewell and disconnecting put the queued messages there on purpose.
|
|
1239
|
+
if (this.#connectedBefore) {
|
|
1240
|
+
this.#resetConversation();
|
|
1241
|
+
}
|
|
1242
|
+
this.#connectedBefore = true;
|
|
1167
1243
|
// Resolve the per-instance storage namespace before any key read/write, so
|
|
1168
1244
|
// this instance doesn't share collapsed/theme/thread state with another on
|
|
1169
1245
|
// the same origin.
|
|
@@ -1206,7 +1282,7 @@ export class AgUiChat extends HTMLElement {
|
|
|
1206
1282
|
this.#skills.init();
|
|
1207
1283
|
// Namespace the built-in default store too (a host-injected store is used
|
|
1208
1284
|
// verbatim). Must precede #wireThreadStore, which wraps the current store.
|
|
1209
|
-
this.conversationStore = this.#storage.scopeStore(this
|
|
1285
|
+
this.conversationStore = this.#storage.scopeStore(this.#unwrappedStore(), this.userKey);
|
|
1210
1286
|
window.addEventListener("resize", this.#onViewportResize);
|
|
1211
1287
|
// The visual viewport changes without the window resizing -- a keyboard
|
|
1212
1288
|
// opening, a pinch-zoom, the URL bar collapsing -- and `scroll` is what
|
|
@@ -1215,7 +1291,7 @@ export class AgUiChat extends HTMLElement {
|
|
|
1215
1291
|
window.visualViewport?.addEventListener("scroll", this.#onViewportResize);
|
|
1216
1292
|
this.#placement.publishVisualViewport();
|
|
1217
1293
|
this.#wireThreadStore();
|
|
1218
|
-
this.#attachments.wire();
|
|
1294
|
+
this.#attachments.wire(this.#connection.signal);
|
|
1219
1295
|
this.#voice.wire();
|
|
1220
1296
|
this.#history.adoptActiveThread();
|
|
1221
1297
|
// The catalog requests go out a microtask later, so a host configuring
|
|
@@ -1290,6 +1366,7 @@ export class AgUiChat extends HTMLElement {
|
|
|
1290
1366
|
*/
|
|
1291
1367
|
disconnectedCallback(): void {
|
|
1292
1368
|
this.#connected = false;
|
|
1369
|
+
this.#connection.abort();
|
|
1293
1370
|
window.removeEventListener("resize", this.#onViewportResize);
|
|
1294
1371
|
window.visualViewport?.removeEventListener("resize", this.#onViewportResize);
|
|
1295
1372
|
window.visualViewport?.removeEventListener("scroll", this.#onViewportResize);
|
|
@@ -1410,18 +1487,38 @@ export class AgUiChat extends HTMLElement {
|
|
|
1410
1487
|
* thread id, the navigation checkpoint) keep their local store either way.
|
|
1411
1488
|
*/
|
|
1412
1489
|
#wireThreadStore(): void {
|
|
1490
|
+
this.#threadStore = null;
|
|
1413
1491
|
const url = this.getAttribute("data-threads-url");
|
|
1414
1492
|
if (url !== null) {
|
|
1415
|
-
this.conversationStore
|
|
1493
|
+
const inner = this.conversationStore;
|
|
1494
|
+
const remote = new RemoteConversationStore(
|
|
1416
1495
|
url,
|
|
1417
1496
|
() => this.#headersFor(url),
|
|
1418
|
-
|
|
1497
|
+
inner,
|
|
1419
1498
|
() => this.#requestCredentials(),
|
|
1420
1499
|
this.getAttribute("data-threads-cache") !== "false",
|
|
1421
1500
|
);
|
|
1501
|
+
this.#threadStore = { remote, inner };
|
|
1502
|
+
this.conversationStore = remote;
|
|
1422
1503
|
}
|
|
1423
1504
|
}
|
|
1424
1505
|
|
|
1506
|
+
/**
|
|
1507
|
+
* The conversation store without the remote this element wrapped it in.
|
|
1508
|
+
*
|
|
1509
|
+
* An element removed and inserted again connects again, and wrapping the
|
|
1510
|
+
* wrapper stacked a second remote on the first, so every rename and delete
|
|
1511
|
+
* reached the server twice. Unwrapping first also lets a `data-threads-url`
|
|
1512
|
+
* removed while detached take the remote away. A store the host assigned
|
|
1513
|
+
* since is theirs, and comes back as it is.
|
|
1514
|
+
*/
|
|
1515
|
+
#unwrappedStore(): ClientConversationStore {
|
|
1516
|
+
const wrapped = this.#threadStore;
|
|
1517
|
+
return wrapped !== null && this.conversationStore === wrapped.remote
|
|
1518
|
+
? wrapped.inner
|
|
1519
|
+
: this.conversationStore;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1425
1522
|
/**
|
|
1426
1523
|
* Replace the host-supplied (client) skill catalog. Merged after the embedded
|
|
1427
1524
|
* and fetched skills (so a client skill overrides a same-named server one).
|
|
@@ -1737,6 +1834,24 @@ export class AgUiChat extends HTMLElement {
|
|
|
1737
1834
|
|
|
1738
1835
|
/** Drop the in-memory run + transcript, leaving the thread id untouched. */
|
|
1739
1836
|
#resetState(): void {
|
|
1837
|
+
this.#resetConversation();
|
|
1838
|
+
// The composer's own history goes with the conversation it was typed
|
|
1839
|
+
// into. The path that makes this more than tidiness is the `user-key`
|
|
1840
|
+
// rescope, which purges storage and wipes the transcript precisely so the
|
|
1841
|
+
// previous principal's words are not visible to the next one -- and would
|
|
1842
|
+
// otherwise leave every one of them a single ArrowUp away.
|
|
1843
|
+
this.#sentDrafts.length = 0;
|
|
1844
|
+
this.#recallIndex = null;
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
/**
|
|
1848
|
+
* Drop the in-memory run and transcript, and nothing the user typed.
|
|
1849
|
+
*
|
|
1850
|
+
* Split from {@link #resetState} for re-insertion, which rebuilds the
|
|
1851
|
+
* conversation from history but is not a new one, so the composer's recall
|
|
1852
|
+
* history stays.
|
|
1853
|
+
*/
|
|
1854
|
+
#resetConversation(): void {
|
|
1740
1855
|
this.#client = null;
|
|
1741
1856
|
// Every path here has just cancelled the run, but a cancelled run ends
|
|
1742
1857
|
// later: once its request closes, or once a host tool's handler returns.
|
|
@@ -1746,13 +1861,6 @@ export class AgUiChat extends HTMLElement {
|
|
|
1746
1861
|
this.#runHandlers.detach();
|
|
1747
1862
|
this.#clearTranscript();
|
|
1748
1863
|
this.#history.forgetRestored();
|
|
1749
|
-
// The composer's own history goes with the conversation it was typed
|
|
1750
|
-
// into. The path that makes this more than tidiness is the `user-key`
|
|
1751
|
-
// rescope, which purges storage and wipes the transcript precisely so the
|
|
1752
|
-
// previous principal's words are not visible to the next one -- and would
|
|
1753
|
-
// otherwise leave every one of them a single ArrowUp away.
|
|
1754
|
-
this.#sentDrafts.length = 0;
|
|
1755
|
-
this.#recallIndex = null;
|
|
1756
1864
|
}
|
|
1757
1865
|
|
|
1758
1866
|
/**
|
|
@@ -1835,7 +1943,19 @@ export class AgUiChat extends HTMLElement {
|
|
|
1835
1943
|
return this.#transcript.append(role, content);
|
|
1836
1944
|
}
|
|
1837
1945
|
|
|
1946
|
+
/**
|
|
1947
|
+
* Build the chrome, from the attributes as they stand.
|
|
1948
|
+
*
|
|
1949
|
+
* Runs on every connect, not once, because removing and re-inserting the
|
|
1950
|
+
* element is the documented way to apply a connect-time attribute written
|
|
1951
|
+
* late. So it has to build the same element the second time as the first:
|
|
1952
|
+
* the elements made here are new each time, the long-lived ones it fills
|
|
1953
|
+
* are filled with `replaceChildren` rather than appended to, and every
|
|
1954
|
+
* listener on a long-lived one goes under the connection's signal. Appending
|
|
1955
|
+
* gave a re-inserted element two headers and two composers.
|
|
1956
|
+
*/
|
|
1838
1957
|
#render(): void {
|
|
1958
|
+
const { signal } = this.#connection;
|
|
1839
1959
|
this.#chat.className = "chat";
|
|
1840
1960
|
this.#chat.setAttribute("part", "panel");
|
|
1841
1961
|
|
|
@@ -1901,7 +2021,7 @@ export class AgUiChat extends HTMLElement {
|
|
|
1901
2021
|
this.#themeToggle.setAttribute("part", "header-button theme-toggle");
|
|
1902
2022
|
this.#themeToggle.title = this.#strings.toggleTheme;
|
|
1903
2023
|
this.#themeToggle.setAttribute("aria-label", this.#strings.toggleTheme);
|
|
1904
|
-
this.#themeToggle.addEventListener("click", () => this.toggleTheme());
|
|
2024
|
+
this.#themeToggle.addEventListener("click", () => this.toggleTheme(), { signal });
|
|
1905
2025
|
this.#syncThemeGlyph();
|
|
1906
2026
|
controls.append(this.#themeToggle);
|
|
1907
2027
|
}
|
|
@@ -1931,11 +2051,11 @@ export class AgUiChat extends HTMLElement {
|
|
|
1931
2051
|
this.#jumpButton.textContent = this.#strings.jumpToLatest;
|
|
1932
2052
|
|
|
1933
2053
|
// The quote offer, and the transcript's settled selections it listens for.
|
|
1934
|
-
this.#excerpts.mount();
|
|
2054
|
+
this.#excerpts.mount(signal);
|
|
1935
2055
|
|
|
1936
2056
|
// Built here rather than at field initialisation: the viewport has to exist
|
|
1937
2057
|
// and the observer has to have something to observe.
|
|
1938
|
-
this.#transcript.mountScroller(this.#jumpButton);
|
|
2058
|
+
this.#transcript.mountScroller(this.#jumpButton, signal);
|
|
1939
2059
|
|
|
1940
2060
|
this.#announcer.mount();
|
|
1941
2061
|
|
|
@@ -1955,7 +2075,6 @@ export class AgUiChat extends HTMLElement {
|
|
|
1955
2075
|
greetingSlot.append(this.#greetingText);
|
|
1956
2076
|
greeting.append(greetingSlot);
|
|
1957
2077
|
this.#syncGreeting();
|
|
1958
|
-
this.#emptyWrap.append(greeting);
|
|
1959
2078
|
const emptySlot = document.createElement("slot");
|
|
1960
2079
|
emptySlot.name = "empty";
|
|
1961
2080
|
// Fallback content, so a host that slots its own gets exactly that and
|
|
@@ -1967,7 +2086,7 @@ export class AgUiChat extends HTMLElement {
|
|
|
1967
2086
|
if (starters !== null) {
|
|
1968
2087
|
emptySlot.append(starters);
|
|
1969
2088
|
}
|
|
1970
|
-
this.#emptyWrap.
|
|
2089
|
+
this.#emptyWrap.replaceChildren(greeting, emptySlot);
|
|
1971
2090
|
this.#queuedRow.className = "queued";
|
|
1972
2091
|
this.#queuedRow.setAttribute("part", "queued");
|
|
1973
2092
|
this.#queuedRow.setAttribute("role", "group");
|
|
@@ -1997,8 +2116,8 @@ export class AgUiChat extends HTMLElement {
|
|
|
1997
2116
|
this.#input.setAttribute("aria-label", this.#strings.message);
|
|
1998
2117
|
this.#input.rows = 1;
|
|
1999
2118
|
this.#input.placeholder = this.#strings.inputPlaceholder;
|
|
2000
|
-
this.#input.addEventListener("keydown", (event) => this.#onKeydown(event));
|
|
2001
|
-
this.#input.addEventListener("input", () => this.#onInput());
|
|
2119
|
+
this.#input.addEventListener("keydown", (event) => this.#onKeydown(event), { signal });
|
|
2120
|
+
this.#input.addEventListener("input", () => this.#onInput(), { signal });
|
|
2002
2121
|
|
|
2003
2122
|
// Icon-only, with both glyphs mounted at once and CSS showing the one the
|
|
2004
2123
|
// state calls for — swapping a single glyph would leave a host that slotted
|
|
@@ -2006,22 +2125,26 @@ export class AgUiChat extends HTMLElement {
|
|
|
2006
2125
|
this.#send.className = "send";
|
|
2007
2126
|
this.#send.type = "button";
|
|
2008
2127
|
this.#send.setAttribute("part", "send");
|
|
2009
|
-
this.#send.
|
|
2128
|
+
this.#send.replaceChildren(
|
|
2010
2129
|
glyphSlot("icon-send", "send-send", ICON_SEND),
|
|
2011
2130
|
glyphSlot("icon-stop", "send-stop", ICON_STOP),
|
|
2012
2131
|
);
|
|
2013
2132
|
this.#send.title = this.#strings.send;
|
|
2014
2133
|
this.#send.setAttribute("aria-label", this.#strings.send);
|
|
2015
2134
|
this.#send.dataset["state"] = "idle";
|
|
2016
|
-
this.#send.addEventListener(
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2135
|
+
this.#send.addEventListener(
|
|
2136
|
+
"click",
|
|
2137
|
+
() => {
|
|
2138
|
+
// One button, two states: Send while idle, Stop while a run is in
|
|
2139
|
+
// flight (no layout change).
|
|
2140
|
+
if (this.#running) {
|
|
2141
|
+
this.#cancelRun();
|
|
2142
|
+
return;
|
|
2143
|
+
}
|
|
2144
|
+
void this.#submit();
|
|
2145
|
+
},
|
|
2146
|
+
{ signal },
|
|
2147
|
+
);
|
|
2025
2148
|
|
|
2026
2149
|
this.#composerHint.className = "skill-hint";
|
|
2027
2150
|
this.#composerHint.setAttribute("part", "skill-hint");
|
|
@@ -2033,17 +2156,19 @@ export class AgUiChat extends HTMLElement {
|
|
|
2033
2156
|
this.#attachButton.className = "attach-btn";
|
|
2034
2157
|
this.#attachButton.type = "button";
|
|
2035
2158
|
this.#attachButton.setAttribute("part", "attach-button");
|
|
2036
|
-
this.#attachButton.
|
|
2159
|
+
this.#attachButton.replaceChildren(glyphSlot("icon-attach", "attach-glyph", ICON_ATTACH));
|
|
2037
2160
|
this.#attachButton.title = this.#strings.attachFiles;
|
|
2038
2161
|
this.#attachButton.setAttribute("aria-label", this.#strings.attachFiles);
|
|
2039
2162
|
this.#attachButton.hidden = true;
|
|
2040
|
-
this.#attachButton.addEventListener("click", () => this.#fileInput.click());
|
|
2163
|
+
this.#attachButton.addEventListener("click", () => this.#fileInput.click(), { signal });
|
|
2041
2164
|
|
|
2042
2165
|
this.#fileInput.className = "attach-input";
|
|
2043
2166
|
this.#fileInput.type = "file";
|
|
2044
2167
|
this.#fileInput.multiple = true;
|
|
2045
2168
|
this.#fileInput.hidden = true;
|
|
2046
|
-
this.#fileInput.addEventListener("change", () => this.#attachments.onFilesPicked()
|
|
2169
|
+
this.#fileInput.addEventListener("change", () => this.#attachments.onFilesPicked(), {
|
|
2170
|
+
signal,
|
|
2171
|
+
});
|
|
2047
2172
|
|
|
2048
2173
|
this.#attachSlot.className = "attachment-slot";
|
|
2049
2174
|
|
|
@@ -2063,9 +2188,9 @@ export class AgUiChat extends HTMLElement {
|
|
|
2063
2188
|
this.#messagesWrap.className = "messages-wrap";
|
|
2064
2189
|
// Sibling of the list inside a shared box, not a child of it: the
|
|
2065
2190
|
// affordance offering to scroll must not scroll away with the content.
|
|
2066
|
-
this.#messagesWrap.
|
|
2191
|
+
this.#messagesWrap.replaceChildren(this.#messages, this.#jumpButton, this.#excerpts.button);
|
|
2067
2192
|
|
|
2068
|
-
this.#chat.
|
|
2193
|
+
this.#chat.replaceChildren(
|
|
2069
2194
|
header,
|
|
2070
2195
|
this.#messagesWrap,
|
|
2071
2196
|
this.#skillsMenu.palette,
|
|
@@ -2087,16 +2212,20 @@ export class AgUiChat extends HTMLElement {
|
|
|
2087
2212
|
// button's own click, so closing here and toggling there would land back open.
|
|
2088
2213
|
// Composed path rather than `target`, because the event is retargeted at the
|
|
2089
2214
|
// shadow boundary and every one of these nodes is inside it.
|
|
2090
|
-
this.#chat.addEventListener(
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2215
|
+
this.#chat.addEventListener(
|
|
2216
|
+
"pointerdown",
|
|
2217
|
+
(event) => {
|
|
2218
|
+
if (!this.#checkpoints.open_) {
|
|
2219
|
+
return;
|
|
2220
|
+
}
|
|
2221
|
+
const path = event.composedPath();
|
|
2222
|
+
if (path.includes(this.#checkpoints.element) || path.includes(checkpoints)) {
|
|
2223
|
+
return;
|
|
2224
|
+
}
|
|
2225
|
+
this.#checkpoints.close();
|
|
2226
|
+
},
|
|
2227
|
+
{ signal },
|
|
2228
|
+
);
|
|
2100
2229
|
|
|
2101
2230
|
// What a collapsed widget shrinks to: a round floating button, or the slim
|
|
2102
2231
|
// edge rail under `placement="sidebar"` — one element, shaped by CSS.
|
|
@@ -2120,20 +2249,20 @@ export class AgUiChat extends HTMLElement {
|
|
|
2120
2249
|
this.#railLabel.setAttribute("part", "rail-label");
|
|
2121
2250
|
this.#railLabel.setAttribute("aria-hidden", "true");
|
|
2122
2251
|
this.#railLabel.textContent = this.getAttribute("title-text") ?? this.#strings.title;
|
|
2123
|
-
this.#launcher.
|
|
2252
|
+
this.#launcher.replaceChildren(
|
|
2124
2253
|
iconElement("launcher", "launcher-icon", ICON_LAUNCHER, readLauncherIconUrl(this)),
|
|
2125
2254
|
this.#railLabel,
|
|
2126
2255
|
this.#badge,
|
|
2127
2256
|
);
|
|
2128
|
-
this.#launcher.addEventListener("click", () => this.setCollapsed(false));
|
|
2129
|
-
this.#placement.enableLauncherDrag();
|
|
2257
|
+
this.#launcher.addEventListener("click", () => this.setCollapsed(false), { signal });
|
|
2258
|
+
this.#placement.enableLauncherDrag(signal);
|
|
2130
2259
|
|
|
2131
2260
|
this.#placement.mountResizeGrips(this.#chat);
|
|
2132
2261
|
adoptStyles(this.#root);
|
|
2133
2262
|
const probe = this.#placement.probe;
|
|
2134
2263
|
probe.className = "viewport-probe";
|
|
2135
2264
|
probe.setAttribute("aria-hidden", "true");
|
|
2136
|
-
this.#root.
|
|
2265
|
+
this.#root.replaceChildren(probe, this.#announcer.region, this.#chat, this.#launcher);
|
|
2137
2266
|
}
|
|
2138
2267
|
|
|
2139
2268
|
/**
|
|
@@ -2328,6 +2457,15 @@ export class AgUiChat extends HTMLElement {
|
|
|
2328
2457
|
* other shape would be a second sender racing the guard above.
|
|
2329
2458
|
*/
|
|
2330
2459
|
#flushQueued(): void {
|
|
2460
|
+
// Guarded here rather than at each caller, because this is what takes the
|
|
2461
|
+
// turn off the queue: a caller that ran while something was still in
|
|
2462
|
+
// flight would shift a turn off and lose it to the no-op in `sendMessage`.
|
|
2463
|
+
// A continuation settles its run before the element lets go of it, so the
|
|
2464
|
+
// settle below arrives while `continuation` is still set, and the release
|
|
2465
|
+
// that follows is what drains.
|
|
2466
|
+
if (this.#running || this.#history.continuation !== null) {
|
|
2467
|
+
return;
|
|
2468
|
+
}
|
|
2331
2469
|
const next = this.#queued.shift();
|
|
2332
2470
|
this.#renderQueued();
|
|
2333
2471
|
if (next !== undefined) {
|
|
@@ -2378,11 +2516,16 @@ export class AgUiChat extends HTMLElement {
|
|
|
2378
2516
|
// the first's still-pending tool cards. That is why this was a dead key --
|
|
2379
2517
|
// Enter during a run did nothing at all, silently.
|
|
2380
2518
|
//
|
|
2519
|
+
// A picked checkpoint counts as in flight from the pick, not from its
|
|
2520
|
+
// first event: `running` is a request behind it, and a turn sent in that
|
|
2521
|
+
// window ran against the conversation the continuation had already frozen
|
|
2522
|
+
// to save against, so one of the two saves dropped the other's turn.
|
|
2523
|
+
//
|
|
2381
2524
|
// Queueing keeps the guard and gives the key something to do. Text only:
|
|
2382
2525
|
// an attachment is settled state the tray is holding and the composer has
|
|
2383
2526
|
// no second copy of, so parking it here would mean deciding what happens
|
|
2384
2527
|
// when the user then removes the chip.
|
|
2385
|
-
if (this.#running) {
|
|
2528
|
+
if (this.#running || this.#history.continuation !== null) {
|
|
2386
2529
|
if (content !== "") {
|
|
2387
2530
|
this.#queued.push(content);
|
|
2388
2531
|
this.#renderQueued();
|
|
@@ -2432,12 +2575,18 @@ export class AgUiChat extends HTMLElement {
|
|
|
2432
2575
|
* `attachments` are durable {@link AttachmentRef}s — what {@link attachFile}
|
|
2433
2576
|
* resolves to and what {@link ATTACHMENT_EVENT} reports.
|
|
2434
2577
|
*
|
|
2435
|
-
* No-ops on an empty message, and while a run
|
|
2436
|
-
*
|
|
2437
|
-
*
|
|
2578
|
+
* No-ops on an empty message, and while a run or a picked checkpoint's
|
|
2579
|
+
* continuation is in flight, since a second concurrent run would orphan the
|
|
2580
|
+
* first. Unlike the built-in Send it does not queue: it returns, and the
|
|
2581
|
+
* caller keeps what it tried to send. Nor does it consult the tray -- what
|
|
2582
|
+
* you pass is what is sent.
|
|
2438
2583
|
*/
|
|
2439
2584
|
async sendMessage(content: string, attachments: readonly AttachmentRef[] = []): Promise<void> {
|
|
2440
|
-
if (
|
|
2585
|
+
if (
|
|
2586
|
+
this.#running ||
|
|
2587
|
+
this.#history.continuation !== null ||
|
|
2588
|
+
(content === "" && attachments.length === 0)
|
|
2589
|
+
) {
|
|
2441
2590
|
return;
|
|
2442
2591
|
}
|
|
2443
2592
|
// Only a send travels. Every other way out of the empty state -- a restored
|
|
@@ -2522,7 +2671,8 @@ export class AgUiChat extends HTMLElement {
|
|
|
2522
2671
|
this.#client = this.#buildClient({
|
|
2523
2672
|
endpoint: this.endpoint,
|
|
2524
2673
|
initialMessages: this.#history.restored,
|
|
2525
|
-
|
|
2674
|
+
// Its history is the whole conversation, so nothing goes ahead of it.
|
|
2675
|
+
follows: [],
|
|
2526
2676
|
});
|
|
2527
2677
|
}
|
|
2528
2678
|
return this.#client;
|
|
@@ -2564,12 +2714,15 @@ export class AgUiChat extends HTMLElement {
|
|
|
2564
2714
|
getContext: () => this.#dispatch.buildContext(),
|
|
2565
2715
|
executeTool: (call) => this.#dispatch.execute(call),
|
|
2566
2716
|
resolveInterrupts: (interrupts) => this.#dispatch.resolveInterrupts(interrupts),
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2717
|
+
// Every client saves the whole conversation, because a store keeps one
|
|
2718
|
+
// list per thread. For the conversation's own client that is its
|
|
2719
|
+
// history; a continuation holds only what it adds, and writes the
|
|
2720
|
+
// conversation it continues ahead of that.
|
|
2721
|
+
onPersist: (messages) => {
|
|
2722
|
+
const conversation = [...seed.follows, ...messages];
|
|
2723
|
+
this.conversationStore.saveMessages(threadId, conversation);
|
|
2724
|
+
seed.onSaved?.(conversation);
|
|
2725
|
+
},
|
|
2573
2726
|
onStateChanged: (state) => this.#onSharedStateChanged(state),
|
|
2574
2727
|
connectionLostMessage: this.#strings.connectionLost,
|
|
2575
2728
|
unfinishedMessage: this.#strings.callNotFinished,
|