@invergent/agent-chat-react 2.17.2 → 2.17.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.
package/dist/index.d.cts CHANGED
@@ -962,7 +962,8 @@ interface AgentChatAdapter {
962
962
  }): Promise<AgentChatThreadRow>;
963
963
  /**
964
964
  * Start a thread the master proposed for the user's computer, in Surogate Desktop only: in a
965
- * folder its user confirms in the desktop's own sheet, which names the project and *title*.
965
+ * folder its user confirms in the desktop's own sheet, which names the project and *title*; or,
966
+ * in a project made on this computer, at once in a copy of the project's folder, asking nothing.
966
967
  */
967
968
  startLocalThread?(input: {
968
969
  projectId: string;
@@ -970,6 +971,19 @@ interface AgentChatAdapter {
970
971
  key: string;
971
972
  title: string;
972
973
  }): Promise<AgentChatThreadRow>;
974
+ /**
975
+ * The id of the computer this client runs on, as Surogate Desktop added it to the agent; null in
976
+ * a browser, and where the app has added none. A project made on a computer starts its threads there alone.
977
+ */
978
+ getLocalDeviceId?(): Promise<string | null>;
979
+ /**
980
+ * Whether a project made on a computer can no longer reach its folder there: that computer was removed from the
981
+ * user's computers or its access revoked, or it could not set the folder up. Its user then uses another computer
982
+ * for the project, in Surogate Desktop. Another adapter says none is.
983
+ */
984
+ isProjectFolderLost?(input: {
985
+ projectId: string;
986
+ }): Promise<boolean>;
973
987
  getArtifact(input: {
974
988
  sessionId: string;
975
989
  artifactId: string;
@@ -1435,6 +1449,14 @@ interface AgentChatAdapterContextValue {
1435
1449
  onOpenSession?: (sessionId: string) => void;
1436
1450
  /** The project whose master this conversation is, or null: its proposal cards start threads. */
1437
1451
  projectId?: string | null;
1452
+ /**
1453
+ * The computer that project works on, by the name the server stamped, for a project made in
1454
+ * Surogate Desktop; null for a project in the cloud. Its cards start their threads there alone.
1455
+ */
1456
+ projectComputer?: {
1457
+ deviceId: string;
1458
+ name: string;
1459
+ } | null;
1438
1460
  /** That project's threads, live, by id: what their cards show. */
1439
1461
  threadRows?: Record<string, AgentChatThreadRow>;
1440
1462
  /** A chat the host only reads (AgentChat's ``disabled``): nothing in it answers the agent's questions. */
package/dist/index.d.ts CHANGED
@@ -962,7 +962,8 @@ interface AgentChatAdapter {
962
962
  }): Promise<AgentChatThreadRow>;
963
963
  /**
964
964
  * Start a thread the master proposed for the user's computer, in Surogate Desktop only: in a
965
- * folder its user confirms in the desktop's own sheet, which names the project and *title*.
965
+ * folder its user confirms in the desktop's own sheet, which names the project and *title*; or,
966
+ * in a project made on this computer, at once in a copy of the project's folder, asking nothing.
966
967
  */
967
968
  startLocalThread?(input: {
968
969
  projectId: string;
@@ -970,6 +971,19 @@ interface AgentChatAdapter {
970
971
  key: string;
971
972
  title: string;
972
973
  }): Promise<AgentChatThreadRow>;
974
+ /**
975
+ * The id of the computer this client runs on, as Surogate Desktop added it to the agent; null in
976
+ * a browser, and where the app has added none. A project made on a computer starts its threads there alone.
977
+ */
978
+ getLocalDeviceId?(): Promise<string | null>;
979
+ /**
980
+ * Whether a project made on a computer can no longer reach its folder there: that computer was removed from the
981
+ * user's computers or its access revoked, or it could not set the folder up. Its user then uses another computer
982
+ * for the project, in Surogate Desktop. Another adapter says none is.
983
+ */
984
+ isProjectFolderLost?(input: {
985
+ projectId: string;
986
+ }): Promise<boolean>;
973
987
  getArtifact(input: {
974
988
  sessionId: string;
975
989
  artifactId: string;
@@ -1435,6 +1449,14 @@ interface AgentChatAdapterContextValue {
1435
1449
  onOpenSession?: (sessionId: string) => void;
1436
1450
  /** The project whose master this conversation is, or null: its proposal cards start threads. */
1437
1451
  projectId?: string | null;
1452
+ /**
1453
+ * The computer that project works on, by the name the server stamped, for a project made in
1454
+ * Surogate Desktop; null for a project in the cloud. Its cards start their threads there alone.
1455
+ */
1456
+ projectComputer?: {
1457
+ deviceId: string;
1458
+ name: string;
1459
+ } | null;
1438
1460
  /** That project's threads, live, by id: what their cards show. */
1439
1461
  threadRows?: Record<string, AgentChatThreadRow>;
1440
1462
  /** A chat the host only reads (AgentChat's ``disabled``): nothing in it answers the agent's questions. */
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import { cjk } from '@streamdown/cjk';
9
9
  import { code } from '@streamdown/code';
10
10
  import { math } from '@streamdown/math';
11
11
  import { mermaid } from '@streamdown/mermaid';
12
- import { Streamdown } from 'streamdown';
12
+ import { defaultRehypePlugins, Streamdown } from 'streamdown';
13
13
  import { useControllableState } from '@radix-ui/react-use-controllable-state';
14
14
  import { ThinkingOrb } from 'thinking-orbs';
15
15
  import { createHighlighter } from 'shiki';
@@ -1134,6 +1134,11 @@ function TooltipContent({
1134
1134
  }
1135
1135
  ) });
1136
1136
  }
1137
+ var streamdownRehypePlugins = Object.entries(defaultRehypePlugins).map(([name, plugin]) => {
1138
+ if (name !== "harden" || !Array.isArray(plugin)) return plugin;
1139
+ const [harden, options] = plugin;
1140
+ return [harden, { ...options, linkBlockPolicy: "text-only" }];
1141
+ });
1137
1142
 
1138
1143
  // src/lib/oauth-popup.ts
1139
1144
  function openOAuthPopup(url) {
@@ -1253,6 +1258,7 @@ var MessageResponse = memo(
1253
1258
  className
1254
1259
  ),
1255
1260
  plugins: streamdownPlugins,
1261
+ rehypePlugins: streamdownRehypePlugins,
1256
1262
  components: streamdownComponents,
1257
1263
  ...props
1258
1264
  }
@@ -1451,6 +1457,7 @@ var ReasoningContent = memo(
1451
1457
  {
1452
1458
  className: "[&_ol]:list-outside [&_ol]:pl-5 [&_ul]:list-outside [&_ul]:pl-5",
1453
1459
  plugins: streamdownPlugins2,
1460
+ rehypePlugins: streamdownRehypePlugins,
1454
1461
  children
1455
1462
  }
1456
1463
  )
@@ -11959,6 +11966,36 @@ function useCardStarts(adapter) {
11959
11966
  );
11960
11967
  return cardStarts.get(adapter) ?? /* @__PURE__ */ new Map();
11961
11968
  }
11969
+ function useLocalDevice(adapter, wanted) {
11970
+ const [found, setFound] = useState(null);
11971
+ useEffect(() => {
11972
+ if (!wanted || !adapter.getLocalDeviceId) return;
11973
+ let live = true;
11974
+ void adapter.getLocalDeviceId().catch(() => null).then((id) => {
11975
+ if (live) setFound({ adapter, id });
11976
+ });
11977
+ return () => {
11978
+ live = false;
11979
+ };
11980
+ }, [adapter, wanted]);
11981
+ if (!adapter.getLocalDeviceId) return null;
11982
+ return found?.adapter === adapter ? found.id : void 0;
11983
+ }
11984
+ function useFolderLost(adapter, projectId, wanted) {
11985
+ const [found, setFound] = useState(null);
11986
+ useEffect(() => {
11987
+ if (!wanted || !projectId || !adapter.isProjectFolderLost) return;
11988
+ let live = true;
11989
+ void adapter.isProjectFolderLost({ projectId }).catch(() => false).then((lost) => {
11990
+ if (live) setFound({ adapter, projectId, lost });
11991
+ });
11992
+ return () => {
11993
+ live = false;
11994
+ };
11995
+ }, [adapter, projectId, wanted]);
11996
+ if (!adapter.isProjectFolderLost) return false;
11997
+ return found?.adapter === adapter && found.projectId === projectId ? found.lost : void 0;
11998
+ }
11962
11999
  function ThreadCards({ message }) {
11963
12000
  if (message.worker) return /* @__PURE__ */ jsx(WorkerCard, { worker: message.worker });
11964
12001
  if (message.proposal) return /* @__PURE__ */ jsx(ProposalCard, { proposal: message.proposal });
@@ -12045,7 +12082,7 @@ function WorkerCard({ worker }) {
12045
12082
  ] });
12046
12083
  }
12047
12084
  function ProposalCard({ proposal }) {
12048
- const { adapter, projectId, onOpenSession } = useAgentChatAdapterContext();
12085
+ const { adapter, projectId, projectComputer, onOpenSession } = useAgentChatAdapterContext();
12049
12086
  const starts = useCardStarts(adapter);
12050
12087
  const startOf = (key) => starts.get(`${proposal.proposalId}:${key}`);
12051
12088
  const told = (key, start2) => setCardStart(adapter, `${proposal.proposalId}:${key}`, start2);
@@ -12062,6 +12099,9 @@ function ProposalCard({ proposal }) {
12062
12099
  const starting = (key) => startingAt(key) !== void 0;
12063
12100
  const canStart = !!projectId && !!adapter.startProposedThread;
12064
12101
  const canStartHere = !!projectId && !!adapter.startLocalThread;
12102
+ const here = useLocalDevice(adapter, !!projectComputer && canStartHere);
12103
+ const onItsComputer = !!projectComputer && canStartHere && here === projectComputer.deviceId;
12104
+ const lost = useFolderLost(adapter, projectId, !!projectComputer && !onItsComputer && !(canStartHere && here === void 0));
12065
12105
  const viewButtons = useRef({});
12066
12106
  useEffect(() => {
12067
12107
  drawnCards.set(proposal.proposalId, (drawnCards.get(proposal.proposalId) ?? 0) + 1);
@@ -12090,16 +12130,16 @@ function ProposalCard({ proposal }) {
12090
12130
  }
12091
12131
  };
12092
12132
  const startable = proposal.threads.filter(
12093
- (thread) => thread.where === "cloud" && !started[thread.key] && !starting(thread.key)
12133
+ (thread) => (projectComputer ? onItsComputer : thread.where === "cloud") && !started[thread.key] && !starting(thread.key)
12094
12134
  );
12095
- const startAll = async (keys) => {
12096
- for (const key of keys) told(key, { state: "starting", where: "cloud" });
12097
- for (const key of keys) await start(key);
12135
+ const startAll = async (threads) => {
12136
+ for (const { key } of threads) told(key, { state: "starting", where: projectComputer ? "device" : "cloud" });
12137
+ for (const { key, title } of threads) await start(key, projectComputer ? title : void 0);
12098
12138
  };
12099
12139
  return /* @__PURE__ */ jsxs("div", { "data-testid": "thread-proposal-card", className: "my-2 rounded-lg border border-border px-3 py-2 text-sm", children: [
12100
12140
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
12101
12141
  /* @__PURE__ */ jsx("span", { className: "flex-1 font-medium text-foreground", children: "Proposed threads" }),
12102
- canStart && startable.length > 1 && /* @__PURE__ */ jsx(Button, { size: "xs", onClick: () => void startAll(startable.map((thread) => thread.key)), children: "Start all" })
12142
+ (projectComputer ? onItsComputer : canStart) && startable.length > 1 && /* @__PURE__ */ jsx(Button, { size: "xs", onClick: () => void startAll(startable), children: "Start all" })
12103
12143
  ] }),
12104
12144
  /* @__PURE__ */ jsx("ul", { className: "mt-1 space-y-2", children: proposal.threads.map((thread) => {
12105
12145
  const threadId = started[thread.key];
@@ -12121,7 +12161,29 @@ function ProposalCard({ proposal }) {
12121
12161
  onClick: () => onOpenSession(threadId),
12122
12162
  children: "View thread"
12123
12163
  }
12124
- ) : thread.where === "device" ? /* @__PURE__ */ jsxs(Fragment, { children: [
12164
+ ) : projectComputer ? onItsComputer ? /* @__PURE__ */ jsxs(Fragment, { children: [
12165
+ /* @__PURE__ */ jsx("span", { children: "Works in its own copy of the project's folder" }),
12166
+ /* @__PURE__ */ jsx(
12167
+ Button,
12168
+ {
12169
+ size: "xs",
12170
+ variant: "outline",
12171
+ disabled: starting(thread.key),
12172
+ "aria-label": `${starting(thread.key) ? "Starting" : "Start"} ${thread.title}`,
12173
+ onClick: () => void start(thread.key, thread.title),
12174
+ children: starting(thread.key) ? "Starting\u2026" : "Start"
12175
+ }
12176
+ )
12177
+ ] }) : canStartHere && here === void 0 || lost === void 0 ? null : lost ? /* @__PURE__ */ jsxs("span", { children: [
12178
+ "This project's folder can no longer be reached on ",
12179
+ /* @__PURE__ */ jsx("bdi", { children: projectComputer.name }),
12180
+ ".",
12181
+ " ",
12182
+ canStartHere ? "Choose Use this computer for this project in its Overview to start its threads here." : "Open the project in Surogate Desktop and choose Use this computer for this project."
12183
+ ] }) : /* @__PURE__ */ jsxs("span", { children: [
12184
+ "Start this thread in Surogate Desktop on ",
12185
+ /* @__PURE__ */ jsx("bdi", { children: projectComputer.name })
12186
+ ] }) : thread.where === "device" ? /* @__PURE__ */ jsxs(Fragment, { children: [
12125
12187
  /* @__PURE__ */ jsx("span", { children: canStartHere ? "Allow Surogate to work in a folder on your device" : "This works in a folder on your device, which needs Surogate Desktop" }),
12126
12188
  canStartHere && /* @__PURE__ */ jsx(
12127
12189
  Button,
@@ -20135,6 +20197,11 @@ function AgentChat({
20135
20197
  });
20136
20198
  const sessionConfig = runtime.session?.config;
20137
20199
  const projectId = sessionConfig?.workstream_role === "coordinator" && typeof sessionConfig.workstream_id === "string" ? sessionConfig.workstream_id : null;
20200
+ const projectComputer = useMemo(() => {
20201
+ const execution = projectId ? sessionConfig?.execution : void 0;
20202
+ const name = sessionComputer(sessionConfig);
20203
+ return execution?.kind === "device" && typeof execution.device_id === "string" && name !== null ? { deviceId: execution.device_id, name } : null;
20204
+ }, [projectId, sessionConfig]);
20138
20205
  const threadRows = useProjectThreads(adapter, projectId);
20139
20206
  useEffect(() => {
20140
20207
  setOpenPane(null);
@@ -20263,6 +20330,7 @@ function AgentChat({
20263
20330
  onOpenBilling,
20264
20331
  onOpenSession: onSessionChange,
20265
20332
  projectId,
20333
+ projectComputer,
20266
20334
  threadRows,
20267
20335
  readOnly: disabled === true
20268
20336
  },