@stigmer/react 0.0.53 → 0.0.54
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 +24 -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 +19 -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/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 +51 -43
- package/src/execution/MessageThread.tsx +18 -0
- package/src/execution/SandboxContext.ts +47 -0
- 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, 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,30 @@ 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
|
-
const [
|
|
84
|
-
useState<
|
|
90
|
+
const [previewEntry, setPreviewEntry] =
|
|
91
|
+
useState<SessionArtifactEntry | null>(null);
|
|
85
92
|
|
|
86
|
-
|
|
93
|
+
const handlePreview = useCallback(
|
|
94
|
+
(entry: SessionArtifactEntry) => setPreviewEntry(entry),
|
|
95
|
+
[],
|
|
96
|
+
);
|
|
87
97
|
|
|
88
|
-
|
|
89
|
-
execution?.status?.phase ?? ExecutionPhase.EXECUTION_PHASE_UNSPECIFIED;
|
|
90
|
-
const isTerminal = isTerminalPhase(phase);
|
|
91
|
-
const executionId = execution?.metadata?.id ?? "";
|
|
98
|
+
if (!hasArtifacts) return null;
|
|
92
99
|
|
|
93
100
|
return (
|
|
94
|
-
<section aria-label="
|
|
101
|
+
<section aria-label="Artifacts" className={cn(className)}>
|
|
95
102
|
<div className="mb-2 flex items-center gap-2">
|
|
96
103
|
<h3 className="text-sm font-medium text-foreground">Artifacts</h3>
|
|
97
104
|
<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 +107,27 @@ export function ArtifactsWidget({
|
|
|
100
107
|
</div>
|
|
101
108
|
|
|
102
109
|
<div role="list" className="space-y-2">
|
|
103
|
-
{artifacts.map((
|
|
104
|
-
<div key={artifact.storageKey} role="listitem">
|
|
110
|
+
{artifacts.map((entry) => (
|
|
111
|
+
<div key={entry.artifact.storageKey} role="listitem">
|
|
105
112
|
<ArtifactCard
|
|
106
|
-
artifact={artifact}
|
|
107
|
-
executionId={executionId}
|
|
113
|
+
artifact={entry.artifact}
|
|
114
|
+
executionId={entry.executionId}
|
|
108
115
|
org={org}
|
|
109
|
-
|
|
116
|
+
hasNameCollision={entry.hasNameCollision}
|
|
117
|
+
onPreview={() => handlePreview(entry)}
|
|
110
118
|
/>
|
|
111
119
|
</div>
|
|
112
120
|
))}
|
|
113
121
|
</div>
|
|
114
122
|
|
|
115
|
-
{
|
|
123
|
+
{previewEntry && (
|
|
116
124
|
<ArtifactPreviewModal
|
|
117
|
-
artifact={
|
|
118
|
-
executionId={executionId}
|
|
125
|
+
artifact={previewEntry.artifact}
|
|
126
|
+
executionId={previewEntry.executionId}
|
|
119
127
|
org={org}
|
|
120
|
-
isTerminal={isTerminal}
|
|
128
|
+
isTerminal={previewEntry.isTerminal}
|
|
121
129
|
open
|
|
122
|
-
onClose={() =>
|
|
130
|
+
onClose={() => setPreviewEntry(null)}
|
|
123
131
|
onApplied={onApplied}
|
|
124
132
|
/>
|
|
125
133
|
)}
|
|
@@ -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;
|
|
@@ -266,6 +276,7 @@ export function MessageThread({
|
|
|
266
276
|
dismissedApprovalIds,
|
|
267
277
|
workspaceEntries,
|
|
268
278
|
onFilePathClick,
|
|
279
|
+
sandboxWorkspaceRoot,
|
|
269
280
|
}: MessageThreadProps) {
|
|
270
281
|
const scrollRef = useRef<HTMLDivElement>(null);
|
|
271
282
|
const isNearBottomRef = useRef(true);
|
|
@@ -298,6 +309,11 @@ export function MessageThread({
|
|
|
298
309
|
[workspaceEntries, onFilePathClick],
|
|
299
310
|
);
|
|
300
311
|
|
|
312
|
+
const sandboxCtx = useMemo<SandboxContextValue>(
|
|
313
|
+
() => ({ sandboxWorkspaceRoot: sandboxWorkspaceRoot ?? "" }),
|
|
314
|
+
[sandboxWorkspaceRoot],
|
|
315
|
+
);
|
|
316
|
+
|
|
301
317
|
return (
|
|
302
318
|
<div
|
|
303
319
|
ref={scrollRef}
|
|
@@ -314,6 +330,7 @@ export function MessageThread({
|
|
|
314
330
|
className,
|
|
315
331
|
)}
|
|
316
332
|
>
|
|
333
|
+
<SandboxContext.Provider value={sandboxCtx}>
|
|
317
334
|
<FilePathContext.Provider value={filePathCtx}>
|
|
318
335
|
{items.map((item) => {
|
|
319
336
|
switch (item.kind) {
|
|
@@ -370,6 +387,7 @@ export function MessageThread({
|
|
|
370
387
|
}
|
|
371
388
|
})}
|
|
372
389
|
</FilePathContext.Provider>
|
|
390
|
+
</SandboxContext.Provider>
|
|
373
391
|
</div>
|
|
374
392
|
);
|
|
375
393
|
}
|
|
@@ -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
|
+
}
|
|
@@ -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)}>
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { WorkspaceWriteBack } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/writeback_pb";
|
|
4
|
+
import { WorkspaceWriteBackPhase } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/writeback_pb";
|
|
5
|
+
import { cn } from "@stigmer/theme";
|
|
6
|
+
|
|
7
|
+
export interface WriteBackCardProps {
|
|
8
|
+
/** The workspace write-back outcome to render. */
|
|
9
|
+
readonly writeBack: WorkspaceWriteBack;
|
|
10
|
+
/** Additional CSS classes for the root element. */
|
|
11
|
+
readonly className?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Renders a single workspace write-back outcome as a compact card.
|
|
16
|
+
*
|
|
17
|
+
* Shows the workspace entry name, branch, diff summary, phase
|
|
18
|
+
* indicator, and a "View PR" link when the pull request was
|
|
19
|
+
* successfully created. On failure, displays the error message.
|
|
20
|
+
*
|
|
21
|
+
* Themed via standard semantic tokens — no hardcoded colors, no
|
|
22
|
+
* Console dependencies. Embedders can theme this card through
|
|
23
|
+
* their Tailwind/CSS configuration.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* const { writeBacks } = useWorkspaceWriteBacks(execution);
|
|
28
|
+
*
|
|
29
|
+
* {writeBacks.map((wb) => (
|
|
30
|
+
* <WriteBackCard
|
|
31
|
+
* key={wb.workspaceEntryName}
|
|
32
|
+
* writeBack={wb}
|
|
33
|
+
* />
|
|
34
|
+
* ))}
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @see useWorkspaceWriteBacks — extracts write-back data from an execution
|
|
38
|
+
*/
|
|
39
|
+
export function WriteBackCard({ writeBack, className }: WriteBackCardProps) {
|
|
40
|
+
const isFailed =
|
|
41
|
+
writeBack.phase === WorkspaceWriteBackPhase.WORKSPACE_WRITE_BACK_FAILED;
|
|
42
|
+
const hasPR = !!writeBack.pullRequestUrl;
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<div
|
|
46
|
+
role="article"
|
|
47
|
+
aria-label={`Write-back: ${writeBack.workspaceEntryName}`}
|
|
48
|
+
className={cn(
|
|
49
|
+
"rounded-md border p-3",
|
|
50
|
+
isFailed ? "border-destructive/40" : "border-border",
|
|
51
|
+
className,
|
|
52
|
+
)}
|
|
53
|
+
>
|
|
54
|
+
{/* Header: icon + workspace name + phase badge */}
|
|
55
|
+
<div className="flex items-start gap-2">
|
|
56
|
+
<span className="mt-0.5 shrink-0 text-muted-foreground">
|
|
57
|
+
<GitBranchIcon />
|
|
58
|
+
</span>
|
|
59
|
+
<div className="min-w-0 flex-1">
|
|
60
|
+
<div className="flex items-center gap-2">
|
|
61
|
+
<span className="truncate text-sm font-medium text-foreground">
|
|
62
|
+
{writeBack.workspaceEntryName}
|
|
63
|
+
</span>
|
|
64
|
+
<PhaseBadge phase={writeBack.phase} />
|
|
65
|
+
</div>
|
|
66
|
+
{writeBack.branchName && (
|
|
67
|
+
<div className="mt-0.5 truncate font-mono text-xs text-muted-foreground">
|
|
68
|
+
{writeBack.branchName}
|
|
69
|
+
{writeBack.baseBranch && (
|
|
70
|
+
<span className="text-muted-foreground/60">
|
|
71
|
+
{" \u2190 "}
|
|
72
|
+
{writeBack.baseBranch}
|
|
73
|
+
</span>
|
|
74
|
+
)}
|
|
75
|
+
</div>
|
|
76
|
+
)}
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
{/* Diff summary */}
|
|
81
|
+
{writeBack.diffSummary && (
|
|
82
|
+
<div className="mt-1.5 whitespace-pre-wrap font-mono text-xs text-muted-foreground">
|
|
83
|
+
{writeBack.diffSummary}
|
|
84
|
+
</div>
|
|
85
|
+
)}
|
|
86
|
+
|
|
87
|
+
{/* Error message */}
|
|
88
|
+
{isFailed && writeBack.error && (
|
|
89
|
+
<div className="mt-1.5 rounded bg-destructive/10 px-2 py-1 text-xs text-destructive">
|
|
90
|
+
{writeBack.error}
|
|
91
|
+
</div>
|
|
92
|
+
)}
|
|
93
|
+
|
|
94
|
+
{/* PR link */}
|
|
95
|
+
{hasPR && (
|
|
96
|
+
<div className="mt-2">
|
|
97
|
+
<a
|
|
98
|
+
href={writeBack.pullRequestUrl}
|
|
99
|
+
target="_blank"
|
|
100
|
+
rel="noopener noreferrer"
|
|
101
|
+
className={cn(
|
|
102
|
+
"inline-flex items-center gap-1 text-xs font-medium text-primary transition-colors hover:text-primary/80",
|
|
103
|
+
FOCUS_RING_CLASSES,
|
|
104
|
+
)}
|
|
105
|
+
>
|
|
106
|
+
<ExternalLinkIcon />
|
|
107
|
+
View PR #{writeBack.pullRequestNumber}
|
|
108
|
+
</a>
|
|
109
|
+
</div>
|
|
110
|
+
)}
|
|
111
|
+
</div>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
// Phase badge
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
const PHASE_CONFIG: Record<
|
|
120
|
+
number,
|
|
121
|
+
{ label: string; className: string }
|
|
122
|
+
> = {
|
|
123
|
+
[WorkspaceWriteBackPhase.WORKSPACE_WRITE_BACK_COMMITTED]: {
|
|
124
|
+
label: "Committed",
|
|
125
|
+
className: "bg-muted text-muted-foreground",
|
|
126
|
+
},
|
|
127
|
+
[WorkspaceWriteBackPhase.WORKSPACE_WRITE_BACK_PUSHED]: {
|
|
128
|
+
label: "Pushed",
|
|
129
|
+
className: "bg-muted text-muted-foreground",
|
|
130
|
+
},
|
|
131
|
+
[WorkspaceWriteBackPhase.WORKSPACE_WRITE_BACK_PR_CREATED]: {
|
|
132
|
+
label: "PR Created",
|
|
133
|
+
className: "bg-primary/10 text-primary",
|
|
134
|
+
},
|
|
135
|
+
[WorkspaceWriteBackPhase.WORKSPACE_WRITE_BACK_FAILED]: {
|
|
136
|
+
label: "Failed",
|
|
137
|
+
className: "bg-destructive/10 text-destructive",
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
function PhaseBadge({ phase }: { phase: number }) {
|
|
142
|
+
const config = PHASE_CONFIG[phase];
|
|
143
|
+
if (!config) return null;
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<span
|
|
147
|
+
className={cn(
|
|
148
|
+
"inline-flex shrink-0 items-center rounded-full px-1.5 py-0.5 text-[10px] font-medium leading-none",
|
|
149
|
+
config.className,
|
|
150
|
+
)}
|
|
151
|
+
>
|
|
152
|
+
{config.label}
|
|
153
|
+
</span>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
// Shared style constants
|
|
159
|
+
// ---------------------------------------------------------------------------
|
|
160
|
+
|
|
161
|
+
const FOCUS_RING_CLASSES =
|
|
162
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:rounded-sm";
|
|
163
|
+
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
// Inline SVG icons
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
|
|
168
|
+
function GitBranchIcon() {
|
|
169
|
+
return (
|
|
170
|
+
<svg
|
|
171
|
+
width="14"
|
|
172
|
+
height="14"
|
|
173
|
+
viewBox="0 0 14 14"
|
|
174
|
+
fill="none"
|
|
175
|
+
stroke="currentColor"
|
|
176
|
+
strokeWidth="1.5"
|
|
177
|
+
strokeLinecap="round"
|
|
178
|
+
strokeLinejoin="round"
|
|
179
|
+
aria-hidden="true"
|
|
180
|
+
>
|
|
181
|
+
<circle cx="4" cy="3.5" r="1.5" />
|
|
182
|
+
<circle cx="4" cy="10.5" r="1.5" />
|
|
183
|
+
<circle cx="10" cy="5.5" r="1.5" />
|
|
184
|
+
<path d="M4 5V9" />
|
|
185
|
+
<path d="M10 7V5.5" />
|
|
186
|
+
<path d="M4 5C4 5 4 7 7 7C10 7 10 5.5 10 5.5" />
|
|
187
|
+
</svg>
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function ExternalLinkIcon() {
|
|
192
|
+
return (
|
|
193
|
+
<svg
|
|
194
|
+
width="10"
|
|
195
|
+
height="10"
|
|
196
|
+
viewBox="0 0 12 12"
|
|
197
|
+
fill="none"
|
|
198
|
+
stroke="currentColor"
|
|
199
|
+
strokeWidth="1.5"
|
|
200
|
+
strokeLinecap="round"
|
|
201
|
+
strokeLinejoin="round"
|
|
202
|
+
className="shrink-0"
|
|
203
|
+
aria-hidden="true"
|
|
204
|
+
>
|
|
205
|
+
<path d="M9 3L3 9" />
|
|
206
|
+
<path d="M5 3H9V7" />
|
|
207
|
+
<path d="M9 8V10H2V3H4" />
|
|
208
|
+
</svg>
|
|
209
|
+
);
|
|
210
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { AgentExecution } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
4
|
+
import { cn } from "@stigmer/theme";
|
|
5
|
+
import { useSessionWriteBacks } from "../session/useSessionWriteBacks";
|
|
6
|
+
import { WriteBackCard } from "./WriteBackCard";
|
|
7
|
+
|
|
8
|
+
export interface WriteBacksWidgetProps {
|
|
9
|
+
/**
|
|
10
|
+
* All executions for the current session — both completed and
|
|
11
|
+
* actively streaming. The widget aggregates write-backs across
|
|
12
|
+
* every execution, deduplicates by `workspace_entry_name` (latest
|
|
13
|
+
* wins), and sorts alphabetically.
|
|
14
|
+
*
|
|
15
|
+
* Renders nothing when the list is empty or no execution has
|
|
16
|
+
* write-backs.
|
|
17
|
+
*/
|
|
18
|
+
readonly executions: readonly AgentExecution[];
|
|
19
|
+
/** Additional CSS classes for the root element. */
|
|
20
|
+
readonly className?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Right-sidebar widget that surfaces pull requests created by the
|
|
25
|
+
* platform's incremental git write-back workflow.
|
|
26
|
+
*
|
|
27
|
+
* Write-backs from multiple executions are aggregated and
|
|
28
|
+
* deduplicated by `workspace_entry_name` (latest execution wins),
|
|
29
|
+
* presenting the user with a flat list of PRs — one per workspace
|
|
30
|
+
* entry.
|
|
31
|
+
*
|
|
32
|
+
* Returns `null` when no execution has write-backs, matching the
|
|
33
|
+
* conditional-render pattern of {@link ArtifactsWidget} and
|
|
34
|
+
* {@link ExecutionProgress}.
|
|
35
|
+
*
|
|
36
|
+
* All visual properties flow through `--stgm-*` tokens. Zero
|
|
37
|
+
* Console dependencies.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```tsx
|
|
41
|
+
* const conv = useSessionConversation(sessionId, org);
|
|
42
|
+
*
|
|
43
|
+
* <WriteBacksWidget
|
|
44
|
+
* executions={[
|
|
45
|
+
* ...conv.completedExecutions,
|
|
46
|
+
* ...(conv.activeStreamExecution ? [conv.activeStreamExecution] : []),
|
|
47
|
+
* ]}
|
|
48
|
+
* />
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @see {@link WriteBackCard} — compact card per write-back
|
|
52
|
+
* @see {@link useSessionWriteBacks} — headless session-level write-back aggregation hook
|
|
53
|
+
* @see {@link useWorkspaceWriteBacks} — headless single-execution write-back extraction hook
|
|
54
|
+
*/
|
|
55
|
+
export function WriteBacksWidget({
|
|
56
|
+
executions,
|
|
57
|
+
className,
|
|
58
|
+
}: WriteBacksWidgetProps) {
|
|
59
|
+
const { writeBacks, hasWriteBacks, writeBackCount } =
|
|
60
|
+
useSessionWriteBacks(executions);
|
|
61
|
+
|
|
62
|
+
if (!hasWriteBacks) return null;
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<section aria-label="Pull Requests" className={cn(className)}>
|
|
66
|
+
<div className="mb-2 flex items-center gap-2">
|
|
67
|
+
<h3 className="text-sm font-medium text-foreground">Pull Requests</h3>
|
|
68
|
+
<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">
|
|
69
|
+
{writeBackCount}
|
|
70
|
+
</span>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<div role="list" className="space-y-2">
|
|
74
|
+
{writeBacks.map((entry) => (
|
|
75
|
+
<div key={entry.writeBack.workspaceEntryName} role="listitem">
|
|
76
|
+
<WriteBackCard writeBack={entry.writeBack} />
|
|
77
|
+
</div>
|
|
78
|
+
))}
|
|
79
|
+
</div>
|
|
80
|
+
</section>
|
|
81
|
+
);
|
|
82
|
+
}
|