@artooi/ag-ui-web-component 0.28.0 → 0.29.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 (59) hide show
  1. package/CHANGELOG.md +417 -1
  2. package/README.md +371 -5
  3. package/dist/ag-ui-web-component.bundle.js +308 -50
  4. package/dist/ag-ui-web-component.bundle.js.map +4 -4
  5. package/dist/constants.d.ts +69 -0
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/core/ag_ui_chat.d.ts +207 -0
  8. package/dist/core/ag_ui_chat.d.ts.map +1 -1
  9. package/dist/core/agui_client.d.ts +38 -0
  10. package/dist/core/agui_client.d.ts.map +1 -1
  11. package/dist/index.d.ts +7 -2
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +1516 -76
  14. package/dist/index.js.map +4 -4
  15. package/dist/ui/approval_card.d.ts +18 -0
  16. package/dist/ui/approval_card.d.ts.map +1 -1
  17. package/dist/ui/checkpoint_menu.d.ts +10 -0
  18. package/dist/ui/checkpoint_menu.d.ts.map +1 -1
  19. package/dist/ui/confirmation_card.d.ts +16 -0
  20. package/dist/ui/confirmation_card.d.ts.map +1 -1
  21. package/dist/ui/message_actions.d.ts +46 -0
  22. package/dist/ui/message_actions.d.ts.map +1 -0
  23. package/dist/ui/page_quote_offer.d.ts +33 -0
  24. package/dist/ui/page_quote_offer.d.ts.map +1 -0
  25. package/dist/ui/quote_selection.d.ts +66 -0
  26. package/dist/ui/quote_selection.d.ts.map +1 -0
  27. package/dist/ui/relative_time.d.ts +10 -0
  28. package/dist/ui/relative_time.d.ts.map +1 -1
  29. package/dist/ui/stick_to_bottom.d.ts +55 -0
  30. package/dist/ui/stick_to_bottom.d.ts.map +1 -0
  31. package/dist/ui/styles.d.ts +1 -1
  32. package/dist/ui/styles.d.ts.map +1 -1
  33. package/dist/ui/suggestion_chips.d.ts +29 -0
  34. package/dist/ui/suggestion_chips.d.ts.map +1 -0
  35. package/dist/ui/thread_drawer.d.ts +10 -0
  36. package/dist/ui/thread_drawer.d.ts.map +1 -1
  37. package/dist/ui/tool_call_card.d.ts +8 -0
  38. package/dist/ui/tool_call_card.d.ts.map +1 -1
  39. package/dist/ui/ui_strings.d.ts +40 -0
  40. package/dist/ui/ui_strings.d.ts.map +1 -1
  41. package/package.json +1 -1
  42. package/src/constants.ts +75 -0
  43. package/src/core/ag_ui_chat.ts +927 -73
  44. package/src/core/agui_client.ts +63 -0
  45. package/src/index.ts +39 -0
  46. package/src/ui/approval_card.ts +90 -2
  47. package/src/ui/checkpoint_menu.ts +22 -5
  48. package/src/ui/confirmation_card.ts +29 -1
  49. package/src/ui/message_actions.ts +158 -0
  50. package/src/ui/page_quote_offer.ts +215 -0
  51. package/src/ui/quote_selection.ts +345 -0
  52. package/src/ui/relative_time.ts +11 -0
  53. package/src/ui/stick_to_bottom.ts +126 -0
  54. package/src/ui/styles.ts +227 -0
  55. package/src/ui/suggestion_chips.ts +73 -0
  56. package/src/ui/thread_drawer.ts +22 -2
  57. package/src/ui/tool_call_card.ts +9 -0
  58. package/src/ui/ui_strings.ts +60 -0
  59. package/src/version.ts +1 -1
package/src/ui/styles.ts CHANGED
@@ -571,6 +571,7 @@ export const STYLES = `
571
571
  opacity: 0;
572
572
  }
573
573
 
574
+ :host([collapsed]:is([placement="embedded"], [placement="page"])) .messages-wrap,
574
575
  :host([collapsed]:is([placement="embedded"], [placement="page"])) .messages,
575
576
  :host([collapsed]:is([placement="embedded"], [placement="page"])) .input-row,
576
577
  :host([collapsed]:is([placement="embedded"], [placement="page"])) .skill-chips,
@@ -579,9 +580,113 @@ export const STYLES = `
579
580
  display: none;
580
581
  }
581
582
 
583
+ /* Jump-to-latest: shown only once the reader has scrolled away *and* missed
584
+ something. Anchored to the panel rather than the list so it does not scroll
585
+ with the content it is offering to scroll to. */
586
+ /* The transcript's own box, and the only one whose foot is the transcript's
587
+ foot. The panel's foot is below the composer, the chips and the footer. */
588
+ .messages-wrap {
589
+ position: relative;
590
+ flex: 1;
591
+ min-height: 0;
592
+ display: flex;
593
+ flex-direction: column;
594
+ }
595
+
596
+ .jump-latest {
597
+ position: absolute;
598
+ left: 50%;
599
+ transform: translateX(-50%);
600
+ bottom: var(--_pad);
601
+ z-index: 2;
602
+ display: none;
603
+ align-items: center;
604
+ gap: 0.35em;
605
+ padding: 0.4em 0.9em;
606
+ border: 1px solid var(--_border);
607
+ border-radius: 999px;
608
+ /* A raised surface, not the panel's own background. Reusing --_bg made the
609
+ pill the same colour as everything behind it, leaving a 1px border and a
610
+ shadow to carry the whole affordance -- and a dark-on-dark shadow carries
611
+ nothing. --_hover is the token that already means "lifted off the panel",
612
+ and it separates in both themes without competing with the accent the send
613
+ button owns. */
614
+ background: var(--_hover);
615
+ color: var(--_text);
616
+ font: inherit;
617
+ font-size: 0.85em;
618
+ cursor: pointer;
619
+ box-shadow: 0 2px 10px rgb(0 0 0 / 0.18);
620
+ }
621
+
622
+ .jump-latest[data-missed="true"] {
623
+ display: flex;
624
+ }
625
+
626
+ .jump-latest:hover {
627
+ border-color: var(--_accent);
628
+ }
629
+
630
+ /* The offer to quote a selection. Positioned in script against the transcript
631
+ box, which is the only ancestor whose top and foot are the transcript's --
632
+ the same reason .jump-latest lives here. The translate is the half the
633
+ script does not do: script sets the point the offer hangs from, CSS decides
634
+ which corner of the offer that point is. */
635
+ .quote-selection {
636
+ position: absolute;
637
+ z-index: 2;
638
+ transform: translate(-50%, -100%);
639
+ padding: 0.25em 0.7em;
640
+ border: 1px solid var(--_border);
641
+ border-radius: 999px;
642
+ background: var(--_hover);
643
+ color: var(--_text);
644
+ font: inherit;
645
+ font-size: 0.8em;
646
+ line-height: 1.6;
647
+ white-space: nowrap;
648
+ cursor: pointer;
649
+ box-shadow: 0 2px 10px rgb(0 0 0 / 0.18);
650
+ }
651
+
652
+ /* Flipped under the selection when there was no room above it. Only the
653
+ vertical half of the translate changes: it still hangs from its own centre
654
+ horizontally. */
655
+ .quote-selection[data-below="true"] {
656
+ transform: translate(-50%, 0);
657
+ }
658
+
659
+ .quote-selection:hover {
660
+ border-color: var(--_accent);
661
+ }
662
+
663
+ /* Screen-reader-only status region. Off-screen rather than display:none or
664
+ visibility:hidden, both of which take the element out of the accessibility
665
+ tree entirely -- a hidden live region announces nothing at all, which is the
666
+ classic way this pattern is written wrong.
667
+
668
+ The 1px box with clip-path, rather than width/height 0, is the shape that
669
+ survives: a zero-sized element is dropped from the tree by some engines. */
670
+ .sr-only {
671
+ position: absolute;
672
+ width: 1px;
673
+ height: 1px;
674
+ margin: -1px;
675
+ padding: 0;
676
+ border: 0;
677
+ overflow: hidden;
678
+ white-space: nowrap;
679
+ clip-path: inset(50%);
680
+ }
681
+
582
682
  .messages {
583
683
  flex: 1;
584
684
  overflow-y: auto;
685
+ /* The browser's own scroll anchoring competes with the scroller for the same
686
+ job and wins unpredictably -- it can hold the view still exactly when we
687
+ want to follow. Turned off so following is decided in one place. Safari
688
+ does not implement it, which is itself a reason not to depend on it. */
689
+ overflow-anchor: none;
585
690
  padding: var(--_pad);
586
691
  display: flex;
587
692
  flex-direction: column;
@@ -1747,6 +1852,7 @@ export const STYLES = `
1747
1852
 
1748
1853
  .confirm-actions {
1749
1854
  display: flex;
1855
+ flex-wrap: wrap;
1750
1856
  gap: 8px;
1751
1857
  justify-content: flex-end;
1752
1858
  }
@@ -1773,6 +1879,99 @@ export const STYLES = `
1773
1879
  color: #ffffff;
1774
1880
  }
1775
1881
 
1882
+ /* The session waiver. Deliberately the quietest of the three: it is the widest
1883
+ decision on the card, so it should be reachable without being the one the eye
1884
+ lands on when the user means to say yes once. */
1885
+ .confirm-btn--always {
1886
+ font-weight: 500;
1887
+ opacity: 0.85;
1888
+ }
1889
+
1890
+ .confirm-btn--always:hover,
1891
+ .confirm-btn--always:focus-visible {
1892
+ opacity: 1;
1893
+ }
1894
+
1895
+ /* Editable arguments on an approval card. A plain field rather than a code
1896
+ editor: it holds the JSON a card already displays, and the only interaction
1897
+ is correcting a value before letting the call run. */
1898
+ .approval-edit {
1899
+ display: flex;
1900
+ flex-direction: column;
1901
+ gap: 6px;
1902
+ }
1903
+
1904
+ .approval-args {
1905
+ box-sizing: border-box;
1906
+ width: 100%;
1907
+ resize: vertical;
1908
+ border: 1px solid var(--_border);
1909
+ border-radius: 8px;
1910
+ padding: 8px;
1911
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
1912
+ font-size: 0.85em;
1913
+ background: var(--_bg);
1914
+ color: var(--_fg);
1915
+ }
1916
+
1917
+ .approval-args:focus-visible {
1918
+ border-color: var(--_accent);
1919
+ outline: none;
1920
+ }
1921
+
1922
+ .approval-error {
1923
+ font-size: 0.85em;
1924
+ color: var(--_danger);
1925
+ }
1926
+
1927
+ /* Message action row. Sits under a finished assistant bubble.
1928
+
1929
+ The wrap is insurance rather than a fix: these buttons are glyph-only, so at
1930
+ every width tested they fit on one line and removing the wrap changes
1931
+ nothing. It is here because the confirmation row one release earlier did
1932
+ overflow when it gained a third button, off the left edge and outside its own
1933
+ card, and the difference between the two rows is only that this one's labels
1934
+ are icons today. */
1935
+ .message-actions {
1936
+ display: flex;
1937
+ flex-wrap: wrap;
1938
+ gap: 4px;
1939
+ /* Negative, and that is the point. The answer group is a flex column with its
1940
+ own gap, so a positive margin here pushes the row further from the message
1941
+ it acts on than the next card is below it -- the buttons then read as
1942
+ belonging to whatever follows. Pulling back inside the gap is what makes
1943
+ them the message's own. */
1944
+ margin-top: -6px;
1945
+ }
1946
+
1947
+ .message-action {
1948
+ border: none;
1949
+ border-radius: 6px;
1950
+ padding: 2px 6px;
1951
+ font: inherit;
1952
+ line-height: 1.2;
1953
+ cursor: pointer;
1954
+ background: transparent;
1955
+ color: var(--_muted);
1956
+ opacity: 0.75;
1957
+ }
1958
+
1959
+ .message-action:hover,
1960
+ .message-action:focus-visible {
1961
+ opacity: 1;
1962
+ background: var(--_border);
1963
+ }
1964
+
1965
+ .message-action[aria-pressed="true"] {
1966
+ opacity: 1;
1967
+ color: var(--_accent);
1968
+ }
1969
+
1970
+ .message-action--confirmed {
1971
+ opacity: 1;
1972
+ color: var(--_accent);
1973
+ }
1974
+
1776
1975
  /* Approval card — the server-side-tool gate (approve/deny an interrupt). */
1777
1976
  .approval {
1778
1977
  align-self: stretch;
@@ -1917,6 +2116,34 @@ export const STYLES = `
1917
2116
  border-color: var(--_accent);
1918
2117
  }
1919
2118
 
2119
+ /* Follow-up suggestion chips. Deliberately the skill chips' shape rather than a
2120
+ second chip vocabulary -- both are "a question you could ask", and the only
2121
+ difference is who chose it. Inside the transcript, so they scroll with the
2122
+ answer they follow instead of hovering above the composer. */
2123
+ .suggestions {
2124
+ display: flex;
2125
+ flex-wrap: wrap;
2126
+ gap: 6px;
2127
+ align-self: stretch;
2128
+ }
2129
+
2130
+ .suggestion-chip {
2131
+ border: 1px solid var(--_border);
2132
+ border-radius: 999px;
2133
+ padding: 4px 12px;
2134
+ font: inherit;
2135
+ font-size: 0.9em;
2136
+ text-align: left;
2137
+ cursor: pointer;
2138
+ background: var(--_assistant-bg);
2139
+ color: var(--_fg);
2140
+ }
2141
+
2142
+ .suggestion-chip:hover,
2143
+ .suggestion-chip:focus-visible {
2144
+ border-color: var(--_accent);
2145
+ }
2146
+
1920
2147
  .skill-palette {
1921
2148
  margin: 8px 12px 0;
1922
2149
  display: flex;
@@ -0,0 +1,73 @@
1
+ import type { UiStrings } from "./ui_strings.js";
2
+
3
+ /**
4
+ * Most prompts one push may draw.
5
+ *
6
+ * **Keep in step with `MAX_SUGGESTIONS` in django-ag-ui's
7
+ * `agent/suggestions_activity.py`**, which raises past it. Mirroring is the
8
+ * whole point: this side silently draws no more than its limit and has no
9
+ * channel to report the difference, so a producer that does not know the same
10
+ * number ships suggestions that never appear. That is the hole the chart bounds
11
+ * exist to close, and it was found there by shipping it.
12
+ */
13
+ export const MAX_SUGGESTIONS = 4;
14
+
15
+ /** Longest one prompt may be. Mirrored for the same reason as the count. */
16
+ export const MAX_SUGGESTION_CHARS = 120;
17
+
18
+ /** The `prompts` a `suggestions` activity carries, or `null` when it carries none. */
19
+ export function suggestionPrompts(content: unknown): string[] | null {
20
+ if (typeof content !== "object" || content === null) {
21
+ return null;
22
+ }
23
+ const raw = (content as { prompts?: unknown }).prompts;
24
+ if (!Array.isArray(raw)) {
25
+ return null;
26
+ }
27
+ const prompts = raw
28
+ .filter((prompt): prompt is string => typeof prompt === "string")
29
+ .map((prompt) => prompt.trim())
30
+ .filter((prompt) => prompt !== "" && prompt.length <= MAX_SUGGESTION_CHARS)
31
+ .slice(0, MAX_SUGGESTIONS);
32
+ return prompts.length === 0 ? null : prompts;
33
+ }
34
+
35
+ /**
36
+ * Draw follow-up prompts as chips that send themselves when clicked.
37
+ *
38
+ * Returns `null` when nothing survives, which is the registry's signal to draw
39
+ * nothing rather than an empty row -- the same contract the chart renderer uses
40
+ * for a spec it cannot draw.
41
+ *
42
+ * Buttons rather than links or list items: each one performs an action in the
43
+ * page, and the thing it sends is the label, so the accessible name is the
44
+ * prompt itself and needs no `aria-label` restating it.
45
+ */
46
+ export function renderSuggestionChips(
47
+ content: unknown,
48
+ strings: UiStrings,
49
+ onPick: (prompt: string) => void,
50
+ ): HTMLElement | null {
51
+ const prompts = suggestionPrompts(content);
52
+ if (prompts === null) {
53
+ return null;
54
+ }
55
+ const row = document.createElement("div");
56
+ row.className = "suggestions";
57
+ row.setAttribute("part", "suggestions");
58
+ // A group, labelled: without it a screen reader meets a row of unrelated
59
+ // buttons with no hint that they are the assistant's offer rather than the
60
+ // page's own controls.
61
+ row.setAttribute("role", "group");
62
+ row.setAttribute("aria-label", strings.suggestions);
63
+ for (const prompt of prompts) {
64
+ const chip = document.createElement("button");
65
+ chip.type = "button";
66
+ chip.className = "suggestion-chip";
67
+ chip.setAttribute("part", "suggestion-chip");
68
+ chip.textContent = prompt;
69
+ chip.addEventListener("click", () => onPick(prompt));
70
+ row.appendChild(chip);
71
+ }
72
+ return row;
73
+ }
@@ -1,5 +1,5 @@
1
1
  import type { ThreadMeta } from "../core/conversation_store.js";
2
- import { relativeTime } from "./relative_time.js";
2
+ import { type RelativeTimeFormatter, relativeTime } from "./relative_time.js";
3
3
  import { DEFAULT_UI_STRINGS, type UiStrings } from "./ui_strings.js";
4
4
 
5
5
  /** Actions the host ({@link AgUiChat}) wires to the drawer's rows. */
@@ -35,6 +35,7 @@ export class ThreadDrawer {
35
35
  readonly #heading: HTMLSpanElement;
36
36
  readonly #newButton: HTMLButtonElement;
37
37
  readonly #list: HTMLDivElement;
38
+ #formatRelativeTime: RelativeTimeFormatter | null = null;
38
39
  #strings: UiStrings;
39
40
  #threads: readonly ThreadMeta[] = [];
40
41
  #activeId = "";
@@ -91,6 +92,25 @@ export class ThreadDrawer {
91
92
  }
92
93
 
93
94
  /** Re-localize the drawer's chrome and rows (the host calls this on connect). */
95
+ /**
96
+ * Replace the timestamp formatter, or restore the built-in with `null`.
97
+ *
98
+ * The built-in is deliberately locale-neutral -- there is no `Intl` anywhere
99
+ * in this component, so it never disagrees with a host's own formatting by
100
+ * guessing a locale. That is a defensible default and a poor requirement, so
101
+ * this is the way out.
102
+ */
103
+ setRelativeTimeFormatter(format: RelativeTimeFormatter | null): void {
104
+ this.#formatRelativeTime = format;
105
+ }
106
+
107
+ /** This row's timestamp, through the host's formatter when it set one. */
108
+ #formatTime(timestamp: number): string {
109
+ return this.#formatRelativeTime !== null
110
+ ? this.#formatRelativeTime(timestamp)
111
+ : relativeTime(timestamp, undefined, this.#strings);
112
+ }
113
+
94
114
  setStrings(strings: UiStrings): void {
95
115
  this.#strings = strings;
96
116
  this.#panel.setAttribute("aria-label", strings.chatHistory);
@@ -202,7 +222,7 @@ export class ThreadDrawer {
202
222
  const time = document.createElement("span");
203
223
  time.className = "drawer-row-time";
204
224
  time.setAttribute("part", "drawer-row-time");
205
- time.textContent = relativeTime(meta.updatedAt, undefined, this.#strings);
225
+ time.textContent = this.#formatTime(meta.updatedAt);
206
226
  const preview = document.createElement("span");
207
227
  preview.className = "drawer-row-preview";
208
228
  preview.setAttribute("part", "drawer-row-preview");
@@ -90,6 +90,14 @@ export class ToolCallCard {
90
90
  readonly #resultLabel: HTMLSpanElement;
91
91
  readonly #resultBody: HTMLPreElement;
92
92
  readonly #strings: UiStrings;
93
+ /**
94
+ * The arguments this call was made with.
95
+ *
96
+ * Retained rather than only rendered, because an approval interrupt names a
97
+ * `toolCallId` and nothing else -- so this card is the only place the args
98
+ * still exist when the user is asked to approve, edit or deny the call.
99
+ */
100
+ readonly args: Record<string, unknown>;
93
101
  #settled = false;
94
102
 
95
103
  constructor(
@@ -99,6 +107,7 @@ export class ToolCallCard {
99
107
  strings: UiStrings = DEFAULT_UI_STRINGS,
100
108
  ) {
101
109
  this.#strings = strings;
110
+ this.args = args;
102
111
 
103
112
  this.element = document.createElement("div");
104
113
  this.element.className = "tool-call";
@@ -33,6 +33,18 @@ export interface UiStrings {
33
33
  // ── Messages region ─────────────────────────────────────────────────────────
34
34
  /** `aria-label` of the scrolling message log. */
35
35
  conversation: string;
36
+ /** The button offering to return to the foot of the transcript. */
37
+ jumpToLatest: string;
38
+ /** Announced when a turn starts. Screen-reader only; never rendered. */
39
+ announceResponding: string;
40
+ /** Announced when the answer has finished arriving. Screen-reader only. */
41
+ announceAnswerReady: string;
42
+ /** Announced when a card is waiting for the user's decision. Token: `{count}`. */
43
+ announceAwaitingDecision: string;
44
+ /** Announced when the user stopped the run. Screen-reader only. */
45
+ announceStopped: string;
46
+ /** Announced when the run failed. Screen-reader only. */
47
+ announceFailed: string;
36
48
  /** `aria-label` of the "thinking" pending indicator, and the thoughts region's
37
49
  * header while the model is still reasoning. */
38
50
  thinking: string;
@@ -48,6 +60,10 @@ export interface UiStrings {
48
60
  declinedAction: string;
49
61
  /** A navigating tool's card text while the page reloads. */
50
62
  navigating: string;
63
+ /** Notice shown when the server replaced the conversation wholesale. */
64
+ historyReplaced: string;
65
+ /** Notice shown when a pushed chart could not be drawn and was removed. */
66
+ chartUndrawable: string;
51
67
  /** Missing-placeholder skill hint. Tokens: `{title}`, `{fields}`. */
52
68
  skillNeeds: string;
53
69
  /** Notice shown when the agent condensed earlier turns. Token: `{count}`. */
@@ -116,8 +132,32 @@ export interface UiStrings {
116
132
  details: string;
117
133
 
118
134
  // ── Confirmation card ───────────────────────────────────────────────────────
135
+ /** `aria-label` of the editable arguments field on an approval card. */
136
+ approvalEditArgs: string;
137
+ /** Shown when the edited arguments are not valid JSON. */
138
+ approvalArgsInvalid: string;
139
+ /** Shown when the edited arguments parse but are not a JSON object. */
140
+ approvalArgsNotAnObject: string;
141
+ /** `aria-label` of the follow-up suggestion chips row. */
142
+ suggestions: string;
143
+ /** `aria-label` of a message's action row. */
144
+ messageActions: string;
145
+ /** The offer that floats beside a selection in the transcript. */
146
+ quoteSelection: string;
147
+ /** Copy this message (button `title` / `aria-label`). Its confirmation and
148
+ * failure text are the code block's `copied` / `copyFailed`, which say the
149
+ * same thing about the same clipboard. */
150
+ copyMessage: string;
151
+ /** Ask for a different answer to the same question. */
152
+ retryMessage: string;
153
+ /** Rate this answer as good. */
154
+ feedbackUp: string;
155
+ /** Rate this answer as poor. */
156
+ feedbackDown: string;
119
157
  /** `aria-label` of the inline confirmation card. */
120
158
  confirmAction: string;
159
+ /** Waive confirmation for this tool for the rest of the session. Token: `{tool}`. */
160
+ confirmAlways: string;
121
161
  /** Generic confirmation prompt when a tool has no `x-confirm`. Token: `{tool}`. */
122
162
  confirmRun: string;
123
163
  /** Confirm button. */
@@ -229,6 +269,12 @@ export const DEFAULT_UI_STRINGS: UiStrings = {
229
269
  forkedRun: "branched",
230
270
 
231
271
  conversation: "Conversation",
272
+ jumpToLatest: "Jump to latest",
273
+ announceResponding: "Assistant is responding",
274
+ announceAnswerReady: "Assistant answered",
275
+ announceAwaitingDecision: "{count} action is waiting for your approval",
276
+ announceStopped: "Response stopped",
277
+ announceFailed: "The response failed",
232
278
  thinking: "Assistant is thinking…",
233
279
  thoughts: "Thoughts",
234
280
  stopped: "⏹ Stopped",
@@ -236,6 +282,9 @@ export const DEFAULT_UI_STRINGS: UiStrings = {
236
282
  noResult: "No result returned.",
237
283
  declinedAction: "User declined the action.",
238
284
  navigating: "Navigating…",
285
+ historyReplaced:
286
+ "The server replaced this conversation's history. Reload to see the updated transcript.",
287
+ chartUndrawable: "A chart could not be drawn from the data sent, so it was removed.",
239
288
  historyCompacted: "Earlier turns condensed to fit the context window ({count} removed)",
240
289
  usingSkill: "Using skill {name}",
241
290
  runInterrupted: "The previous response didn’t finish — the page changed before it arrived.",
@@ -270,7 +319,18 @@ export const DEFAULT_UI_STRINGS: UiStrings = {
270
319
  declinedLabel: "Declined",
271
320
  details: "Details",
272
321
 
322
+ approvalEditArgs: "Edit the arguments before approving",
323
+ approvalArgsInvalid: "That is not valid JSON, so nothing was sent.",
324
+ approvalArgsNotAnObject: "Arguments have to be a JSON object.",
325
+ suggestions: "Suggested follow-ups",
326
+ messageActions: "Message actions",
327
+ quoteSelection: "Quote",
328
+ copyMessage: "Copy message",
329
+ retryMessage: "Try again",
330
+ feedbackUp: "Good answer",
331
+ feedbackDown: "Poor answer",
273
332
  confirmAction: "Confirm action",
333
+ confirmAlways: "Always allow",
274
334
  confirmRun: "Run “{tool}”?",
275
335
  confirm: "Confirm",
276
336
  cancel: "Cancel",
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const VERSION: string = "0.28.0";
1
+ export const VERSION: string = "0.29.0";