@stigmer/react 3.0.8-dev.20260613041848 → 3.0.8-dev.20260613051837
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/execution/ExecutionProgress.d.ts.map +1 -1
- package/execution/ExecutionProgress.js +5 -1
- package/execution/ExecutionProgress.js.map +1 -1
- package/execution/MessageThread.d.ts +32 -3
- package/execution/MessageThread.d.ts.map +1 -1
- package/execution/MessageThread.js +59 -10
- package/execution/MessageThread.js.map +1 -1
- 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/internal/VirtualizedThread.d.ts +3 -1
- package/internal/VirtualizedThread.d.ts.map +1 -1
- package/internal/VirtualizedThread.js +4 -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 +23 -1
- package/session/SessionViewer.js.map +1 -1
- package/session/useSessionConversation.d.ts +34 -3
- package/session/useSessionConversation.d.ts.map +1 -1
- package/session/useSessionConversation.js +73 -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/execution/ExecutionProgress.tsx +12 -0
- package/src/execution/MessageThread.tsx +174 -5
- package/src/execution/__tests__/MessageThread.test.tsx +64 -0
- package/src/execution/__tests__/useExecutionStream.test.tsx +95 -0
- package/src/execution/useExecutionStream.ts +80 -4
- package/src/internal/VirtualizedThread.tsx +7 -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 +62 -0
- package/src/session/__tests__/useSessionConversation.test.tsx +53 -0
- package/src/session/useSessionConversation.ts +113 -14
- package/src/session/useSessionExecutions.ts +23 -1
- package/styles.css +1 -1
|
@@ -114,11 +114,28 @@ export interface UseSessionConversationReturn {
|
|
|
114
114
|
readonly canSendFollowUp: boolean;
|
|
115
115
|
/** True during the create RPC call (between submit and execution ID). */
|
|
116
116
|
readonly isSending: boolean;
|
|
117
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* Error from the last `sendFollowUp` attempt, or `null`.
|
|
119
|
+
*
|
|
120
|
+
* Covers **both** failing paths — the optional `session.update()` and the
|
|
121
|
+
* `create()` RPC — so a follow-up never fails silently. When set, the user's
|
|
122
|
+
* message is preserved (see {@link pendingUserMessage}) and can be re-sent
|
|
123
|
+
* via {@link retryLastSend}.
|
|
124
|
+
*/
|
|
118
125
|
readonly sendError: Error | null;
|
|
119
|
-
/** Reset `sendError` to `null
|
|
126
|
+
/** Reset `sendError` to `null` (keeps the preserved pending message). */
|
|
120
127
|
readonly clearSendError: () => void;
|
|
121
|
-
/**
|
|
128
|
+
/**
|
|
129
|
+
* Re-send the most recent `sendFollowUp` (same message and options). No-op
|
|
130
|
+
* when nothing has been sent yet. Use as the "Retry" affordance on a failed
|
|
131
|
+
* turn; clears {@link sendError} for the new attempt.
|
|
132
|
+
*/
|
|
133
|
+
readonly retryLastSend: () => void;
|
|
134
|
+
/**
|
|
135
|
+
* The user's message text, shown in the thread before the stream delivers it.
|
|
136
|
+
* Retained when a send fails so the typed message is never lost — pair with
|
|
137
|
+
* {@link sendError} to render the turn as failed with a retry control.
|
|
138
|
+
*/
|
|
122
139
|
readonly pendingUserMessage: string | null;
|
|
123
140
|
/** Current workspace entries from the session spec. Empty array when session is not loaded. */
|
|
124
141
|
readonly workspaceEntries: readonly ProtoWorkspaceEntry[];
|
|
@@ -146,6 +163,20 @@ export interface UseSessionConversationReturn {
|
|
|
146
163
|
* surface a subtle "Reconnecting…" hint rather than an error banner.
|
|
147
164
|
*/
|
|
148
165
|
readonly isReconnecting: boolean;
|
|
166
|
+
/**
|
|
167
|
+
* `true` when the stream opened but never delivered a first snapshot within
|
|
168
|
+
* the watchdog window (even after a silent retry) — the agent hasn't started.
|
|
169
|
+
* Distinct from `streamError`: nothing threw, the stream is simply silent.
|
|
170
|
+
* Surface an actionable "the agent hasn't started — Retry" banner wired to
|
|
171
|
+
* {@link reconnectStream}.
|
|
172
|
+
*/
|
|
173
|
+
readonly connectTimedOut: boolean;
|
|
174
|
+
/**
|
|
175
|
+
* `true` when a live, non-terminal stream has been silent past the slow
|
|
176
|
+
* threshold. Purely informational ("still working — taking longer than
|
|
177
|
+
* usual"); cleared by the next update. Never an error.
|
|
178
|
+
*/
|
|
179
|
+
readonly isSlow: boolean;
|
|
149
180
|
/** Error from the execution stream, or `null` when healthy or reconnecting. */
|
|
150
181
|
readonly streamError: Error | null;
|
|
151
182
|
/** Reset the stream error and re-establish the execution stream subscription. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSessionConversation.d.ts","sourceRoot":"","sources":["../../src/session/useSessionConversation.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6DAA6D,CAAC;AAClG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kEAAkE,CAAC;AACxG,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,8DAA8D,CAAC;AAC9G,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sDAAsD,CAAC;AACpF,OAAO,KAAK,EAAE,cAAc,IAAI,mBAAmB,EAAE,MAAM,qDAAqD,CAAC;AACjH,OAAO,KAAK,EAAE,cAAc,IAAI,mBAAmB,EAAE,MAAM,4DAA4D,CAAC;AACxH,OAAO,KAAK,EAAE,oBAAoB,IAAI,yBAAyB,EAAE,MAAM,sDAAsD,CAAC;AAC9H,OAAO,KAAK,EACV,eAAe,EACf,WAAW,EACX,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACpB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"useSessionConversation.d.ts","sourceRoot":"","sources":["../../src/session/useSessionConversation.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { isTerminalPhase } from "../execution/execution-phases";
|
|
4
4
|
import { useStigmer } from "../hooks";
|
|
5
|
+
import { toError } from "../internal/toError";
|
|
5
6
|
import { useConversationStoreRef } from "../internal/store";
|
|
6
7
|
import { useCreateAgentExecution } from "../execution/useCreateAgentExecution";
|
|
7
8
|
import { useExecutionStream } from "../execution/useExecutionStream";
|
|
@@ -11,6 +12,13 @@ import { useSessionExecutions } from "./useSessionExecutions";
|
|
|
11
12
|
import { useUpdateSession } from "./useUpdateSession";
|
|
12
13
|
import { useLocalSessionWorker } from "./useLocalSessionWorker";
|
|
13
14
|
import { specWorkspaceToInput, specMcpUsagesToInput, specSkillRefsToInput, } from "./session-spec-converters";
|
|
15
|
+
/**
|
|
16
|
+
* Cadence for re-discovering the session's executions while the live stream
|
|
17
|
+
* cannot be relied on (a created-but-not-yet-listed execution, a silent
|
|
18
|
+
* connect-timeout, or an exhausted stream error). Disabled the instant the
|
|
19
|
+
* stream is healthy or terminal, so this never competes with the live feed.
|
|
20
|
+
*/
|
|
21
|
+
const REDISCOVERY_POLL_INTERVAL_MS = 5_000;
|
|
14
22
|
/**
|
|
15
23
|
* Behavior hook that encapsulates the full conversation lifecycle for
|
|
16
24
|
* a session: loading data, streaming the active execution, submitting
|
|
@@ -62,8 +70,17 @@ import { specWorkspaceToInput, specMcpUsagesToInput, specSkillRefsToInput, } fro
|
|
|
62
70
|
export function useSessionConversation(sessionId, org) {
|
|
63
71
|
const stigmer = useStigmer();
|
|
64
72
|
const { session, isLoading: sessionLoading, error: sessionError, refetch: refetchSession, } = useSession(sessionId);
|
|
65
|
-
|
|
66
|
-
|
|
73
|
+
// Bounded re-discovery (see REDISCOVERY_POLL_INTERVAL_MS). The gate depends on
|
|
74
|
+
// the stream below, so the decision is synced into state via an effect and fed
|
|
75
|
+
// back here on the next render — a one-frame lag that is immaterial at 5s.
|
|
76
|
+
const [rediscoveryActive, setRediscoveryActive] = useState(false);
|
|
77
|
+
const { executions, isLoading: executionsLoading, error: executionsError, refetch, } = useSessionExecutions(sessionId, {
|
|
78
|
+
refetchInterval: rediscoveryActive ? REDISCOVERY_POLL_INTERVAL_MS : false,
|
|
79
|
+
// Re-list on app-relaunch / tab refocus so an execution that appeared while
|
|
80
|
+
// backgrounded is picked up without the user having to act.
|
|
81
|
+
refetchOnWindowFocus: true,
|
|
82
|
+
});
|
|
83
|
+
const { create, isCreating, clearError: clearCreateError, } = useCreateAgentExecution();
|
|
67
84
|
const { update: updateSession } = useUpdateSession();
|
|
68
85
|
const { submitApproval: rawSubmitApproval, submittingToolCallIds, error: approvalError, clearError: clearApprovalError, } = useSubmitApproval();
|
|
69
86
|
// Local execution: attach the session's runner worker while it is open and
|
|
@@ -71,6 +88,11 @@ export function useSessionConversation(sessionId, org) {
|
|
|
71
88
|
useLocalSessionWorker(sessionId, session);
|
|
72
89
|
const [pendingExecutionId, setPendingExecutionId] = useState(null);
|
|
73
90
|
const [pendingUserMessage, setPendingUserMessage] = useState(null);
|
|
91
|
+
// Dedicated send-failure state, distinct from the create hook's internal
|
|
92
|
+
// error so it can also cover the session.update() path. The last send's
|
|
93
|
+
// arguments are captured for an exact retry.
|
|
94
|
+
const [sendError, setSendError] = useState(null);
|
|
95
|
+
const lastSendRef = useRef(null);
|
|
74
96
|
const listActiveId = useMemo(() => {
|
|
75
97
|
for (let i = executions.length - 1; i >= 0; i--) {
|
|
76
98
|
const phase = executions[i].status?.phase;
|
|
@@ -89,6 +111,21 @@ export function useSessionConversation(sessionId, org) {
|
|
|
89
111
|
const stream = useExecutionStream(activeExecutionId, {
|
|
90
112
|
store: conversationStore,
|
|
91
113
|
});
|
|
114
|
+
// Re-discovery gate. Poll only while the live stream cannot carry us:
|
|
115
|
+
// • a fresh session whose first execution is created but not yet listed
|
|
116
|
+
// (`executions.length === 0`) — the race this fix targets,
|
|
117
|
+
// • a silent connect-timeout, or an exhausted stream error.
|
|
118
|
+
// Never while the stream is healthy (`isStreaming`) or the active execution
|
|
119
|
+
// has reached a terminal phase — the live feed is then the source of truth.
|
|
120
|
+
const streamTerminal = activeExecutionId !== null && isTerminalPhase(stream.phase);
|
|
121
|
+
const needsRediscovery = !stream.isStreaming &&
|
|
122
|
+
!streamTerminal &&
|
|
123
|
+
((activeExecutionId === null && executions.length === 0) ||
|
|
124
|
+
stream.connectTimedOut ||
|
|
125
|
+
stream.error !== null);
|
|
126
|
+
useEffect(() => {
|
|
127
|
+
setRediscoveryActive(needsRediscovery);
|
|
128
|
+
}, [needsRediscovery]);
|
|
92
129
|
// Clear pendingExecutionId once the execution appears in the fetched list
|
|
93
130
|
useEffect(() => {
|
|
94
131
|
if (pendingExecutionId &&
|
|
@@ -96,12 +133,20 @@ export function useSessionConversation(sessionId, org) {
|
|
|
96
133
|
setPendingExecutionId(null);
|
|
97
134
|
}
|
|
98
135
|
}, [pendingExecutionId, executions]);
|
|
99
|
-
// Clear optimistic message
|
|
136
|
+
// Clear the optimistic message — and any stale send error — once the stream
|
|
137
|
+
// delivers a real snapshot. This also handles recovery: if a failed send's
|
|
138
|
+
// execution is later re-discovered and streams, the failed turn resolves into
|
|
139
|
+
// the live one instead of lingering. (At send time the composer is only
|
|
140
|
+
// enabled when no execution is active, so a *fresh* failure cannot be cleared
|
|
141
|
+
// here prematurely — `stream.execution` is null then.)
|
|
100
142
|
useEffect(() => {
|
|
101
|
-
if (
|
|
143
|
+
if (!stream.execution)
|
|
144
|
+
return;
|
|
145
|
+
if (pendingUserMessage)
|
|
102
146
|
setPendingUserMessage(null);
|
|
103
|
-
|
|
104
|
-
|
|
147
|
+
if (sendError)
|
|
148
|
+
setSendError(null);
|
|
149
|
+
}, [pendingUserMessage, sendError, stream.execution]);
|
|
105
150
|
// Refetch executions when stream reaches a terminal phase so the
|
|
106
151
|
// fetched list reflects the completed status and listActiveId clears.
|
|
107
152
|
useEffect(() => {
|
|
@@ -132,6 +177,9 @@ export function useSessionConversation(sessionId, org) {
|
|
|
132
177
|
const sendFollowUp = useCallback(async (message, options) => {
|
|
133
178
|
if (!sessionId || !session)
|
|
134
179
|
return;
|
|
180
|
+
// Capture for retry and clear any prior failure before the new attempt.
|
|
181
|
+
lastSendRef.current = { message, options };
|
|
182
|
+
setSendError(null);
|
|
135
183
|
setPendingUserMessage(message);
|
|
136
184
|
try {
|
|
137
185
|
const needsSessionUpdate = options?.agentInstanceId !== undefined ||
|
|
@@ -166,12 +214,24 @@ export function useSessionConversation(sessionId, org) {
|
|
|
166
214
|
refetch();
|
|
167
215
|
}
|
|
168
216
|
catch (err) {
|
|
169
|
-
|
|
217
|
+
// Surface the failure and KEEP the user's message visible (do not clear
|
|
218
|
+
// pendingUserMessage) so the turn renders as failed-with-retry instead
|
|
219
|
+
// of vanishing. Covers both the update() and create() paths.
|
|
220
|
+
setSendError(toError(err));
|
|
170
221
|
if (process.env.NODE_ENV !== "production") {
|
|
171
222
|
console.error("[useSessionConversation] sendFollowUp failed:", err);
|
|
172
223
|
}
|
|
173
224
|
}
|
|
174
225
|
}, [sessionId, session, org, stigmer, create, updateSession, refetch, refetchSession]);
|
|
226
|
+
const retryLastSend = useCallback(() => {
|
|
227
|
+
const last = lastSendRef.current;
|
|
228
|
+
if (last)
|
|
229
|
+
void sendFollowUp(last.message, last.options);
|
|
230
|
+
}, [sendFollowUp]);
|
|
231
|
+
const clearSendError = useCallback(() => {
|
|
232
|
+
setSendError(null);
|
|
233
|
+
clearCreateError();
|
|
234
|
+
}, [clearCreateError]);
|
|
175
235
|
const pendingApprovals = useMemo(() => activeStreamExecution?.status?.pendingApprovals ?? [], [activeStreamExecution]);
|
|
176
236
|
const submitApproval = useCallback(async (toolCallId, action, comment) => {
|
|
177
237
|
if (!activeExecutionId)
|
|
@@ -190,8 +250,9 @@ export function useSessionConversation(sessionId, org) {
|
|
|
190
250
|
sendFollowUp,
|
|
191
251
|
canSendFollowUp,
|
|
192
252
|
isSending: isCreating,
|
|
193
|
-
sendError
|
|
194
|
-
clearSendError
|
|
253
|
+
sendError,
|
|
254
|
+
clearSendError,
|
|
255
|
+
retryLastSend,
|
|
195
256
|
pendingUserMessage,
|
|
196
257
|
workspaceEntries,
|
|
197
258
|
mcpServerUsages,
|
|
@@ -204,6 +265,8 @@ export function useSessionConversation(sessionId, org) {
|
|
|
204
265
|
isLoading,
|
|
205
266
|
loadError,
|
|
206
267
|
isReconnecting: stream.isReconnecting,
|
|
268
|
+
connectTimedOut: stream.connectTimedOut,
|
|
269
|
+
isSlow: stream.isSlow,
|
|
207
270
|
streamError: stream.error,
|
|
208
271
|
reconnectStream: stream.reconnect,
|
|
209
272
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSessionConversation.js","sourceRoot":"","sources":["../../src/session/useSessionConversation.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"useSessionConversation.js","sourceRoot":"","sources":["../../src/session/useSessionConversation.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
import type { AgentExecution } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
2
|
+
/** Options for {@link useSessionExecutions}. */
|
|
3
|
+
export interface UseSessionExecutionsOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Poll interval in milliseconds for re-listing the session's executions.
|
|
6
|
+
* Used by the conversation loop to re-discover a created-but-not-yet-listed
|
|
7
|
+
* execution. Pass `false` (the default) to disable polling and rely on the
|
|
8
|
+
* live stream plus imperative {@link UseSessionExecutionsReturn.refetch}.
|
|
9
|
+
*/
|
|
10
|
+
readonly refetchInterval?: number | false;
|
|
11
|
+
/**
|
|
12
|
+
* Re-list when the window regains focus / the tab becomes visible — covers
|
|
13
|
+
* the app-relaunch case where an execution may have appeared while
|
|
14
|
+
* backgrounded. Defaults to `false`.
|
|
15
|
+
*/
|
|
16
|
+
readonly refetchOnWindowFocus?: boolean;
|
|
17
|
+
}
|
|
2
18
|
/** Return value of {@link useSessionExecutions}. */
|
|
3
19
|
export interface UseSessionExecutionsReturn {
|
|
4
20
|
/** All executions for the session, empty while loading or on error. */
|
|
@@ -46,5 +62,5 @@ export interface UseSessionExecutionsReturn {
|
|
|
46
62
|
* const { executions } = useSessionExecutions(sessionId ?? null);
|
|
47
63
|
* ```
|
|
48
64
|
*/
|
|
49
|
-
export declare function useSessionExecutions(sessionId: string | null): UseSessionExecutionsReturn;
|
|
65
|
+
export declare function useSessionExecutions(sessionId: string | null, options?: UseSessionExecutionsOptions): UseSessionExecutionsReturn;
|
|
50
66
|
//# sourceMappingURL=useSessionExecutions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSessionExecutions.d.ts","sourceRoot":"","sources":["../../src/session/useSessionExecutions.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6DAA6D,CAAC;AAKlG,oDAAoD;AACpD,MAAM,WAAW,0BAA0B;IACzC,uEAAuE;IACvE,QAAQ,CAAC,UAAU,EAAE,SAAS,cAAc,EAAE,CAAC;IAC/C,mDAAmD;IACnD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,sDAAsD;IACtD,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,kEAAkE;IAClE,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAC7B,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"useSessionExecutions.d.ts","sourceRoot":"","sources":["../../src/session/useSessionExecutions.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6DAA6D,CAAC;AAKlG,gDAAgD;AAChD,MAAM,WAAW,2BAA2B;IAC1C;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC;CACzC;AAED,oDAAoD;AACpD,MAAM,WAAW,0BAA0B;IACzC,uEAAuE;IACvE,QAAQ,CAAC,UAAU,EAAE,SAAS,cAAc,EAAE,CAAC;IAC/C,mDAAmD;IACnD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,sDAAsD;IACtD,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,kEAAkE;IAClE,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAC7B,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,OAAO,CAAC,EAAE,2BAA2B,GACpC,0BAA0B,CAyB5B"}
|
|
@@ -37,7 +37,7 @@ import { useFetch } from "../internal/useFetch";
|
|
|
37
37
|
* const { executions } = useSessionExecutions(sessionId ?? null);
|
|
38
38
|
* ```
|
|
39
39
|
*/
|
|
40
|
-
export function useSessionExecutions(sessionId) {
|
|
40
|
+
export function useSessionExecutions(sessionId, options) {
|
|
41
41
|
const stigmer = useStigmer();
|
|
42
42
|
const { data: executions, isLoading, isRefetching, error, refetch } = useFetch(sessionId
|
|
43
43
|
? () => stigmer.agentExecution
|
|
@@ -46,7 +46,11 @@ export function useSessionExecutions(sessionId) {
|
|
|
46
46
|
pageSize: 100,
|
|
47
47
|
}))
|
|
48
48
|
.then((result) => result.entries)
|
|
49
|
-
: null, [sessionId, stigmer], [], {
|
|
49
|
+
: null, [sessionId, stigmer], [], {
|
|
50
|
+
cacheKey: sessionId ? `session-executions:${sessionId}` : undefined,
|
|
51
|
+
refetchInterval: options?.refetchInterval,
|
|
52
|
+
refetchOnWindowFocus: options?.refetchOnWindowFocus,
|
|
53
|
+
});
|
|
50
54
|
return { executions, isLoading, isRefetching, error, refetch };
|
|
51
55
|
}
|
|
52
56
|
//# sourceMappingURL=useSessionExecutions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSessionExecutions.js","sourceRoot":"","sources":["../../src/session/useSessionExecutions.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,OAAO,EAAE,yCAAyC,EAAE,MAAM,4DAA4D,CAAC;AACvH,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"useSessionExecutions.js","sourceRoot":"","sources":["../../src/session/useSessionExecutions.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,OAAO,EAAE,yCAAyC,EAAE,MAAM,4DAA4D,CAAC;AACvH,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAiChD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,UAAU,oBAAoB,CAClC,SAAwB,EACxB,OAAqC;IAErC,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAE7B,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,QAAQ,CAC5E,SAAS;QACP,CAAC,CAAC,GAAG,EAAE,CACH,OAAO,CAAC,cAAc;aACnB,aAAa,CACZ,MAAM,CAAC,yCAAyC,EAAE;YAChD,SAAS;YACT,QAAQ,EAAE,GAAG;SACd,CAAC,CACH;aACA,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC;QACvC,CAAC,CAAC,IAAI,EACR,CAAC,SAAS,EAAE,OAAO,CAAC,EACpB,EAAsB,EACtB;QACE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,sBAAsB,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS;QACnE,eAAe,EAAE,OAAO,EAAE,eAAe;QACzC,oBAAoB,EAAE,OAAO,EAAE,oBAAoB;KACpD,CACF,CAAC;IAEF,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AACjE,CAAC"}
|
|
@@ -47,6 +47,10 @@ export function ExecutionProgress({
|
|
|
47
47
|
if (phase === undefined) return null;
|
|
48
48
|
|
|
49
49
|
const todos = execution.status?.todos;
|
|
50
|
+
// Populated by the server only on EXECUTION_FAILED — surface it next to the
|
|
51
|
+
// badge so this widget explains a failure rather than showing a bare phase
|
|
52
|
+
// (consistent with the failure reason in the message thread).
|
|
53
|
+
const error = execution.status?.error;
|
|
50
54
|
|
|
51
55
|
return (
|
|
52
56
|
<div
|
|
@@ -55,6 +59,14 @@ export function ExecutionProgress({
|
|
|
55
59
|
aria-label="Execution progress"
|
|
56
60
|
>
|
|
57
61
|
<ExecutionPhaseBadge phase={phase} />
|
|
62
|
+
{error && (
|
|
63
|
+
<p
|
|
64
|
+
role="alert"
|
|
65
|
+
className="text-xs whitespace-pre-wrap break-words text-destructive"
|
|
66
|
+
>
|
|
67
|
+
{error}
|
|
68
|
+
</p>
|
|
69
|
+
)}
|
|
58
70
|
{todos && <TodoList todos={todos} />}
|
|
59
71
|
</div>
|
|
60
72
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { lazy, memo, Suspense, useCallback, useMemo } from "react";
|
|
3
|
+
import { lazy, memo, Suspense, useCallback, useMemo, useState } from "react";
|
|
4
4
|
import { create } from "@bufbuild/protobuf";
|
|
5
5
|
import type { AgentExecution } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
6
6
|
import type { AgentMessage, ToolCall } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
@@ -60,6 +60,27 @@ export interface MessageThreadProps {
|
|
|
60
60
|
* first snapshot.
|
|
61
61
|
*/
|
|
62
62
|
readonly pendingUserMessage?: string | null;
|
|
63
|
+
/**
|
|
64
|
+
* Marks the pending user message as failed-to-send. The optimistic
|
|
65
|
+
* bubble renders an inline "Couldn't send — Retry" affordance instead
|
|
66
|
+
* of the sending indicator, so a failed follow-up never silently
|
|
67
|
+
* vanishes. Pair with {@link onRetrySend}.
|
|
68
|
+
*
|
|
69
|
+
* @default false
|
|
70
|
+
*/
|
|
71
|
+
readonly pendingMessageFailed?: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Retry handler for a failed pending message. Wired to the inline
|
|
74
|
+
* "Retry" control when {@link pendingMessageFailed} is `true`.
|
|
75
|
+
*/
|
|
76
|
+
readonly onRetrySend?: () => void;
|
|
77
|
+
/**
|
|
78
|
+
* Retry handler for a terminal-failed execution that exposed a server
|
|
79
|
+
* error reason. Receives the originating message; the consumer typically
|
|
80
|
+
* resends it as a new execution. When omitted, no Retry control is shown
|
|
81
|
+
* beside the surfaced failure reason.
|
|
82
|
+
*/
|
|
83
|
+
readonly onRetryExecution?: (message: string) => void;
|
|
63
84
|
/** Additional CSS class names for the root container. */
|
|
64
85
|
readonly className?: string;
|
|
65
86
|
/**
|
|
@@ -178,10 +199,11 @@ export interface MessageThreadProps {
|
|
|
178
199
|
* part of the public API.
|
|
179
200
|
*/
|
|
180
201
|
export type ThreadItem =
|
|
181
|
-
| { readonly kind: "message"; readonly message: AgentMessage; readonly key: string; readonly isPending?: boolean }
|
|
202
|
+
| { readonly kind: "message"; readonly message: AgentMessage; readonly key: string; readonly isPending?: boolean; readonly isFailed?: boolean }
|
|
182
203
|
| { readonly kind: "tool-group"; readonly toolCalls: readonly ToolCall[]; readonly subAgentExecutions: readonly SubAgentExecution[]; readonly key: string }
|
|
183
204
|
| { readonly kind: "sub-agent"; readonly subAgentExecution: SubAgentExecution; readonly key: string }
|
|
184
205
|
| { readonly kind: "phase-badge"; readonly phase: ExecutionPhase; readonly key: string }
|
|
206
|
+
| { readonly kind: "execution-error"; readonly error: string; readonly retryMessage?: string; readonly key: string }
|
|
185
207
|
| { readonly kind: "approval-request"; readonly pendingApproval: PendingApproval; readonly key: string }
|
|
186
208
|
| { readonly kind: "setup-progress"; readonly workspaceEntries: readonly WorkspaceEntry[]; readonly serverPhase?: string; readonly isAwaitingResponse?: boolean; readonly key: string }
|
|
187
209
|
| { readonly kind: "context-compacted"; readonly event: SummarizationEventView; readonly key: string }
|
|
@@ -216,6 +238,7 @@ export function buildThreadItems(
|
|
|
216
238
|
includeApprovals: boolean,
|
|
217
239
|
workspaceEntries: readonly WorkspaceEntry[] | undefined,
|
|
218
240
|
summarizationEvents?: readonly SummarizationEventView[],
|
|
241
|
+
pendingMessageFailed = false,
|
|
219
242
|
): ThreadItem[] {
|
|
220
243
|
const items: ThreadItem[] = [];
|
|
221
244
|
const allExecutions = activeStreamExecution
|
|
@@ -393,6 +416,22 @@ export function buildThreadItems(
|
|
|
393
416
|
phase: lastPhase,
|
|
394
417
|
key: `phase-${lastPhase}`,
|
|
395
418
|
});
|
|
419
|
+
|
|
420
|
+
// The server populates `status.error` only on EXECUTION_FAILED. Surface it
|
|
421
|
+
// beside the badge so a failure that produced no messages still explains
|
|
422
|
+
// itself (the CLI shows this reason; the chat previously showed nothing).
|
|
423
|
+
// Kept as its own item so the badge component stays presentational.
|
|
424
|
+
const reason = lastExec?.status?.error;
|
|
425
|
+
if (reason) {
|
|
426
|
+
const specMessage = lastExec?.spec?.message;
|
|
427
|
+
items.push({
|
|
428
|
+
kind: "execution-error",
|
|
429
|
+
error: reason,
|
|
430
|
+
retryMessage:
|
|
431
|
+
specMessage && specMessage !== "execute" ? specMessage : undefined,
|
|
432
|
+
key: `execution-error-${lastExec?.metadata?.id ?? lastPhase}`,
|
|
433
|
+
});
|
|
434
|
+
}
|
|
396
435
|
}
|
|
397
436
|
|
|
398
437
|
if (
|
|
@@ -430,7 +469,10 @@ export function buildThreadItems(
|
|
|
430
469
|
kind: "message",
|
|
431
470
|
message: syntheticPending,
|
|
432
471
|
key: "pending-user-turn",
|
|
433
|
-
|
|
472
|
+
// A failed turn shows the inline error instead of the dimmed
|
|
473
|
+
// sending state — the two are mutually exclusive.
|
|
474
|
+
isPending: !pendingMessageFailed,
|
|
475
|
+
isFailed: pendingMessageFailed,
|
|
434
476
|
});
|
|
435
477
|
}
|
|
436
478
|
}
|
|
@@ -465,6 +507,9 @@ export function MessageThread({
|
|
|
465
507
|
executions,
|
|
466
508
|
activeStreamExecution,
|
|
467
509
|
pendingUserMessage,
|
|
510
|
+
pendingMessageFailed = false,
|
|
511
|
+
onRetrySend,
|
|
512
|
+
onRetryExecution,
|
|
468
513
|
className,
|
|
469
514
|
formatToolCallSummary,
|
|
470
515
|
onApprovalSubmit,
|
|
@@ -483,8 +528,8 @@ export function MessageThread({
|
|
|
483
528
|
|
|
484
529
|
const includeApprovals = onApprovalSubmit != null;
|
|
485
530
|
const items = useMemo(
|
|
486
|
-
() => buildThreadItems(executions, activeStreamExecution, pendingUserMessage, includeApprovals, workspaceEntries, summarizationEvents),
|
|
487
|
-
[executions, activeStreamExecution, pendingUserMessage, includeApprovals, workspaceEntries, summarizationEvents],
|
|
531
|
+
() => buildThreadItems(executions, activeStreamExecution, pendingUserMessage, includeApprovals, workspaceEntries, summarizationEvents, pendingMessageFailed),
|
|
532
|
+
[executions, activeStreamExecution, pendingUserMessage, includeApprovals, workspaceEntries, summarizationEvents, pendingMessageFailed],
|
|
488
533
|
);
|
|
489
534
|
|
|
490
535
|
useKeyStability(items);
|
|
@@ -517,6 +562,8 @@ export function MessageThread({
|
|
|
517
562
|
org={org}
|
|
518
563
|
planActionsDisabled={planActionsDisabled}
|
|
519
564
|
centerContent={centerContent}
|
|
565
|
+
onRetrySend={onRetrySend}
|
|
566
|
+
onRetryExecution={onRetryExecution}
|
|
520
567
|
/>
|
|
521
568
|
</Suspense>
|
|
522
569
|
</div>
|
|
@@ -536,6 +583,8 @@ export function MessageThread({
|
|
|
536
583
|
onBuildFromPlan={onBuildFromPlan}
|
|
537
584
|
org={org}
|
|
538
585
|
planActionsDisabled={planActionsDisabled}
|
|
586
|
+
onRetrySend={onRetrySend}
|
|
587
|
+
onRetryExecution={onRetryExecution}
|
|
539
588
|
/>
|
|
540
589
|
);
|
|
541
590
|
}
|
|
@@ -560,6 +609,8 @@ interface NonVirtualizedThreadProps {
|
|
|
560
609
|
readonly onBuildFromPlan?: () => void;
|
|
561
610
|
readonly org?: string;
|
|
562
611
|
readonly planActionsDisabled?: boolean;
|
|
612
|
+
readonly onRetrySend?: () => void;
|
|
613
|
+
readonly onRetryExecution?: (message: string) => void;
|
|
563
614
|
}
|
|
564
615
|
|
|
565
616
|
function NonVirtualizedThread({
|
|
@@ -574,6 +625,8 @@ function NonVirtualizedThread({
|
|
|
574
625
|
onBuildFromPlan,
|
|
575
626
|
org,
|
|
576
627
|
planActionsDisabled,
|
|
628
|
+
onRetrySend,
|
|
629
|
+
onRetryExecution,
|
|
577
630
|
}: NonVirtualizedThreadProps) {
|
|
578
631
|
const { scrollRef, sentinelRef, contentRef, isFollowing, jumpToLatest } =
|
|
579
632
|
useAutoScroll();
|
|
@@ -609,6 +662,8 @@ function NonVirtualizedThread({
|
|
|
609
662
|
onBuildFromPlan={onBuildFromPlan}
|
|
610
663
|
org={org}
|
|
611
664
|
planActionsDisabled={planActionsDisabled}
|
|
665
|
+
onRetrySend={onRetrySend}
|
|
666
|
+
onRetryExecution={onRetryExecution}
|
|
612
667
|
/>
|
|
613
668
|
</ThreadItemWrapper>
|
|
614
669
|
))}
|
|
@@ -645,6 +700,8 @@ export interface ThreadItemRendererProps {
|
|
|
645
700
|
readonly onBuildFromPlan?: () => void;
|
|
646
701
|
readonly org?: string;
|
|
647
702
|
readonly planActionsDisabled?: boolean;
|
|
703
|
+
readonly onRetrySend?: () => void;
|
|
704
|
+
readonly onRetryExecution?: (message: string) => void;
|
|
648
705
|
}
|
|
649
706
|
|
|
650
707
|
/**
|
|
@@ -666,9 +723,16 @@ export function ThreadItemRenderer({
|
|
|
666
723
|
onBuildFromPlan,
|
|
667
724
|
org,
|
|
668
725
|
planActionsDisabled,
|
|
726
|
+
onRetrySend,
|
|
727
|
+
onRetryExecution,
|
|
669
728
|
}: ThreadItemRendererProps) {
|
|
670
729
|
switch (item.kind) {
|
|
671
730
|
case "message":
|
|
731
|
+
if (item.isFailed) {
|
|
732
|
+
return (
|
|
733
|
+
<FailedUserMessage message={item.message} onRetry={onRetrySend} />
|
|
734
|
+
);
|
|
735
|
+
}
|
|
672
736
|
return (
|
|
673
737
|
<MessageEntry
|
|
674
738
|
message={item.message}
|
|
@@ -697,6 +761,14 @@ export function ThreadItemRenderer({
|
|
|
697
761
|
<ExecutionPhaseBadge phase={item.phase} />
|
|
698
762
|
</div>
|
|
699
763
|
);
|
|
764
|
+
case "execution-error":
|
|
765
|
+
return (
|
|
766
|
+
<ExecutionErrorNotice
|
|
767
|
+
error={item.error}
|
|
768
|
+
retryMessage={item.retryMessage}
|
|
769
|
+
onRetry={onRetryExecution}
|
|
770
|
+
/>
|
|
771
|
+
);
|
|
700
772
|
case "approval-request":
|
|
701
773
|
return (
|
|
702
774
|
<ApprovalCardRow
|
|
@@ -736,6 +808,103 @@ export function ThreadItemRenderer({
|
|
|
736
808
|
}
|
|
737
809
|
}
|
|
738
810
|
|
|
811
|
+
// ---------------------------------------------------------------------------
|
|
812
|
+
// FailedUserMessage — optimistic turn whose send failed
|
|
813
|
+
// ---------------------------------------------------------------------------
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* Renders a user message whose send failed: the message itself stays visible
|
|
817
|
+
* (so the typed text is never lost) with an inline, actionable error beneath
|
|
818
|
+
* it. The error copy is intentionally short — the full reason is surfaced by
|
|
819
|
+
* the consumer's send-error banner; this is the in-thread "Retry" affordance.
|
|
820
|
+
*/
|
|
821
|
+
function FailedUserMessage({
|
|
822
|
+
message,
|
|
823
|
+
onRetry,
|
|
824
|
+
}: {
|
|
825
|
+
message: AgentMessage;
|
|
826
|
+
onRetry?: () => void;
|
|
827
|
+
}) {
|
|
828
|
+
return (
|
|
829
|
+
<div className="flex flex-col gap-1">
|
|
830
|
+
<MessageEntry message={message} />
|
|
831
|
+
<div
|
|
832
|
+
role="alert"
|
|
833
|
+
className="mx-4 flex items-center gap-2 text-xs text-destructive"
|
|
834
|
+
>
|
|
835
|
+
<span className="min-w-0 flex-1 truncate">Couldn’t send.</span>
|
|
836
|
+
{onRetry && (
|
|
837
|
+
<button
|
|
838
|
+
type="button"
|
|
839
|
+
onClick={onRetry}
|
|
840
|
+
className="shrink-0 rounded font-medium underline-offset-2 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
841
|
+
>
|
|
842
|
+
Retry
|
|
843
|
+
</button>
|
|
844
|
+
)}
|
|
845
|
+
</div>
|
|
846
|
+
</div>
|
|
847
|
+
);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// ---------------------------------------------------------------------------
|
|
851
|
+
// ExecutionErrorNotice — server failure reason for a terminal-failed execution
|
|
852
|
+
// ---------------------------------------------------------------------------
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* Renders the server-reported failure reason (`AgentExecutionStatus.error`)
|
|
856
|
+
* for an execution that died — typically before producing any messages. The
|
|
857
|
+
* reason can be a long Temporal error, so it is clamped by default with a
|
|
858
|
+
* Show more / Show less toggle. An optional Retry resends the originating
|
|
859
|
+
* message as a fresh execution.
|
|
860
|
+
*/
|
|
861
|
+
function ExecutionErrorNotice({
|
|
862
|
+
error,
|
|
863
|
+
retryMessage,
|
|
864
|
+
onRetry,
|
|
865
|
+
}: {
|
|
866
|
+
error: string;
|
|
867
|
+
retryMessage?: string;
|
|
868
|
+
onRetry?: (message: string) => void;
|
|
869
|
+
}) {
|
|
870
|
+
const [expanded, setExpanded] = useState(false);
|
|
871
|
+
const canRetry = !!onRetry && !!retryMessage;
|
|
872
|
+
|
|
873
|
+
return (
|
|
874
|
+
<div
|
|
875
|
+
role="alert"
|
|
876
|
+
className="mx-4 flex flex-col gap-1.5 rounded-md bg-destructive-subtle px-3 py-2"
|
|
877
|
+
>
|
|
878
|
+
<p
|
|
879
|
+
className={cn(
|
|
880
|
+
"text-xs whitespace-pre-wrap break-words text-destructive",
|
|
881
|
+
!expanded && "line-clamp-3",
|
|
882
|
+
)}
|
|
883
|
+
>
|
|
884
|
+
{error}
|
|
885
|
+
</p>
|
|
886
|
+
<div className="flex items-center gap-3 text-xs">
|
|
887
|
+
<button
|
|
888
|
+
type="button"
|
|
889
|
+
onClick={() => setExpanded((v) => !v)}
|
|
890
|
+
className="font-medium text-muted-foreground underline-offset-2 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
891
|
+
>
|
|
892
|
+
{expanded ? "Show less" : "Show more"}
|
|
893
|
+
</button>
|
|
894
|
+
{canRetry && (
|
|
895
|
+
<button
|
|
896
|
+
type="button"
|
|
897
|
+
onClick={() => onRetry!(retryMessage!)}
|
|
898
|
+
className="font-medium text-foreground underline-offset-2 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
899
|
+
>
|
|
900
|
+
Retry
|
|
901
|
+
</button>
|
|
902
|
+
)}
|
|
903
|
+
</div>
|
|
904
|
+
</div>
|
|
905
|
+
);
|
|
906
|
+
}
|
|
907
|
+
|
|
739
908
|
// ---------------------------------------------------------------------------
|
|
740
909
|
// ApprovalCardRow — stabilizes the onSubmit callback for React.memo
|
|
741
910
|
// ---------------------------------------------------------------------------
|