@stigmer/react 0.0.53 → 0.0.55
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/ArtifactCard.d.ts +11 -1
- package/execution/ArtifactCard.d.ts.map +1 -1
- package/execution/ArtifactCard.js +22 -3
- package/execution/ArtifactCard.js.map +1 -1
- package/execution/ArtifactPreviewModal.d.ts.map +1 -1
- package/execution/ArtifactPreviewModal.js +1 -1
- package/execution/ArtifactPreviewModal.js.map +1 -1
- package/execution/ArtifactsWidget.d.ts +26 -19
- package/execution/ArtifactsWidget.d.ts.map +1 -1
- package/execution/ArtifactsWidget.js +32 -26
- package/execution/ArtifactsWidget.js.map +1 -1
- package/execution/MessageThread.d.ts +10 -1
- package/execution/MessageThread.d.ts.map +1 -1
- package/execution/MessageThread.js +21 -17
- package/execution/MessageThread.js.map +1 -1
- package/execution/SandboxContext.d.ts +32 -0
- package/execution/SandboxContext.d.ts.map +1 -0
- package/execution/SandboxContext.js +26 -0
- package/execution/SandboxContext.js.map +1 -0
- package/execution/SetupProgress.d.ts +23 -13
- package/execution/SetupProgress.d.ts.map +1 -1
- package/execution/SetupProgress.js +18 -12
- package/execution/SetupProgress.js.map +1 -1
- package/execution/ToolArgsView.d.ts.map +1 -1
- package/execution/ToolArgsView.js +3 -1
- package/execution/ToolArgsView.js.map +1 -1
- package/execution/ToolCallDetail.d.ts.map +1 -1
- package/execution/ToolCallDetail.js +3 -1
- package/execution/ToolCallDetail.js.map +1 -1
- package/execution/ToolCallItem.d.ts.map +1 -1
- package/execution/ToolCallItem.js +7 -1
- package/execution/ToolCallItem.js.map +1 -1
- package/execution/WriteBackCard.d.ts +34 -0
- package/execution/WriteBackCard.d.ts.map +1 -0
- package/execution/WriteBackCard.js +75 -0
- package/execution/WriteBackCard.js.map +1 -0
- package/execution/WriteBacksWidget.d.ts +49 -0
- package/execution/WriteBacksWidget.d.ts.map +1 -0
- package/execution/WriteBacksWidget.js +44 -0
- package/execution/WriteBacksWidget.js.map +1 -0
- package/execution/__tests__/file-path-resolver.test.d.ts +2 -0
- package/execution/__tests__/file-path-resolver.test.d.ts.map +1 -0
- package/execution/__tests__/file-path-resolver.test.js +180 -0
- package/execution/__tests__/file-path-resolver.test.js.map +1 -0
- package/execution/file-path-resolver.d.ts +3 -3
- package/execution/file-path-resolver.d.ts.map +1 -1
- package/execution/file-path-resolver.js +23 -12
- package/execution/file-path-resolver.js.map +1 -1
- package/execution/index.d.ts +9 -0
- package/execution/index.d.ts.map +1 -1
- package/execution/index.js +5 -0
- package/execution/index.js.map +1 -1
- package/execution/sandbox-path-normalizer.d.ts +46 -0
- package/execution/sandbox-path-normalizer.d.ts.map +1 -0
- package/execution/sandbox-path-normalizer.js +73 -0
- package/execution/sandbox-path-normalizer.js.map +1 -0
- package/execution/useArtifactContent.d.ts +5 -1
- package/execution/useArtifactContent.d.ts.map +1 -1
- package/execution/useArtifactContent.js +6 -2
- package/execution/useArtifactContent.js.map +1 -1
- package/execution/useWorkspaceWriteBacks.d.ts +40 -0
- package/execution/useWorkspaceWriteBacks.d.ts.map +1 -0
- package/execution/useWorkspaceWriteBacks.js +41 -0
- package/execution/useWorkspaceWriteBacks.js.map +1 -0
- package/github/GitHubRepoPicker.d.ts +5 -2
- package/github/GitHubRepoPicker.d.ts.map +1 -1
- package/github/GitHubRepoPicker.js +133 -36
- package/github/GitHubRepoPicker.js.map +1 -1
- package/github/index.d.ts +1 -0
- package/github/index.d.ts.map +1 -1
- package/github/index.js +1 -0
- package/github/index.js.map +1 -1
- package/github/useGitHubSearch.d.ts +20 -0
- package/github/useGitHubSearch.d.ts.map +1 -0
- package/github/useGitHubSearch.js +127 -0
- package/github/useGitHubSearch.js.map +1 -0
- package/index.d.ts +6 -6
- package/index.d.ts.map +1 -1
- package/index.js +3 -3
- package/index.js.map +1 -1
- package/package.json +4 -4
- package/session/index.d.ts +4 -0
- package/session/index.d.ts.map +1 -1
- package/session/index.js +2 -0
- package/session/index.js.map +1 -1
- package/session/useSessionArtifacts.d.ts +73 -0
- package/session/useSessionArtifacts.d.ts.map +1 -0
- package/session/useSessionArtifacts.js +95 -0
- package/session/useSessionArtifacts.js.map +1 -0
- package/session/useSessionWriteBacks.d.ts +56 -0
- package/session/useSessionWriteBacks.d.ts.map +1 -0
- package/session/useSessionWriteBacks.js +56 -0
- package/session/useSessionWriteBacks.js.map +1 -0
- package/src/execution/ArtifactCard.tsx +40 -0
- package/src/execution/ArtifactPreviewModal.tsx +2 -0
- package/src/execution/ArtifactsWidget.tsx +67 -43
- package/src/execution/MessageThread.tsx +23 -1
- package/src/execution/SandboxContext.ts +47 -0
- package/src/execution/SetupProgress.tsx +33 -14
- package/src/execution/ToolArgsView.tsx +3 -1
- package/src/execution/ToolCallDetail.tsx +3 -1
- package/src/execution/ToolCallItem.tsx +7 -1
- package/src/execution/WriteBackCard.tsx +210 -0
- package/src/execution/WriteBacksWidget.tsx +82 -0
- package/src/execution/__tests__/file-path-resolver.test.ts +295 -0
- package/src/execution/file-path-resolver.ts +24 -12
- package/src/execution/index.ts +13 -0
- package/src/execution/sandbox-path-normalizer.ts +80 -0
- package/src/execution/useArtifactContent.ts +6 -1
- package/src/execution/useWorkspaceWriteBacks.ts +56 -0
- package/src/github/GitHubRepoPicker.tsx +413 -108
- package/src/github/index.ts +5 -0
- package/src/github/useGitHubSearch.ts +162 -0
- package/src/index.ts +14 -0
- package/src/session/index.ts +12 -0
- package/src/session/useSessionArtifacts.ts +143 -0
- package/src/session/useSessionWriteBacks.ts +94 -0
- package/styles.css +1 -1
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useState } from "react";
|
|
3
|
+
import { useCallback, useMemo, useState } from "react";
|
|
4
4
|
import type { AgentExecution } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
5
|
-
import type { ExecutionArtifact } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/artifact_pb";
|
|
6
|
-
import { ExecutionPhase } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
7
5
|
import { cn } from "@stigmer/theme";
|
|
8
|
-
import {
|
|
9
|
-
|
|
6
|
+
import {
|
|
7
|
+
useSessionArtifacts,
|
|
8
|
+
type SessionArtifactEntry,
|
|
9
|
+
} from "../session/useSessionArtifacts";
|
|
10
10
|
import { ArtifactCard } from "./ArtifactCard";
|
|
11
11
|
import { ArtifactPreviewModal } from "./ArtifactPreviewModal";
|
|
12
12
|
import type { ApplyResourceResult } from "../library/useApplyResource";
|
|
13
13
|
|
|
14
14
|
export interface ArtifactsWidgetProps {
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* All executions for the current session — both completed and
|
|
17
|
+
* actively streaming. The widget aggregates artifacts across every
|
|
18
|
+
* execution, deduplicates by `sandbox_path` (latest wins), and
|
|
19
|
+
* sorts alphabetically by name.
|
|
20
|
+
*
|
|
21
|
+
* Renders nothing when the list is empty or no execution has
|
|
22
|
+
* artifacts.
|
|
18
23
|
*/
|
|
19
|
-
readonly
|
|
24
|
+
readonly executions: readonly AgentExecution[];
|
|
20
25
|
/** Organization slug for the "Apply to [org]" CTA in the preview modal. */
|
|
21
26
|
readonly org: string;
|
|
22
27
|
/**
|
|
@@ -31,24 +36,22 @@ export interface ArtifactsWidgetProps {
|
|
|
31
36
|
}
|
|
32
37
|
|
|
33
38
|
/**
|
|
34
|
-
* Right-sidebar widget that surfaces
|
|
35
|
-
*
|
|
39
|
+
* Right-sidebar widget that surfaces all artifacts produced during a
|
|
40
|
+
* session as a unified, alphabetically-sorted file listing.
|
|
41
|
+
*
|
|
42
|
+
* Artifacts from multiple executions are aggregated and deduplicated
|
|
43
|
+
* by `sandbox_path` (latest execution wins), presenting the user with
|
|
44
|
+
* a file-explorer-like view of the conversation's output — no
|
|
45
|
+
* execution/turn concepts are exposed.
|
|
36
46
|
*
|
|
37
47
|
* Composes {@link ArtifactCard} (summary + detection badges) with
|
|
38
48
|
* {@link ArtifactPreviewModal} (full content review + Apply/Push CTA).
|
|
39
49
|
* The card's "Preview" action opens the modal; the modal is the sole
|
|
40
50
|
* location for Apply/Push actions (review-before-apply pattern).
|
|
41
51
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* - **Terminal phase**: derived via {@link isTerminalPhase} — controls
|
|
46
|
-
* whether the Apply CTA in the modal is enabled
|
|
47
|
-
* - **Execution ID**: read from `execution.metadata.id`
|
|
48
|
-
*
|
|
49
|
-
* Returns `null` when the execution is `null` or has no artifacts,
|
|
50
|
-
* matching the conditional-render pattern of {@link ExecutionProgress}
|
|
51
|
-
* and {@link ExecutionCostSummary}.
|
|
52
|
+
* Returns `null` when the executions list is empty or no execution
|
|
53
|
+
* has artifacts, matching the conditional-render pattern of
|
|
54
|
+
* {@link ExecutionProgress} and {@link ExecutionCostSummary}.
|
|
52
55
|
*
|
|
53
56
|
* Renders without card chrome — each {@link ArtifactCard} provides its
|
|
54
57
|
* own border and padding. The consumer controls the container styling
|
|
@@ -58,10 +61,13 @@ export interface ArtifactsWidgetProps {
|
|
|
58
61
|
*
|
|
59
62
|
* @example
|
|
60
63
|
* ```tsx
|
|
61
|
-
* const
|
|
64
|
+
* const conv = useSessionConversation(sessionId, org);
|
|
62
65
|
*
|
|
63
66
|
* <ArtifactsWidget
|
|
64
|
-
*
|
|
67
|
+
* executions={[
|
|
68
|
+
* ...conv.completedExecutions,
|
|
69
|
+
* ...(conv.activeStreamExecution ? [conv.activeStreamExecution] : []),
|
|
70
|
+
* ]}
|
|
65
71
|
* org={activeOrg}
|
|
66
72
|
* onApplied={(result) => toast(`${result.kind} applied`)}
|
|
67
73
|
* />
|
|
@@ -69,29 +75,46 @@ export interface ArtifactsWidgetProps {
|
|
|
69
75
|
*
|
|
70
76
|
* @see {@link ArtifactCard} — compact summary card per artifact
|
|
71
77
|
* @see {@link ArtifactPreviewModal} — full preview with Apply/Push CTA
|
|
72
|
-
* @see {@link
|
|
78
|
+
* @see {@link useSessionArtifacts} — headless session-level artifact aggregation hook
|
|
79
|
+
* @see {@link useExecutionArtifacts} — headless single-execution artifact extraction hook
|
|
73
80
|
*/
|
|
74
81
|
export function ArtifactsWidget({
|
|
75
|
-
|
|
82
|
+
executions,
|
|
76
83
|
org,
|
|
77
84
|
onApplied,
|
|
78
85
|
className,
|
|
79
86
|
}: ArtifactsWidgetProps) {
|
|
80
87
|
const { artifacts, hasArtifacts, artifactCount } =
|
|
81
|
-
|
|
88
|
+
useSessionArtifacts(executions);
|
|
82
89
|
|
|
83
|
-
|
|
84
|
-
|
|
90
|
+
// Store the dedup key (sandboxPath or name) instead of a snapshot of
|
|
91
|
+
// the full entry. The actual entry is derived from the live artifacts
|
|
92
|
+
// list on every render, so the preview modal always reflects the
|
|
93
|
+
// latest artifact version — even when a newer execution publishes an
|
|
94
|
+
// updated artifact for the same path while the modal is open.
|
|
95
|
+
const [previewKey, setPreviewKey] = useState<string | null>(null);
|
|
85
96
|
|
|
86
|
-
|
|
97
|
+
const previewEntry = useMemo<SessionArtifactEntry | null>(
|
|
98
|
+
() =>
|
|
99
|
+
previewKey !== null
|
|
100
|
+
? artifacts.find(
|
|
101
|
+
(e) =>
|
|
102
|
+
(e.artifact.sandboxPath || e.artifact.name) === previewKey,
|
|
103
|
+
) ?? null
|
|
104
|
+
: null,
|
|
105
|
+
[previewKey, artifacts],
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const handlePreview = useCallback(
|
|
109
|
+
(entry: SessionArtifactEntry) =>
|
|
110
|
+
setPreviewKey(entry.artifact.sandboxPath || entry.artifact.name),
|
|
111
|
+
[],
|
|
112
|
+
);
|
|
87
113
|
|
|
88
|
-
|
|
89
|
-
execution?.status?.phase ?? ExecutionPhase.EXECUTION_PHASE_UNSPECIFIED;
|
|
90
|
-
const isTerminal = isTerminalPhase(phase);
|
|
91
|
-
const executionId = execution?.metadata?.id ?? "";
|
|
114
|
+
if (!hasArtifacts) return null;
|
|
92
115
|
|
|
93
116
|
return (
|
|
94
|
-
<section aria-label="
|
|
117
|
+
<section aria-label="Artifacts" className={cn(className)}>
|
|
95
118
|
<div className="mb-2 flex items-center gap-2">
|
|
96
119
|
<h3 className="text-sm font-medium text-foreground">Artifacts</h3>
|
|
97
120
|
<span className="inline-flex min-w-[1.25rem] items-center justify-center rounded-full bg-muted px-1.5 text-xs tabular-nums text-muted-foreground">
|
|
@@ -100,26 +123,27 @@ export function ArtifactsWidget({
|
|
|
100
123
|
</div>
|
|
101
124
|
|
|
102
125
|
<div role="list" className="space-y-2">
|
|
103
|
-
{artifacts.map((
|
|
104
|
-
<div key={artifact.storageKey} role="listitem">
|
|
126
|
+
{artifacts.map((entry) => (
|
|
127
|
+
<div key={entry.artifact.storageKey} role="listitem">
|
|
105
128
|
<ArtifactCard
|
|
106
|
-
artifact={artifact}
|
|
107
|
-
executionId={executionId}
|
|
129
|
+
artifact={entry.artifact}
|
|
130
|
+
executionId={entry.executionId}
|
|
108
131
|
org={org}
|
|
109
|
-
|
|
132
|
+
hasNameCollision={entry.hasNameCollision}
|
|
133
|
+
onPreview={() => handlePreview(entry)}
|
|
110
134
|
/>
|
|
111
135
|
</div>
|
|
112
136
|
))}
|
|
113
137
|
</div>
|
|
114
138
|
|
|
115
|
-
{
|
|
139
|
+
{previewEntry && (
|
|
116
140
|
<ArtifactPreviewModal
|
|
117
|
-
artifact={
|
|
118
|
-
executionId={executionId}
|
|
141
|
+
artifact={previewEntry.artifact}
|
|
142
|
+
executionId={previewEntry.executionId}
|
|
119
143
|
org={org}
|
|
120
|
-
isTerminal={isTerminal}
|
|
144
|
+
isTerminal={previewEntry.isTerminal}
|
|
121
145
|
open
|
|
122
|
-
onClose={() =>
|
|
146
|
+
onClose={() => setPreviewKey(null)}
|
|
123
147
|
onApplied={onApplied}
|
|
124
148
|
/>
|
|
125
149
|
)}
|
|
@@ -22,6 +22,7 @@ import { SetupProgress } from "./SetupProgress";
|
|
|
22
22
|
import { ApprovalCard } from "./ApprovalCard";
|
|
23
23
|
import { FilePathContext, type FilePathContextValue } from "./FilePathContext";
|
|
24
24
|
import type { ResolvedPathAction } from "./file-path-resolver";
|
|
25
|
+
import { SandboxContext, type SandboxContextValue } from "./SandboxContext";
|
|
25
26
|
|
|
26
27
|
export interface MessageThreadProps {
|
|
27
28
|
/** Completed executions in chronological order. */
|
|
@@ -87,6 +88,15 @@ export interface MessageThreadProps {
|
|
|
87
88
|
path: string,
|
|
88
89
|
resolved: ResolvedPathAction,
|
|
89
90
|
) => void;
|
|
91
|
+
/**
|
|
92
|
+
* Absolute sandbox workspace root (e.g. `/home/daytona/workspace`).
|
|
93
|
+
* When provided, shell commands and tool output normalize absolute
|
|
94
|
+
* sandbox paths to workspace-relative display paths.
|
|
95
|
+
*
|
|
96
|
+
* Pass an empty string or omit for local sessions where paths are
|
|
97
|
+
* the user's own filesystem (no normalization needed).
|
|
98
|
+
*/
|
|
99
|
+
readonly sandboxWorkspaceRoot?: string;
|
|
90
100
|
}
|
|
91
101
|
|
|
92
102
|
const AUTO_SCROLL_THRESHOLD_PX = 80;
|
|
@@ -103,7 +113,7 @@ type ThreadItem =
|
|
|
103
113
|
| { readonly kind: "phase-badge"; readonly phase: ExecutionPhase; readonly key: string }
|
|
104
114
|
| { readonly kind: "pending-message"; readonly content: string; readonly key: string }
|
|
105
115
|
| { readonly kind: "approval-request"; readonly pendingApproval: PendingApproval; readonly key: string }
|
|
106
|
-
| { readonly kind: "setup-progress"; readonly workspaceEntries: readonly WorkspaceEntry[]; readonly key: string };
|
|
116
|
+
| { readonly kind: "setup-progress"; readonly workspaceEntries: readonly WorkspaceEntry[]; readonly serverPhase?: string; readonly key: string };
|
|
107
117
|
|
|
108
118
|
function hasAiMessages(execution: AgentExecution): boolean {
|
|
109
119
|
const messages = execution.status?.messages;
|
|
@@ -186,9 +196,12 @@ function buildThreadItems(
|
|
|
186
196
|
lastPhase === ExecutionPhase.EXECUTION_PHASE_UNSPECIFIED) &&
|
|
187
197
|
!hasAiMessages(activeStreamExecution)
|
|
188
198
|
) {
|
|
199
|
+
const serverPhase =
|
|
200
|
+
activeStreamExecution.status?.setupProgress?.currentPhase || undefined;
|
|
189
201
|
items.push({
|
|
190
202
|
kind: "setup-progress",
|
|
191
203
|
workspaceEntries: workspaceEntries ?? [],
|
|
204
|
+
serverPhase,
|
|
192
205
|
key: "setup-progress",
|
|
193
206
|
});
|
|
194
207
|
}
|
|
@@ -266,6 +279,7 @@ export function MessageThread({
|
|
|
266
279
|
dismissedApprovalIds,
|
|
267
280
|
workspaceEntries,
|
|
268
281
|
onFilePathClick,
|
|
282
|
+
sandboxWorkspaceRoot,
|
|
269
283
|
}: MessageThreadProps) {
|
|
270
284
|
const scrollRef = useRef<HTMLDivElement>(null);
|
|
271
285
|
const isNearBottomRef = useRef(true);
|
|
@@ -298,6 +312,11 @@ export function MessageThread({
|
|
|
298
312
|
[workspaceEntries, onFilePathClick],
|
|
299
313
|
);
|
|
300
314
|
|
|
315
|
+
const sandboxCtx = useMemo<SandboxContextValue>(
|
|
316
|
+
() => ({ sandboxWorkspaceRoot: sandboxWorkspaceRoot ?? "" }),
|
|
317
|
+
[sandboxWorkspaceRoot],
|
|
318
|
+
);
|
|
319
|
+
|
|
301
320
|
return (
|
|
302
321
|
<div
|
|
303
322
|
ref={scrollRef}
|
|
@@ -314,6 +333,7 @@ export function MessageThread({
|
|
|
314
333
|
className,
|
|
315
334
|
)}
|
|
316
335
|
>
|
|
336
|
+
<SandboxContext.Provider value={sandboxCtx}>
|
|
317
337
|
<FilePathContext.Provider value={filePathCtx}>
|
|
318
338
|
{items.map((item) => {
|
|
319
339
|
switch (item.kind) {
|
|
@@ -352,6 +372,7 @@ export function MessageThread({
|
|
|
352
372
|
<SetupProgress
|
|
353
373
|
key={item.key}
|
|
354
374
|
workspaceEntries={item.workspaceEntries}
|
|
375
|
+
serverPhase={item.serverPhase}
|
|
355
376
|
/>
|
|
356
377
|
);
|
|
357
378
|
case "pending-message":
|
|
@@ -370,6 +391,7 @@ export function MessageThread({
|
|
|
370
391
|
}
|
|
371
392
|
})}
|
|
372
393
|
</FilePathContext.Provider>
|
|
394
|
+
</SandboxContext.Provider>
|
|
373
395
|
</div>
|
|
374
396
|
);
|
|
375
397
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { createContext, useCallback, useContext } from "react";
|
|
2
|
+
import { normalizeSandboxPaths } from "./sandbox-path-normalizer";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Context value that carries the sandbox workspace root for display-time
|
|
6
|
+
* path normalization.
|
|
7
|
+
*
|
|
8
|
+
* Provided by {@link MessageThread} (or a platform builder's custom
|
|
9
|
+
* wrapper). When no provider is present, path normalization is a no-op.
|
|
10
|
+
*/
|
|
11
|
+
export interface SandboxContextValue {
|
|
12
|
+
/**
|
|
13
|
+
* Absolute sandbox workspace root (e.g. `/home/daytona/workspace`).
|
|
14
|
+
* Empty string disables normalization (local mode, backward compat).
|
|
15
|
+
*/
|
|
16
|
+
readonly sandboxWorkspaceRoot: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const DEFAULT_VALUE: SandboxContextValue = {
|
|
20
|
+
sandboxWorkspaceRoot: "",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const SandboxContext =
|
|
24
|
+
createContext<SandboxContextValue>(DEFAULT_VALUE);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Returns a stable normalizer function that replaces absolute sandbox
|
|
28
|
+
* paths in the given text with workspace-relative display paths.
|
|
29
|
+
*
|
|
30
|
+
* When `sandboxWorkspaceRoot` is empty (no provider or local mode),
|
|
31
|
+
* returns the identity function — zero overhead.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```tsx
|
|
35
|
+
* function ShellCommand({ command }: { command: string }) {
|
|
36
|
+
* const normalize = useSandboxNormalize();
|
|
37
|
+
* return <code>{normalize(command)}</code>;
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export function useSandboxNormalize(): (text: string) => string {
|
|
42
|
+
const { sandboxWorkspaceRoot } = useContext(SandboxContext);
|
|
43
|
+
return useCallback(
|
|
44
|
+
(text: string) => normalizeSandboxPaths(text, sandboxWorkspaceRoot),
|
|
45
|
+
[sandboxWorkspaceRoot],
|
|
46
|
+
);
|
|
47
|
+
}
|
|
@@ -7,14 +7,25 @@ import { cn } from "@stigmer/theme";
|
|
|
7
7
|
export interface SetupProgressProps {
|
|
8
8
|
/**
|
|
9
9
|
* Workspace entries from the session spec. When git-sourced entries
|
|
10
|
-
* are present, the indicator shows workspace-specific messaging
|
|
11
|
-
* (e.g. "Setting up workspace...")
|
|
12
|
-
* setup sequence.
|
|
10
|
+
* are present, the fallback indicator shows workspace-specific messaging
|
|
11
|
+
* (e.g. "Setting up workspace...").
|
|
13
12
|
*/
|
|
14
13
|
readonly workspaceEntries?: readonly WorkspaceEntry[];
|
|
14
|
+
/**
|
|
15
|
+
* Server-reported setup phase label from
|
|
16
|
+
* `AgentExecutionStatus.setup_progress.current_phase`.
|
|
17
|
+
*
|
|
18
|
+
* When non-empty, rendered directly — the timer-based fallback is
|
|
19
|
+
* bypassed. When absent or empty (older backends that don't emit
|
|
20
|
+
* setup progress), the component falls back to the time-based
|
|
21
|
+
* step sequence derived from `workspaceEntries`.
|
|
22
|
+
*/
|
|
23
|
+
readonly serverPhase?: string;
|
|
15
24
|
readonly className?: string;
|
|
16
25
|
}
|
|
17
26
|
|
|
27
|
+
/* ── Timer-based fallback (used when no serverPhase is available) ─── */
|
|
28
|
+
|
|
18
29
|
interface SetupStep {
|
|
19
30
|
readonly message: string;
|
|
20
31
|
readonly durationMs: number;
|
|
@@ -55,28 +66,33 @@ function buildSteps(
|
|
|
55
66
|
* Animated inline indicator shown in the message thread while an
|
|
56
67
|
* execution is in the `PENDING` phase and no AI messages have arrived.
|
|
57
68
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* merging environment variables, loading skills, and connecting MCP
|
|
62
|
-
* servers.
|
|
69
|
+
* **Server-driven mode** — when the backend reports
|
|
70
|
+
* `setup_progress.current_phase` through the execution stream, the
|
|
71
|
+
* component renders the server-reported label directly.
|
|
63
72
|
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* timer with server-reported progress.
|
|
73
|
+
* **Timer-based fallback** — when `serverPhase` is absent (older
|
|
74
|
+
* backends), the component cycles through contextual status messages
|
|
75
|
+
* derived from the session configuration on a fixed interval.
|
|
68
76
|
*
|
|
69
77
|
* All visual properties flow through `--stgm-*` tokens.
|
|
70
78
|
*
|
|
71
79
|
* @example
|
|
72
80
|
* ```tsx
|
|
81
|
+
* // Server-driven (preferred)
|
|
82
|
+
* <SetupProgress serverPhase={execution.status?.setupProgress?.currentPhase} />
|
|
83
|
+
*
|
|
84
|
+
* // Fallback (no server phase available)
|
|
73
85
|
* <SetupProgress workspaceEntries={session.spec?.workspaceEntries} />
|
|
74
86
|
* ```
|
|
75
87
|
*/
|
|
76
88
|
export function SetupProgress({
|
|
77
89
|
workspaceEntries,
|
|
90
|
+
serverPhase,
|
|
78
91
|
className,
|
|
79
92
|
}: SetupProgressProps) {
|
|
93
|
+
const useServerPhase = !!serverPhase;
|
|
94
|
+
|
|
95
|
+
/* ── Timer-based fallback state ─────────────────────────────────── */
|
|
80
96
|
const steps = useMemo(() => buildSteps(workspaceEntries), [workspaceEntries]);
|
|
81
97
|
const [stepIndex, setStepIndex] = useState(0);
|
|
82
98
|
|
|
@@ -85,6 +101,7 @@ export function SetupProgress({
|
|
|
85
101
|
}, [steps]);
|
|
86
102
|
|
|
87
103
|
useEffect(() => {
|
|
104
|
+
if (useServerPhase) return;
|
|
88
105
|
if (stepIndex >= steps.length - 1) return;
|
|
89
106
|
|
|
90
107
|
const timer = setTimeout(() => {
|
|
@@ -92,9 +109,11 @@ export function SetupProgress({
|
|
|
92
109
|
}, steps[stepIndex].durationMs);
|
|
93
110
|
|
|
94
111
|
return () => clearTimeout(timer);
|
|
95
|
-
}, [stepIndex, steps]);
|
|
112
|
+
}, [useServerPhase, stepIndex, steps]);
|
|
96
113
|
|
|
97
|
-
|
|
114
|
+
/* ── Resolve display message ────────────────────────────────────── */
|
|
115
|
+
const currentMessage =
|
|
116
|
+
serverPhase || steps[Math.min(stepIndex, steps.length - 1)].message;
|
|
98
117
|
|
|
99
118
|
return (
|
|
100
119
|
<div
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import type { ToolCategory, ToolCategoryInfo } from "./tool-categories";
|
|
10
10
|
import { FilePathLink } from "./FilePathLink";
|
|
11
11
|
import { McpArgsView, McpMetadataRow } from "./McpToolDetail";
|
|
12
|
+
import { useSandboxNormalize } from "./SandboxContext";
|
|
12
13
|
import {
|
|
13
14
|
CollapsibleCode,
|
|
14
15
|
FilePathIcon,
|
|
@@ -142,13 +143,14 @@ function CategoryArgsDispatch({
|
|
|
142
143
|
// ---------------------------------------------------------------------------
|
|
143
144
|
|
|
144
145
|
function ShellArgsView({ command }: { command: string }) {
|
|
146
|
+
const normalize = useSandboxNormalize();
|
|
145
147
|
return (
|
|
146
148
|
<div className="rounded-md border border-border bg-[var(--stgm-terminal-bg,#1a1a2e)] p-2.5">
|
|
147
149
|
<pre className="whitespace-pre-wrap break-words font-mono text-xs text-[var(--stgm-terminal-fg,#e0e0e0)]">
|
|
148
150
|
<span className="select-none text-[var(--stgm-terminal-prompt,#6b7280)]">
|
|
149
151
|
${" "}
|
|
150
152
|
</span>
|
|
151
|
-
{command}
|
|
153
|
+
{normalize(command)}
|
|
152
154
|
</pre>
|
|
153
155
|
</div>
|
|
154
156
|
);
|
|
@@ -5,6 +5,7 @@ import { ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecutio
|
|
|
5
5
|
import { cn } from "@stigmer/theme";
|
|
6
6
|
import { resolveToolCategory } from "./tool-categories";
|
|
7
7
|
import { McpToolDetail } from "./McpToolDetail";
|
|
8
|
+
import { useSandboxNormalize } from "./SandboxContext";
|
|
8
9
|
import { ToolArgsView } from "./ToolArgsView";
|
|
9
10
|
import {
|
|
10
11
|
CollapsibleCode,
|
|
@@ -105,6 +106,7 @@ function CategoryRenderer({
|
|
|
105
106
|
|
|
106
107
|
function ShellToolDetail({ toolCall }: { toolCall: ToolCall }) {
|
|
107
108
|
const duration = formatDuration(toolCall.startedAt, toolCall.completedAt);
|
|
109
|
+
const normalize = useSandboxNormalize();
|
|
108
110
|
|
|
109
111
|
return (
|
|
110
112
|
<>
|
|
@@ -121,7 +123,7 @@ function ShellToolDetail({ toolCall }: { toolCall: ToolCall }) {
|
|
|
121
123
|
<span className="font-medium text-muted-foreground">Output</span>
|
|
122
124
|
<div className="rounded-md border border-border bg-[var(--stgm-terminal-bg,#1a1a2e)] p-2.5">
|
|
123
125
|
<CollapsiblePre
|
|
124
|
-
content={toolCall.result}
|
|
126
|
+
content={normalize(toolCall.result)}
|
|
125
127
|
className="text-[var(--stgm-terminal-fg,#e0e0e0)]"
|
|
126
128
|
/>
|
|
127
129
|
</div>
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
extractPrimaryArg,
|
|
17
17
|
type ToolCategory,
|
|
18
18
|
} from "./tool-categories";
|
|
19
|
+
import { useSandboxNormalize } from "./SandboxContext";
|
|
19
20
|
|
|
20
21
|
export interface ToolCallItemProps {
|
|
21
22
|
readonly toolCall: ToolCall;
|
|
@@ -74,6 +75,7 @@ export function ToolCallItem({
|
|
|
74
75
|
? subAgentExecution.subject || subAgentExecution.name || categoryInfo.label
|
|
75
76
|
: categoryInfo.label;
|
|
76
77
|
|
|
78
|
+
const normalize = useSandboxNormalize();
|
|
77
79
|
const approvalBadge = getApprovalBadge(toolCall);
|
|
78
80
|
|
|
79
81
|
// Completed/skipped Read items are non-expandable — the clickable
|
|
@@ -144,7 +146,11 @@ export function ToolCallItem({
|
|
|
144
146
|
);
|
|
145
147
|
}
|
|
146
148
|
|
|
147
|
-
const displaySubtitle = isSubAgent
|
|
149
|
+
const displaySubtitle = isSubAgent
|
|
150
|
+
? null
|
|
151
|
+
: categoryInfo.category === "shell" && primaryArg
|
|
152
|
+
? normalize(primaryArg)
|
|
153
|
+
: primaryArg;
|
|
148
154
|
|
|
149
155
|
return (
|
|
150
156
|
<div className={cn("border-b border-border/50 last:border-b-0", className)}>
|