@invergent/agent-chat-react 2.18.0 → 2.18.1

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.d.cts CHANGED
@@ -521,7 +521,7 @@ interface AgentChatInboxEventStream {
521
521
  close(): void;
522
522
  onerror: (() => void) | null;
523
523
  }
524
- type AgentChatEventType = "user.message" | "coordinator.message" | "llm.request" | "llm.response" | "llm.thinking" | "llm.delta" | "tool.call" | "tool.result" | "session.start" | "session.pause" | "session.resume" | "session.complete" | "session.fail" | "session.done" | "harness.wake" | "harness.crash" | "context.compact" | "skill.invoked" | "policy.denied" | "stream.timeout" | "expert.delegation" | "expert.result" | "expert.failure" | "expert.endorse" | "expert.override" | "code.run_started" | "code.run_progress" | "code.run_result" | "user.feedback" | "artifact.created" | "artifact.updated" | "browser.provisioned" | "browser.destroyed" | "browser.unavailable" | "browser.control_granted" | "browser.control_returned" | "ask_user_question.response" | "iteration.summary" | "turn.summary" | "loop.result" | "device.waiting" | "device.resumed" | "worker.spawned" | "worker.complete" | "worker.failed" | "thread.proposed" | "office.conflict" | "office.save_failed";
524
+ type AgentChatEventType = "user.message" | "coordinator.message" | "llm.request" | "llm.response" | "llm.thinking" | "llm.delta" | "tool.call" | "tool.result" | "session.start" | "session.pause" | "session.resume" | "session.complete" | "session.fail" | "session.done" | "harness.wake" | "harness.crash" | "context.compact" | "skill.invoked" | "policy.denied" | "stream.timeout" | "expert.delegation" | "expert.result" | "expert.failure" | "expert.endorse" | "expert.override" | "code.run_started" | "code.run_progress" | "code.run_result" | "user.feedback" | "artifact.created" | "artifact.updated" | "browser.provisioned" | "browser.destroyed" | "browser.unavailable" | "browser.control_granted" | "browser.control_returned" | "ask_user_question.response" | "iteration.summary" | "turn.summary" | "loop.result" | "device.waiting" | "device.resumed" | "worker.spawned" | "worker.complete" | "worker.failed" | "thread.proposed" | "office.conflict" | "office.save_failed" | "stream.live";
525
525
  interface AgentChatRuntimeEvent {
526
526
  type: AgentChatEventType;
527
527
  eventId: number;
@@ -602,6 +602,13 @@ interface AgentChatState {
602
602
  deviceWait: AgentChatDeviceWaitState | null;
603
603
  /** The latest save trouble the document editor reported, by workspace path. */
604
604
  officeEvents: Record<string, AgentChatOfficeEvent>;
605
+ /** The stream has sent the session's backlog: what arrives from now on happens now. */
606
+ live: boolean;
607
+ /** The office document a live edit of the agent last changed, once the edit ran: the chat opens it in the file pane. */
608
+ officeAutoOpen: {
609
+ path: string;
610
+ eventId: number;
611
+ } | null;
605
612
  }
606
613
  /** A save from the document editor that met trouble: the file changed outside it, or it could not land yet. */
607
614
  interface AgentChatOfficeEvent {
package/dist/index.d.ts CHANGED
@@ -521,7 +521,7 @@ interface AgentChatInboxEventStream {
521
521
  close(): void;
522
522
  onerror: (() => void) | null;
523
523
  }
524
- type AgentChatEventType = "user.message" | "coordinator.message" | "llm.request" | "llm.response" | "llm.thinking" | "llm.delta" | "tool.call" | "tool.result" | "session.start" | "session.pause" | "session.resume" | "session.complete" | "session.fail" | "session.done" | "harness.wake" | "harness.crash" | "context.compact" | "skill.invoked" | "policy.denied" | "stream.timeout" | "expert.delegation" | "expert.result" | "expert.failure" | "expert.endorse" | "expert.override" | "code.run_started" | "code.run_progress" | "code.run_result" | "user.feedback" | "artifact.created" | "artifact.updated" | "browser.provisioned" | "browser.destroyed" | "browser.unavailable" | "browser.control_granted" | "browser.control_returned" | "ask_user_question.response" | "iteration.summary" | "turn.summary" | "loop.result" | "device.waiting" | "device.resumed" | "worker.spawned" | "worker.complete" | "worker.failed" | "thread.proposed" | "office.conflict" | "office.save_failed";
524
+ type AgentChatEventType = "user.message" | "coordinator.message" | "llm.request" | "llm.response" | "llm.thinking" | "llm.delta" | "tool.call" | "tool.result" | "session.start" | "session.pause" | "session.resume" | "session.complete" | "session.fail" | "session.done" | "harness.wake" | "harness.crash" | "context.compact" | "skill.invoked" | "policy.denied" | "stream.timeout" | "expert.delegation" | "expert.result" | "expert.failure" | "expert.endorse" | "expert.override" | "code.run_started" | "code.run_progress" | "code.run_result" | "user.feedback" | "artifact.created" | "artifact.updated" | "browser.provisioned" | "browser.destroyed" | "browser.unavailable" | "browser.control_granted" | "browser.control_returned" | "ask_user_question.response" | "iteration.summary" | "turn.summary" | "loop.result" | "device.waiting" | "device.resumed" | "worker.spawned" | "worker.complete" | "worker.failed" | "thread.proposed" | "office.conflict" | "office.save_failed" | "stream.live";
525
525
  interface AgentChatRuntimeEvent {
526
526
  type: AgentChatEventType;
527
527
  eventId: number;
@@ -602,6 +602,13 @@ interface AgentChatState {
602
602
  deviceWait: AgentChatDeviceWaitState | null;
603
603
  /** The latest save trouble the document editor reported, by workspace path. */
604
604
  officeEvents: Record<string, AgentChatOfficeEvent>;
605
+ /** The stream has sent the session's backlog: what arrives from now on happens now. */
606
+ live: boolean;
607
+ /** The office document a live edit of the agent last changed, once the edit ran: the chat opens it in the file pane. */
608
+ officeAutoOpen: {
609
+ path: string;
610
+ eventId: number;
611
+ } | null;
605
612
  }
606
613
  /** A save from the document editor that met trouble: the file changed outside it, or it could not land yet. */
607
614
  interface AgentChatOfficeEvent {
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { cn, __publicField } from './chunk-47MF6JUW.js';
2
2
  import { createContext, memo, useRef, useEffect, useCallback, useMemo, useState, useLayoutEffect, forwardRef, useId, lazy, useContext, useSyncExternalStore, Fragment as Fragment$1, Suspense, Children } from 'react';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
- import { ChevronDownIcon, AlertTriangle, ChevronDown, ChevronRight as ChevronRight$1, RefreshCw, CreditCard, File as File$1, FileImage, FileAudio, FileVideo, FileText, FileCode, FileArchive, FileSpreadsheet, PlugZapIcon, ChevronRightIcon, ArrowLeftIcon, Loader2Icon, InboxIcon, Loader2, AlertCircle, Pause, Play, Ban, TargetIcon, CalendarClockIcon, UsersIcon, Pen, Eraser, Type, MousePointer2, Hand, Maximize2, Undo2, Redo2, CheckIcon, CopyIcon, DownloadIcon, Maximize2Icon, LaptopIcon, GlobeIcon, FolderIcon, MessageSquareIcon, UploadIcon, RefreshCwIcon, AlertCircleIcon, ExternalLinkIcon, ClipboardCheckIcon, ShieldAlertIcon, TimerIcon, CircleDotIcon, ChevronLeftIcon, XIcon, Trash2Icon, SquareIcon, ArrowDownIcon, TrashIcon, FileTextIcon, Clock, ArrowRight, ListTreeIcon, PenLineIcon, TerminalIcon, SparklesIcon, ClockIcon, PaperclipIcon, CloudIcon, HardDriveIcon, IdCardIcon, PlusIcon, ShrinkIcon, FolderOpenIcon, FileIcon, ImageIcon, CircleDot, XCircle, CheckCircle2, SearchIcon, ZapIcon, ArrowUpIcon, CircleCheckIcon, ThumbsUpIcon, ThumbsDownIcon, BookOpenIcon, GitBranchIcon, ActivityIcon, EyeIcon, LibraryIcon, ListTodoIcon, BotIcon, ListIcon, FileEditIcon, WrenchIcon, FileCodeIcon, CodeIcon, FileArchiveIcon, FileVideoIcon, FileAudioIcon, FileImageIcon, FileChartLineIcon, FileSpreadsheetIcon, XCircleIcon, CheckCircle2Icon, Code, CircleXIcon, SkullIcon, CheckCircleIcon, CircleIcon } from 'lucide-react';
4
+ import { ChevronDownIcon, AlertTriangle, ChevronDown, ChevronRight as ChevronRight$1, RefreshCw, CreditCard, File as File$1, FileImage, FileAudio, FileVideo, FileText, FileCode, FileArchive, FileSpreadsheet, PlugZapIcon, ChevronRightIcon, ArrowLeftIcon, Loader2Icon, InboxIcon, Loader2, AlertCircle, Pause, Play, Ban, TargetIcon, CalendarClockIcon, UsersIcon, Pen, Eraser, Type, MousePointer2, Hand, Maximize2, Undo2, Redo2, CheckIcon, CopyIcon, DownloadIcon, Maximize2Icon, LaptopIcon, GlobeIcon, FolderIcon, MessageSquareIcon, UploadIcon, RefreshCwIcon, AlertCircleIcon, ExternalLinkIcon, ClipboardCheckIcon, ShieldAlertIcon, TimerIcon, CircleDotIcon, ChevronLeftIcon, XIcon, Trash2Icon, SquareIcon, ArrowDownIcon, TrashIcon, FileTextIcon, Minimize2Icon, Clock, ArrowRight, ListTreeIcon, PenLineIcon, TerminalIcon, SparklesIcon, ClockIcon, PaperclipIcon, CloudIcon, HardDriveIcon, IdCardIcon, PlusIcon, ShrinkIcon, FolderOpenIcon, FileIcon, ImageIcon, CircleDot, XCircle, CheckCircle2, SearchIcon, ZapIcon, ArrowUpIcon, CircleCheckIcon, ThumbsUpIcon, ThumbsDownIcon, BookOpenIcon, GitBranchIcon, ActivityIcon, EyeIcon, LibraryIcon, ListTodoIcon, BotIcon, ListIcon, FileEditIcon, WrenchIcon, FileCodeIcon, CodeIcon, FileArchiveIcon, FileVideoIcon, FileAudioIcon, FileImageIcon, FileChartLineIcon, FileSpreadsheetIcon, XCircleIcon, CheckCircle2Icon, Code, CircleXIcon, SkullIcon, CheckCircleIcon, CircleIcon } from 'lucide-react';
5
5
  import { cva } from 'class-variance-authority';
6
6
  import { Collapsible as Collapsible$1, Slot, Tooltip as Tooltip$1, Checkbox as Checkbox$1, Label as Label$1, Progress as Progress$1, Tabs as Tabs$1, Dialog as Dialog$1, ScrollArea as ScrollArea$1, Popover as Popover$1, DropdownMenu as DropdownMenu$1 } from 'radix-ui';
7
7
  import { StickToBottom, useStickToBottomContext } from 'use-stick-to-bottom';
@@ -13528,7 +13528,8 @@ var WORKSPACE_MUTATING_TOOLS = /* @__PURE__ */ new Set([
13528
13528
  "write_file",
13529
13529
  "patch",
13530
13530
  "execute_code",
13531
- "browser_screenshot"
13531
+ "browser_screenshot",
13532
+ "office_edit"
13532
13533
  ]);
13533
13534
  var AGENT_CHAT_LISTENED_EVENTS = [
13534
13535
  "user.message",
@@ -13578,7 +13579,8 @@ var AGENT_CHAT_LISTENED_EVENTS = [
13578
13579
  "worker.failed",
13579
13580
  "thread.proposed",
13580
13581
  "office.conflict",
13581
- "office.save_failed"
13582
+ "office.save_failed",
13583
+ "stream.live"
13582
13584
  ];
13583
13585
  var AGENT_CHAT_LISTENED_EVENT_SET = new Set(
13584
13586
  AGENT_CHAT_LISTENED_EVENTS
@@ -13824,7 +13826,9 @@ function createInitialAgentChatState(options = {}) {
13824
13826
  viewMode: options.viewMode ?? "simple",
13825
13827
  researchSources: [],
13826
13828
  deviceWait: null,
13827
- officeEvents: {}
13829
+ officeEvents: {},
13830
+ live: false,
13831
+ officeAutoOpen: null
13828
13832
  };
13829
13833
  }
13830
13834
  function applyAgentChatEvent(state, event) {
@@ -13842,6 +13846,9 @@ function applyAgentChatEvent(state, event) {
13842
13846
  case "user.message":
13843
13847
  // A project coordinator's follow-up to its thread: shown as the thread reads it, its header saying whose it is.
13844
13848
  case "coordinator.message":
13849
+ if (event.type === "user.message" && PLATFORM_NOTES.has(stringValue2(event.data.synthetic))) {
13850
+ return nextState;
13851
+ }
13845
13852
  return withMessages(
13846
13853
  { ...nextState, terminal: false, stopped: false, isRunning: true },
13847
13854
  applyUserMessage(nextState.messages, event.eventId, event.data)
@@ -14012,17 +14019,22 @@ function applyAgentChatEvent(state, event) {
14012
14019
  return applyLlmResponse(nextState, event);
14013
14020
  case "llm.thinking":
14014
14021
  return applyLlmThinking(nextState, event);
14022
+ case "stream.live":
14023
+ return { ...nextState, live: true };
14015
14024
  case "tool.call":
14016
14025
  return applyToolCall(nextState, event);
14017
14026
  case "tool.result": {
14018
14027
  const toolCallId = stringValue2(event.data.tool_call_id);
14019
- const toolName = findToolNameById(nextState.messages, toolCallId);
14020
14028
  const messages = applyToolResult(nextState.messages, event.data);
14029
+ const call = findToolCallById(messages, toolCallId);
14030
+ const toolName = call?.toolName ?? null;
14021
14031
  const mutatesWorkspace = toolName !== null && WORKSPACE_MUTATING_TOOLS.has(toolName);
14032
+ const officePath = nextState.live && call?.toolName === "office_edit" && !toolCallFailed(call) ? officeEditPath(call.args) : null;
14022
14033
  const withResult = {
14023
14034
  ...nextState,
14024
14035
  messages,
14025
- workspaceRefreshKey: mutatesWorkspace ? nextState.workspaceRefreshKey + 1 : nextState.workspaceRefreshKey
14036
+ workspaceRefreshKey: mutatesWorkspace ? nextState.workspaceRefreshKey + 1 : nextState.workspaceRefreshKey,
14037
+ officeAutoOpen: officePath ? { path: officePath, eventId: event.eventId } : nextState.officeAutoOpen
14026
14038
  };
14027
14039
  return collectResearchSource(withResult, toolName, event.data);
14028
14040
  }
@@ -14159,6 +14171,7 @@ function typedBehindTheClear(messages, answers) {
14159
14171
  }
14160
14172
  return messages.slice(first);
14161
14173
  }
14174
+ var PLATFORM_NOTES = /* @__PURE__ */ new Set(["checkin", "checkin_opener", "outreach_opener"]);
14162
14175
  function applyUserMessage(messages, eventId, data) {
14163
14176
  const content = stringValue2(data.content);
14164
14177
  const images = parseUserMessageImages(data.images);
@@ -14224,6 +14237,22 @@ function parseUserMessageAttachments(raw) {
14224
14237
  }
14225
14238
  return out.length > 0 ? out : void 0;
14226
14239
  }
14240
+ function officeEditPath(args) {
14241
+ try {
14242
+ const path = JSON.parse(args)?.path;
14243
+ return typeof path === "string" ? path.replace(/^__WORKSPACE__\//, "") || null : null;
14244
+ } catch {
14245
+ return null;
14246
+ }
14247
+ }
14248
+ function toolCallFailed(call) {
14249
+ if (call.status === "error") return true;
14250
+ try {
14251
+ return JSON.parse(call.result ?? "")?.error != null;
14252
+ } catch {
14253
+ return false;
14254
+ }
14255
+ }
14227
14256
  function onComputer(event) {
14228
14257
  return event.data.computer === true ? { computer: true } : {};
14229
14258
  }
@@ -15078,11 +15107,11 @@ function hasUserAfterIndex(messages, idx) {
15078
15107
  }
15079
15108
  return false;
15080
15109
  }
15081
- function findToolNameById(messages, toolCallId) {
15110
+ function findToolCallById(messages, toolCallId) {
15082
15111
  if (!toolCallId) return null;
15083
15112
  for (let i = messages.length - 1; i >= 0; i--) {
15084
15113
  const tc = messages[i]?.toolCalls?.find((c) => c.id === toolCallId);
15085
- if (tc) return tc.toolName;
15114
+ if (tc) return tc;
15086
15115
  }
15087
15116
  return null;
15088
15117
  }
@@ -15263,7 +15292,7 @@ function useAgentChatRuntime({
15263
15292
  stream.addEventListener(eventType, (messageEvent) => {
15264
15293
  if (streamRef.current !== stream) return;
15265
15294
  const data = parseEventData(messageEvent.data);
15266
- const eventId = messageEvent.lastEventId ? Number(messageEvent.lastEventId) : 0;
15295
+ const eventId = eventType === "stream.live" ? 0 : messageEvent.lastEventId ? Number(messageEvent.lastEventId) : 0;
15267
15296
  setState(
15268
15297
  (prev) => applyRuntimeEvent(prev, {
15269
15298
  type: eventType,
@@ -19202,6 +19231,13 @@ function OfficeEditorPane({ adapter, sessionId, path, view, officeEvent, onClose
19202
19231
  const notice = officeEvent && officeEvent.eventId > openedAfter ? officeEvent : null;
19203
19232
  const [reopenedFor, setReopenedFor] = useState(0);
19204
19233
  if (notice?.type === "office.conflict" && notice.eventId !== reopenedFor) setReopenedFor(notice.eventId);
19234
+ const paneRef = useRef(null);
19235
+ const [fullScreen, setFullScreen] = useState(false);
19236
+ useEffect(() => {
19237
+ const follow = () => setFullScreen(document.fullscreenElement === paneRef.current);
19238
+ document.addEventListener("fullscreenchange", follow);
19239
+ return () => document.removeEventListener("fullscreenchange", follow);
19240
+ }, []);
19205
19241
  useEffect(() => {
19206
19242
  let cancelled = false;
19207
19243
  let editor = null;
@@ -19230,6 +19266,7 @@ function OfficeEditorPane({ adapter, sessionId, path, view, officeEvent, onClose
19230
19266
  return /* @__PURE__ */ jsxs(
19231
19267
  "aside",
19232
19268
  {
19269
+ ref: paneRef,
19233
19270
  "data-testid": "file-preview-panel",
19234
19271
  className: "flex h-full min-h-0 w-full flex-col overflow-hidden border-l border-line bg-card",
19235
19272
  children: [
@@ -19247,6 +19284,17 @@ function OfficeEditorPane({ adapter, sessionId, path, view, officeEvent, onClose
19247
19284
  children: /* @__PURE__ */ jsx(DownloadIcon, { className: "size-4" })
19248
19285
  }
19249
19286
  ),
19287
+ /* @__PURE__ */ jsx(
19288
+ "button",
19289
+ {
19290
+ type: "button",
19291
+ onClick: () => void (fullScreen ? document.exitFullscreen() : paneRef.current?.requestFullscreen()),
19292
+ "aria-label": fullScreen ? "Exit full screen" : "Full screen",
19293
+ title: fullScreen ? "Exit full screen" : "Full screen",
19294
+ className: "shrink-0 rounded p-0.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
19295
+ children: fullScreen ? /* @__PURE__ */ jsx(Minimize2Icon, { className: "size-4" }) : /* @__PURE__ */ jsx(Maximize2Icon, { className: "size-4" })
19296
+ }
19297
+ ),
19250
19298
  /* @__PURE__ */ jsx(
19251
19299
  "button",
19252
19300
  {
@@ -20011,10 +20059,16 @@ function isSubAgentSession(session) {
20011
20059
  if (session.channel && _SUB_AGENT_CHANNELS.has(session.channel)) return true;
20012
20060
  return false;
20013
20061
  }
20062
+ function isProjectThread(session) {
20063
+ return session?.config?.workstream_role === "thread";
20064
+ }
20014
20065
  function readOnlyReasonForSession(session) {
20015
20066
  if (isScheduledRunSession(session)) {
20016
20067
  return { readOnly: true, reason: "Scheduled run is read-only" };
20017
20068
  }
20069
+ if (isProjectThread(session)) {
20070
+ return { readOnly: false };
20071
+ }
20018
20072
  if (isSubAgentSession(session)) {
20019
20073
  return {
20020
20074
  readOnly: true,
@@ -20084,11 +20138,13 @@ function AgentChat({
20084
20138
  setOpenPane(null);
20085
20139
  setFilesOpen(false);
20086
20140
  }, [runtime.viewMode]);
20141
+ const officeAutoOpened = useRef(/* @__PURE__ */ new Set());
20087
20142
  useEffect(() => {
20088
20143
  setFilesOpen(false);
20089
20144
  setWorkspacePath(null);
20090
20145
  setPreviewPath(null);
20091
20146
  setOpenPane(null);
20147
+ officeAutoOpened.current.clear();
20092
20148
  }, [sessionId]);
20093
20149
  const readOnly = readOnlyReasonForSession(runtime.session);
20094
20150
  const boardAvailable = whiteboardEnabled && isBoardSession(runtime.session);
@@ -20171,6 +20227,16 @@ function AgentChat({
20171
20227
  setOpenPane("file");
20172
20228
  setMobileView("file");
20173
20229
  }, []);
20230
+ const officeAutoOpen = runtime.state.officeAutoOpen;
20231
+ useEffect(() => {
20232
+ if (!officeAutoOpen || !isOfficeDocument(officeAutoOpen.path)) return;
20233
+ if (openPane === "file" && previewPath !== null && previewPath !== officeAutoOpen.path) return;
20234
+ if (officeAutoOpened.current.has(officeAutoOpen.path)) return;
20235
+ officeAutoOpened.current.add(officeAutoOpen.path);
20236
+ setWorkspacePath(officeAutoOpen.path);
20237
+ setPreviewPath(officeAutoOpen.path);
20238
+ setOpenPane("file");
20239
+ }, [officeAutoOpen?.eventId]);
20174
20240
  const handleSelectedPathChange = useCallback((path) => {
20175
20241
  setWorkspacePath(path);
20176
20242
  if (path === null) setPreviewPath(null);