@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.d.ts CHANGED
@@ -82,7 +82,7 @@ export { DEFAULT_STATEWIRE_MODEL, useAthenaStatewireRuntime, } from './runtime/u
82
82
  export type { AthenaStatewireRuntimeConfig } from './runtime/useAthenaStatewireRuntime';
83
83
  export { buildStatewireRunConfig } from './runtime/statewire-run-config';
84
84
  export type { StatewireRunConfigOptions } from './runtime/statewire-run-config';
85
- export { ATHENA_TRANSPORTS, DEFAULT_ATHENA_TRANSPORT, resolveAthenaTransport, } from './runtime/transport';
85
+ export { allowsMidRunSend, ATHENA_TRANSPORTS, DEFAULT_ATHENA_TRANSPORT, resolveAthenaTransport, } from './runtime/transport';
86
86
  export type { AthenaTransport, ResolvedAthenaTransport } from './runtime/transport';
87
87
  export { useParentAuth, useParentBridge } from './runtime/auth';
88
88
  export type { ParentBridgeState } from './runtime/auth';
package/dist/index.js CHANGED
@@ -23,6 +23,7 @@ import { fromThreadMessageLike, bindExternalStoreMessage, getExternalStoreMessag
23
23
  import * as React from "react";
24
24
  import React__default, { useMemo, useState, useRef, useEffect, useContext, createContext, useCallback, useLayoutEffect, useReducer, useDebugValue, forwardRef, createRef, memo, createElement, version as version$1, useImperativeHandle } from "react";
25
25
  import { A as AthenaAuthContext } from "./AthenaAuthContext-DQsdayH2.js";
26
+ import { LocalStorageSession } from "@assistant-ui/react-statewire/local-storage";
26
27
  import { resource, withKey } from "@assistant-ui/tap";
27
28
  import { StatewireSendError, StatewireSSE, StatewireWS } from "statewire";
28
29
  import "@assistant-ui/core";
@@ -10837,6 +10838,7 @@ function useDeepAgentThread({
10837
10838
  sse = false,
10838
10839
  headers,
10839
10840
  sessionStore,
10841
+ storage,
10840
10842
  isNewChat = false,
10841
10843
  attachKey,
10842
10844
  suspended = false,
@@ -11004,6 +11006,9 @@ function useDeepAgentThread({
11004
11006
  );
11005
11007
  })()
11006
11008
  ),
11009
+ // Survives the transport remounts above: a bumped attachKey re-attaches the
11010
+ // same lane, and a persistent session storage carries it across reloads.
11011
+ ...storage && { storage },
11007
11012
  // The run subsystem (status, lanes, input requests, runId) is mounted on
11008
11013
  // the state root and always replicated by react-statewire >= 0.14 (the
11009
11014
  // former `runs: true` opt-in is gone) — the converter only maps app-owned
@@ -11241,6 +11246,7 @@ function useAthenaStatewireRuntime(config2) {
11241
11246
  const clientTools = useStatewireClientTools(frontendToolkit);
11242
11247
  const clientToolsRef = useRef(clientTools);
11243
11248
  clientToolsRef.current = clientTools;
11249
+ const [canPersistSession] = useState(() => typeof localStorage !== "undefined");
11244
11250
  const thread = useDeepAgentThread({
11245
11251
  threadId,
11246
11252
  baseUrl: syncUrl,
@@ -11258,6 +11264,17 @@ function useAthenaStatewireRuntime(config2) {
11258
11264
  // Queued entries render in the SDK's queue panel, so only the steer lane
11259
11265
  // projects into the message tail.
11260
11266
  laneProjection: "steer-only",
11267
+ // Durable session: a reload resumes the lane and resends whatever the
11268
+ // server has not marked durable. Scoped by host — a thread id is only
11269
+ // meaningful against the backend that minted it.
11270
+ ...canPersistSession && {
11271
+ storage: {
11272
+ session: LocalStorageSession({
11273
+ threadId,
11274
+ key: (id) => `${syncUrl}:${id}`
11275
+ })
11276
+ }
11277
+ },
11261
11278
  capabilities: { edit: true, reload: true, continue: true },
11262
11279
  onConnectionChange,
11263
11280
  onRawConnectionChange,
@@ -11337,7 +11354,9 @@ function athenaStatewireErrorNotice(error2) {
11337
11354
  return {
11338
11355
  kind: "command",
11339
11356
  title: "Message was not sent",
11340
- message: error2 instanceof Error && error2.message.length > 0 ? error2.message : "The chat runtime rejected the command. Please try again."
11357
+ // The server's own detail names the actionable reason; the Error message is
11358
+ // generic client-side text like `command rejected (500)`.
11359
+ 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.")
11341
11360
  };
11342
11361
  }
11343
11362
  const AthenaStatewireLifecycleContext = createContext(null);
@@ -11365,6 +11384,20 @@ function StatewireClientToolBridge({
11365
11384
  const toolsRef = useRef(tools);
11366
11385
  toolsRef.current = tools;
11367
11386
  const pendingRequest = inputRequests == null ? void 0 : inputRequests.find(isPendingClientToolRequest);
11387
+ const ownedClaimsRef = useRef(/* @__PURE__ */ new Set());
11388
+ const activeRef = useRef(true);
11389
+ const releaseClaim = useCallback((claim) => {
11390
+ ownedClaimsRef.current.delete(claim);
11391
+ clientToolRequests.release(claim);
11392
+ }, []);
11393
+ useEffect(() => {
11394
+ activeRef.current = true;
11395
+ return () => {
11396
+ activeRef.current = false;
11397
+ for (const claim of ownedClaimsRef.current) clientToolRequests.release(claim);
11398
+ ownedClaimsRef.current.clear();
11399
+ };
11400
+ }, []);
11368
11401
  const liveRequestIdsRef = useRef(/* @__PURE__ */ new Set());
11369
11402
  liveRequestIdsRef.current = new Set((inputRequests ?? []).map((request) => request.id));
11370
11403
  const executeRequest = useCallback(
@@ -11375,8 +11408,8 @@ function StatewireClientToolBridge({
11375
11408
  const registry2 = new Map(toolsRef.current.map((tool) => [tool.name, tool]));
11376
11409
  const results = {};
11377
11410
  for (const call of calls) {
11378
- if (!liveRequestIdsRef.current.has(request.id)) {
11379
- clientToolRequests.release(requestKey(threadId, request.id));
11411
+ if (!activeRef.current || !liveRequestIdsRef.current.has(request.id)) {
11412
+ releaseClaim(requestKey(threadId, request.id));
11380
11413
  return;
11381
11414
  }
11382
11415
  const tool = registry2.get(call.tool_name);
@@ -11398,8 +11431,8 @@ function StatewireClientToolBridge({
11398
11431
  results[call.interrupt_id] = clientToolErrorEnvelope(error2);
11399
11432
  }
11400
11433
  }
11401
- if (!liveRequestIdsRef.current.has(request.id)) {
11402
- clientToolRequests.release(requestKey(threadId, request.id));
11434
+ if (!activeRef.current || !liveRequestIdsRef.current.has(request.id)) {
11435
+ releaseClaim(requestKey(threadId, request.id));
11403
11436
  return;
11404
11437
  }
11405
11438
  try {
@@ -11409,16 +11442,19 @@ function StatewireClientToolBridge({
11409
11442
  requestId: request.id,
11410
11443
  response: { type: "resume", value: wireValue }
11411
11444
  });
11445
+ ownedClaimsRef.current.delete(requestKey(threadId, request.id));
11412
11446
  } catch (error2) {
11413
- clientToolRequests.release(requestKey(threadId, request.id));
11447
+ releaseClaim(requestKey(threadId, request.id));
11414
11448
  console.error("[AthenaSDK] failed to resume client tool results:", error2);
11415
11449
  }
11416
11450
  },
11417
- [sendCommand, threadId]
11451
+ [releaseClaim, sendCommand, threadId]
11418
11452
  );
11419
11453
  useEffect(() => {
11420
11454
  if (!pendingRequest) return;
11421
- if (!clientToolRequests.claim(requestKey(threadId, pendingRequest.id))) return;
11455
+ const claim = requestKey(threadId, pendingRequest.id);
11456
+ if (!clientToolRequests.claim(claim)) return;
11457
+ ownedClaimsRef.current.add(claim);
11422
11458
  void executeRequest(pendingRequest);
11423
11459
  }, [executeRequest, pendingRequest, threadId]);
11424
11460
  return null;
@@ -11433,6 +11469,9 @@ function resolveAthenaTransport({
11433
11469
  }) {
11434
11470
  return { transport: transport ?? DEFAULT_ATHENA_TRANSPORT, fallbackReason: null };
11435
11471
  }
11472
+ function allowsMidRunSend(transport) {
11473
+ return transport === ATHENA_TRANSPORTS.statewire;
11474
+ }
11436
11475
  var __defProp$i = Object.defineProperty;
11437
11476
  var __name$h = (target, value) => __defProp$i(target, "name", { value, configurable: true });
11438
11477
  function setRef$1(ref, value) {
@@ -18768,16 +18807,19 @@ function StatewireThreadListState({
18768
18807
  initialThreadId,
18769
18808
  children
18770
18809
  }) {
18810
+ var _a3;
18771
18811
  const [active, setActive] = useState(
18772
18812
  () => initialThreadId ? { id: initialThreadId, isNew: false } : { id: mintThreadId(), isNew: true }
18773
18813
  );
18774
18814
  const tokenRef = useRef(token);
18775
18815
  tokenRef.current = token;
18816
+ const authContext = useContext(AthenaAuthContext);
18817
+ const principal = ((_a3 = authContext == null ? void 0 : authContext.user) == null ? void 0 : _a3.userId) ?? "";
18776
18818
  const hasAuth = !!(apiKey || token);
18777
18819
  const { data, isLoading, refetch } = useQuery({
18778
- // Keyed on the user identity surface, not the raw token — token rotation
18779
- // for the same session must not refetch the whole list.
18780
- queryKey: [THREADS_QUERY_KEY, backendUrl, appId ?? "", apiKey ?? "", hasAuth],
18820
+ queryKey: [THREADS_QUERY_KEY, backendUrl, appId ?? "", principal, hasAuth],
18821
+ // Never serve another principal's list while the new one loads.
18822
+ placeholderData: void 0,
18781
18823
  queryFn: async () => {
18782
18824
  const { threads } = await listThreads(
18783
18825
  backendUrl,
@@ -18795,6 +18837,7 @@ function StatewireThreadListState({
18795
18837
  void refetch();
18796
18838
  }, [refetch]);
18797
18839
  const selectThread = useCallback((threadId) => {
18840
+ if (!threadId) return;
18798
18841
  setActive(
18799
18842
  (current) => current.id === threadId ? current : { id: threadId, isNew: false }
18800
18843
  );
@@ -18802,6 +18845,12 @@ function StatewireThreadListState({
18802
18845
  const newThread = useCallback(() => {
18803
18846
  setActive({ id: mintThreadId(), isNew: true });
18804
18847
  }, []);
18848
+ const isPersisted = !!(data == null ? void 0 : data.some((thread) => thread.id === active.id));
18849
+ useEffect(() => {
18850
+ if (isPersisted) {
18851
+ setActive((current) => current.isNew ? { ...current, isNew: false } : current);
18852
+ }
18853
+ }, [isPersisted]);
18805
18854
  const value = useMemo(
18806
18855
  () => ({
18807
18856
  threads: data ?? [],
@@ -18971,8 +19020,6 @@ function useAthenaThreadManager() {
18971
19020
  const statewireManager = useMemo(() => {
18972
19021
  if (!statewireList) return null;
18973
19022
  return {
18974
- // A never-started local chat stays out of the URL, mirroring the
18975
- // legacy manager's local-placeholder handling.
18976
19023
  activeThreadId: statewireList.isNewChat ? null : statewireList.activeThreadId,
18977
19024
  isListLoading: statewireList.isLoading,
18978
19025
  isThreadLoading: statewireIsThreadLoading,
@@ -19802,9 +19849,16 @@ const initialStatewireLifecycle = {
19802
19849
  reconnect: null
19803
19850
  };
19804
19851
  function statewireLifecycleReducer(state, action) {
19852
+ var _a3, _b2;
19805
19853
  switch (action.type) {
19806
- case "connection":
19807
- return { ...state, connection: action.connection };
19854
+ case "connection": {
19855
+ const healthy = ((_a3 = action.connection) == null ? void 0 : _a3.status) === "live" || ((_b2 = action.connection) == null ? void 0 : _b2.status) === "idle";
19856
+ return {
19857
+ ...state,
19858
+ connection: action.connection,
19859
+ error: healthy ? null : state.error
19860
+ };
19861
+ }
19808
19862
  case "error":
19809
19863
  return { ...state, error: action.error };
19810
19864
  case "legacy-read-only":
@@ -20170,6 +20224,8 @@ function AthenaProvider({
20170
20224
  const configuredAppUrl = (config2 == null ? void 0 : config2.appUrl) ?? appUrl;
20171
20225
  const configuredTransport = (config2 == null ? void 0 : config2.transport) ?? transport;
20172
20226
  const configuredStatewireSyncUrl = (config2 == null ? void 0 : config2.statewireSyncUrl) ?? statewireSyncUrl;
20227
+ const configuredGetToken = (config2 == null ? void 0 : config2.getToken) ?? getToken;
20228
+ const configuredExtraRunConfig = (config2 == null ? void 0 : config2.extraRunConfig) ?? extraRunConfig;
20173
20229
  const configuredTrustedParentOrigins = config2 == null ? void 0 : config2.trustedParentOrigins;
20174
20230
  const posthogConfig = (config2 == null ? void 0 : config2.posthog) ?? posthogProp;
20175
20231
  const { transport: effectiveTransport, fallbackReason: transportFallbackReason } = resolveAthenaTransport({
@@ -20204,7 +20260,7 @@ function AthenaProvider({
20204
20260
  appUrl: effectiveAppUrl,
20205
20261
  apiKey: configuredApiKey,
20206
20262
  token: effectiveToken,
20207
- getToken,
20263
+ getToken: configuredGetToken,
20208
20264
  model,
20209
20265
  tools,
20210
20266
  frontendToolIds: frontendToolNames,
@@ -20214,7 +20270,7 @@ function AthenaProvider({
20214
20270
  systemPrompt,
20215
20271
  customToolConfigs,
20216
20272
  appId,
20217
- extraRunConfig,
20273
+ extraRunConfig: configuredExtraRunConfig,
20218
20274
  linkClicks,
20219
20275
  citationLinks
20220
20276
  };
@@ -55267,7 +55323,7 @@ const TiptapComposer = ({ tools = [], rootCategories }) => {
55267
55323
  const isUploadingRef = useRef(isUploading);
55268
55324
  isUploadingRef.current = isUploading;
55269
55325
  const isRunningThread = useAuiState((s) => s.thread.isRunning);
55270
- const isThreadRunning = transport === "statewire" ? false : isRunningThread;
55326
+ const isThreadRunning = allowsMidRunSend(transport) ? false : isRunningThread;
55271
55327
  const isThreadRunningRef = useRef(isThreadRunning);
55272
55328
  isThreadRunningRef.current = isThreadRunning;
55273
55329
  const handleSubmit = useCallback(() => {
@@ -55289,10 +55345,8 @@ const TiptapComposer = ({ tools = [], rootCategories }) => {
55289
55345
  appUrl
55290
55346
  });
55291
55347
  if (fullMessage) {
55292
- aui.thread.append({
55293
- role: "user",
55294
- content: [{ type: "text", text: fullMessage }]
55295
- });
55348
+ aui.composer.setText(fullMessage);
55349
+ aui.composer.send();
55296
55350
  clearAttachments();
55297
55351
  clearQuote();
55298
55352
  }
@@ -55541,14 +55595,20 @@ const StatewireApprovalCardInner = () => {
55541
55595
  }
55542
55596
  const isRunning = useAuiState((s) => s.thread.isRunning);
55543
55597
  const sawResumeRunRef = useRef(false);
55544
- if (pending && isRunning) {
55545
- sawResumeRunRef.current = true;
55546
- } else if (pending && sawResumeRunRef.current && !isRunning) {
55547
- sawResumeRunRef.current = false;
55548
- setPending(false);
55549
- } else if (!pending) {
55550
- sawResumeRunRef.current = false;
55551
- }
55598
+ useEffect(() => {
55599
+ if (!pending) {
55600
+ sawResumeRunRef.current = false;
55601
+ return;
55602
+ }
55603
+ if (isRunning) {
55604
+ sawResumeRunRef.current = true;
55605
+ return;
55606
+ }
55607
+ if (sawResumeRunRef.current) {
55608
+ sawResumeRunRef.current = false;
55609
+ setPending(false);
55610
+ }
55611
+ }, [pending, isRunning]);
55552
55612
  useEffect(() => {
55553
55613
  if (!pending) return;
55554
55614
  const timer = setTimeout(() => {
@@ -55839,7 +55899,7 @@ const StatewireLegacyReadOnlyBanner = () => {
55839
55899
  /* @__PURE__ */ jsx(Archive, { className: "mt-0.5 size-4 shrink-0" }),
55840
55900
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
55841
55901
  /* @__PURE__ */ jsx("p", { className: "font-semibold text-xs", children: "Read-only conversation" }),
55842
- /* @__PURE__ */ 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." })
55902
+ /* @__PURE__ */ 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." })
55843
55903
  ] })
55844
55904
  ] });
55845
55905
  };
@@ -61078,7 +61138,7 @@ const ThreadScrollToBottom = () => /* @__PURE__ */ jsx(ThreadPrimitive.ScrollToB
61078
61138
  ) });
61079
61139
  const ComposerAction = () => {
61080
61140
  const { transport } = useAthenaConfig();
61081
- const queuesWhileRunning = transport === "statewire";
61141
+ const queuesWhileRunning = allowsMidRunSend(transport);
61082
61142
  return /* @__PURE__ */ jsxs("div", { className: "aui-composer-action-wrapper relative mx-2 mb-2 flex items-center justify-between", children: [
61083
61143
  /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: /* @__PURE__ */ jsx(FileUploadButton, {}) }),
61084
61144
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
@@ -61105,7 +61165,7 @@ const ComposerSendWithQuote = () => {
61105
61165
  const editorRef = useComposerEditorRef();
61106
61166
  const editorEmpty = useComposerEditorEmpty();
61107
61167
  const isRunningThread = useAuiState((s) => s.thread.isRunning);
61108
- const isThreadRunning = transport === "statewire" ? false : isRunningThread;
61168
+ const isThreadRunning = allowsMidRunSend(transport) ? false : isRunningThread;
61109
61169
  const hasExtras = !!quote || attachments.length > 0;
61110
61170
  const handleSend = useCallback(() => {
61111
61171
  var _a3;
@@ -61121,10 +61181,8 @@ const ComposerSendWithQuote = () => {
61121
61181
  appUrl
61122
61182
  });
61123
61183
  if (!fullMessage) return;
61124
- aui.thread.append({
61125
- role: "user",
61126
- content: [{ type: "text", text: fullMessage }]
61127
- });
61184
+ aui.composer.setText(fullMessage);
61185
+ aui.composer.send();
61128
61186
  clearQuote();
61129
61187
  clearAttachments();
61130
61188
  } else {
@@ -62431,6 +62489,7 @@ export {
62431
62489
  TooltipTrigger,
62432
62490
  UpdateSheetRangeToolUI,
62433
62491
  WebSearchToolUI,
62492
+ allowsMidRunSend,
62434
62493
  archiveThread,
62435
62494
  asHitlApproval,
62436
62495
  athenaStatewireErrorNotice,