@stigmer/react 3.0.8-dev.20260613041848 → 3.0.8-dev.20260613071809
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/composer/ComposerToolbar.d.ts +9 -1
- package/composer/ComposerToolbar.d.ts.map +1 -1
- package/composer/ComposerToolbar.js +3 -3
- package/composer/ComposerToolbar.js.map +1 -1
- package/composer/SessionComposer.d.ts +12 -0
- package/composer/SessionComposer.d.ts.map +1 -1
- package/composer/SessionComposer.js +6 -3
- package/composer/SessionComposer.js.map +1 -1
- package/composer/icons.d.ts +2 -0
- package/composer/icons.d.ts.map +1 -1
- package/composer/icons.js +4 -0
- package/composer/icons.js.map +1 -1
- package/execution/ExecutionProgress.d.ts.map +1 -1
- package/execution/ExecutionProgress.js +5 -1
- package/execution/ExecutionProgress.js.map +1 -1
- package/execution/MessageEntry.d.ts +7 -0
- package/execution/MessageEntry.d.ts.map +1 -1
- package/execution/MessageEntry.js +7 -4
- package/execution/MessageEntry.js.map +1 -1
- package/execution/MessageThread.d.ts +45 -3
- package/execution/MessageThread.d.ts.map +1 -1
- package/execution/MessageThread.js +66 -11
- package/execution/MessageThread.js.map +1 -1
- package/execution/index.d.ts +2 -0
- package/execution/index.d.ts.map +1 -1
- package/execution/index.js +1 -0
- package/execution/index.js.map +1 -1
- package/execution/useAgentExecutionActions.d.ts +67 -0
- package/execution/useAgentExecutionActions.d.ts.map +1 -0
- package/execution/useAgentExecutionActions.js +105 -0
- package/execution/useAgentExecutionActions.js.map +1 -0
- package/execution/useExecutionStream.d.ts +27 -0
- package/execution/useExecutionStream.d.ts.map +1 -1
- package/execution/useExecutionStream.js +48 -5
- package/execution/useExecutionStream.js.map +1 -1
- package/index.d.ts +2 -2
- package/index.d.ts.map +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/internal/VirtualizedThread.d.ts +4 -1
- package/internal/VirtualizedThread.d.ts.map +1 -1
- package/internal/VirtualizedThread.js +5 -2
- package/internal/VirtualizedThread.js.map +1 -1
- package/internal/store/conversation-store.d.ts +22 -0
- package/internal/store/conversation-store.d.ts.map +1 -1
- package/internal/store/conversation-store.js +43 -2
- package/internal/store/conversation-store.js.map +1 -1
- package/internal/stream-controller.d.ts +46 -2
- package/internal/stream-controller.d.ts.map +1 -1
- package/internal/stream-controller.js +95 -4
- package/internal/stream-controller.js.map +1 -1
- package/internal/useFetch.d.ts +7 -0
- package/internal/useFetch.d.ts.map +1 -1
- package/internal/useFetch.js +21 -0
- package/internal/useFetch.js.map +1 -1
- package/package.json +4 -4
- package/session/SessionViewer.js +39 -2
- package/session/SessionViewer.js.map +1 -1
- package/session/useSessionConversation.d.ts +55 -3
- package/session/useSessionConversation.d.ts.map +1 -1
- package/session/useSessionConversation.js +95 -10
- package/session/useSessionConversation.js.map +1 -1
- package/session/useSessionExecutions.d.ts +17 -1
- package/session/useSessionExecutions.d.ts.map +1 -1
- package/session/useSessionExecutions.js +6 -2
- package/session/useSessionExecutions.js.map +1 -1
- package/src/composer/ComposerToolbar.tsx +32 -9
- package/src/composer/SessionComposer.tsx +22 -1
- package/src/composer/__tests__/SessionComposer-stop.test.tsx +98 -0
- package/src/composer/icons.tsx +15 -0
- package/src/execution/ExecutionProgress.tsx +12 -0
- package/src/execution/MessageEntry.tsx +57 -2
- package/src/execution/MessageThread.tsx +203 -5
- package/src/execution/__tests__/MessageThread.test.tsx +130 -0
- package/src/execution/__tests__/useAgentExecutionActions.test.tsx +299 -0
- package/src/execution/__tests__/useExecutionStream.test.tsx +95 -0
- package/src/execution/index.ts +6 -0
- package/src/execution/useAgentExecutionActions.ts +205 -0
- package/src/execution/useExecutionStream.ts +80 -4
- package/src/index.ts +3 -0
- package/src/internal/VirtualizedThread.tsx +10 -1
- package/src/internal/__tests__/stream-controller.test.ts +165 -10
- package/src/internal/__tests__/useFetch.test.tsx +59 -0
- package/src/internal/store/__tests__/conversation-store.test.ts +61 -0
- package/src/internal/store/conversation-store.ts +46 -3
- package/src/internal/stream-controller.ts +123 -3
- package/src/internal/useFetch.ts +26 -0
- package/src/session/SessionViewer.tsx +87 -1
- package/src/session/__tests__/useSessionConversation.test.tsx +145 -0
- package/src/session/useSessionConversation.ts +163 -14
- package/src/session/useSessionExecutions.ts +23 -1
- package/styles.css +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import type { AgentExecution } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
5
5
|
import type { PendingApproval } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/approval_pb";
|
|
6
6
|
import { ApprovalAction, ExecutionPhase } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
@@ -17,9 +17,11 @@ import type {
|
|
|
17
17
|
} from "@stigmer/sdk";
|
|
18
18
|
import { isTerminalPhase } from "../execution/execution-phases";
|
|
19
19
|
import { useStigmer } from "../hooks";
|
|
20
|
+
import { toError } from "../internal/toError";
|
|
20
21
|
import { useConversationStoreRef } from "../internal/store";
|
|
21
22
|
import { useCreateAgentExecution } from "../execution/useCreateAgentExecution";
|
|
22
23
|
import { useExecutionStream } from "../execution/useExecutionStream";
|
|
24
|
+
import { useAgentExecutionActions } from "../execution/useAgentExecutionActions";
|
|
23
25
|
import { useSubmitApproval } from "../execution/useSubmitApproval";
|
|
24
26
|
import { useSession } from "./useSession";
|
|
25
27
|
import { useSessionExecutions } from "./useSessionExecutions";
|
|
@@ -31,6 +33,14 @@ import {
|
|
|
31
33
|
specSkillRefsToInput,
|
|
32
34
|
} from "./session-spec-converters";
|
|
33
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Cadence for re-discovering the session's executions while the live stream
|
|
38
|
+
* cannot be relied on (a created-but-not-yet-listed execution, a silent
|
|
39
|
+
* connect-timeout, or an exhausted stream error). Disabled the instant the
|
|
40
|
+
* stream is healthy or terminal, so this never competes with the live feed.
|
|
41
|
+
*/
|
|
42
|
+
const REDISCOVERY_POLL_INTERVAL_MS = 5_000;
|
|
43
|
+
|
|
34
44
|
/**
|
|
35
45
|
* Options for {@link UseSessionConversationReturn.sendFollowUp}.
|
|
36
46
|
*
|
|
@@ -144,12 +154,29 @@ export interface UseSessionConversationReturn {
|
|
|
144
154
|
readonly canSendFollowUp: boolean;
|
|
145
155
|
/** True during the create RPC call (between submit and execution ID). */
|
|
146
156
|
readonly isSending: boolean;
|
|
147
|
-
/**
|
|
157
|
+
/**
|
|
158
|
+
* Error from the last `sendFollowUp` attempt, or `null`.
|
|
159
|
+
*
|
|
160
|
+
* Covers **both** failing paths — the optional `session.update()` and the
|
|
161
|
+
* `create()` RPC — so a follow-up never fails silently. When set, the user's
|
|
162
|
+
* message is preserved (see {@link pendingUserMessage}) and can be re-sent
|
|
163
|
+
* via {@link retryLastSend}.
|
|
164
|
+
*/
|
|
148
165
|
readonly sendError: Error | null;
|
|
149
|
-
/** Reset `sendError` to `null
|
|
166
|
+
/** Reset `sendError` to `null` (keeps the preserved pending message). */
|
|
150
167
|
readonly clearSendError: () => void;
|
|
168
|
+
/**
|
|
169
|
+
* Re-send the most recent `sendFollowUp` (same message and options). No-op
|
|
170
|
+
* when nothing has been sent yet. Use as the "Retry" affordance on a failed
|
|
171
|
+
* turn; clears {@link sendError} for the new attempt.
|
|
172
|
+
*/
|
|
173
|
+
readonly retryLastSend: () => void;
|
|
151
174
|
|
|
152
|
-
/**
|
|
175
|
+
/**
|
|
176
|
+
* The user's message text, shown in the thread before the stream delivers it.
|
|
177
|
+
* Retained when a send fails so the typed message is never lost — pair with
|
|
178
|
+
* {@link sendError} to render the turn as failed with a retry control.
|
|
179
|
+
*/
|
|
153
180
|
readonly pendingUserMessage: string | null;
|
|
154
181
|
|
|
155
182
|
/** Current workspace entries from the session spec. Empty array when session is not loaded. */
|
|
@@ -174,6 +201,28 @@ export interface UseSessionConversationReturn {
|
|
|
174
201
|
/** Reset `approvalError` to `null`. */
|
|
175
202
|
readonly clearApprovalError: () => void;
|
|
176
203
|
|
|
204
|
+
/**
|
|
205
|
+
* `true` when the active execution can be stopped — i.e. it is in a phase
|
|
206
|
+
* the backend accepts a cancel/terminate from (`PENDING` or `IN_PROGRESS`).
|
|
207
|
+
*
|
|
208
|
+
* Distinct from "is something active": an execution paused at an approval
|
|
209
|
+
* gate (`WAITING_FOR_APPROVAL`) is active but **not** stoppable — the
|
|
210
|
+
* approval card (approve / skip / reject) is its control surface. Drive the
|
|
211
|
+
* composer's Stop affordance off this so it only appears when {@link stop}
|
|
212
|
+
* will actually succeed.
|
|
213
|
+
*/
|
|
214
|
+
readonly isStoppable: boolean;
|
|
215
|
+
/**
|
|
216
|
+
* Stop the active execution, with progressive escalation: the first call
|
|
217
|
+
* gracefully cancels; a repeat call (because the run is still winding down)
|
|
218
|
+
* forcefully terminates. No-op when nothing is {@link isStoppable}.
|
|
219
|
+
*/
|
|
220
|
+
readonly stop: (reason?: string) => Promise<void>;
|
|
221
|
+
/** `true` while a stop (cancel/terminate) request is in flight. */
|
|
222
|
+
readonly isStopping: boolean;
|
|
223
|
+
/** Error from the last stop attempt, or `null` when healthy. */
|
|
224
|
+
readonly stopError: Error | null;
|
|
225
|
+
|
|
177
226
|
/** `true` while the session or execution list is loading. */
|
|
178
227
|
readonly isLoading: boolean;
|
|
179
228
|
/** Error from session or execution list loading, or `null` when healthy. */
|
|
@@ -185,6 +234,20 @@ export interface UseSessionConversationReturn {
|
|
|
185
234
|
* surface a subtle "Reconnecting…" hint rather than an error banner.
|
|
186
235
|
*/
|
|
187
236
|
readonly isReconnecting: boolean;
|
|
237
|
+
/**
|
|
238
|
+
* `true` when the stream opened but never delivered a first snapshot within
|
|
239
|
+
* the watchdog window (even after a silent retry) — the agent hasn't started.
|
|
240
|
+
* Distinct from `streamError`: nothing threw, the stream is simply silent.
|
|
241
|
+
* Surface an actionable "the agent hasn't started — Retry" banner wired to
|
|
242
|
+
* {@link reconnectStream}.
|
|
243
|
+
*/
|
|
244
|
+
readonly connectTimedOut: boolean;
|
|
245
|
+
/**
|
|
246
|
+
* `true` when a live, non-terminal stream has been silent past the slow
|
|
247
|
+
* threshold. Purely informational ("still working — taking longer than
|
|
248
|
+
* usual"); cleared by the next update. Never an error.
|
|
249
|
+
*/
|
|
250
|
+
readonly isSlow: boolean;
|
|
188
251
|
/** Error from the execution stream, or `null` when healthy or reconnecting. */
|
|
189
252
|
readonly streamError: Error | null;
|
|
190
253
|
/** Reset the stream error and re-establish the execution stream subscription. */
|
|
@@ -250,16 +313,24 @@ export function useSessionConversation(
|
|
|
250
313
|
error: sessionError,
|
|
251
314
|
refetch: refetchSession,
|
|
252
315
|
} = useSession(sessionId);
|
|
316
|
+
// Bounded re-discovery (see REDISCOVERY_POLL_INTERVAL_MS). The gate depends on
|
|
317
|
+
// the stream below, so the decision is synced into state via an effect and fed
|
|
318
|
+
// back here on the next render — a one-frame lag that is immaterial at 5s.
|
|
319
|
+
const [rediscoveryActive, setRediscoveryActive] = useState(false);
|
|
253
320
|
const {
|
|
254
321
|
executions,
|
|
255
322
|
isLoading: executionsLoading,
|
|
256
323
|
error: executionsError,
|
|
257
324
|
refetch,
|
|
258
|
-
} = useSessionExecutions(sessionId
|
|
325
|
+
} = useSessionExecutions(sessionId, {
|
|
326
|
+
refetchInterval: rediscoveryActive ? REDISCOVERY_POLL_INTERVAL_MS : false,
|
|
327
|
+
// Re-list on app-relaunch / tab refocus so an execution that appeared while
|
|
328
|
+
// backgrounded is picked up without the user having to act.
|
|
329
|
+
refetchOnWindowFocus: true,
|
|
330
|
+
});
|
|
259
331
|
const {
|
|
260
332
|
create,
|
|
261
333
|
isCreating,
|
|
262
|
-
error: createError,
|
|
263
334
|
clearError: clearCreateError,
|
|
264
335
|
} = useCreateAgentExecution();
|
|
265
336
|
const { update: updateSession } = useUpdateSession();
|
|
@@ -280,6 +351,14 @@ export function useSessionConversation(
|
|
|
280
351
|
const [pendingUserMessage, setPendingUserMessage] = useState<string | null>(
|
|
281
352
|
null,
|
|
282
353
|
);
|
|
354
|
+
// Dedicated send-failure state, distinct from the create hook's internal
|
|
355
|
+
// error so it can also cover the session.update() path. The last send's
|
|
356
|
+
// arguments are captured for an exact retry.
|
|
357
|
+
const [sendError, setSendError] = useState<Error | null>(null);
|
|
358
|
+
const lastSendRef = useRef<{
|
|
359
|
+
message: string;
|
|
360
|
+
options?: SendFollowUpOptions;
|
|
361
|
+
} | null>(null);
|
|
283
362
|
|
|
284
363
|
const listActiveId = useMemo(() => {
|
|
285
364
|
for (let i = executions.length - 1; i >= 0; i--) {
|
|
@@ -302,6 +381,25 @@ export function useSessionConversation(
|
|
|
302
381
|
store: conversationStore,
|
|
303
382
|
});
|
|
304
383
|
|
|
384
|
+
// Re-discovery gate. Poll only while the live stream cannot carry us:
|
|
385
|
+
// • a fresh session whose first execution is created but not yet listed
|
|
386
|
+
// (`executions.length === 0`) — the race this fix targets,
|
|
387
|
+
// • a silent connect-timeout, or an exhausted stream error.
|
|
388
|
+
// Never while the stream is healthy (`isStreaming`) or the active execution
|
|
389
|
+
// has reached a terminal phase — the live feed is then the source of truth.
|
|
390
|
+
const streamTerminal =
|
|
391
|
+
activeExecutionId !== null && isTerminalPhase(stream.phase);
|
|
392
|
+
const needsRediscovery =
|
|
393
|
+
!stream.isStreaming &&
|
|
394
|
+
!streamTerminal &&
|
|
395
|
+
((activeExecutionId === null && executions.length === 0) ||
|
|
396
|
+
stream.connectTimedOut ||
|
|
397
|
+
stream.error !== null);
|
|
398
|
+
|
|
399
|
+
useEffect(() => {
|
|
400
|
+
setRediscoveryActive(needsRediscovery);
|
|
401
|
+
}, [needsRediscovery]);
|
|
402
|
+
|
|
305
403
|
// Clear pendingExecutionId once the execution appears in the fetched list
|
|
306
404
|
useEffect(() => {
|
|
307
405
|
if (
|
|
@@ -312,12 +410,17 @@ export function useSessionConversation(
|
|
|
312
410
|
}
|
|
313
411
|
}, [pendingExecutionId, executions]);
|
|
314
412
|
|
|
315
|
-
// Clear optimistic message
|
|
413
|
+
// Clear the optimistic message — and any stale send error — once the stream
|
|
414
|
+
// delivers a real snapshot. This also handles recovery: if a failed send's
|
|
415
|
+
// execution is later re-discovered and streams, the failed turn resolves into
|
|
416
|
+
// the live one instead of lingering. (At send time the composer is only
|
|
417
|
+
// enabled when no execution is active, so a *fresh* failure cannot be cleared
|
|
418
|
+
// here prematurely — `stream.execution` is null then.)
|
|
316
419
|
useEffect(() => {
|
|
317
|
-
if (
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
}, [pendingUserMessage, stream.execution]);
|
|
420
|
+
if (!stream.execution) return;
|
|
421
|
+
if (pendingUserMessage) setPendingUserMessage(null);
|
|
422
|
+
if (sendError) setSendError(null);
|
|
423
|
+
}, [pendingUserMessage, sendError, stream.execution]);
|
|
321
424
|
|
|
322
425
|
// Refetch executions when stream reaches a terminal phase so the
|
|
323
426
|
// fetched list reflects the completed status and listActiveId clears.
|
|
@@ -351,6 +454,28 @@ export function useSessionConversation(
|
|
|
351
454
|
return stream.phase;
|
|
352
455
|
}, [activeExecutionId, stream.phase]);
|
|
353
456
|
|
|
457
|
+
// Stop is only valid in phases the backend cancels/terminates from
|
|
458
|
+
// (PENDING / IN_PROGRESS). Other non-terminal phases (e.g.
|
|
459
|
+
// WAITING_FOR_APPROVAL) are handled by their own control surface.
|
|
460
|
+
const isStoppable =
|
|
461
|
+
activePhase === ExecutionPhase.EXECUTION_PENDING ||
|
|
462
|
+
activePhase === ExecutionPhase.EXECUTION_IN_PROGRESS;
|
|
463
|
+
|
|
464
|
+
const stopActions = useAgentExecutionActions(activeExecutionId, {
|
|
465
|
+
// The cancel/terminate also broadcasts the new phase over the stream, but
|
|
466
|
+
// refetch is the belt-and-suspenders that clears the active id even if the
|
|
467
|
+
// stream has already ended — mirrors the workflow viewer's onSuccess.
|
|
468
|
+
onSuccess: refetch,
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
const stop = useCallback(
|
|
472
|
+
async (reason?: string): Promise<void> => {
|
|
473
|
+
if (!isStoppable) return;
|
|
474
|
+
await stopActions.stop(reason);
|
|
475
|
+
},
|
|
476
|
+
[isStoppable, stopActions.stop],
|
|
477
|
+
);
|
|
478
|
+
|
|
354
479
|
const canSendFollowUp = !isCreating && activeExecutionId === null;
|
|
355
480
|
|
|
356
481
|
const workspaceEntries = useMemo<readonly ProtoWorkspaceEntry[]>(
|
|
@@ -372,6 +497,9 @@ export function useSessionConversation(
|
|
|
372
497
|
async (message: string, options?: SendFollowUpOptions): Promise<void> => {
|
|
373
498
|
if (!sessionId || !session) return;
|
|
374
499
|
|
|
500
|
+
// Capture for retry and clear any prior failure before the new attempt.
|
|
501
|
+
lastSendRef.current = { message, options };
|
|
502
|
+
setSendError(null);
|
|
375
503
|
setPendingUserMessage(message);
|
|
376
504
|
|
|
377
505
|
try {
|
|
@@ -411,7 +539,10 @@ export function useSessionConversation(
|
|
|
411
539
|
setPendingExecutionId(result.executionId);
|
|
412
540
|
refetch();
|
|
413
541
|
} catch (err) {
|
|
414
|
-
|
|
542
|
+
// Surface the failure and KEEP the user's message visible (do not clear
|
|
543
|
+
// pendingUserMessage) so the turn renders as failed-with-retry instead
|
|
544
|
+
// of vanishing. Covers both the update() and create() paths.
|
|
545
|
+
setSendError(toError(err));
|
|
415
546
|
if (process.env.NODE_ENV !== "production") {
|
|
416
547
|
console.error("[useSessionConversation] sendFollowUp failed:", err);
|
|
417
548
|
}
|
|
@@ -420,6 +551,16 @@ export function useSessionConversation(
|
|
|
420
551
|
[sessionId, session, org, stigmer, create, updateSession, refetch, refetchSession],
|
|
421
552
|
);
|
|
422
553
|
|
|
554
|
+
const retryLastSend = useCallback(() => {
|
|
555
|
+
const last = lastSendRef.current;
|
|
556
|
+
if (last) void sendFollowUp(last.message, last.options);
|
|
557
|
+
}, [sendFollowUp]);
|
|
558
|
+
|
|
559
|
+
const clearSendError = useCallback(() => {
|
|
560
|
+
setSendError(null);
|
|
561
|
+
clearCreateError();
|
|
562
|
+
}, [clearCreateError]);
|
|
563
|
+
|
|
423
564
|
const pendingApprovals = useMemo<readonly PendingApproval[]>(
|
|
424
565
|
() => activeStreamExecution?.status?.pendingApprovals ?? [],
|
|
425
566
|
[activeStreamExecution],
|
|
@@ -451,8 +592,9 @@ export function useSessionConversation(
|
|
|
451
592
|
sendFollowUp,
|
|
452
593
|
canSendFollowUp,
|
|
453
594
|
isSending: isCreating,
|
|
454
|
-
sendError
|
|
455
|
-
clearSendError
|
|
595
|
+
sendError,
|
|
596
|
+
clearSendError,
|
|
597
|
+
retryLastSend,
|
|
456
598
|
|
|
457
599
|
pendingUserMessage,
|
|
458
600
|
|
|
@@ -466,10 +608,17 @@ export function useSessionConversation(
|
|
|
466
608
|
approvalError,
|
|
467
609
|
clearApprovalError,
|
|
468
610
|
|
|
611
|
+
isStoppable,
|
|
612
|
+
stop,
|
|
613
|
+
isStopping: stopActions.isSubmitting,
|
|
614
|
+
stopError: stopActions.error,
|
|
615
|
+
|
|
469
616
|
isLoading,
|
|
470
617
|
loadError,
|
|
471
618
|
|
|
472
619
|
isReconnecting: stream.isReconnecting,
|
|
620
|
+
connectTimedOut: stream.connectTimedOut,
|
|
621
|
+
isSlow: stream.isSlow,
|
|
473
622
|
streamError: stream.error,
|
|
474
623
|
reconnectStream: stream.reconnect,
|
|
475
624
|
};
|
|
@@ -6,6 +6,23 @@ import { ListAgentExecutionsBySessionRequestSchema } from "@stigmer/protos/ai/st
|
|
|
6
6
|
import { useStigmer } from "../hooks";
|
|
7
7
|
import { useFetch } from "../internal/useFetch";
|
|
8
8
|
|
|
9
|
+
/** Options for {@link useSessionExecutions}. */
|
|
10
|
+
export interface UseSessionExecutionsOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Poll interval in milliseconds for re-listing the session's executions.
|
|
13
|
+
* Used by the conversation loop to re-discover a created-but-not-yet-listed
|
|
14
|
+
* execution. Pass `false` (the default) to disable polling and rely on the
|
|
15
|
+
* live stream plus imperative {@link UseSessionExecutionsReturn.refetch}.
|
|
16
|
+
*/
|
|
17
|
+
readonly refetchInterval?: number | false;
|
|
18
|
+
/**
|
|
19
|
+
* Re-list when the window regains focus / the tab becomes visible — covers
|
|
20
|
+
* the app-relaunch case where an execution may have appeared while
|
|
21
|
+
* backgrounded. Defaults to `false`.
|
|
22
|
+
*/
|
|
23
|
+
readonly refetchOnWindowFocus?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
9
26
|
/** Return value of {@link useSessionExecutions}. */
|
|
10
27
|
export interface UseSessionExecutionsReturn {
|
|
11
28
|
/** All executions for the session, empty while loading or on error. */
|
|
@@ -56,6 +73,7 @@ export interface UseSessionExecutionsReturn {
|
|
|
56
73
|
*/
|
|
57
74
|
export function useSessionExecutions(
|
|
58
75
|
sessionId: string | null,
|
|
76
|
+
options?: UseSessionExecutionsOptions,
|
|
59
77
|
): UseSessionExecutionsReturn {
|
|
60
78
|
const stigmer = useStigmer();
|
|
61
79
|
|
|
@@ -73,7 +91,11 @@ export function useSessionExecutions(
|
|
|
73
91
|
: null,
|
|
74
92
|
[sessionId, stigmer],
|
|
75
93
|
[] as AgentExecution[],
|
|
76
|
-
{
|
|
94
|
+
{
|
|
95
|
+
cacheKey: sessionId ? `session-executions:${sessionId}` : undefined,
|
|
96
|
+
refetchInterval: options?.refetchInterval,
|
|
97
|
+
refetchOnWindowFocus: options?.refetchOnWindowFocus,
|
|
98
|
+
},
|
|
77
99
|
);
|
|
78
100
|
|
|
79
101
|
return { executions, isLoading, isRefetching, error, refetch };
|