@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.cjs CHANGED
@@ -13851,7 +13851,8 @@ var WORKSPACE_MUTATING_TOOLS = /* @__PURE__ */ new Set([
13851
13851
  "write_file",
13852
13852
  "patch",
13853
13853
  "execute_code",
13854
- "browser_screenshot"
13854
+ "browser_screenshot",
13855
+ "office_edit"
13855
13856
  ]);
13856
13857
  var AGENT_CHAT_LISTENED_EVENTS = [
13857
13858
  "user.message",
@@ -13901,7 +13902,8 @@ var AGENT_CHAT_LISTENED_EVENTS = [
13901
13902
  "worker.failed",
13902
13903
  "thread.proposed",
13903
13904
  "office.conflict",
13904
- "office.save_failed"
13905
+ "office.save_failed",
13906
+ "stream.live"
13905
13907
  ];
13906
13908
  var AGENT_CHAT_LISTENED_EVENT_SET = new Set(
13907
13909
  AGENT_CHAT_LISTENED_EVENTS
@@ -14147,7 +14149,9 @@ function createInitialAgentChatState(options = {}) {
14147
14149
  viewMode: options.viewMode ?? "simple",
14148
14150
  researchSources: [],
14149
14151
  deviceWait: null,
14150
- officeEvents: {}
14152
+ officeEvents: {},
14153
+ live: false,
14154
+ officeAutoOpen: null
14151
14155
  };
14152
14156
  }
14153
14157
  function applyAgentChatEvent(state, event) {
@@ -14165,6 +14169,9 @@ function applyAgentChatEvent(state, event) {
14165
14169
  case "user.message":
14166
14170
  // A project coordinator's follow-up to its thread: shown as the thread reads it, its header saying whose it is.
14167
14171
  case "coordinator.message":
14172
+ if (event.type === "user.message" && PLATFORM_NOTES.has(stringValue2(event.data.synthetic))) {
14173
+ return nextState;
14174
+ }
14168
14175
  return withMessages(
14169
14176
  { ...nextState, terminal: false, stopped: false, isRunning: true },
14170
14177
  applyUserMessage(nextState.messages, event.eventId, event.data)
@@ -14335,17 +14342,22 @@ function applyAgentChatEvent(state, event) {
14335
14342
  return applyLlmResponse(nextState, event);
14336
14343
  case "llm.thinking":
14337
14344
  return applyLlmThinking(nextState, event);
14345
+ case "stream.live":
14346
+ return { ...nextState, live: true };
14338
14347
  case "tool.call":
14339
14348
  return applyToolCall(nextState, event);
14340
14349
  case "tool.result": {
14341
14350
  const toolCallId = stringValue2(event.data.tool_call_id);
14342
- const toolName = findToolNameById(nextState.messages, toolCallId);
14343
14351
  const messages = applyToolResult(nextState.messages, event.data);
14352
+ const call = findToolCallById(messages, toolCallId);
14353
+ const toolName = call?.toolName ?? null;
14344
14354
  const mutatesWorkspace = toolName !== null && WORKSPACE_MUTATING_TOOLS.has(toolName);
14355
+ const officePath = nextState.live && call?.toolName === "office_edit" && !toolCallFailed(call) ? officeEditPath(call.args) : null;
14345
14356
  const withResult = {
14346
14357
  ...nextState,
14347
14358
  messages,
14348
- workspaceRefreshKey: mutatesWorkspace ? nextState.workspaceRefreshKey + 1 : nextState.workspaceRefreshKey
14359
+ workspaceRefreshKey: mutatesWorkspace ? nextState.workspaceRefreshKey + 1 : nextState.workspaceRefreshKey,
14360
+ officeAutoOpen: officePath ? { path: officePath, eventId: event.eventId } : nextState.officeAutoOpen
14349
14361
  };
14350
14362
  return collectResearchSource(withResult, toolName, event.data);
14351
14363
  }
@@ -14482,6 +14494,7 @@ function typedBehindTheClear(messages, answers) {
14482
14494
  }
14483
14495
  return messages.slice(first);
14484
14496
  }
14497
+ var PLATFORM_NOTES = /* @__PURE__ */ new Set(["checkin", "checkin_opener", "outreach_opener"]);
14485
14498
  function applyUserMessage(messages, eventId, data) {
14486
14499
  const content = stringValue2(data.content);
14487
14500
  const images = parseUserMessageImages(data.images);
@@ -14547,6 +14560,22 @@ function parseUserMessageAttachments(raw) {
14547
14560
  }
14548
14561
  return out.length > 0 ? out : void 0;
14549
14562
  }
14563
+ function officeEditPath(args) {
14564
+ try {
14565
+ const path = JSON.parse(args)?.path;
14566
+ return typeof path === "string" ? path.replace(/^__WORKSPACE__\//, "") || null : null;
14567
+ } catch {
14568
+ return null;
14569
+ }
14570
+ }
14571
+ function toolCallFailed(call) {
14572
+ if (call.status === "error") return true;
14573
+ try {
14574
+ return JSON.parse(call.result ?? "")?.error != null;
14575
+ } catch {
14576
+ return false;
14577
+ }
14578
+ }
14550
14579
  function onComputer(event) {
14551
14580
  return event.data.computer === true ? { computer: true } : {};
14552
14581
  }
@@ -15401,11 +15430,11 @@ function hasUserAfterIndex(messages, idx) {
15401
15430
  }
15402
15431
  return false;
15403
15432
  }
15404
- function findToolNameById(messages, toolCallId) {
15433
+ function findToolCallById(messages, toolCallId) {
15405
15434
  if (!toolCallId) return null;
15406
15435
  for (let i = messages.length - 1; i >= 0; i--) {
15407
15436
  const tc = messages[i]?.toolCalls?.find((c) => c.id === toolCallId);
15408
- if (tc) return tc.toolName;
15437
+ if (tc) return tc;
15409
15438
  }
15410
15439
  return null;
15411
15440
  }
@@ -15586,7 +15615,7 @@ function useAgentChatRuntime({
15586
15615
  stream.addEventListener(eventType, (messageEvent) => {
15587
15616
  if (streamRef.current !== stream) return;
15588
15617
  const data = parseEventData(messageEvent.data);
15589
- const eventId = messageEvent.lastEventId ? Number(messageEvent.lastEventId) : 0;
15618
+ const eventId = eventType === "stream.live" ? 0 : messageEvent.lastEventId ? Number(messageEvent.lastEventId) : 0;
15590
15619
  setState(
15591
15620
  (prev) => applyRuntimeEvent(prev, {
15592
15621
  type: eventType,
@@ -19535,6 +19564,13 @@ function OfficeEditorPane({ adapter, sessionId, path, view, officeEvent, onClose
19535
19564
  const notice = officeEvent && officeEvent.eventId > openedAfter ? officeEvent : null;
19536
19565
  const [reopenedFor, setReopenedFor] = react.useState(0);
19537
19566
  if (notice?.type === "office.conflict" && notice.eventId !== reopenedFor) setReopenedFor(notice.eventId);
19567
+ const paneRef = react.useRef(null);
19568
+ const [fullScreen, setFullScreen] = react.useState(false);
19569
+ react.useEffect(() => {
19570
+ const follow = () => setFullScreen(document.fullscreenElement === paneRef.current);
19571
+ document.addEventListener("fullscreenchange", follow);
19572
+ return () => document.removeEventListener("fullscreenchange", follow);
19573
+ }, []);
19538
19574
  react.useEffect(() => {
19539
19575
  let cancelled = false;
19540
19576
  let editor = null;
@@ -19563,6 +19599,7 @@ function OfficeEditorPane({ adapter, sessionId, path, view, officeEvent, onClose
19563
19599
  return /* @__PURE__ */ jsxRuntime.jsxs(
19564
19600
  "aside",
19565
19601
  {
19602
+ ref: paneRef,
19566
19603
  "data-testid": "file-preview-panel",
19567
19604
  className: "flex h-full min-h-0 w-full flex-col overflow-hidden border-l border-line bg-card",
19568
19605
  children: [
@@ -19580,6 +19617,17 @@ function OfficeEditorPane({ adapter, sessionId, path, view, officeEvent, onClose
19580
19617
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.DownloadIcon, { className: "size-4" })
19581
19618
  }
19582
19619
  ),
19620
+ /* @__PURE__ */ jsxRuntime.jsx(
19621
+ "button",
19622
+ {
19623
+ type: "button",
19624
+ onClick: () => void (fullScreen ? document.exitFullscreen() : paneRef.current?.requestFullscreen()),
19625
+ "aria-label": fullScreen ? "Exit full screen" : "Full screen",
19626
+ title: fullScreen ? "Exit full screen" : "Full screen",
19627
+ className: "shrink-0 rounded p-0.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
19628
+ children: fullScreen ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Minimize2Icon, { className: "size-4" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Maximize2Icon, { className: "size-4" })
19629
+ }
19630
+ ),
19583
19631
  /* @__PURE__ */ jsxRuntime.jsx(
19584
19632
  "button",
19585
19633
  {
@@ -20351,10 +20399,16 @@ function isSubAgentSession(session) {
20351
20399
  if (session.channel && _SUB_AGENT_CHANNELS.has(session.channel)) return true;
20352
20400
  return false;
20353
20401
  }
20402
+ function isProjectThread(session) {
20403
+ return session?.config?.workstream_role === "thread";
20404
+ }
20354
20405
  function readOnlyReasonForSession(session) {
20355
20406
  if (isScheduledRunSession(session)) {
20356
20407
  return { readOnly: true, reason: "Scheduled run is read-only" };
20357
20408
  }
20409
+ if (isProjectThread(session)) {
20410
+ return { readOnly: false };
20411
+ }
20358
20412
  if (isSubAgentSession(session)) {
20359
20413
  return {
20360
20414
  readOnly: true,
@@ -20424,11 +20478,13 @@ function AgentChat({
20424
20478
  setOpenPane(null);
20425
20479
  setFilesOpen(false);
20426
20480
  }, [runtime.viewMode]);
20481
+ const officeAutoOpened = react.useRef(/* @__PURE__ */ new Set());
20427
20482
  react.useEffect(() => {
20428
20483
  setFilesOpen(false);
20429
20484
  setWorkspacePath(null);
20430
20485
  setPreviewPath(null);
20431
20486
  setOpenPane(null);
20487
+ officeAutoOpened.current.clear();
20432
20488
  }, [sessionId]);
20433
20489
  const readOnly = readOnlyReasonForSession(runtime.session);
20434
20490
  const boardAvailable = whiteboardEnabled && isBoardSession(runtime.session);
@@ -20511,6 +20567,16 @@ function AgentChat({
20511
20567
  setOpenPane("file");
20512
20568
  setMobileView("file");
20513
20569
  }, []);
20570
+ const officeAutoOpen = runtime.state.officeAutoOpen;
20571
+ react.useEffect(() => {
20572
+ if (!officeAutoOpen || !isOfficeDocument(officeAutoOpen.path)) return;
20573
+ if (openPane === "file" && previewPath !== null && previewPath !== officeAutoOpen.path) return;
20574
+ if (officeAutoOpened.current.has(officeAutoOpen.path)) return;
20575
+ officeAutoOpened.current.add(officeAutoOpen.path);
20576
+ setWorkspacePath(officeAutoOpen.path);
20577
+ setPreviewPath(officeAutoOpen.path);
20578
+ setOpenPane("file");
20579
+ }, [officeAutoOpen?.eventId]);
20514
20580
  const handleSelectedPathChange = react.useCallback((path) => {
20515
20581
  setWorkspacePath(path);
20516
20582
  if (path === null) setPreviewPath(null);