@athenaintel/react 0.10.41-rc.1 → 0.10.41

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
@@ -45,6 +45,7 @@ const jsxRuntime = require("react/jsx-runtime");
45
45
  const react$1 = require("@assistant-ui/react");
46
46
  const React = require("react");
47
47
  const AthenaAuthContext = require("./AthenaAuthContext-B3AwLA5Z.cjs");
48
+ const localStorage$1 = require("@assistant-ui/react-statewire/local-storage");
48
49
  const tap = require("@assistant-ui/tap");
49
50
  const statewire = require("statewire");
50
51
  require("@assistant-ui/core");
@@ -10876,6 +10877,7 @@ function useDeepAgentThread({
10876
10877
  sse = false,
10877
10878
  headers,
10878
10879
  sessionStore,
10880
+ storage,
10879
10881
  isNewChat = false,
10880
10882
  attachKey,
10881
10883
  suspended = false,
@@ -11043,6 +11045,9 @@ function useDeepAgentThread({
11043
11045
  );
11044
11046
  })()
11045
11047
  ),
11048
+ // Survives the transport remounts above: a bumped attachKey re-attaches the
11049
+ // same lane, and a persistent session storage carries it across reloads.
11050
+ ...storage && { storage },
11046
11051
  // The run subsystem (status, lanes, input requests, runId) is mounted on
11047
11052
  // the state root and always replicated by react-statewire >= 0.14 (the
11048
11053
  // former `runs: true` opt-in is gone) — the converter only maps app-owned
@@ -11280,6 +11285,7 @@ function useAthenaStatewireRuntime(config2) {
11280
11285
  const clientTools = useStatewireClientTools(frontendToolkit);
11281
11286
  const clientToolsRef = React.useRef(clientTools);
11282
11287
  clientToolsRef.current = clientTools;
11288
+ const [canPersistSession] = React.useState(() => typeof localStorage !== "undefined");
11283
11289
  const thread = useDeepAgentThread({
11284
11290
  threadId,
11285
11291
  baseUrl: syncUrl,
@@ -11297,6 +11303,17 @@ function useAthenaStatewireRuntime(config2) {
11297
11303
  // Queued entries render in the SDK's queue panel, so only the steer lane
11298
11304
  // projects into the message tail.
11299
11305
  laneProjection: "steer-only",
11306
+ // Durable session: a reload resumes the lane and resends whatever the
11307
+ // server has not marked durable. Scoped by host — a thread id is only
11308
+ // meaningful against the backend that minted it.
11309
+ ...canPersistSession && {
11310
+ storage: {
11311
+ session: localStorage$1.LocalStorageSession({
11312
+ threadId,
11313
+ key: (id) => `${syncUrl}:${id}`
11314
+ })
11315
+ }
11316
+ },
11300
11317
  capabilities: { edit: true, reload: true, continue: true },
11301
11318
  onConnectionChange,
11302
11319
  onRawConnectionChange,
@@ -11376,7 +11393,9 @@ function athenaStatewireErrorNotice(error2) {
11376
11393
  return {
11377
11394
  kind: "command",
11378
11395
  title: "Message was not sent",
11379
- message: error2 instanceof Error && error2.message.length > 0 ? error2.message : "The chat runtime rejected the command. Please try again."
11396
+ // The server's own detail names the actionable reason; the Error message is
11397
+ // generic client-side text like `command rejected (500)`.
11398
+ message: readDetailMessage(rejection == null ? void 0 : rejection.detail) ?? (error2 instanceof Error && error2.message.length > 0 ? error2.message : "The chat runtime rejected the command. Please try again.")
11380
11399
  };
11381
11400
  }
11382
11401
  const AthenaStatewireLifecycleContext = React.createContext(null);
@@ -11404,6 +11423,20 @@ function StatewireClientToolBridge({
11404
11423
  const toolsRef = React.useRef(tools);
11405
11424
  toolsRef.current = tools;
11406
11425
  const pendingRequest = inputRequests == null ? void 0 : inputRequests.find(isPendingClientToolRequest);
11426
+ const ownedClaimsRef = React.useRef(/* @__PURE__ */ new Set());
11427
+ const activeRef = React.useRef(true);
11428
+ const releaseClaim = React.useCallback((claim) => {
11429
+ ownedClaimsRef.current.delete(claim);
11430
+ clientToolRequests.release(claim);
11431
+ }, []);
11432
+ React.useEffect(() => {
11433
+ activeRef.current = true;
11434
+ return () => {
11435
+ activeRef.current = false;
11436
+ for (const claim of ownedClaimsRef.current) clientToolRequests.release(claim);
11437
+ ownedClaimsRef.current.clear();
11438
+ };
11439
+ }, []);
11407
11440
  const liveRequestIdsRef = React.useRef(/* @__PURE__ */ new Set());
11408
11441
  liveRequestIdsRef.current = new Set((inputRequests ?? []).map((request) => request.id));
11409
11442
  const executeRequest = React.useCallback(
@@ -11414,8 +11447,8 @@ function StatewireClientToolBridge({
11414
11447
  const registry2 = new Map(toolsRef.current.map((tool) => [tool.name, tool]));
11415
11448
  const results = {};
11416
11449
  for (const call of calls) {
11417
- if (!liveRequestIdsRef.current.has(request.id)) {
11418
- clientToolRequests.release(requestKey(threadId, request.id));
11450
+ if (!activeRef.current || !liveRequestIdsRef.current.has(request.id)) {
11451
+ releaseClaim(requestKey(threadId, request.id));
11419
11452
  return;
11420
11453
  }
11421
11454
  const tool = registry2.get(call.tool_name);
@@ -11437,8 +11470,8 @@ function StatewireClientToolBridge({
11437
11470
  results[call.interrupt_id] = clientToolErrorEnvelope(error2);
11438
11471
  }
11439
11472
  }
11440
- if (!liveRequestIdsRef.current.has(request.id)) {
11441
- clientToolRequests.release(requestKey(threadId, request.id));
11473
+ if (!activeRef.current || !liveRequestIdsRef.current.has(request.id)) {
11474
+ releaseClaim(requestKey(threadId, request.id));
11442
11475
  return;
11443
11476
  }
11444
11477
  try {
@@ -11448,16 +11481,19 @@ function StatewireClientToolBridge({
11448
11481
  requestId: request.id,
11449
11482
  response: { type: "resume", value: wireValue }
11450
11483
  });
11484
+ ownedClaimsRef.current.delete(requestKey(threadId, request.id));
11451
11485
  } catch (error2) {
11452
- clientToolRequests.release(requestKey(threadId, request.id));
11486
+ releaseClaim(requestKey(threadId, request.id));
11453
11487
  console.error("[AthenaSDK] failed to resume client tool results:", error2);
11454
11488
  }
11455
11489
  },
11456
- [sendCommand, threadId]
11490
+ [releaseClaim, sendCommand, threadId]
11457
11491
  );
11458
11492
  React.useEffect(() => {
11459
11493
  if (!pendingRequest) return;
11460
- if (!clientToolRequests.claim(requestKey(threadId, pendingRequest.id))) return;
11494
+ const claim = requestKey(threadId, pendingRequest.id);
11495
+ if (!clientToolRequests.claim(claim)) return;
11496
+ ownedClaimsRef.current.add(claim);
11461
11497
  void executeRequest(pendingRequest);
11462
11498
  }, [executeRequest, pendingRequest, threadId]);
11463
11499
  return null;
@@ -11472,6 +11508,9 @@ function resolveAthenaTransport({
11472
11508
  }) {
11473
11509
  return { transport: transport ?? DEFAULT_ATHENA_TRANSPORT, fallbackReason: null };
11474
11510
  }
11511
+ function allowsMidRunSend(transport) {
11512
+ return transport === ATHENA_TRANSPORTS.statewire;
11513
+ }
11475
11514
  var __defProp$i = Object.defineProperty;
11476
11515
  var __name$h = (target, value) => __defProp$i(target, "name", { value, configurable: true });
11477
11516
  function setRef$1(ref, value) {
@@ -18807,16 +18846,19 @@ function StatewireThreadListState({
18807
18846
  initialThreadId,
18808
18847
  children
18809
18848
  }) {
18849
+ var _a3;
18810
18850
  const [active, setActive] = React.useState(
18811
18851
  () => initialThreadId ? { id: initialThreadId, isNew: false } : { id: mintThreadId(), isNew: true }
18812
18852
  );
18813
18853
  const tokenRef = React.useRef(token);
18814
18854
  tokenRef.current = token;
18855
+ const authContext = React.useContext(AthenaAuthContext.AthenaAuthContext);
18856
+ const principal = ((_a3 = authContext == null ? void 0 : authContext.user) == null ? void 0 : _a3.userId) ?? "";
18815
18857
  const hasAuth = !!(apiKey || token);
18816
18858
  const { data, isLoading, refetch } = useQuery({
18817
- // Keyed on the user identity surface, not the raw token — token rotation
18818
- // for the same session must not refetch the whole list.
18819
- queryKey: [THREADS_QUERY_KEY, backendUrl, appId ?? "", apiKey ?? "", hasAuth],
18859
+ queryKey: [THREADS_QUERY_KEY, backendUrl, appId ?? "", principal, hasAuth],
18860
+ // Never serve another principal's list while the new one loads.
18861
+ placeholderData: void 0,
18820
18862
  queryFn: async () => {
18821
18863
  const { threads } = await listThreads(
18822
18864
  backendUrl,
@@ -18834,6 +18876,7 @@ function StatewireThreadListState({
18834
18876
  void refetch();
18835
18877
  }, [refetch]);
18836
18878
  const selectThread = React.useCallback((threadId) => {
18879
+ if (!threadId) return;
18837
18880
  setActive(
18838
18881
  (current) => current.id === threadId ? current : { id: threadId, isNew: false }
18839
18882
  );
@@ -18841,6 +18884,12 @@ function StatewireThreadListState({
18841
18884
  const newThread = React.useCallback(() => {
18842
18885
  setActive({ id: mintThreadId(), isNew: true });
18843
18886
  }, []);
18887
+ const isPersisted = !!(data == null ? void 0 : data.some((thread) => thread.id === active.id));
18888
+ React.useEffect(() => {
18889
+ if (isPersisted) {
18890
+ setActive((current) => current.isNew ? { ...current, isNew: false } : current);
18891
+ }
18892
+ }, [isPersisted]);
18844
18893
  const value = React.useMemo(
18845
18894
  () => ({
18846
18895
  threads: data ?? [],
@@ -19010,8 +19059,6 @@ function useAthenaThreadManager() {
19010
19059
  const statewireManager = React.useMemo(() => {
19011
19060
  if (!statewireList) return null;
19012
19061
  return {
19013
- // A never-started local chat stays out of the URL, mirroring the
19014
- // legacy manager's local-placeholder handling.
19015
19062
  activeThreadId: statewireList.isNewChat ? null : statewireList.activeThreadId,
19016
19063
  isListLoading: statewireList.isLoading,
19017
19064
  isThreadLoading: statewireIsThreadLoading,
@@ -19841,9 +19888,16 @@ const initialStatewireLifecycle = {
19841
19888
  reconnect: null
19842
19889
  };
19843
19890
  function statewireLifecycleReducer(state, action) {
19891
+ var _a3, _b2;
19844
19892
  switch (action.type) {
19845
- case "connection":
19846
- return { ...state, connection: action.connection };
19893
+ case "connection": {
19894
+ const healthy = ((_a3 = action.connection) == null ? void 0 : _a3.status) === "live" || ((_b2 = action.connection) == null ? void 0 : _b2.status) === "idle";
19895
+ return {
19896
+ ...state,
19897
+ connection: action.connection,
19898
+ error: healthy ? null : state.error
19899
+ };
19900
+ }
19847
19901
  case "error":
19848
19902
  return { ...state, error: action.error };
19849
19903
  case "legacy-read-only":
@@ -20209,6 +20263,8 @@ function AthenaProvider({
20209
20263
  const configuredAppUrl = (config2 == null ? void 0 : config2.appUrl) ?? appUrl;
20210
20264
  const configuredTransport = (config2 == null ? void 0 : config2.transport) ?? transport;
20211
20265
  const configuredStatewireSyncUrl = (config2 == null ? void 0 : config2.statewireSyncUrl) ?? statewireSyncUrl;
20266
+ const configuredGetToken = (config2 == null ? void 0 : config2.getToken) ?? getToken;
20267
+ const configuredExtraRunConfig = (config2 == null ? void 0 : config2.extraRunConfig) ?? extraRunConfig;
20212
20268
  const configuredTrustedParentOrigins = config2 == null ? void 0 : config2.trustedParentOrigins;
20213
20269
  const posthogConfig = (config2 == null ? void 0 : config2.posthog) ?? posthogProp;
20214
20270
  const { transport: effectiveTransport, fallbackReason: transportFallbackReason } = resolveAthenaTransport({
@@ -20243,7 +20299,7 @@ function AthenaProvider({
20243
20299
  appUrl: effectiveAppUrl,
20244
20300
  apiKey: configuredApiKey,
20245
20301
  token: effectiveToken,
20246
- getToken,
20302
+ getToken: configuredGetToken,
20247
20303
  model,
20248
20304
  tools,
20249
20305
  frontendToolIds: frontendToolNames,
@@ -20253,7 +20309,7 @@ function AthenaProvider({
20253
20309
  systemPrompt,
20254
20310
  customToolConfigs,
20255
20311
  appId,
20256
- extraRunConfig,
20312
+ extraRunConfig: configuredExtraRunConfig,
20257
20313
  linkClicks,
20258
20314
  citationLinks
20259
20315
  };
@@ -55306,7 +55362,7 @@ const TiptapComposer = ({ tools = [], rootCategories }) => {
55306
55362
  const isUploadingRef = React.useRef(isUploading);
55307
55363
  isUploadingRef.current = isUploading;
55308
55364
  const isRunningThread = react$1.useAuiState((s) => s.thread.isRunning);
55309
- const isThreadRunning = transport === "statewire" ? false : isRunningThread;
55365
+ const isThreadRunning = allowsMidRunSend(transport) ? false : isRunningThread;
55310
55366
  const isThreadRunningRef = React.useRef(isThreadRunning);
55311
55367
  isThreadRunningRef.current = isThreadRunning;
55312
55368
  const handleSubmit = React.useCallback(() => {
@@ -55328,10 +55384,8 @@ const TiptapComposer = ({ tools = [], rootCategories }) => {
55328
55384
  appUrl
55329
55385
  });
55330
55386
  if (fullMessage) {
55331
- aui.thread.append({
55332
- role: "user",
55333
- content: [{ type: "text", text: fullMessage }]
55334
- });
55387
+ aui.composer.setText(fullMessage);
55388
+ aui.composer.send();
55335
55389
  clearAttachments();
55336
55390
  clearQuote();
55337
55391
  }
@@ -55580,14 +55634,20 @@ const StatewireApprovalCardInner = () => {
55580
55634
  }
55581
55635
  const isRunning = react$1.useAuiState((s) => s.thread.isRunning);
55582
55636
  const sawResumeRunRef = React.useRef(false);
55583
- if (pending && isRunning) {
55584
- sawResumeRunRef.current = true;
55585
- } else if (pending && sawResumeRunRef.current && !isRunning) {
55586
- sawResumeRunRef.current = false;
55587
- setPending(false);
55588
- } else if (!pending) {
55589
- sawResumeRunRef.current = false;
55590
- }
55637
+ React.useEffect(() => {
55638
+ if (!pending) {
55639
+ sawResumeRunRef.current = false;
55640
+ return;
55641
+ }
55642
+ if (isRunning) {
55643
+ sawResumeRunRef.current = true;
55644
+ return;
55645
+ }
55646
+ if (sawResumeRunRef.current) {
55647
+ sawResumeRunRef.current = false;
55648
+ setPending(false);
55649
+ }
55650
+ }, [pending, isRunning]);
55591
55651
  React.useEffect(() => {
55592
55652
  if (!pending) return;
55593
55653
  const timer = setTimeout(() => {
@@ -55878,7 +55938,7 @@ const StatewireLegacyReadOnlyBanner = () => {
55878
55938
  /* @__PURE__ */ jsxRuntime.jsx(Archive, { className: "mt-0.5 size-4 shrink-0" }),
55879
55939
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
55880
55940
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-xs", children: "Read-only conversation" }),
55881
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pt-0.5 text-xs leading-snug text-amber-800", children: "Its history remains available, but new messages cannot be added. Start a new chat to continue." })
55941
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pt-0.5 text-xs leading-snug text-amber-800", children: "This conversation’s history remains available, but new messages cannot be added. Start a new chat to continue." })
55882
55942
  ] })
55883
55943
  ] });
55884
55944
  };
@@ -61117,7 +61177,7 @@ const ThreadScrollToBottom = () => /* @__PURE__ */ jsxRuntime.jsx(react$1.Thread
61117
61177
  ) });
61118
61178
  const ComposerAction = () => {
61119
61179
  const { transport } = useAthenaConfig();
61120
- const queuesWhileRunning = transport === "statewire";
61180
+ const queuesWhileRunning = allowsMidRunSend(transport);
61121
61181
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "aui-composer-action-wrapper relative mx-2 mb-2 flex items-center justify-between", children: [
61122
61182
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: /* @__PURE__ */ jsxRuntime.jsx(FileUploadButton, {}) }),
61123
61183
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
@@ -61144,7 +61204,7 @@ const ComposerSendWithQuote = () => {
61144
61204
  const editorRef = useComposerEditorRef();
61145
61205
  const editorEmpty = useComposerEditorEmpty();
61146
61206
  const isRunningThread = react$1.useAuiState((s) => s.thread.isRunning);
61147
- const isThreadRunning = transport === "statewire" ? false : isRunningThread;
61207
+ const isThreadRunning = allowsMidRunSend(transport) ? false : isRunningThread;
61148
61208
  const hasExtras = !!quote || attachments.length > 0;
61149
61209
  const handleSend = React.useCallback(() => {
61150
61210
  var _a3;
@@ -61160,10 +61220,8 @@ const ComposerSendWithQuote = () => {
61160
61220
  appUrl
61161
61221
  });
61162
61222
  if (!fullMessage) return;
61163
- aui.thread.append({
61164
- role: "user",
61165
- content: [{ type: "text", text: fullMessage }]
61166
- });
61223
+ aui.composer.setText(fullMessage);
61224
+ aui.composer.send();
61167
61225
  clearQuote();
61168
61226
  clearAttachments();
61169
61227
  } else {
@@ -62469,6 +62527,7 @@ exports.TooltipProvider = TooltipProvider;
62469
62527
  exports.TooltipTrigger = TooltipTrigger;
62470
62528
  exports.UpdateSheetRangeToolUI = UpdateSheetRangeToolUI;
62471
62529
  exports.WebSearchToolUI = WebSearchToolUI;
62530
+ exports.allowsMidRunSend = allowsMidRunSend;
62472
62531
  exports.archiveThread = archiveThread;
62473
62532
  exports.asHitlApproval = asHitlApproval;
62474
62533
  exports.athenaStatewireErrorNotice = athenaStatewireErrorNotice;