@invergent/agent-chat-react 1.5.10 → 1.6.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/dist/index.cjs CHANGED
@@ -6,9 +6,9 @@ var react = require('react');
6
6
  var auto = require('chart.js/auto');
7
7
  var nextThemes = require('next-themes');
8
8
  var jsxRuntime = require('react/jsx-runtime');
9
+ var lucideReact = require('lucide-react');
9
10
  var classVarianceAuthority = require('class-variance-authority');
10
11
  var radixUi = require('radix-ui');
11
- var lucideReact = require('lucide-react');
12
12
  var useStickToBottom = require('use-stick-to-bottom');
13
13
  var cjk = require('@streamdown/cjk');
14
14
  var code = require('@streamdown/code');
@@ -263,6 +263,117 @@ function Button({
263
263
  }
264
264
  );
265
265
  }
266
+ function BrowserControlBar({
267
+ sessionId,
268
+ hasControl,
269
+ adapter
270
+ }) {
271
+ const [pending, setPending] = react.useState(false);
272
+ const [error, setError] = react.useState(null);
273
+ async function toggleControl() {
274
+ if (!sessionId || pending) return;
275
+ setPending(true);
276
+ setError(null);
277
+ try {
278
+ if (hasControl) {
279
+ await adapter.releaseBrowserControl(sessionId);
280
+ } else {
281
+ await adapter.acquireBrowserControl(sessionId);
282
+ }
283
+ } catch (nextError) {
284
+ setError(nextError.message);
285
+ } finally {
286
+ setPending(false);
287
+ }
288
+ }
289
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-11 items-center gap-2 border-t border-line bg-card px-3 py-2", children: [
290
+ /* @__PURE__ */ jsxRuntime.jsxs(
291
+ Button,
292
+ {
293
+ type: "button",
294
+ size: "xs",
295
+ variant: hasControl ? "secondary" : "default",
296
+ disabled: !sessionId || pending,
297
+ onClick: () => void toggleControl(),
298
+ children: [
299
+ hasControl ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcwIcon, { className: "size-3", "aria-hidden": "true" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MousePointer2Icon, { className: "size-3", "aria-hidden": "true" }),
300
+ hasControl ? "Return control" : "Take control"
301
+ ]
302
+ }
303
+ ),
304
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-auto flex items-center gap-1.5 text-[11px] text-muted-foreground", children: [
305
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "size-1.5 rounded-full bg-red-500", "aria-hidden": "true" }),
306
+ "Live"
307
+ ] }),
308
+ error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-40 truncate text-[11px] text-destructive", children: error })
309
+ ] });
310
+ }
311
+ function BrowserLiveView({ src }) {
312
+ return /* @__PURE__ */ jsxRuntime.jsx(
313
+ "iframe",
314
+ {
315
+ "data-testid": "browser-iframe",
316
+ title: "Browser live view",
317
+ src,
318
+ className: "h-full w-full border-0 bg-black",
319
+ allow: "clipboard-read; clipboard-write"
320
+ }
321
+ );
322
+ }
323
+
324
+ // src/components/browser/browser-status-dot.tsx
325
+ init_utils();
326
+ function BrowserStatusDot({
327
+ status
328
+ }) {
329
+ return /* @__PURE__ */ jsxRuntime.jsx(
330
+ "span",
331
+ {
332
+ "aria-hidden": "true",
333
+ className: cn(
334
+ "inline-block size-2 shrink-0 rounded-full",
335
+ status === "live" && "bg-emerald-500",
336
+ status === "user-control" && "bg-amber-500",
337
+ status === "provisioning" && "animate-pulse bg-sky-500",
338
+ status === "closed" && "bg-muted-foreground"
339
+ )
340
+ }
341
+ );
342
+ }
343
+ function BrowserPane({ sessionId, state, adapter }) {
344
+ const liveViewUrl = react.useMemo(
345
+ () => adapter.browserLiveViewUrl(sessionId),
346
+ [adapter, sessionId]
347
+ );
348
+ const hasLiveView = state.status !== "provisioning" && state.status !== "closed";
349
+ return /* @__PURE__ */ jsxRuntime.jsxs(
350
+ "div",
351
+ {
352
+ "data-testid": "browser-pane",
353
+ className: "flex h-full min-h-0 flex-col bg-background",
354
+ children: [
355
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex min-h-10 items-center gap-2 border-b border-line bg-card px-3 text-xs text-foreground", children: [
356
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ZapIcon, { className: "size-3.5 text-muted-foreground", "aria-hidden": "true" }),
357
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Browser" }),
358
+ /* @__PURE__ */ jsxRuntime.jsx(BrowserStatusDot, { status: state.status }),
359
+ state.controlOwner && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0 truncate text-amber-500", children: [
360
+ state.controlOwner,
361
+ " has control"
362
+ ] })
363
+ ] }),
364
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-0 flex-1 bg-black", children: state.status === "provisioning" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Starting browser..." }) : state.status === "closed" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Browser closed." }) : /* @__PURE__ */ jsxRuntime.jsx(BrowserLiveView, { src: liveViewUrl }) }),
365
+ hasLiveView && /* @__PURE__ */ jsxRuntime.jsx(
366
+ BrowserControlBar,
367
+ {
368
+ sessionId,
369
+ hasControl: state.status === "user-control",
370
+ adapter
371
+ }
372
+ )
373
+ ]
374
+ }
375
+ );
376
+ }
266
377
 
267
378
  // src/components/ai-elements/conversation.tsx
268
379
  init_utils();
@@ -767,6 +878,80 @@ function TimelineSeparator({
767
878
  }
768
879
  );
769
880
  }
881
+ function BrowserActivityGroup({ calls }) {
882
+ const [open, setOpen] = react.useState(false);
883
+ const latest = calls[calls.length - 1];
884
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-none border border-line bg-card text-xs", children: [
885
+ /* @__PURE__ */ jsxRuntime.jsxs(
886
+ "button",
887
+ {
888
+ type: "button",
889
+ onClick: () => setOpen((current) => !current),
890
+ className: "flex w-full items-center gap-2 px-3 py-2 text-left hover:bg-muted/40",
891
+ children: [
892
+ open ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDownIcon, { className: "size-3 shrink-0", "aria-hidden": "true" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRightIcon, { className: "size-3 shrink-0", "aria-hidden": "true" }),
893
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ZapIcon, { className: "size-3 shrink-0 text-amber-500", "aria-hidden": "true" }),
894
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground", children: "browser" }),
895
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0 truncate text-muted-foreground", children: [
896
+ "(",
897
+ calls.length,
898
+ " ",
899
+ calls.length === 1 ? "action" : "actions",
900
+ " - latest:",
901
+ " ",
902
+ latest ? summarise(latest) : "none",
903
+ ")"
904
+ ] })
905
+ ]
906
+ }
907
+ ),
908
+ open && /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "border-t border-line py-1", children: calls.map((call) => {
909
+ const error = stringValue(parseJson(call.result).error);
910
+ return /* @__PURE__ */ jsxRuntime.jsxs(
911
+ "li",
912
+ {
913
+ className: "flex items-center gap-2 px-6 py-1 font-mono text-[11px]",
914
+ children: [
915
+ error && /* @__PURE__ */ jsxRuntime.jsx(
916
+ lucideReact.AlertCircleIcon,
917
+ {
918
+ "data-testid": `activity-error-${call.id}`,
919
+ className: "size-3 shrink-0 text-destructive",
920
+ "aria-hidden": "true"
921
+ }
922
+ ),
923
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate", children: summarise(call) }),
924
+ error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto max-w-40 truncate text-destructive", children: error })
925
+ ]
926
+ },
927
+ call.id
928
+ );
929
+ }) })
930
+ ] });
931
+ }
932
+ function summarise(call) {
933
+ const verb = call.toolName.replace(/^browser_/, "");
934
+ const args = parseJson(call.args);
935
+ const ref = stringValue(args.ref);
936
+ const url = stringValue(args.url);
937
+ const text = stringValue(args.text);
938
+ if (url) return `${verb} ${url}`;
939
+ if (ref) return `${verb} ${ref}`;
940
+ if (text) return `${verb} "${text.slice(0, 24)}"`;
941
+ return verb;
942
+ }
943
+ function parseJson(raw) {
944
+ if (!raw) return {};
945
+ try {
946
+ const parsed = JSON.parse(raw);
947
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
948
+ } catch {
949
+ return {};
950
+ }
951
+ }
952
+ function stringValue(value) {
953
+ return typeof value === "string" ? value : "";
954
+ }
770
955
  init_utils();
771
956
 
772
957
  // src/components/chat/tools/shared.ts
@@ -6178,6 +6363,32 @@ function messageToEntries(msg, isLast, artifactFallbacks) {
6178
6363
  }
6179
6364
  return entries;
6180
6365
  }
6366
+ function groupBrowserActivityEntries(entries) {
6367
+ const grouped = [];
6368
+ let buffer = [];
6369
+ const flush = () => {
6370
+ if (buffer.length === 0) return;
6371
+ grouped.push({
6372
+ kind: "browser_activity",
6373
+ key: `browser-activity-${buffer[0]?.id}-${buffer[buffer.length - 1]?.id}`,
6374
+ calls: buffer
6375
+ });
6376
+ buffer = [];
6377
+ };
6378
+ for (const entry of entries) {
6379
+ if (entry.kind === "tool" && isBrowserToolCall(entry.tc)) {
6380
+ buffer.push(entry.tc);
6381
+ continue;
6382
+ }
6383
+ flush();
6384
+ grouped.push(entry);
6385
+ }
6386
+ flush();
6387
+ return grouped;
6388
+ }
6389
+ function isBrowserToolCall(call) {
6390
+ return call.toolName.startsWith("browser_");
6391
+ }
6181
6392
  function groupMessages(messages) {
6182
6393
  const groups = [];
6183
6394
  let pendingSystem = [];
@@ -6334,6 +6545,15 @@ function TimelineEntryItem({
6334
6545
  ] }) })
6335
6546
  ] });
6336
6547
  }
6548
+ if (entry.kind === "browser_activity") {
6549
+ return /* @__PURE__ */ jsxRuntime.jsxs(TimelineItem, { step, children: [
6550
+ /* @__PURE__ */ jsxRuntime.jsxs(TimelineHeader, { children: [
6551
+ /* @__PURE__ */ jsxRuntime.jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
6552
+ /* @__PURE__ */ jsxRuntime.jsx(TimelineIndicator, { className: "size-2 border-none bg-amber-500" })
6553
+ ] }),
6554
+ /* @__PURE__ */ jsxRuntime.jsx(TimelineContent, { children: /* @__PURE__ */ jsxRuntime.jsx(BrowserActivityGroup, { calls: entry.calls }) })
6555
+ ] });
6556
+ }
6337
6557
  if (entry.kind === "text") {
6338
6558
  return /* @__PURE__ */ jsxRuntime.jsxs(TimelineItem, { step, children: [
6339
6559
  /* @__PURE__ */ jsxRuntime.jsxs(TimelineHeader, { children: [
@@ -6395,11 +6615,12 @@ function AssistantGroup({
6395
6615
  onFileSelect,
6396
6616
  onRetry
6397
6617
  }) {
6398
- const entries = [];
6618
+ let entries = [];
6399
6619
  for (let i = 0; i < messages.length; i++) {
6400
6620
  const isLast = i === messages.length - 1 && lastGlobalIndex === totalMessages - 1;
6401
6621
  entries.push(...messageToEntries(messages[i], isLast, artifactFallbacks));
6402
6622
  }
6623
+ entries = groupBrowserActivityEntries(entries);
6403
6624
  const isTailGroup = lastGlobalIndex === totalMessages - 1;
6404
6625
  const lastEntry = entries[entries.length - 1];
6405
6626
  const hasRunningTool = entries.some(
@@ -7476,6 +7697,7 @@ function WorkspacePanel({
7476
7697
  return /* @__PURE__ */ jsxRuntime.jsx(
7477
7698
  "aside",
7478
7699
  {
7700
+ "data-testid": "workspace-panel",
7479
7701
  role: "button",
7480
7702
  tabIndex: 0,
7481
7703
  className: "relative z-10 flex min-h-0 w-10 shrink-0 cursor-pointer items-center justify-center border-l border-muted-foreground/20 bg-card text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/30",
@@ -7494,6 +7716,7 @@ function WorkspacePanel({
7494
7716
  return /* @__PURE__ */ jsxRuntime.jsxs(
7495
7717
  "aside",
7496
7718
  {
7719
+ "data-testid": "workspace-panel",
7497
7720
  className: "relative z-10 flex min-h-0 flex-col overflow-hidden border-l border-muted-foreground/20 bg-card",
7498
7721
  style: { width, minWidth: MIN_WIDTH, maxWidth: MAX_WIDTH },
7499
7722
  children: [
@@ -7735,6 +7958,10 @@ var AGENT_CHAT_LISTENED_EVENTS = [
7735
7958
  "expert.override",
7736
7959
  "artifact.created",
7737
7960
  "artifact.updated",
7961
+ "browser.provisioned",
7962
+ "browser.destroyed",
7963
+ "browser.control_granted",
7964
+ "browser.control_returned",
7738
7965
  "clarify.response"
7739
7966
  ];
7740
7967
 
@@ -7759,7 +7986,8 @@ function createInitialAgentChatState(options = {}) {
7759
7986
  sessionDone: false,
7760
7987
  hadDeltas: false,
7761
7988
  terminal: false,
7762
- workspaceRefreshKey: 0
7989
+ workspaceRefreshKey: 0,
7990
+ browser: null
7763
7991
  };
7764
7992
  }
7765
7993
  function applyAgentChatEvent(state, event) {
@@ -7783,7 +8011,7 @@ function applyAgentChatEvent(state, event) {
7783
8011
  {
7784
8012
  id: `evt-${event.eventId}`,
7785
8013
  role: "system",
7786
- content: stringValue(event.data.skill),
8014
+ content: stringValue2(event.data.skill),
7787
8015
  createdAt: /* @__PURE__ */ new Date(),
7788
8016
  status: "complete",
7789
8017
  systemKind: "skill_invoked",
@@ -7800,7 +8028,7 @@ function applyAgentChatEvent(state, event) {
7800
8028
  {
7801
8029
  id: `evt-${event.eventId}`,
7802
8030
  role: "system",
7803
- content: stringValue(event.data.name),
8031
+ content: stringValue2(event.data.name),
7804
8032
  createdAt: /* @__PURE__ */ new Date(),
7805
8033
  status: "complete",
7806
8034
  systemKind: "artifact",
@@ -7813,6 +8041,23 @@ function applyAgentChatEvent(state, event) {
7813
8041
  }
7814
8042
  }
7815
8043
  ]);
8044
+ case "browser.provisioned":
8045
+ return applyBrowserEvent(nextState, event, {
8046
+ status: "live",
8047
+ controlOwner: null
8048
+ });
8049
+ case "browser.control_granted":
8050
+ return applyBrowserEvent(nextState, event, {
8051
+ status: "user-control",
8052
+ controlOwner: stringValue2(event.data.owner_user_id) || null
8053
+ });
8054
+ case "browser.control_returned":
8055
+ return applyBrowserEvent(nextState, event, {
8056
+ status: "live",
8057
+ controlOwner: null
8058
+ });
8059
+ case "browser.destroyed":
8060
+ return applyBrowserEvent(nextState, event, null);
7816
8061
  case "llm.delta":
7817
8062
  return applyLlmDelta(nextState, event);
7818
8063
  case "llm.response":
@@ -7822,7 +8067,7 @@ function applyAgentChatEvent(state, event) {
7822
8067
  case "tool.call":
7823
8068
  return applyToolCall(nextState, event);
7824
8069
  case "tool.result": {
7825
- const toolCallId = stringValue(event.data.tool_call_id);
8070
+ const toolCallId = stringValue2(event.data.tool_call_id);
7826
8071
  const toolName = findToolNameById(nextState.messages, toolCallId);
7827
8072
  const messages = applyToolResult(nextState.messages, event.data);
7828
8073
  const mutatesWorkspace = toolName !== null && WORKSPACE_MUTATING_TOOLS.has(toolName);
@@ -7892,8 +8137,8 @@ function applyRetryIndicator(state, event) {
7892
8137
  case "session.done":
7893
8138
  return { ...state, retryIndicator: null };
7894
8139
  case "harness.crash": {
7895
- const title = stringValue(event.data.error_title) || "A transient error occurred, retrying...";
7896
- const detail = stringValue(event.data.error_detail) || stringValue(event.data.error);
8140
+ const title = stringValue2(event.data.error_title) || "A transient error occurred, retrying...";
8141
+ const detail = stringValue2(event.data.error_detail) || stringValue2(event.data.error);
7897
8142
  return {
7898
8143
  ...state,
7899
8144
  retryIndicator: {
@@ -7908,7 +8153,7 @@ function applyRetryIndicator(state, event) {
7908
8153
  }
7909
8154
  }
7910
8155
  function applyUserMessage(messages, eventId, data) {
7911
- const content = stringValue(data.content);
8156
+ const content = stringValue2(data.content);
7912
8157
  const next = [...messages];
7913
8158
  const localIdx = next.findIndex(
7914
8159
  (m) => m.role === "user" && m.id.startsWith("local-") && m.content === content
@@ -7926,9 +8171,47 @@ function applyUserMessage(messages, eventId, data) {
7926
8171
  });
7927
8172
  return next;
7928
8173
  }
8174
+ function applyBrowserEvent(state, event, browser) {
8175
+ return withMessages(
8176
+ { ...state, browser },
8177
+ [...state.messages, browserMarker(event)]
8178
+ );
8179
+ }
8180
+ function browserMarker(event) {
8181
+ const labels = {
8182
+ "browser.provisioned": {
8183
+ content: "Browser ready.",
8184
+ warning: false
8185
+ },
8186
+ "browser.control_granted": {
8187
+ content: "A user took control of the browser.",
8188
+ warning: true
8189
+ },
8190
+ "browser.control_returned": {
8191
+ content: "Browser control returned to the agent.",
8192
+ warning: false
8193
+ },
8194
+ "browser.destroyed": {
8195
+ content: "Browser closed.",
8196
+ warning: false
8197
+ }
8198
+ };
8199
+ const label = labels[event.type] ?? {
8200
+ content: "Browser updated.",
8201
+ warning: false
8202
+ };
8203
+ return {
8204
+ id: `browser-marker-${event.eventId}`,
8205
+ role: "system",
8206
+ content: label.content,
8207
+ createdAt: /* @__PURE__ */ new Date(),
8208
+ status: "complete",
8209
+ systemKind: label.warning ? "browser_marker_warning" : "browser_marker"
8210
+ };
8211
+ }
7929
8212
  function applyLlmDelta(state, event) {
7930
- const deltaContent = stringValue(event.data.content);
7931
- const deltaReasoning = stringValue(event.data.reasoning);
8213
+ const deltaContent = stringValue2(event.data.content);
8214
+ const deltaReasoning = stringValue2(event.data.reasoning);
7932
8215
  const messages = [...state.messages];
7933
8216
  const lastIdx = findLastAssistantIndex(messages);
7934
8217
  const lastMsg = lastIdx >= 0 ? messages[lastIdx] : null;
@@ -7965,7 +8248,7 @@ function applyLlmDelta(state, event) {
7965
8248
  function applyLlmResponse(state, event) {
7966
8249
  const messages = [...state.messages];
7967
8250
  const message = objectValue(event.data.message);
7968
- const responseContent = stringValue(message?.content) || stringValue(event.data.content);
8251
+ const responseContent = stringValue2(message?.content) || stringValue2(event.data.content);
7969
8252
  const toolCalls = Array.isArray(message?.tool_calls) ? message?.tool_calls : [];
7970
8253
  const hasToolCalls = toolCalls.length > 0;
7971
8254
  const idx = findLastAssistantIndex(messages);
@@ -8023,12 +8306,12 @@ function applyLlmResponse(state, event) {
8023
8306
  cachedInputTokens: numberValue(event.data.cache_read_tokens),
8024
8307
  totalTokens: inputTokens + outputTokens,
8025
8308
  contextWindow: numberValue(event.data.context_window),
8026
- model: stringValue(event.data.model)
8309
+ model: stringValue2(event.data.model)
8027
8310
  }
8028
8311
  };
8029
8312
  }
8030
8313
  function applyLlmThinking(state, event) {
8031
- const reasoningText = stringValue(event.data.reasoning) || stringValue(event.data.content);
8314
+ const reasoningText = stringValue2(event.data.reasoning) || stringValue2(event.data.content);
8032
8315
  const messages = [...state.messages];
8033
8316
  const idx = findLastAssistantIndex(messages);
8034
8317
  const prev = idx >= 0 ? messages[idx] : null;
@@ -8076,11 +8359,11 @@ function applyToolCall(state, event) {
8076
8359
  messages[assistantIdx] = assistant;
8077
8360
  }
8078
8361
  const existingCalls = assistant.toolCalls ?? [];
8079
- const toolCallId = stringValue(event.data.tool_call_id) || `tc-${event.eventId}`;
8362
+ const toolCallId = stringValue2(event.data.tool_call_id) || `tc-${event.eventId}`;
8080
8363
  if (!existingCalls.some((tc) => tc.id === toolCallId)) {
8081
8364
  const entry = {
8082
8365
  id: toolCallId,
8083
- toolName: stringValue(event.data.name) || stringValue(event.data.tool_name) || "unknown",
8366
+ toolName: stringValue2(event.data.name) || stringValue2(event.data.tool_name) || "unknown",
8084
8367
  args: typeof event.data.arguments === "string" ? event.data.arguments : JSON.stringify(event.data.arguments ?? {}),
8085
8368
  status: "running"
8086
8369
  };
@@ -8096,7 +8379,7 @@ function applyToolCall(state, event) {
8096
8379
  };
8097
8380
  }
8098
8381
  function applyToolResult(messages, data) {
8099
- const targetId = stringValue(data.tool_call_id);
8382
+ const targetId = stringValue2(data.tool_call_id);
8100
8383
  let matchIdx = -1;
8101
8384
  for (let i = messages.length - 1; i >= 0; i--) {
8102
8385
  if (messages[i]?.role === "assistant" && messages[i]?.toolCalls?.some((tc) => tc.id === targetId)) {
@@ -8165,8 +8448,8 @@ function buildErrorInfo(event) {
8165
8448
  }
8166
8449
  return {
8167
8450
  category: event.data.error_category,
8168
- title: stringValue(event.data.error_title) || "The session failed due to an internal error.",
8169
- detail: stringValue(event.data.error_detail) || stringValue(event.data.error),
8451
+ title: stringValue2(event.data.error_title) || "The session failed due to an internal error.",
8452
+ detail: stringValue2(event.data.error_detail) || stringValue2(event.data.error),
8170
8453
  retryable: Boolean(event.data.retryable)
8171
8454
  };
8172
8455
  }
@@ -8210,7 +8493,7 @@ function applyExpertFeedback(messages, type, data) {
8210
8493
  return messages;
8211
8494
  }
8212
8495
  function applyClarifyResponse(messages, data) {
8213
- const targetToolId = stringValue(data.tool_call_id);
8496
+ const targetToolId = stringValue2(data.tool_call_id);
8214
8497
  const responses = Array.isArray(data.responses) ? data.responses : void 0;
8215
8498
  if (!targetToolId || !responses) return messages;
8216
8499
  const next = [...messages];
@@ -8225,8 +8508,8 @@ function applyClarifyResponse(messages, data) {
8225
8508
  clarifyAnswers: responses.map((response) => {
8226
8509
  const row = objectValue(response) ?? {};
8227
8510
  return {
8228
- question: stringValue(row.question),
8229
- answer: stringValue(row.answer),
8511
+ question: stringValue2(row.question),
8512
+ answer: stringValue2(row.answer),
8230
8513
  is_other: Boolean(row.is_other)
8231
8514
  };
8232
8515
  })
@@ -8246,7 +8529,7 @@ function applyPolicyDenied(state, data) {
8246
8529
  ...policyMsg,
8247
8530
  content: `${policyMsg.content}
8248
8531
 
8249
- **Policy denied**: ${stringValue(data.reason) || "Action blocked by governance policy."}`,
8532
+ **Policy denied**: ${stringValue2(data.reason) || "Action blocked by governance policy."}`,
8250
8533
  status: "error"
8251
8534
  };
8252
8535
  }
@@ -8298,7 +8581,7 @@ function objectValue(value) {
8298
8581
  }
8299
8582
  return void 0;
8300
8583
  }
8301
- function stringValue(value) {
8584
+ function stringValue2(value) {
8302
8585
  return typeof value === "string" ? value : "";
8303
8586
  }
8304
8587
  function numberValue(value) {
@@ -8532,6 +8815,7 @@ function useAgentChatRuntime({
8532
8815
  }
8533
8816
  }, [adapter, sessionId]);
8534
8817
  return {
8818
+ state,
8535
8819
  session,
8536
8820
  messages: state.messages,
8537
8821
  isRunning: state.isRunning,
@@ -8583,6 +8867,7 @@ function AgentChat({
8583
8867
  const isReadOnlySession = isScheduledRunSession(runtime.session);
8584
8868
  const effectiveDisabled = disabled || isReadOnlySession;
8585
8869
  const disabledReason = isReadOnlySession ? "Scheduled run is read-only" : void 0;
8870
+ const browserState = runtime.state.browser;
8586
8871
  react.useEffect(() => {
8587
8872
  onMessagesChange?.(runtime.messages);
8588
8873
  }, [onMessagesChange, runtime.messages]);
@@ -8619,19 +8904,29 @@ function AgentChat({
8619
8904
  retryIndicator: runtime.retryIndicator
8620
8905
  }
8621
8906
  ) }),
8622
- /* @__PURE__ */ jsxRuntime.jsx(
8623
- WorkspacePanel,
8624
- {
8625
- adapter,
8626
- sessionId,
8627
- selectedPath: workspacePath,
8628
- onSelectedPathChange: setWorkspacePath,
8629
- collapsed: workspaceCollapsed,
8630
- onCollapsedChange: setWorkspaceCollapsed,
8631
- refreshSignal: runtime.workspaceRefreshKey,
8632
- disabled: effectiveDisabled
8633
- }
8634
- )
8907
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-testid": "right-stack", className: "flex min-h-0 shrink-0 flex-col", children: [
8908
+ browserState !== null && sessionId && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-0 flex-1 border-b border-line", children: /* @__PURE__ */ jsxRuntime.jsx(
8909
+ BrowserPane,
8910
+ {
8911
+ sessionId,
8912
+ state: browserState,
8913
+ adapter
8914
+ }
8915
+ ) }),
8916
+ /* @__PURE__ */ jsxRuntime.jsx(
8917
+ WorkspacePanel,
8918
+ {
8919
+ adapter,
8920
+ sessionId,
8921
+ selectedPath: workspacePath,
8922
+ onSelectedPathChange: setWorkspacePath,
8923
+ collapsed: workspaceCollapsed,
8924
+ onCollapsedChange: setWorkspaceCollapsed,
8925
+ refreshSignal: runtime.workspaceRefreshKey,
8926
+ disabled: effectiveDisabled
8927
+ }
8928
+ )
8929
+ ] })
8635
8930
  ] }) })
8636
8931
  }
8637
8932
  );