@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.
Files changed (41) hide show
  1. package/CHANGELOG.md +86 -1
  2. package/README.md +98 -5
  3. package/dist/ag-ui-web-component.bundle.js +126 -50
  4. package/dist/ag-ui-web-component.bundle.js.map +4 -4
  5. package/dist/constants.d.ts +9 -0
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/core/ag_ui_chat.d.ts +29 -4
  8. package/dist/core/ag_ui_chat.d.ts.map +1 -1
  9. package/dist/core/agui_client.d.ts +11 -0
  10. package/dist/core/agui_client.d.ts.map +1 -1
  11. package/dist/core/create_http_agent.d.ts +6 -0
  12. package/dist/core/create_http_agent.d.ts.map +1 -1
  13. package/dist/core/run_index.d.ts +50 -0
  14. package/dist/core/run_index.d.ts.map +1 -0
  15. package/dist/index.d.ts +9 -3
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +453 -63
  18. package/dist/index.js.map +3 -3
  19. package/dist/tools/page_state.d.ts +40 -0
  20. package/dist/tools/page_state.d.ts.map +1 -0
  21. package/dist/ui/checkpoint_menu.d.ts +32 -0
  22. package/dist/ui/checkpoint_menu.d.ts.map +1 -0
  23. package/dist/ui/styles.d.ts +1 -1
  24. package/dist/ui/styles.d.ts.map +1 -1
  25. package/dist/ui/ui_strings.d.ts +10 -0
  26. package/dist/ui/ui_strings.d.ts.map +1 -1
  27. package/package.json +1 -1
  28. package/src/constants.ts +10 -0
  29. package/src/core/ag_ui_chat.ts +153 -6
  30. package/src/core/agui_client.ts +28 -0
  31. package/src/core/create_http_agent.ts +7 -0
  32. package/src/core/run_index.ts +91 -0
  33. package/src/index.ts +14 -1
  34. package/src/tools/page_state.ts +72 -0
  35. package/src/ui/checkpoint_menu.ts +153 -0
  36. package/src/ui/styles.ts +76 -0
  37. package/src/ui/ui_strings.ts +15 -0
  38. package/src/version.ts +1 -1
  39. package/dist/tools/state_hook.d.ts +0 -23
  40. package/dist/tools/state_hook.d.ts.map +0 -1
  41. package/src/tools/state_hook.ts +0 -53
package/CHANGELOG.md CHANGED
@@ -7,6 +7,89 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.13.0] — 2026-07-27
11
+
12
+ ### Added
13
+
14
+ - **AG-UI shared state.** `<ag-ui-chat>` now speaks the protocol's own state
15
+ channel: assign `chat.sharedState = {...}` to seed it, listen for the
16
+ `ag-ui-state` event (`detail.state`) to react when the agent changes it.
17
+ State rides `RunAgentInput.state` on every run and is replaced in place when
18
+ the server streams `STATE_SNAPSHOT` / `STATE_DELTA`.
19
+ - **Adoption, not implementation.** `@ag-ui/client` already applies both
20
+ events to `agent.state` and exposes an `onStateChanged` subscriber hook;
21
+ what was missing was seeding it (`initialState`) and surfacing it to the
22
+ host. Deriving state from the raw event stream ourselves would have
23
+ duplicated — and eventually contradicted — the client's own handling,
24
+ including its JSON-Patch delta application.
25
+ - Assigning **after** the conversation has started pushes through to the live
26
+ agent rather than waiting for a new one, so a host that seeds state late
27
+ isn't silently stranded.
28
+ - The event is `composed: true`, so a host listening on `document` receives it
29
+ through the shadow boundary.
30
+ - **Distinct from `registerPageState`**, which exposes host state to the agent
31
+ as ordinary *tools*. Use shared state when agent and page edit the same
32
+ object; use page-state tools when the agent should *ask* — a tool call is
33
+ visible in the transcript and can be gated by a confirmation card, which
34
+ state events cannot.
35
+
36
+ ### Changed
37
+
38
+ - **`registerStateHook` is now `registerPageState`** (and `createStateHookTools` /
39
+ `StateHook` are `createPageStateTools` / `PageState`). **The old names implied a
40
+ feature that does not exist**: they read as AG-UI shared-state sync —
41
+ `STATE_SNAPSHOT` / `STATE_DELTA`, the protocol events that carry a state object
42
+ between agent and client — which neither this component nor `django-ag-ui`
43
+ implements. What the method actually does is generate two ordinary client tools
44
+ (`read_<name>` / `set_<name>`) over host page state, which the agent calls like
45
+ any other tool. `page` matches the vocabulary already used throughout the
46
+ component (`page_map`, `page_action_tools`, `route_map`, the DOM driver).
47
+ - **Not a hard break.** The old spellings are kept as deprecated aliases and
48
+ behave identically; they will be removed in a future major. A rename is only
49
+ ever cheaper the earlier it happens, which is why this ships now rather than
50
+ waiting for state support to exist.
51
+
52
+ ### Documentation
53
+
54
+ - **The README now distinguishes the two state mechanisms**, which the old
55
+ method name conflated. `sharedState` is the protocol's state channel;
56
+ `registerPageState` generates ordinary client tools. The rename and the
57
+ feature landed together, so the docs answer "which one" rather than leaving
58
+ two similar names side by side.
59
+
60
+ ## [0.12.0] — 2026-07-27
61
+
62
+ ### Added
63
+
64
+ - **Resume or fork a run — the checkpoint UI.** With `data-runs-url` pointed at
65
+ django-ag-ui's run index (`RunsView`, 0.23+), a ⭯ button appears in the header
66
+ opening a *Continue a run* panel. Type the next turn, pick a row, and the run
67
+ continues from its last server-side checkpoint — **Resume** to carry on,
68
+ **Fork** to branch without touching the original. The client half of durable
69
+ step persistence, whose server half shipped in django-ag-ui 0.20.0.
70
+ - **Only continuable runs are offered.** The server reports whether a run has
71
+ a snapshot to seed from; one that never reached a provider-valid boundary
72
+ has none, so resuming it would start from nothing. Rows show when the run
73
+ started (id on hover, for correlating with server logs) and mark a branched
74
+ run so a fork doesn't read as a duplicate of its parent.
75
+ - **One URL configures three endpoints.** `resume/<id>/` and `fork/<id>/` are
76
+ siblings of the index — the server mounts all three together — so they are
77
+ derived rather than configured, and a half-configured set isn't expressible.
78
+ - **The client contract is structural, not a rule to remember.** Those
79
+ endpoints require a *fresh run id* and *only the new turn*, because the
80
+ server supplies prior turns from the snapshot and re-sending them would
81
+ duplicate the conversation. A continuation therefore runs on its own
82
+ short-lived agent, pointed at the resume endpoint and seeded with **no**
83
+ history — so the new turn is the only thing it *can* send, the fresh run id
84
+ comes free, and the main agent's history is never touched.
85
+ - A resumed run is otherwise a normal run: frontend tools execute, approval
86
+ interrupts render, and `headers` are re-read per request so a rotated
87
+ token still reaches the endpoint. An unreachable index shows the panel's
88
+ empty state rather than an error.
89
+ - New exports: `RunIndex` / `RunRow`, `CheckpointMenu` / `CheckpointVerb`, and
90
+ five UI strings (`checkpoints`, `noCheckpoints`, `resumeRun`, `forkRun`,
91
+ `forkedRun`) for localization.
92
+
10
93
  ## [0.11.0] — 2026-07-14
11
94
 
12
95
  ### Added
@@ -495,7 +578,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
495
578
  ### Notes
496
579
  - First release — exercising the automated npm OIDC publish pipeline end-to-end.
497
580
 
498
- [Unreleased]: https://github.com/Artui/ag-ui-web-component/compare/v0.11.0...HEAD
581
+ [Unreleased]: https://github.com/Artui/ag-ui-web-component/compare/v0.13.0...HEAD
582
+ [0.13.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.12.0...v0.13.0
583
+ [0.12.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.11.0...v0.12.0
499
584
  [0.11.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.10.0...v0.11.0
500
585
  [0.10.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.9.0...v0.10.0
501
586
  [0.9.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.8.1...v0.9.0
package/README.md CHANGED
@@ -155,6 +155,7 @@ That's the whole integration: an `endpoint` attribute pointing at your AG-UI ser
155
155
  | `data-skills-url` | — | URL of a JSON skill catalog (fetched with `headers`). |
156
156
  | `data-tools-url` | — | URL of a server tool-label catalog (`[{ name, summary, description? }]`), fetched with `headers`; labels tool-call cards for server-side tools. |
157
157
  | `data-threads-url` | — | URL of a server thread index (django-ag-ui's `ThreadsView`); enables durable, cross-device chat history. |
158
+ | `data-runs-url` | — | URL of a server run index (django-ag-ui's `RunsView`); reveals the header's ⭯ *Continue a run* panel. See [Resuming a run](#resuming-a-run). |
158
159
  | `data-attachments-url` | — | URL of the file-upload endpoint (django-ag-ui's `AttachmentsView`); reveals the composer's 📎 picker + drag-and-drop. |
159
160
  | `data-attachment-accept` | — | `<input accept>` list for client-side type filtering (e.g. `image/*,.pdf`). The server stays authoritative. |
160
161
  | `data-attachment-max-bytes` | — | Client-side upload size cap in bytes (default 10 MiB; `0` disables). The server stays authoritative. |
@@ -190,7 +191,9 @@ the django-ag-ui `@tool` registry), whose schema never reaches the browser — e
190
191
  labels are fetched automatically — per card, `x-summary` → an explicit
191
192
  `toolSummaries` entry → the fetched catalog → the raw name.
192
193
 
193
- **Methods**: `registerTool`, `registerStateHook`, `setSkills`, `appendMessage`, `newChat`,
194
+ **Properties** (selected): `sharedState` AG-UI shared state (documented under Tools & state).
195
+
196
+ **Methods**: `registerTool`, `registerPageState`, `setSkills`, `appendMessage`, `newChat`,
194
197
  `setCollapsed`, `toggleCollapsed`.
195
198
 
196
199
  A self-contained live playground lives in [`demo/`](demo/) — run `make demo` to serve it against a
@@ -620,12 +623,12 @@ so it reflects the page the agent is currently looking at:
620
623
  chat.getPageMap = () => ({ fields: introspectForm(), buttons: visibleButtons() });
621
624
  ```
622
625
 
623
- **`registerStateHook({ name, read, write?, schema? })`** — ergonomic sugar over `registerTool` for
626
+ **`registerPageState({ name, read, write?, schema? })`** — ergonomic sugar over `registerTool` for
624
627
  SPA app state (Redux/Zustand/signals). It auto-generates a `read_<name>` (read-only) tool and, when
625
628
  `write` is supplied, a `set_<name>` tool stamped `x-destructive`:
626
629
 
627
630
  ```js
628
- chat.registerStateHook({
631
+ chat.registerPageState({
629
632
  name: "cart",
630
633
  read: () => store.getState().cart,
631
634
  write: ({ items }) => store.dispatch(setCart(items)),
@@ -633,6 +636,42 @@ chat.registerStateHook({
633
636
  });
634
637
  ```
635
638
 
639
+ **This is not AG-UI shared state** — that is `sharedState`, below. `registerPageState` generates two
640
+ ordinary client tools; the agent reads or writes your store by *calling a tool*. The method was
641
+ called `registerStateHook` through 0.12, a name that read as protocol state sync; the old spelling
642
+ still works and is deprecated.
643
+
644
+ **`sharedState`** *(property)* — AG-UI **shared state**: the protocol's own state channel, sent as
645
+ `RunAgentInput.state` on every run and replaced in place when the server streams `STATE_SNAPSHOT` /
646
+ `STATE_DELTA`. Assign to seed it, listen for `ag-ui-state` to react:
647
+
648
+ ```js
649
+ chat.sharedState = { document: "" };
650
+
651
+ chat.addEventListener("ag-ui-state", (e) => {
652
+ editor.value = e.detail.state.document; // the agent rewrote it
653
+ });
654
+ ```
655
+
656
+ Server-side, a tool mutates `ctx.deps.state` and returns the snapshot as `ToolReturn` metadata —
657
+ pydantic-ai does not emit deltas for you:
658
+
659
+ ```python
660
+ @tool(registry)
661
+ async def write_document(ctx: RunContext[AgentDeps], body: str) -> ToolReturn:
662
+ """Replace the shared document."""
663
+ ctx.deps.state = {**(ctx.deps.state or {}), "document": body}
664
+ return ToolReturn(
665
+ return_value="written",
666
+ metadata=[StateSnapshotEvent(type=EventType.STATE_SNAPSHOT, snapshot=ctx.deps.state)],
667
+ )
668
+ ```
669
+
670
+ Use this when the agent and the page are editing **the same object** (a document, a form, a
671
+ canvas). Use `registerPageState` when the agent should *ask* for a value or *request* a change —
672
+ the tool call is visible in the transcript and can be gated by a confirmation card, which state
673
+ events cannot.
674
+
636
675
  **`navigate(path): void`** *(optional)* — a host routing callback. **This single seam is what
637
676
  distinguishes an SPA from an MPA.** When set, `navigate_to_route` routes client-side (no reload) and
638
677
  the in-memory run loop simply continues — the whole resumable-loop / checkpoint machinery is
@@ -646,6 +685,56 @@ chat.navigate = (path) => router.push(path); // SPA: in-page, no reload
646
685
 
647
686
  Route map + `navigate()` and the reload model are the same feature seen from two ends.
648
687
 
688
+ ## Resuming a run
689
+
690
+ When the server persists run checkpoints (django-ag-ui's `step_store`), a run
691
+ that stopped part-way can be **continued** rather than restarted. Point the
692
+ component at the run index and a ⭯ button appears in the header:
693
+
694
+ ```html
695
+ <ag-ui-chat endpoint="/agent/" data-runs-url="/agent/runs/"></ag-ui-chat>
696
+ ```
697
+
698
+ The panel lists runs the server marked **continuable** — those with a saved
699
+ snapshot to seed from. A run that never reached a provider-valid boundary has
700
+ none, so it isn't offered: resuming it would start from nothing. Each row shows
701
+ when the run started (the id is on hover, for correlating with server logs) and
702
+ marks a run that branched from another, so a fork doesn't read as a duplicate
703
+ of its parent.
704
+
705
+ Type the next turn in the composer, then pick a row:
706
+
707
+ - **Resume** — continue that run.
708
+ - **Fork** — branch it, leaving the original untouched.
709
+
710
+ Both send to the matching server endpoint and stream into the same transcript.
711
+
712
+ ### One URL, three endpoints
713
+
714
+ `data-runs-url` is the only thing to configure. `resume/<id>/` and `fork/<id>/`
715
+ are siblings of the index — django-ag-ui mounts all three under one prefix
716
+ whenever a step store is set — so they're derived, and there's no way to end up
717
+ with a half-configured set.
718
+
719
+ ### The client contract, handled for you
720
+
721
+ Those endpoints expect a request carrying a **fresh run id** and **only the new
722
+ turn**: the server supplies the prior turns from the snapshot, so re-sending
723
+ them would duplicate the conversation.
724
+
725
+ The component satisfies that structurally rather than by remembering a rule. A
726
+ continuation runs on its own short-lived agent, built pointing at the resume
727
+ endpoint and seeded with **no** history — so "only the new turn" is the only
728
+ thing it *can* send, and the fresh run id comes free because a new agent mints
729
+ one. Your main agent's history is never touched.
730
+
731
+ A resumed run is a normal run in every other respect: frontend tools execute,
732
+ approval interrupts render their card, and `headers` are re-read per request so
733
+ a rotated CSRF token or JWT still reaches the endpoint.
734
+
735
+ If the index can't be reached, the panel shows its empty state rather than an
736
+ error — a history affordance that fails is empty, not broken.
737
+
649
738
  ## File uploads
650
739
 
651
740
  Set **`data-attachments-url`** (django-ag-ui's `AttachmentsView`) to let the user attach files
@@ -747,8 +836,8 @@ re-export point. Internal modules import from leaf paths.
747
836
  | `RouteWithParams` | type | A route resolved with `:param` path segments + leftover query params. |
748
837
  | `createPageMapContext(...)` | function | Build the per-run `page_map` context entry. |
749
838
  | `PageMap` | type | The compact page-surface shape. |
750
- | `createStateHookTools(hook)` | function | Build `read_<name>` / `set_<name>` tools. |
751
- | `StateHook` | type | A state-binding declaration. |
839
+ | `createPageStateTools(binding)` | function | Build `read_<name>` / `set_<name>` tools. |
840
+ | `PageState` | type | A page-state binding declaration. |
752
841
  | `Skill` | type | A launchable prompt (chip / `/`-command). |
753
842
 
754
843
  ### Durability
@@ -760,6 +849,10 @@ re-export point. Internal modules import from leaf paths.
760
849
  | `ClientConversationStore` | type | The persistence seam. |
761
850
  | `ThreadMeta` | type | A thread-drawer row (`{ threadId, title, updatedAt, preview }`). |
762
851
  | `NavigationCheckpoint` | type | The pre-reload checkpoint marker. |
852
+ | `RunIndex` | class | Reads a `data-runs-url` run index and derives its resume / fork endpoints. |
853
+ | `RunRow` | type | One run index row (`{ run_id, thread_id, parent_run_id, started_at, continuable }`). |
854
+ | `CheckpointMenu` | class | The *Continue a run* panel. |
855
+ | `CheckpointVerb` | type | `"resume" | "fork"`. |
763
856
 
764
857
  ### Attachments
765
858