@burdenoff/fe-libs 2026.926.2 → 2026.926.4

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 (64) hide show
  1. package/dist/shared/assistant/autoRouting.d.ts +1 -0
  2. package/dist/shared/assistant/autoRouting.d.ts.map +1 -1
  3. package/dist/shared/assistant/autoRouting.js +1 -1
  4. package/dist/shared/assistant/capabilities.d.ts +5 -0
  5. package/dist/shared/assistant/capabilities.d.ts.map +1 -1
  6. package/dist/shared/assistant/capabilities.js +2 -1
  7. package/dist/shared/assistant/conversation.d.ts +29 -0
  8. package/dist/shared/assistant/conversation.d.ts.map +1 -1
  9. package/dist/shared/assistant/guardedStorage.d.ts +32 -0
  10. package/dist/shared/assistant/guardedStorage.d.ts.map +1 -0
  11. package/dist/shared/assistant/guardedStorage.js +36 -0
  12. package/dist/shared/assistant/i18nManifest.d.ts +7 -0
  13. package/dist/shared/assistant/i18nManifest.d.ts.map +1 -1
  14. package/dist/shared/assistant/i18nManifest.js +7 -0
  15. package/dist/shared/assistant/index.d.ts +5 -0
  16. package/dist/shared/assistant/index.d.ts.map +1 -1
  17. package/dist/shared/assistant/liveTurns.d.ts +145 -0
  18. package/dist/shared/assistant/liveTurns.d.ts.map +1 -0
  19. package/dist/shared/assistant/liveTurns.js +99 -0
  20. package/dist/shared/assistant/operations.d.ts +7 -0
  21. package/dist/shared/assistant/operations.d.ts.map +1 -1
  22. package/dist/shared/assistant/operations.js +2 -2
  23. package/dist/shared/assistant/prewarm.d.ts +32 -4
  24. package/dist/shared/assistant/prewarm.d.ts.map +1 -1
  25. package/dist/shared/assistant/prewarm.js +35 -15
  26. package/dist/shared/assistant/product.d.ts +11 -1
  27. package/dist/shared/assistant/product.d.ts.map +1 -1
  28. package/dist/shared/assistant/product.js +8 -6
  29. package/dist/shared/assistant/store.d.ts.map +1 -1
  30. package/dist/shared/assistant/store.js +11 -10
  31. package/dist/shared/assistant/turnState.d.ts +12 -0
  32. package/dist/shared/assistant/turnState.d.ts.map +1 -1
  33. package/dist/shared/assistant/turnState.js +4 -1
  34. package/dist/shared/assistant/turnTimeline.d.ts +37 -3
  35. package/dist/shared/assistant/turnTimeline.d.ts.map +1 -1
  36. package/dist/shared/assistant/turnTimeline.js +45 -33
  37. package/dist/shared/assistant/turnTimingReport.d.ts +86 -0
  38. package/dist/shared/assistant/turnTimingReport.d.ts.map +1 -0
  39. package/dist/shared/assistant/turnTimingReport.js +123 -0
  40. package/dist/shared/assistant/turnTimingReporter.d.ts +146 -0
  41. package/dist/shared/assistant/turnTimingReporter.d.ts.map +1 -0
  42. package/dist/shared/assistant/turnTimingReporter.js +321 -0
  43. package/dist/shared/assistant/ui/AssistantChatArea.d.ts.map +1 -1
  44. package/dist/shared/assistant/ui/AssistantChatArea.js +703 -544
  45. package/dist/shared/assistant/ui/AssistantMessageItem.d.ts +19 -0
  46. package/dist/shared/assistant/ui/AssistantMessageItem.d.ts.map +1 -1
  47. package/dist/shared/assistant/ui/AssistantMessageItem.js +92 -88
  48. package/dist/shared/assistant/ui/AssistantMessageList.d.ts +22 -2
  49. package/dist/shared/assistant/ui/AssistantMessageList.d.ts.map +1 -1
  50. package/dist/shared/assistant/ui/AssistantMessageList.js +37 -36
  51. package/dist/shared/assistant/ui/AssistantPendingWriteCard.d.ts +34 -0
  52. package/dist/shared/assistant/ui/AssistantPendingWriteCard.d.ts.map +1 -1
  53. package/dist/shared/assistant/ui/AssistantPendingWriteCard.js +31 -28
  54. package/dist/shared/assistant/ui/useVoiceSession.d.ts +1 -0
  55. package/dist/shared/assistant/ui/useVoiceSession.d.ts.map +1 -1
  56. package/dist/shared/assistant/ui/useVoiceSession.js +119 -117
  57. package/dist/shared/assistant/voice/voiceReopen.d.ts +117 -0
  58. package/dist/shared/assistant/voice/voiceReopen.d.ts.map +1 -0
  59. package/dist/shared/assistant/voice/voiceReopen.js +49 -0
  60. package/dist/shared/graphql/fetch.d.ts +10 -0
  61. package/dist/shared/graphql/fetch.d.ts.map +1 -1
  62. package/dist/shared/graphql/fetch.js +64 -62
  63. package/dist/shared-assistant.js +136 -131
  64. package/package.json +2 -2
@@ -5,6 +5,21 @@ interface Props {
5
5
  onApprovePendingWrite?: (reference: string) => void;
6
6
  /** Reference currently being approved, so only that card shows a spinner. */
7
7
  approvingWriteRef?: string | null;
8
+ /**
9
+ * This message's pending write can no longer be approved (BOFF-7335) — see
10
+ * `AssistantPendingWriteCard`'s `expired`.
11
+ */
12
+ pendingWriteExpired?: boolean;
13
+ /**
14
+ * This message's pending write was approved (BOFF-7335) — see
15
+ * `AssistantPendingWriteCard`'s `approved`.
16
+ */
17
+ pendingWriteApproved?: boolean;
18
+ /**
19
+ * The panel sent this approved write's "go ahead" (BOFF-7335) — see
20
+ * `AssistantPendingWriteCard`'s `goAheadSent`.
21
+ */
22
+ pendingWriteGoAheadSent?: boolean;
8
23
  message: Message;
9
24
  /**
10
25
  * The processing entry of this message's conversation. It decides what the
@@ -24,6 +39,10 @@ interface Props {
24
39
  * The most recent assistant turn. Retry is offered here too — a turn can
25
40
  * "complete" with an apology (e.g. a gateway 403 the model reports as text)
26
41
  * without ever tripping `isFailed`, and the user still needs a way to re-run.
42
+ *
43
+ * ★ BOFF-7335: set by the list only while that turn ran in THIS page load
44
+ * (`resolveRetryableMessageId`). A reply reopened from history is a record;
45
+ * re-running it would put an old prompt to a new session.
27
46
  */
28
47
  isLastAssistant?: boolean;
29
48
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantMessageItem.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantMessageItem.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAwBnD,OAAO,KAAK,EACV,+BAA+B,EAG/B,OAAO,EACR,MAAM,iBAAiB,CAAC;AAIzB,UAAU,KAAK;IACb,mEAAmE;IACnE,qBAAqB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,6EAA6E;IAC7E,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,+BAA+B,GAAG,IAAI,CAAC;IACpD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAyCD,eAAO,MAAM,oBAAoB,EAAE,EAAE,CAAC,KAAK,CAiV1C,CAAC"}
1
+ {"version":3,"file":"AssistantMessageItem.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantMessageItem.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAwBnD,OAAO,KAAK,EACV,+BAA+B,EAG/B,OAAO,EACR,MAAM,iBAAiB,CAAC;AAIzB,UAAU,KAAK;IACb,mEAAmE;IACnE,qBAAqB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,6EAA6E;IAC7E,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;OAGG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,+BAA+B,GAAG,IAAI,CAAC;IACpD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAyCD,eAAO,MAAM,oBAAoB,EAAE,EAAE,CAAC,KAAK,CAwV1C,CAAC"}
@@ -14,90 +14,90 @@ import { AssistantPendingWriteCard as f } from "./AssistantPendingWriteCard.js";
14
14
  import { AssistantSpeakButton as p } from "./AssistantSpeakButton.js";
15
15
  import { SaveToFilesButton as m } from "../../../storage/SaveToFilesButton.js";
16
16
  import "../../../storage/index.js";
17
- import { AssistantTurnActivity as h } from "./AssistantTurnActivity.js";
18
- import { useMemo as g, useState as _ } from "react";
19
- import { Check as v, Copy as y, RotateCcw as b } from "lucide-react";
20
- import { Fragment as x, jsx as S, jsxs as C } from "react/jsx-runtime";
17
+ import { AssistantTurnActivity as ee } from "./AssistantTurnActivity.js";
18
+ import { useMemo as h, useState as g } from "react";
19
+ import { Check as _, Copy as v, RotateCcw as y } from "lucide-react";
20
+ import { Fragment as b, jsx as x, jsxs as S } from "react/jsx-runtime";
21
21
  //#region src/shared/assistant/ui/AssistantMessageItem.tsx
22
- function w(e) {
22
+ function C(e) {
23
23
  return e.role === "assistant" ? e.metadata : void 0;
24
24
  }
25
- function T(e) {
25
+ function te(e) {
26
26
  return e?.artifacts ?? [];
27
27
  }
28
- function E(e) {
28
+ function w(e) {
29
29
  return window.atob(e);
30
30
  }
31
- function D(e, t, n) {
32
- let r = E(e), i = new Uint8Array(r.length);
31
+ function T(e, t, n) {
32
+ let r = w(e), i = new Uint8Array(r.length);
33
33
  for (let e = 0; e < r.length; e += 1) i[e] = r.charCodeAt(e);
34
34
  let a = new Blob([i], { type: n }), o = URL.createObjectURL(a), s = document.createElement("a");
35
35
  s.href = o, s.download = t, document.body.appendChild(s), s.click(), document.body.removeChild(s), URL.revokeObjectURL(o);
36
36
  }
37
- var O = ({ onApprovePendingWrite: E, approvingWriteRef: O = null, message: k, processing: A, isStreaming: j, isFailed: M, isLastAssistant: N, isNewestAssistant: ee, onRetry: P }) => {
38
- let F = n(), I = t(), { getAccessToken: L, getWorkspaceToken: R } = e(), z = u(), [B, V] = _(null), [H, U] = _(!1), W = k.role === "user", G = g(() => w(k), [k]), K = g(() => T(G), [G]), q = A === void 0 ? j ? {
37
+ var E = ({ onApprovePendingWrite: w, approvingWriteRef: E = null, pendingWriteExpired: D = !1, pendingWriteApproved: O = !1, pendingWriteGoAheadSent: k = !1, message: A, processing: j, isStreaming: M, isFailed: N, isLastAssistant: P, isNewestAssistant: F, onRetry: I }) => {
38
+ let L = n(), R = t(), { getAccessToken: z, getWorkspaceToken: B } = e(), V = u(), [H, U] = g(null), [W, G] = g(!1), K = A.role === "user", q = h(() => C(A), [A]), J = h(() => te(q), [q]), Y = j === void 0 ? M ? {
39
39
  status: "streaming",
40
- messageId: k.id,
41
- updatedAt: k.timestamp
42
- } : null : A, J = o(k, q, F), { steps: Y, text: X } = g(() => W ? {
40
+ messageId: A.id,
41
+ updatedAt: A.timestamp
42
+ } : null : j, X = o(A, Y, L), { steps: Z, text: Q } = h(() => K ? {
43
43
  steps: [],
44
- text: k.content
45
- } : J.kind === "output" ? c(J.text) : {
44
+ text: A.content
45
+ } : X.kind === "output" ? c(X.text) : {
46
46
  steps: [],
47
47
  text: ""
48
48
  }, [
49
- W,
50
- k.content,
51
- J.kind,
52
- J.text
53
- ]), Z = () => {
54
- r(X).then((e) => {
55
- e && (U(!0), setTimeout(() => U(!1), 2e3));
49
+ K,
50
+ A.content,
51
+ X.kind,
52
+ X.text
53
+ ]), ne = () => {
54
+ r(Q).then((e) => {
55
+ e && (G(!0), setTimeout(() => G(!1), 2e3));
56
56
  });
57
- }, Q = async (e) => {
58
- if (!G?.sandboxId || !G.sessionId) return;
57
+ }, re = async (e) => {
58
+ if (!q?.sandboxId || !q.sessionId) return;
59
59
  let t = new URLSearchParams(window.location.search).get("workspace") ?? "";
60
60
  if (t) {
61
- V(e.path);
61
+ U(e.path);
62
62
  try {
63
- let n = await a(G.sandboxId, t, G.sessionId, e.path, G.mode, {
64
- accessToken: L(),
65
- workspaceToken: R()
63
+ let n = await a(q.sandboxId, t, q.sessionId, e.path, q.mode, {
64
+ accessToken: z(),
65
+ workspaceToken: B()
66
66
  });
67
- D(n.contentBase64, n.filename, n.mimeType);
67
+ T(n.contentBase64, n.filename, n.mimeType);
68
68
  } finally {
69
- V(null);
69
+ U(null);
70
70
  }
71
71
  }
72
- }, $ = new Date(k.timestamp).toLocaleTimeString([], {
72
+ }, $ = new Date(A.timestamp).toLocaleTimeString([], {
73
73
  hour: "2-digit",
74
74
  minute: "2-digit"
75
75
  });
76
- if (k.role === "system") return /* @__PURE__ */ S("div", {
76
+ if (A.role === "system") return /* @__PURE__ */ x("div", {
77
77
  className: "flex justify-center px-2 py-1",
78
78
  "data-testid": "assistant-message-system",
79
- children: /* @__PURE__ */ S("p", {
79
+ children: /* @__PURE__ */ x("p", {
80
80
  className: "max-w-[90%] whitespace-pre-wrap break-words rounded-lg border border-border-subtle bg-bg-sunken/60 px-3 py-2 text-center text-xs leading-relaxed text-text-muted",
81
- children: k.content
81
+ children: A.content
82
82
  })
83
83
  });
84
- if (W) {
85
- let e = k.metadata?.attachments ?? [];
86
- return /* @__PURE__ */ S("div", {
84
+ if (K) {
85
+ let e = A.metadata?.attachments ?? [];
86
+ return /* @__PURE__ */ x("div", {
87
87
  className: "flex justify-end",
88
88
  "data-testid": "assistant-message-user",
89
- children: /* @__PURE__ */ C("div", {
89
+ children: /* @__PURE__ */ S("div", {
90
90
  className: "max-w-[85%]",
91
91
  children: [
92
- k.content && /* @__PURE__ */ S("div", {
92
+ A.content && /* @__PURE__ */ x("div", {
93
93
  className: "rounded-2xl rounded-br-md bg-action-primary-bg/12 px-3.5 py-2.5 text-[15px] leading-relaxed text-text-primary",
94
- children: /* @__PURE__ */ S("p", {
94
+ children: /* @__PURE__ */ x("p", {
95
95
  className: "whitespace-pre-wrap",
96
- children: k.content
96
+ children: A.content
97
97
  })
98
98
  }),
99
- /* @__PURE__ */ S(d, { attachments: e }),
100
- /* @__PURE__ */ S("div", {
99
+ /* @__PURE__ */ x(d, { attachments: e }),
100
+ /* @__PURE__ */ x("div", {
101
101
  className: "mt-1 pr-1 text-right text-[10px] text-text-muted",
102
102
  children: $
103
103
  })
@@ -105,99 +105,103 @@ var O = ({ onApprovePendingWrite: E, approvingWriteRef: O = null, message: k, pr
105
105
  })
106
106
  });
107
107
  }
108
- return /* @__PURE__ */ C("div", {
108
+ return /* @__PURE__ */ S("div", {
109
109
  className: "flex gap-2.5",
110
110
  "data-testid": "assistant-message-assistant",
111
- children: [/* @__PURE__ */ S(l, { active: j }), /* @__PURE__ */ C("div", {
111
+ children: [/* @__PURE__ */ x(l, { active: M }), /* @__PURE__ */ S("div", {
112
112
  className: "min-w-0 flex-1",
113
113
  children: [
114
- Y.length > 0 && /* @__PURE__ */ S(s, { steps: Y }),
115
- G?.pendingWrite ? /* @__PURE__ */ S(f, {
116
- pendingWrite: G.pendingWrite,
117
- onApprove: E,
118
- approving: O !== null && O === G.pendingWrite.reference
114
+ Z.length > 0 && /* @__PURE__ */ x(s, { steps: Z }),
115
+ q?.pendingWrite ? /* @__PURE__ */ x(f, {
116
+ pendingWrite: q.pendingWrite,
117
+ onApprove: w,
118
+ approving: E !== null && E === q.pendingWrite.reference,
119
+ expired: D,
120
+ approved: O,
121
+ goAheadSent: k,
122
+ awaitingTurn: !!M
119
123
  }) : null,
120
- X && /* @__PURE__ */ S("div", {
124
+ Q && /* @__PURE__ */ x("div", {
121
125
  className: "text-[15px] leading-relaxed text-text-primary",
122
- children: /* @__PURE__ */ S(i, { content: X })
126
+ children: /* @__PURE__ */ x(i, { content: Q })
123
127
  }),
124
- J.kind === "working" && /* @__PURE__ */ S(h, { processing: q }),
125
- J.kind === "stopped" && /* @__PURE__ */ S("p", {
128
+ X.kind === "working" && /* @__PURE__ */ x(ee, { processing: Y }),
129
+ X.kind === "stopped" && /* @__PURE__ */ x("p", {
126
130
  className: "text-[15px] leading-relaxed text-text-secondary",
127
131
  "data-testid": "assistant-turn-stopped",
128
- children: J.text
132
+ children: X.text
129
133
  }),
130
- j && /* @__PURE__ */ S("span", {
134
+ M && /* @__PURE__ */ x("span", {
131
135
  "aria-hidden": "true",
132
136
  className: "ml-0.5 inline-block h-4 w-1.5 rounded-sm bg-text-secondary align-middle motion-safe:animate-pulse"
133
137
  }),
134
- K.length > 0 && G?.sandboxId && G.sessionId && /* @__PURE__ */ C("div", {
138
+ J.length > 0 && q?.sandboxId && q.sessionId && /* @__PURE__ */ S("div", {
135
139
  className: "mt-3 space-y-2 border-t border-border-subtle pt-2",
136
- children: [/* @__PURE__ */ S("p", {
140
+ children: [/* @__PURE__ */ x("p", {
137
141
  className: "text-[10px] font-medium uppercase tracking-wide text-text-muted",
138
- children: F("message.downloads", "Downloads")
139
- }), /* @__PURE__ */ S("div", {
142
+ children: L("message.downloads", "Downloads")
143
+ }), /* @__PURE__ */ x("div", {
140
144
  className: "flex flex-wrap gap-2",
141
- children: K.map((e) => {
145
+ children: J.map((e) => {
142
146
  let t = `${e.name}${e.type === "directory" ? ".tar.gz" : ""}`;
143
- return /* @__PURE__ */ S(m, {
147
+ return /* @__PURE__ */ x(m, {
144
148
  label: t,
145
- disabled: B === e.path,
149
+ disabled: H === e.path,
146
150
  testId: `assistant-artifact-${e.name}`,
147
151
  source: {
148
152
  kind: "sandbox-artifact",
149
- sandboxId: G.sandboxId,
150
- sessionId: G.sessionId,
153
+ sandboxId: q.sandboxId,
154
+ sessionId: q.sessionId,
151
155
  path: e.path,
152
156
  filename: t
153
157
  },
154
- defaultFolderPath: I.artifacts,
155
- onDownload: () => Q(e)
158
+ defaultFolderPath: R.artifacts,
159
+ onDownload: () => re(e)
156
160
  }, e.path);
157
161
  })
158
162
  })]
159
163
  }),
160
- !j && (X || M || N) && /* @__PURE__ */ C("div", {
164
+ !M && (Q || N || P) && /* @__PURE__ */ S("div", {
161
165
  className: "mt-1.5 flex items-center gap-1",
162
166
  children: [
163
- X && /* @__PURE__ */ S("button", {
167
+ Q && /* @__PURE__ */ x("button", {
164
168
  type: "button",
165
- onClick: Z,
169
+ onClick: ne,
166
170
  className: "flex items-center gap-1 rounded-md px-1.5 py-1 text-[11px] text-text-muted transition-colors hover:bg-bg-elevated hover:text-text-secondary",
167
- "aria-label": F("message.copy", "Copy message"),
171
+ "aria-label": L("message.copy", "Copy message"),
168
172
  "data-testid": "assistant-message-copy",
169
- children: H ? /* @__PURE__ */ C(x, { children: [/* @__PURE__ */ S(v, { className: "size-3 text-status-success-text" }), /* @__PURE__ */ S("span", { children: F("message.copied", "Copied") })] }) : /* @__PURE__ */ C(x, { children: [/* @__PURE__ */ S(y, { className: "size-3" }), /* @__PURE__ */ S("span", { children: F("message.copyShort", "Copy") })] })
173
+ children: W ? /* @__PURE__ */ S(b, { children: [/* @__PURE__ */ x(_, { className: "size-3 text-status-success-text" }), /* @__PURE__ */ x("span", { children: L("message.copied", "Copied") })] }) : /* @__PURE__ */ S(b, { children: [/* @__PURE__ */ x(v, { className: "size-3" }), /* @__PURE__ */ x("span", { children: L("message.copyShort", "Copy") })] })
170
174
  }),
171
- X && z.isSupported && (() => {
172
- let e = z.blockedFor(k.id) ?? z.disabledReason, t = !!e && !!ee, n = t ? `speak-reason-${k.id}` : void 0;
173
- return /* @__PURE__ */ C(x, { children: [/* @__PURE__ */ S(p, {
174
- isSpeaking: z.isSpeaking(k.id),
175
+ Q && V.isSupported && (() => {
176
+ let e = V.blockedFor(A.id) ?? V.disabledReason, t = !!e && !!F, n = t ? `speak-reason-${A.id}` : void 0;
177
+ return /* @__PURE__ */ S(b, { children: [/* @__PURE__ */ x(p, {
178
+ isSpeaking: V.isSpeaking(A.id),
175
179
  disabled: e !== null,
176
180
  disabledReason: e,
177
181
  describedById: n,
178
- onClick: () => z.toggle(k.id, X, G?.lang)
179
- }), t && /* @__PURE__ */ S("span", {
182
+ onClick: () => V.toggle(A.id, Q, q?.lang)
183
+ }), t && /* @__PURE__ */ x("span", {
180
184
  id: n,
181
185
  className: "text-[11px] text-text-muted",
182
186
  "data-testid": "assistant-speak-reason",
183
187
  children: e
184
188
  })] });
185
189
  })(),
186
- (M || N) && P && !k.metadata?.localAction && /* @__PURE__ */ C("button", {
190
+ (N || P) && I && !A.metadata?.localAction && /* @__PURE__ */ S("button", {
187
191
  type: "button",
188
- onClick: P,
189
- className: `flex items-center gap-1 rounded-md px-1.5 py-1 text-[11px] transition-colors hover:bg-bg-elevated ${M ? "text-status-error-text" : "text-text-muted hover:text-text-secondary"}`,
190
- "aria-label": F("message.retry", "Retry"),
192
+ onClick: I,
193
+ className: `flex items-center gap-1 rounded-md px-1.5 py-1 text-[11px] transition-colors hover:bg-bg-elevated ${N ? "text-status-error-text" : "text-text-muted hover:text-text-secondary"}`,
194
+ "aria-label": L("message.retry", "Retry"),
191
195
  "data-testid": "assistant-message-retry",
192
- children: [/* @__PURE__ */ S(b, { className: "size-3" }), /* @__PURE__ */ S("span", { children: F("message.retry", "Retry") })]
196
+ children: [/* @__PURE__ */ x(y, { className: "size-3" }), /* @__PURE__ */ x("span", { children: L("message.retry", "Retry") })]
193
197
  })
194
198
  ]
195
199
  }),
196
- /* @__PURE__ */ C("div", {
200
+ /* @__PURE__ */ S("div", {
197
201
  className: "mt-1 text-[10px] text-text-muted",
198
- children: [$, k.tokens && /* @__PURE__ */ C("span", {
202
+ children: [$, A.tokens && /* @__PURE__ */ S("span", {
199
203
  className: "ml-2",
200
- children: [k.tokens.input + k.tokens.output, " tokens"]
204
+ children: [A.tokens.input + A.tokens.output, " tokens"]
201
205
  })]
202
206
  })
203
207
  ]
@@ -205,4 +209,4 @@ var O = ({ onApprovePendingWrite: E, approvingWriteRef: O = null, message: k, pr
205
209
  });
206
210
  };
207
211
  //#endregion
208
- export { O as AssistantMessageItem };
212
+ export { E as AssistantMessageItem };
@@ -1,9 +1,25 @@
1
1
  import { FC } from 'react';
2
2
  import { AssistantConversationProcessing, Message } from '../conversation';
3
3
  interface Props {
4
- /** Forwarded to each pending-write card; see AssistantPendingWriteCard. */
4
+ /**
5
+ * Forwarded ONLY to the approvable card (and to the reply still streaming,
6
+ * whose button waits for its turn); see AssistantPendingWriteCard.
7
+ */
5
8
  onApprovePendingWrite?: (reference: string) => void;
6
9
  approvingWriteRef?: string | null;
10
+ /**
11
+ * ★★ BOFF-7335: the one reply whose pending write may be approved — see
12
+ * `resolveApprovablePendingWrite`. Every other card is shown approved (the
13
+ * person approved it — `approvedPendingWriteIds`) or expired. Absent, nothing
14
+ * is approvable: a host that cannot say which turn ran in this page load must
15
+ * not put a live button on history.
16
+ */
17
+ approvableMessageId?: string | null;
18
+ /**
19
+ * The reply whose turn Retry may re-run — see `resolveRetryableMessageId`.
20
+ * Absent, only a turn that failed in this page load offers Retry.
21
+ */
22
+ retryableMessageId?: string | null;
7
23
  messages: Message[];
8
24
  /**
9
25
  * The conversation's processing entry, which each item reads to decide what
@@ -14,7 +30,11 @@ interface Props {
14
30
  streamingMessageId: string | null;
15
31
  /** The assistant message (if any) whose turn ended in an error. */
16
32
  failedMessageId?: string | null;
17
- /** Re-send the last user prompt — shown on the failed message. */
33
+ /**
34
+ * Re-send the last user prompt — shown on the failed message. Never on the
35
+ * answer to an approval's "go ahead" (`approvalFollowUpReplyIds`), failed or
36
+ * not.
37
+ */
18
38
  onRetry?: () => void;
19
39
  }
20
40
  export declare const AssistantMessageList: FC<Props>;
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantMessageList.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantMessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAGnD,OAAO,KAAK,EAAE,+BAA+B,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAGhF,UAAU,KAAK;IACb,2EAA2E;IAC3E,qBAAqB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,+BAA+B,GAAG,IAAI,CAAC;IACpD,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,mEAAmE;IACnE,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,eAAO,MAAM,oBAAoB,EAAE,EAAE,CAAC,KAAK,CA+F1C,CAAC"}
1
+ {"version":3,"file":"AssistantMessageList.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantMessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAGnD,OAAO,KAAK,EAAE,+BAA+B,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAQhF,UAAU,KAAK;IACb;;;OAGG;IACH,qBAAqB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,+BAA+B,GAAG,IAAI,CAAC;IACpD,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,mEAAmE;IACnE,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,eAAO,MAAM,oBAAoB,EAAE,EAAE,CAAC,KAAK,CAyG1C,CAAC"}
@@ -1,48 +1,49 @@
1
1
  import { useAssistantTr as e } from "../i18n.js";
2
- import { readReducedMotion as t } from "./usePrefersReducedMotion.js";
3
- import { AssistantMessageItem as n } from "./AssistantMessageItem.js";
4
- import { useEffect as r, useRef as i } from "react";
5
- import { jsx as a, jsxs as o } from "react/jsx-runtime";
2
+ import { approvalFollowUpReplyIds as t, approvedPendingWriteIds as n, goAheadSentReplyIds as r } from "../liveTurns.js";
3
+ import { readReducedMotion as i } from "./usePrefersReducedMotion.js";
4
+ import { AssistantMessageItem as a } from "./AssistantMessageItem.js";
5
+ import { useEffect as o, useRef as s } from "react";
6
+ import { jsx as c, jsxs as l } from "react/jsx-runtime";
6
7
  //#region src/shared/assistant/ui/AssistantMessageList.tsx
7
- var s = ({ onApprovePendingWrite: s, approvingWriteRef: c = null, messages: l, processing: u, streamingMessageId: d, failedMessageId: f, onRetry: p }) => {
8
- let m = e(), h = (() => {
9
- for (let e = l.length - 1; e >= 0; --e) if (l[e].role === "assistant") return l[e].id;
8
+ var u = ({ onApprovePendingWrite: u, approvingWriteRef: d = null, approvableMessageId: f = null, retryableMessageId: p = null, messages: m, processing: h, streamingMessageId: g, failedMessageId: _, onRetry: v }) => {
9
+ let y = e(), b = (() => {
10
+ for (let e = m.length - 1; e >= 0; --e) if (m[e].role === "assistant") return m[e].id;
10
11
  return null;
11
- })(), g = (() => {
12
- for (let e = l.length - 1; e >= 0; --e) {
13
- let t = l[e];
14
- if (t.role === "assistant" && !t.metadata?.localAction) return t.id;
15
- }
16
- return null;
17
- })(), _ = i(null), v = i(null);
18
- return r(() => {
19
- let e = v.current;
12
+ })(), x = n(m), S = r(m), C = t(m), w = s(null), T = s(null);
13
+ return o(() => {
14
+ let e = T.current;
20
15
  if (e && e.scrollHeight - e.scrollTop - e.clientHeight < 100) {
21
- let e = t();
22
- _.current?.scrollIntoView({ behavior: e ? "auto" : "smooth" });
16
+ let e = i();
17
+ w.current?.scrollIntoView({ behavior: e ? "auto" : "smooth" });
23
18
  }
24
- }, [l, d]), r(() => {
25
- _.current?.scrollIntoView({ behavior: "instant" });
26
- }, []), /* @__PURE__ */ o("div", {
27
- ref: v,
19
+ }, [m, g]), o(() => {
20
+ w.current?.scrollIntoView({ behavior: "instant" });
21
+ }, []), /* @__PURE__ */ l("div", {
22
+ ref: T,
28
23
  className: "scrollbar-slim flex-1 space-y-4 overflow-y-auto p-3",
29
24
  role: "log",
30
25
  "aria-live": "polite",
31
26
  "aria-relevant": "additions",
32
- "aria-busy": d !== null,
33
- "aria-label": m("messageList.ariaLabel", "Conversation messages"),
34
- children: [l.map((e) => /* @__PURE__ */ a(n, {
35
- onApprovePendingWrite: s,
36
- approvingWriteRef: c,
37
- message: e,
38
- processing: u,
39
- isStreaming: e.id === d,
40
- isFailed: e.id === f,
41
- isLastAssistant: e.id === g,
42
- isNewestAssistant: e.id === h,
43
- onRetry: p
44
- }, e.id)), /* @__PURE__ */ a("div", { ref: _ })]
27
+ "aria-busy": g !== null,
28
+ "aria-label": y("messageList.ariaLabel", "Conversation messages"),
29
+ children: [m.map((e) => {
30
+ let t = x.has(e.id), n = !t && (e.id === f || e.id === g);
31
+ return /* @__PURE__ */ c(a, {
32
+ onApprovePendingWrite: n ? u : void 0,
33
+ approvingWriteRef: d,
34
+ pendingWriteApproved: t,
35
+ pendingWriteGoAheadSent: S.has(e.id),
36
+ pendingWriteExpired: !t && !n,
37
+ message: e,
38
+ processing: h,
39
+ isStreaming: e.id === g,
40
+ isFailed: e.id === _,
41
+ isLastAssistant: e.id === p,
42
+ isNewestAssistant: e.id === b,
43
+ onRetry: C.has(e.id) ? void 0 : v
44
+ }, e.id);
45
+ }), /* @__PURE__ */ c("div", { ref: w })]
45
46
  });
46
47
  };
47
48
  //#endregion
48
- export { s as AssistantMessageList };
49
+ export { u as AssistantMessageList };
@@ -9,6 +9,40 @@ interface Props {
9
9
  onApprove?: (reference: string) => void;
10
10
  /** True while the approval is in flight, so the button cannot be double-sent. */
11
11
  approving?: boolean;
12
+ /**
13
+ * ★ BOFF-7335: this request can no longer be approved — it was reopened from
14
+ * history, the conversation has moved past it, its session or sandbox has
15
+ * been replaced, or its window has passed. The card stays as the record of
16
+ * what was asked, says so, and offers no button whatever `onApprove` holds.
17
+ *
18
+ * ★★ It does NOT say nothing changed. What became of the request is not
19
+ * known here — it may have been carried out another way since — so the card
20
+ * says only that it can no longer be approved.
21
+ */
22
+ expired?: boolean;
23
+ /**
24
+ * ★ BOFF-7335: the person approved this request (`approvedPendingWriteIds`).
25
+ * Wins over `expired`: once approved, a card is no longer approvable either,
26
+ * and reading "expired" would tell someone whose change was made that it was
27
+ * not. No button.
28
+ */
29
+ approved?: boolean;
30
+ /**
31
+ * ★ BOFF-7335: with `approved`, the panel then asked the assistant to go
32
+ * ahead (`goAheadSentReplyIds`). Without it — the person opened another
33
+ * conversation, or the mode, session or sandbox changed while the approval
34
+ * was out — nothing asked, and the card must not say the next reply will
35
+ * tell them whether the change was made.
36
+ */
37
+ goAheadSent?: boolean;
38
+ /**
39
+ * The turn that raised this request is still running. The button shows but
40
+ * cannot be pressed until it ends: an approval cannot be spent in the turn
41
+ * that raised it, and the follow-up that spends it cannot start while a turn
42
+ * is in flight — pressed now, the approval would land and nothing would ask
43
+ * the model to use it.
44
+ */
45
+ awaitingTurn?: boolean;
12
46
  }
13
47
  /**
14
48
  * What the assistant is asking permission to change — taken from the request it
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantPendingWriteCard.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantPendingWriteCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAEhC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGpD,UAAU,KAAK;IACb,YAAY,EAAE,YAAY,CAAC;IAC3B;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,iFAAiF;IACjF,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,yBAAyB,EAAE,EAAE,CAAC,KAAK,CAkG/C,CAAC"}
1
+ {"version":3,"file":"AssistantPendingWriteCard.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantPendingWriteCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAEhC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGpD,UAAU,KAAK;IACb,YAAY,EAAE,YAAY,CAAC;IAC3B;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,iFAAiF;IACjF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,yBAAyB,EAAE,EAAE,CAAC,KAAK,CAsJ/C,CAAC"}
@@ -1,67 +1,70 @@
1
1
  import { useAssistantTr as e } from "../i18n.js";
2
- import { ShieldAlert as t } from "lucide-react";
3
- import { jsx as n, jsxs as r } from "react/jsx-runtime";
2
+ import { ShieldAlert as t, ShieldCheck as n } from "lucide-react";
3
+ import { jsx as r, jsxs as i } from "react/jsx-runtime";
4
4
  //#region src/shared/assistant/ui/AssistantPendingWriteCard.tsx
5
- var i = ({ pendingWrite: i, onApprove: a, approving: o = !1 }) => {
6
- let s = e(), c = !!i.reference && !!a;
7
- return /* @__PURE__ */ r("div", {
5
+ var a = ({ pendingWrite: a, onApprove: o, approving: s = !1, expired: c = !1, approved: l = !1, goAheadSent: u = !1, awaitingTurn: d = !1 }) => {
6
+ let f = e(), p = !(l || c) && !!a.reference && !!o;
7
+ return /* @__PURE__ */ i("div", {
8
8
  className: "my-2 overflow-hidden rounded-xl border border-status-warning bg-status-warning/10",
9
9
  "data-testid": "assistant-pending-write",
10
10
  role: "note",
11
- "aria-label": s("pendingWrite.aria", "Change awaiting your approval"),
11
+ "aria-label": l ? f("pendingWrite.approvedAria", "Approved change request") : c ? f("pendingWrite.expiredAria", "Expired change request") : f("pendingWrite.aria", "Change awaiting your approval"),
12
12
  children: [
13
- /* @__PURE__ */ r("div", {
13
+ /* @__PURE__ */ i("div", {
14
14
  className: "flex items-start gap-2 border-b border-status-warning/40 px-3 py-2",
15
- children: [/* @__PURE__ */ n(t, {
15
+ children: [l ? /* @__PURE__ */ r(n, {
16
+ className: "mt-0.5 size-4 flex-shrink-0 text-status-success-text",
17
+ "aria-hidden": "true"
18
+ }) : /* @__PURE__ */ r(t, {
16
19
  className: "mt-0.5 size-4 flex-shrink-0 text-status-warning",
17
20
  "aria-hidden": "true"
18
- }), /* @__PURE__ */ r("div", {
21
+ }), /* @__PURE__ */ i("div", {
19
22
  className: "min-w-0",
20
- children: [/* @__PURE__ */ n("p", {
23
+ children: [/* @__PURE__ */ r("p", {
21
24
  className: "text-xs font-semibold text-text-primary",
22
- children: s("pendingWrite.title", "Nothing has changed yet")
23
- }), /* @__PURE__ */ n("p", {
25
+ children: l ? f("pendingWrite.approvedTitle", "You approved this change") : c ? f("pendingWrite.expiredTitle", "This request can no longer be approved") : f("pendingWrite.title", "Nothing has changed yet")
26
+ }), /* @__PURE__ */ r("p", {
24
27
  className: "text-[11px] leading-relaxed text-text-secondary",
25
- children: c ? s("pendingWrite.subtitle", "The assistant asked to make this change and was stopped. Approve it, or say what to change.") : s("pendingWrite.subtitleReply", "The assistant asked to make this change and was stopped. Reply to approve or decline.")
28
+ children: l ? u ? f("pendingWrite.approved", "The assistant's next reply says whether it was carried out.") : f("pendingWrite.approvedNotSent", "The assistant was not told to go ahead with it. If you still want this change, ask for it again.") : c ? f("pendingWrite.expired", "It stays here as a record of what was asked. If you still need the change, ask again.") : p ? f("pendingWrite.subtitle", "The assistant asked to make this change and was stopped. Approve it, or say what to change.") : f("pendingWrite.subtitleReply", "The assistant asked to make this change and was stopped. Reply to approve or decline.")
26
29
  })]
27
30
  })]
28
31
  }),
29
- /* @__PURE__ */ r("div", {
32
+ /* @__PURE__ */ i("div", {
30
33
  className: "px-3 py-2",
31
- children: [/* @__PURE__ */ n("p", {
34
+ children: [/* @__PURE__ */ r("p", {
32
35
  className: "font-mono text-xs text-text-primary",
33
36
  "data-testid": "assistant-pending-write-operation",
34
- children: i.operation
35
- }), i.values && i.values.length > 0 ? /* @__PURE__ */ n("dl", {
37
+ children: a.operation
38
+ }), a.values && a.values.length > 0 ? /* @__PURE__ */ r("dl", {
36
39
  className: "mt-2 grid grid-cols-[minmax(0,auto)_minmax(0,1fr)] gap-x-3 gap-y-1 text-xs",
37
- children: i.values.map((e) => /* @__PURE__ */ r("div", {
40
+ children: a.values.map((e) => /* @__PURE__ */ i("div", {
38
41
  className: "contents",
39
- children: [/* @__PURE__ */ n("dt", {
42
+ children: [/* @__PURE__ */ r("dt", {
40
43
  className: "truncate text-text-muted",
41
44
  children: e.key
42
- }), /* @__PURE__ */ n("dd", {
45
+ }), /* @__PURE__ */ r("dd", {
43
46
  className: "min-w-0 break-words font-medium text-text-primary",
44
47
  children: e.value
45
48
  })]
46
49
  }, e.key))
47
- }) : /* @__PURE__ */ n("p", {
50
+ }) : /* @__PURE__ */ r("p", {
48
51
  className: "mt-1 text-[11px] text-text-muted",
49
- children: s("pendingWrite.noValues", "No field values were included with this request.")
52
+ children: f("pendingWrite.noValues", "No field values were included with this request.")
50
53
  })]
51
54
  }),
52
- c ? /* @__PURE__ */ n("div", {
55
+ p ? /* @__PURE__ */ r("div", {
53
56
  className: "flex justify-end border-t border-status-warning/40 px-3 py-2",
54
- children: /* @__PURE__ */ n("button", {
57
+ children: /* @__PURE__ */ r("button", {
55
58
  type: "button",
56
- onClick: () => a?.(i.reference),
57
- disabled: o,
59
+ onClick: () => o?.(a.reference),
60
+ disabled: s || d,
58
61
  className: "rounded-md bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-fg transition-opacity hover:opacity-90 disabled:opacity-60 focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong",
59
62
  "data-testid": "assistant-pending-write-approve",
60
- children: o ? s("pendingWrite.approving", "Approving…") : s("pendingWrite.approve", "Approve this change")
63
+ children: s ? f("pendingWrite.approving", "Approving…") : f("pendingWrite.approve", "Approve this change")
61
64
  })
62
65
  }) : null
63
66
  ]
64
67
  });
65
68
  };
66
69
  //#endregion
67
- export { i as AssistantPendingWriteCard };
70
+ export { a as AssistantPendingWriteCard };
@@ -87,6 +87,7 @@ export declare function useVoiceSession({ onSubmit, latestReply, language }: Opt
87
87
  available: boolean;
88
88
  start: () => void;
89
89
  end: () => void;
90
+ isCurrentSession: (voiceSessionId: string) => boolean;
90
91
  toggleMute: () => void;
91
92
  interrupt: () => void;
92
93
  };