@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,16 +1,26 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { ExecutionPhase } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
3
4
|
import { isTerminalPhase } from "../execution/execution-phases";
|
|
4
5
|
import { useStigmer } from "../hooks";
|
|
6
|
+
import { toError } from "../internal/toError";
|
|
5
7
|
import { useConversationStoreRef } from "../internal/store";
|
|
6
8
|
import { useCreateAgentExecution } from "../execution/useCreateAgentExecution";
|
|
7
9
|
import { useExecutionStream } from "../execution/useExecutionStream";
|
|
10
|
+
import { useAgentExecutionActions } from "../execution/useAgentExecutionActions";
|
|
8
11
|
import { useSubmitApproval } from "../execution/useSubmitApproval";
|
|
9
12
|
import { useSession } from "./useSession";
|
|
10
13
|
import { useSessionExecutions } from "./useSessionExecutions";
|
|
11
14
|
import { useUpdateSession } from "./useUpdateSession";
|
|
12
15
|
import { useLocalSessionWorker } from "./useLocalSessionWorker";
|
|
13
16
|
import { specWorkspaceToInput, specMcpUsagesToInput, specSkillRefsToInput, } from "./session-spec-converters";
|
|
17
|
+
/**
|
|
18
|
+
* Cadence for re-discovering the session's executions while the live stream
|
|
19
|
+
* cannot be relied on (a created-but-not-yet-listed execution, a silent
|
|
20
|
+
* connect-timeout, or an exhausted stream error). Disabled the instant the
|
|
21
|
+
* stream is healthy or terminal, so this never competes with the live feed.
|
|
22
|
+
*/
|
|
23
|
+
const REDISCOVERY_POLL_INTERVAL_MS = 5_000;
|
|
14
24
|
/**
|
|
15
25
|
* Behavior hook that encapsulates the full conversation lifecycle for
|
|
16
26
|
* a session: loading data, streaming the active execution, submitting
|
|
@@ -62,8 +72,17 @@ import { specWorkspaceToInput, specMcpUsagesToInput, specSkillRefsToInput, } fro
|
|
|
62
72
|
export function useSessionConversation(sessionId, org) {
|
|
63
73
|
const stigmer = useStigmer();
|
|
64
74
|
const { session, isLoading: sessionLoading, error: sessionError, refetch: refetchSession, } = useSession(sessionId);
|
|
65
|
-
|
|
66
|
-
|
|
75
|
+
// Bounded re-discovery (see REDISCOVERY_POLL_INTERVAL_MS). The gate depends on
|
|
76
|
+
// the stream below, so the decision is synced into state via an effect and fed
|
|
77
|
+
// back here on the next render — a one-frame lag that is immaterial at 5s.
|
|
78
|
+
const [rediscoveryActive, setRediscoveryActive] = useState(false);
|
|
79
|
+
const { executions, isLoading: executionsLoading, error: executionsError, refetch, } = useSessionExecutions(sessionId, {
|
|
80
|
+
refetchInterval: rediscoveryActive ? REDISCOVERY_POLL_INTERVAL_MS : false,
|
|
81
|
+
// Re-list on app-relaunch / tab refocus so an execution that appeared while
|
|
82
|
+
// backgrounded is picked up without the user having to act.
|
|
83
|
+
refetchOnWindowFocus: true,
|
|
84
|
+
});
|
|
85
|
+
const { create, isCreating, clearError: clearCreateError, } = useCreateAgentExecution();
|
|
67
86
|
const { update: updateSession } = useUpdateSession();
|
|
68
87
|
const { submitApproval: rawSubmitApproval, submittingToolCallIds, error: approvalError, clearError: clearApprovalError, } = useSubmitApproval();
|
|
69
88
|
// Local execution: attach the session's runner worker while it is open and
|
|
@@ -71,6 +90,11 @@ export function useSessionConversation(sessionId, org) {
|
|
|
71
90
|
useLocalSessionWorker(sessionId, session);
|
|
72
91
|
const [pendingExecutionId, setPendingExecutionId] = useState(null);
|
|
73
92
|
const [pendingUserMessage, setPendingUserMessage] = useState(null);
|
|
93
|
+
// Dedicated send-failure state, distinct from the create hook's internal
|
|
94
|
+
// error so it can also cover the session.update() path. The last send's
|
|
95
|
+
// arguments are captured for an exact retry.
|
|
96
|
+
const [sendError, setSendError] = useState(null);
|
|
97
|
+
const lastSendRef = useRef(null);
|
|
74
98
|
const listActiveId = useMemo(() => {
|
|
75
99
|
for (let i = executions.length - 1; i >= 0; i--) {
|
|
76
100
|
const phase = executions[i].status?.phase;
|
|
@@ -89,6 +113,21 @@ export function useSessionConversation(sessionId, org) {
|
|
|
89
113
|
const stream = useExecutionStream(activeExecutionId, {
|
|
90
114
|
store: conversationStore,
|
|
91
115
|
});
|
|
116
|
+
// Re-discovery gate. Poll only while the live stream cannot carry us:
|
|
117
|
+
// • a fresh session whose first execution is created but not yet listed
|
|
118
|
+
// (`executions.length === 0`) — the race this fix targets,
|
|
119
|
+
// • a silent connect-timeout, or an exhausted stream error.
|
|
120
|
+
// Never while the stream is healthy (`isStreaming`) or the active execution
|
|
121
|
+
// has reached a terminal phase — the live feed is then the source of truth.
|
|
122
|
+
const streamTerminal = activeExecutionId !== null && isTerminalPhase(stream.phase);
|
|
123
|
+
const needsRediscovery = !stream.isStreaming &&
|
|
124
|
+
!streamTerminal &&
|
|
125
|
+
((activeExecutionId === null && executions.length === 0) ||
|
|
126
|
+
stream.connectTimedOut ||
|
|
127
|
+
stream.error !== null);
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
setRediscoveryActive(needsRediscovery);
|
|
130
|
+
}, [needsRediscovery]);
|
|
92
131
|
// Clear pendingExecutionId once the execution appears in the fetched list
|
|
93
132
|
useEffect(() => {
|
|
94
133
|
if (pendingExecutionId &&
|
|
@@ -96,12 +135,20 @@ export function useSessionConversation(sessionId, org) {
|
|
|
96
135
|
setPendingExecutionId(null);
|
|
97
136
|
}
|
|
98
137
|
}, [pendingExecutionId, executions]);
|
|
99
|
-
// Clear optimistic message
|
|
138
|
+
// Clear the optimistic message — and any stale send error — once the stream
|
|
139
|
+
// delivers a real snapshot. This also handles recovery: if a failed send's
|
|
140
|
+
// execution is later re-discovered and streams, the failed turn resolves into
|
|
141
|
+
// the live one instead of lingering. (At send time the composer is only
|
|
142
|
+
// enabled when no execution is active, so a *fresh* failure cannot be cleared
|
|
143
|
+
// here prematurely — `stream.execution` is null then.)
|
|
100
144
|
useEffect(() => {
|
|
101
|
-
if (
|
|
145
|
+
if (!stream.execution)
|
|
146
|
+
return;
|
|
147
|
+
if (pendingUserMessage)
|
|
102
148
|
setPendingUserMessage(null);
|
|
103
|
-
|
|
104
|
-
|
|
149
|
+
if (sendError)
|
|
150
|
+
setSendError(null);
|
|
151
|
+
}, [pendingUserMessage, sendError, stream.execution]);
|
|
105
152
|
// Refetch executions when stream reaches a terminal phase so the
|
|
106
153
|
// fetched list reflects the completed status and listActiveId clears.
|
|
107
154
|
useEffect(() => {
|
|
@@ -125,6 +172,22 @@ export function useSessionConversation(sessionId, org) {
|
|
|
125
172
|
return null;
|
|
126
173
|
return stream.phase;
|
|
127
174
|
}, [activeExecutionId, stream.phase]);
|
|
175
|
+
// Stop is only valid in phases the backend cancels/terminates from
|
|
176
|
+
// (PENDING / IN_PROGRESS). Other non-terminal phases (e.g.
|
|
177
|
+
// WAITING_FOR_APPROVAL) are handled by their own control surface.
|
|
178
|
+
const isStoppable = activePhase === ExecutionPhase.EXECUTION_PENDING ||
|
|
179
|
+
activePhase === ExecutionPhase.EXECUTION_IN_PROGRESS;
|
|
180
|
+
const stopActions = useAgentExecutionActions(activeExecutionId, {
|
|
181
|
+
// The cancel/terminate also broadcasts the new phase over the stream, but
|
|
182
|
+
// refetch is the belt-and-suspenders that clears the active id even if the
|
|
183
|
+
// stream has already ended — mirrors the workflow viewer's onSuccess.
|
|
184
|
+
onSuccess: refetch,
|
|
185
|
+
});
|
|
186
|
+
const stop = useCallback(async (reason) => {
|
|
187
|
+
if (!isStoppable)
|
|
188
|
+
return;
|
|
189
|
+
await stopActions.stop(reason);
|
|
190
|
+
}, [isStoppable, stopActions.stop]);
|
|
128
191
|
const canSendFollowUp = !isCreating && activeExecutionId === null;
|
|
129
192
|
const workspaceEntries = useMemo(() => session?.spec?.workspaceEntries ?? [], [session]);
|
|
130
193
|
const mcpServerUsages = useMemo(() => session?.spec?.mcpServerUsages ?? [], [session]);
|
|
@@ -132,6 +195,9 @@ export function useSessionConversation(sessionId, org) {
|
|
|
132
195
|
const sendFollowUp = useCallback(async (message, options) => {
|
|
133
196
|
if (!sessionId || !session)
|
|
134
197
|
return;
|
|
198
|
+
// Capture for retry and clear any prior failure before the new attempt.
|
|
199
|
+
lastSendRef.current = { message, options };
|
|
200
|
+
setSendError(null);
|
|
135
201
|
setPendingUserMessage(message);
|
|
136
202
|
try {
|
|
137
203
|
const needsSessionUpdate = options?.agentInstanceId !== undefined ||
|
|
@@ -166,12 +232,24 @@ export function useSessionConversation(sessionId, org) {
|
|
|
166
232
|
refetch();
|
|
167
233
|
}
|
|
168
234
|
catch (err) {
|
|
169
|
-
|
|
235
|
+
// Surface the failure and KEEP the user's message visible (do not clear
|
|
236
|
+
// pendingUserMessage) so the turn renders as failed-with-retry instead
|
|
237
|
+
// of vanishing. Covers both the update() and create() paths.
|
|
238
|
+
setSendError(toError(err));
|
|
170
239
|
if (process.env.NODE_ENV !== "production") {
|
|
171
240
|
console.error("[useSessionConversation] sendFollowUp failed:", err);
|
|
172
241
|
}
|
|
173
242
|
}
|
|
174
243
|
}, [sessionId, session, org, stigmer, create, updateSession, refetch, refetchSession]);
|
|
244
|
+
const retryLastSend = useCallback(() => {
|
|
245
|
+
const last = lastSendRef.current;
|
|
246
|
+
if (last)
|
|
247
|
+
void sendFollowUp(last.message, last.options);
|
|
248
|
+
}, [sendFollowUp]);
|
|
249
|
+
const clearSendError = useCallback(() => {
|
|
250
|
+
setSendError(null);
|
|
251
|
+
clearCreateError();
|
|
252
|
+
}, [clearCreateError]);
|
|
175
253
|
const pendingApprovals = useMemo(() => activeStreamExecution?.status?.pendingApprovals ?? [], [activeStreamExecution]);
|
|
176
254
|
const submitApproval = useCallback(async (toolCallId, action, comment) => {
|
|
177
255
|
if (!activeExecutionId)
|
|
@@ -190,8 +268,9 @@ export function useSessionConversation(sessionId, org) {
|
|
|
190
268
|
sendFollowUp,
|
|
191
269
|
canSendFollowUp,
|
|
192
270
|
isSending: isCreating,
|
|
193
|
-
sendError
|
|
194
|
-
clearSendError
|
|
271
|
+
sendError,
|
|
272
|
+
clearSendError,
|
|
273
|
+
retryLastSend,
|
|
195
274
|
pendingUserMessage,
|
|
196
275
|
workspaceEntries,
|
|
197
276
|
mcpServerUsages,
|
|
@@ -201,9 +280,15 @@ export function useSessionConversation(sessionId, org) {
|
|
|
201
280
|
submittingApprovalIds: submittingToolCallIds,
|
|
202
281
|
approvalError,
|
|
203
282
|
clearApprovalError,
|
|
283
|
+
isStoppable,
|
|
284
|
+
stop,
|
|
285
|
+
isStopping: stopActions.isSubmitting,
|
|
286
|
+
stopError: stopActions.error,
|
|
204
287
|
isLoading,
|
|
205
288
|
loadError,
|
|
206
289
|
isReconnecting: stream.isReconnecting,
|
|
290
|
+
connectTimedOut: stream.connectTimedOut,
|
|
291
|
+
isSlow: stream.isSlow,
|
|
207
292
|
streamError: stream.error,
|
|
208
293
|
reconnectStream: stream.reconnect,
|
|
209
294
|
};
|
|
@@ -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"}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
WorkspaceIcon,
|
|
13
13
|
ArrowUpIcon,
|
|
14
14
|
SpinnerIcon,
|
|
15
|
+
StopIcon,
|
|
15
16
|
} from "./icons";
|
|
16
17
|
|
|
17
18
|
export interface ComposerToolbarProps {
|
|
@@ -19,6 +20,14 @@ export interface ComposerToolbarProps {
|
|
|
19
20
|
readonly isSubmitting: boolean;
|
|
20
21
|
readonly canSend: boolean;
|
|
21
22
|
readonly onSend: () => void;
|
|
23
|
+
/**
|
|
24
|
+
* When provided, the primary button becomes a Stop control (enabled even
|
|
25
|
+
* while the rest of the composer is disabled) and the Send button is hidden.
|
|
26
|
+
* Driven by the active execution being stoppable.
|
|
27
|
+
*/
|
|
28
|
+
readonly onStop?: () => void;
|
|
29
|
+
/** `true` while a stop request is in flight — shows a spinner on the Stop button. */
|
|
30
|
+
readonly isStopping?: boolean;
|
|
22
31
|
|
|
23
32
|
// -- Left group: Primary state --------------------------------------------
|
|
24
33
|
|
|
@@ -82,6 +91,8 @@ export function ComposerToolbar({
|
|
|
82
91
|
isSubmitting,
|
|
83
92
|
canSend,
|
|
84
93
|
onSend,
|
|
94
|
+
onStop,
|
|
95
|
+
isStopping = false,
|
|
85
96
|
showAttach,
|
|
86
97
|
attachmentCount,
|
|
87
98
|
onAttachClick,
|
|
@@ -210,15 +221,27 @@ export function ComposerToolbar({
|
|
|
210
221
|
disabled={disabled}
|
|
211
222
|
/>
|
|
212
223
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
224
|
+
{onStop ? (
|
|
225
|
+
<button
|
|
226
|
+
type="button"
|
|
227
|
+
onClick={onStop}
|
|
228
|
+
className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary text-primary-foreground transition-colors hover:bg-primary-hover"
|
|
229
|
+
aria-label="Stop generating"
|
|
230
|
+
title="Stop"
|
|
231
|
+
>
|
|
232
|
+
{isStopping ? <SpinnerIcon /> : <StopIcon />}
|
|
233
|
+
</button>
|
|
234
|
+
) : (
|
|
235
|
+
<button
|
|
236
|
+
type="button"
|
|
237
|
+
disabled={!canSend}
|
|
238
|
+
onClick={onSend}
|
|
239
|
+
className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary text-primary-foreground transition-colors hover:bg-primary-hover disabled:pointer-events-none disabled:opacity-40"
|
|
240
|
+
aria-label="Send message"
|
|
241
|
+
>
|
|
242
|
+
{isSubmitting ? <SpinnerIcon /> : <ArrowUpIcon />}
|
|
243
|
+
</button>
|
|
244
|
+
)}
|
|
222
245
|
</div>
|
|
223
246
|
</div>
|
|
224
247
|
);
|
|
@@ -163,6 +163,19 @@ export interface SessionComposerProps {
|
|
|
163
163
|
/** Disables the entire composer (e.g., while an execution streams). */
|
|
164
164
|
readonly disabled?: boolean;
|
|
165
165
|
|
|
166
|
+
/**
|
|
167
|
+
* When provided, the Send button becomes a Stop control while an execution
|
|
168
|
+
* is in flight — clicking it calls this handler (graceful cancel, escalating
|
|
169
|
+
* to terminate on a repeat press). The textarea stays disabled, but the card
|
|
170
|
+
* is not dimmed so Stop reads as live and clickable.
|
|
171
|
+
*
|
|
172
|
+
* Opt-in: when omitted, the composer behaves exactly as before. Typically
|
|
173
|
+
* wired to `useSessionConversation`'s `stop`, gated on `isStoppable`.
|
|
174
|
+
*/
|
|
175
|
+
readonly onStop?: () => void;
|
|
176
|
+
/** `true` while a stop request is in flight — shows a spinner on the Stop button. */
|
|
177
|
+
readonly isStopping?: boolean;
|
|
178
|
+
|
|
166
179
|
/**
|
|
167
180
|
* Currently selected execution harness.
|
|
168
181
|
*
|
|
@@ -490,6 +503,8 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
|
|
|
490
503
|
onSubmit,
|
|
491
504
|
isSubmitting = false,
|
|
492
505
|
disabled = false,
|
|
506
|
+
onStop,
|
|
507
|
+
isStopping = false,
|
|
493
508
|
harness,
|
|
494
509
|
onHarnessChange,
|
|
495
510
|
showHarnessSelector = false,
|
|
@@ -552,6 +567,10 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
|
|
|
552
567
|
|
|
553
568
|
const isDisabled = disabled || isSubmitting;
|
|
554
569
|
|
|
570
|
+
// Stop-mode: an in-flight, stoppable execution. The textarea/config stay
|
|
571
|
+
// disabled, but the card is kept un-dimmed so the Stop button reads as live.
|
|
572
|
+
const stopMode = onStop != null;
|
|
573
|
+
|
|
555
574
|
const showAgent = onAgentRefChange != null && org != null;
|
|
556
575
|
const showMcp = onMcpServerUsagesChange != null && org != null;
|
|
557
576
|
const showWorkspace = workspace != null;
|
|
@@ -1324,7 +1343,7 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
|
|
|
1324
1343
|
className={cn(
|
|
1325
1344
|
"rounded-xl border border-border bg-card shadow-sm",
|
|
1326
1345
|
"focus-within:ring-2 focus-within:ring-ring",
|
|
1327
|
-
isDisabled && "opacity-50",
|
|
1346
|
+
isDisabled && !stopMode && "opacity-50",
|
|
1328
1347
|
isDragOver && "ring-2 ring-primary/50",
|
|
1329
1348
|
)}
|
|
1330
1349
|
onDragOver={handleDragOver}
|
|
@@ -1448,6 +1467,8 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
|
|
|
1448
1467
|
isSubmitting={isSubmitting}
|
|
1449
1468
|
canSend={canSend}
|
|
1450
1469
|
onSend={composer.submit}
|
|
1470
|
+
onStop={onStop}
|
|
1471
|
+
isStopping={isStopping}
|
|
1451
1472
|
showWorkspace={showWorkspace}
|
|
1452
1473
|
workspaceCount={workspaceCount}
|
|
1453
1474
|
onWorkspaceDirectAction={workspaceDirectAction}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
2
|
+
import { render, screen, cleanup, fireEvent } from "@testing-library/react";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import type { Stigmer } from "@stigmer/sdk";
|
|
5
|
+
import { StigmerContext } from "../../context";
|
|
6
|
+
import { ModelRegistryContext } from "../../models/ModelRegistryContext";
|
|
7
|
+
import { SessionComposer } from "../SessionComposer";
|
|
8
|
+
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Helpers
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
function createMinimalStigmerMock(): Stigmer {
|
|
14
|
+
return {
|
|
15
|
+
agentExecution: { uploadAttachment: vi.fn() },
|
|
16
|
+
environment: { getPersonal: vi.fn().mockResolvedValue(null) },
|
|
17
|
+
baseUrl: "http://localhost:8080",
|
|
18
|
+
getAuthCredential: vi.fn().mockResolvedValue("test-token"),
|
|
19
|
+
config: {
|
|
20
|
+
baseUrl: "http://localhost:8080",
|
|
21
|
+
getAccessToken: vi.fn().mockResolvedValue(""),
|
|
22
|
+
},
|
|
23
|
+
} as unknown as Stigmer;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function createWrapper(client: Stigmer) {
|
|
27
|
+
return function Wrapper({ children }: { children: ReactNode }) {
|
|
28
|
+
return (
|
|
29
|
+
<StigmerContext.Provider value={client}>
|
|
30
|
+
<ModelRegistryContext.Provider
|
|
31
|
+
value={{ models: [], isLoading: false, error: null, refetch: vi.fn() }}
|
|
32
|
+
>
|
|
33
|
+
{children}
|
|
34
|
+
</ModelRegistryContext.Provider>
|
|
35
|
+
</StigmerContext.Provider>
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function renderComposer(
|
|
41
|
+
props?: Partial<React.ComponentProps<typeof SessionComposer>>,
|
|
42
|
+
) {
|
|
43
|
+
const client = createMinimalStigmerMock();
|
|
44
|
+
const onSubmit = vi.fn();
|
|
45
|
+
const result = render(
|
|
46
|
+
<SessionComposer onSubmit={onSubmit} {...props} />,
|
|
47
|
+
{ wrapper: createWrapper(client) },
|
|
48
|
+
);
|
|
49
|
+
return { ...result, onSubmit, client };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
afterEach(cleanup);
|
|
53
|
+
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Tests
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
describe("SessionComposer — Stop affordance", () => {
|
|
59
|
+
it("renders Send (not Stop) when onStop is not provided", () => {
|
|
60
|
+
renderComposer();
|
|
61
|
+
|
|
62
|
+
expect(screen.getByRole("button", { name: "Send message" })).toBeTruthy();
|
|
63
|
+
expect(
|
|
64
|
+
screen.queryByRole("button", { name: "Stop generating" }),
|
|
65
|
+
).toBeNull();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("renders Stop (not Send) when onStop is provided", () => {
|
|
69
|
+
renderComposer({ onStop: vi.fn(), disabled: true });
|
|
70
|
+
|
|
71
|
+
expect(screen.getByRole("button", { name: "Stop generating" })).toBeTruthy();
|
|
72
|
+
expect(
|
|
73
|
+
screen.queryByRole("button", { name: "Send message" }),
|
|
74
|
+
).toBeNull();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("clicking Stop calls onStop, even while the composer is disabled", () => {
|
|
78
|
+
const onStop = vi.fn();
|
|
79
|
+
renderComposer({ onStop, disabled: true });
|
|
80
|
+
|
|
81
|
+
fireEvent.click(screen.getByRole("button", { name: "Stop generating" }));
|
|
82
|
+
expect(onStop).toHaveBeenCalledTimes(1);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("Stop stays clickable while stopping so the user can escalate", () => {
|
|
86
|
+
const onStop = vi.fn();
|
|
87
|
+
renderComposer({ onStop, disabled: true, isStopping: true });
|
|
88
|
+
|
|
89
|
+
const stop = screen.getByRole("button", {
|
|
90
|
+
name: "Stop generating",
|
|
91
|
+
}) as HTMLButtonElement;
|
|
92
|
+
expect(stop.disabled).toBe(false);
|
|
93
|
+
|
|
94
|
+
fireEvent.click(stop);
|
|
95
|
+
fireEvent.click(stop);
|
|
96
|
+
expect(onStop).toHaveBeenCalledTimes(2);
|
|
97
|
+
});
|
|
98
|
+
});
|
package/src/composer/icons.tsx
CHANGED
|
@@ -72,6 +72,21 @@ export function SpinnerIcon() {
|
|
|
72
72
|
);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/** Filled rounded square — the universal "stop" glyph (mirrors Cursor's composer). */
|
|
76
|
+
export function StopIcon() {
|
|
77
|
+
return (
|
|
78
|
+
<svg
|
|
79
|
+
width="16"
|
|
80
|
+
height="16"
|
|
81
|
+
viewBox="0 0 16 16"
|
|
82
|
+
fill="currentColor"
|
|
83
|
+
aria-hidden="true"
|
|
84
|
+
>
|
|
85
|
+
<rect x="4" y="4" width="8" height="8" rx="1.5" />
|
|
86
|
+
</svg>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
75
90
|
export function XIcon() {
|
|
76
91
|
return (
|
|
77
92
|
<svg
|
|
@@ -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
|
);
|