@stigmer/react 3.0.2-dev.20260609093630 → 3.0.3
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/SessionComposer.d.ts +23 -5
- package/composer/SessionComposer.d.ts.map +1 -1
- package/composer/SessionComposer.js +12 -5
- package/composer/SessionComposer.js.map +1 -1
- package/composer/index.d.ts +1 -0
- package/composer/index.d.ts.map +1 -1
- package/composer/index.js +1 -0
- package/composer/index.js.map +1 -1
- package/composer/interaction-mode.d.ts +21 -0
- package/composer/interaction-mode.d.ts.map +1 -0
- package/composer/interaction-mode.js +37 -0
- package/composer/interaction-mode.js.map +1 -0
- package/execution/ArtifactPreviewModal.d.ts +15 -2
- package/execution/ArtifactPreviewModal.d.ts.map +1 -1
- package/execution/ArtifactPreviewModal.js +16 -6
- package/execution/ArtifactPreviewModal.js.map +1 -1
- package/execution/MessageThread.d.ts +17 -2
- package/execution/MessageThread.d.ts.map +1 -1
- package/execution/MessageThread.js +7 -7
- package/execution/MessageThread.js.map +1 -1
- package/execution/PlanArtifactCard.d.ts +13 -5
- package/execution/PlanArtifactCard.d.ts.map +1 -1
- package/execution/PlanArtifactCard.js +14 -34
- package/execution/PlanArtifactCard.js.map +1 -1
- package/execution/useCreateAgentExecution.d.ts.map +1 -1
- package/execution/useCreateAgentExecution.js +2 -6
- package/execution/useCreateAgentExecution.js.map +1 -1
- package/internal/VirtualizedThread.d.ts +3 -1
- package/internal/VirtualizedThread.d.ts.map +1 -1
- package/internal/VirtualizedThread.js +4 -2
- package/internal/VirtualizedThread.js.map +1 -1
- package/package.json +4 -4
- package/session/SessionViewer.d.ts.map +1 -1
- package/session/SessionViewer.js +20 -12
- package/session/SessionViewer.js.map +1 -1
- package/session/inspector/ArtifactsTab.d.ts +7 -1
- package/session/inspector/ArtifactsTab.d.ts.map +1 -1
- package/session/inspector/ArtifactsTab.js +3 -2
- package/session/inspector/ArtifactsTab.js.map +1 -1
- package/session/inspector/SessionInspector.d.ts +5 -0
- package/session/inspector/SessionInspector.d.ts.map +1 -1
- package/session/inspector/SessionInspector.js +2 -2
- package/session/inspector/SessionInspector.js.map +1 -1
- package/session/useSessionPageFlow.d.ts +12 -1
- package/session/useSessionPageFlow.d.ts.map +1 -1
- package/session/useSessionPageFlow.js +12 -0
- package/session/useSessionPageFlow.js.map +1 -1
- package/src/composer/SessionComposer.tsx +34 -9
- package/src/composer/__tests__/SessionComposer-contract.test.tsx +42 -2
- package/src/composer/__tests__/interaction-mode.test.ts +44 -0
- package/src/composer/index.ts +5 -0
- package/src/composer/interaction-mode.ts +43 -0
- package/src/execution/ArtifactPreviewModal.tsx +61 -1
- package/src/execution/MessageThread.tsx +35 -1
- package/src/execution/PlanArtifactCard.tsx +45 -85
- package/src/execution/__tests__/ArtifactPreviewModal.test.tsx +85 -0
- package/src/execution/__tests__/PlanArtifactCard.test.tsx +122 -0
- package/src/execution/useCreateAgentExecution.ts +2 -7
- package/src/internal/VirtualizedThread.tsx +7 -1
- package/src/session/SessionViewer.tsx +25 -10
- package/src/session/inspector/ArtifactsTab.tsx +11 -1
- package/src/session/inspector/SessionInspector.tsx +7 -0
- package/src/session/inspector/__tests__/ArtifactsTab.test.tsx +90 -0
- package/src/session/useSessionPageFlow.ts +33 -1
|
@@ -8,12 +8,19 @@ import {
|
|
|
8
8
|
} from "../useSessionArtifacts";
|
|
9
9
|
import { ArtifactCard } from "../../execution/ArtifactCard";
|
|
10
10
|
import { ArtifactPreviewModal } from "../../execution/ArtifactPreviewModal";
|
|
11
|
+
import { isPlanArtifact } from "../../library/detect-plan-artifact";
|
|
11
12
|
import type { ApplyResourceResult } from "../../library/useApplyResource";
|
|
12
13
|
|
|
13
14
|
export interface ArtifactsTabProps {
|
|
14
15
|
readonly executions: readonly AgentExecution[];
|
|
15
16
|
readonly org: string;
|
|
16
17
|
readonly onApplied?: (result: ApplyResourceResult) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Implement a plan. When provided, the preview of a `plan.md` artifact
|
|
20
|
+
* shows an Implement action (turn the plan into an Agent run) — the same
|
|
21
|
+
* action as the message-thread plan card.
|
|
22
|
+
*/
|
|
23
|
+
readonly onImplementPlan?: () => void;
|
|
17
24
|
}
|
|
18
25
|
|
|
19
26
|
/**
|
|
@@ -23,7 +30,7 @@ export interface ArtifactsTabProps {
|
|
|
23
30
|
* `ArtifactPreviewModal` — the same content as `ArtifactsWidget`
|
|
24
31
|
* without the section heading.
|
|
25
32
|
*/
|
|
26
|
-
export function ArtifactsTab({ executions, org, onApplied }: ArtifactsTabProps) {
|
|
33
|
+
export function ArtifactsTab({ executions, org, onApplied, onImplementPlan }: ArtifactsTabProps) {
|
|
27
34
|
const { artifacts, hasArtifacts } = useSessionArtifacts(executions);
|
|
28
35
|
const [previewEntry, setPreviewEntry] = useState<SessionArtifactEntry | null>(null);
|
|
29
36
|
|
|
@@ -70,6 +77,9 @@ export function ArtifactsTab({ executions, org, onApplied }: ArtifactsTabProps)
|
|
|
70
77
|
open
|
|
71
78
|
onClose={handleClosePreview}
|
|
72
79
|
onApplied={onApplied}
|
|
80
|
+
onImplement={
|
|
81
|
+
isPlanArtifact(previewEntry.artifact) ? onImplementPlan : undefined
|
|
82
|
+
}
|
|
73
83
|
/>
|
|
74
84
|
)}
|
|
75
85
|
</>
|
|
@@ -39,6 +39,11 @@ export interface SessionInspectorProps {
|
|
|
39
39
|
readonly selectedItem: SelectedThreadItem | null;
|
|
40
40
|
/** Called after a resource is applied from the Artifacts tab. */
|
|
41
41
|
readonly onApplied?: (result: ApplyResourceResult) => void;
|
|
42
|
+
/**
|
|
43
|
+
* Implement a plan from the Artifacts tab. When provided, plan
|
|
44
|
+
* artifacts (`plan.md`) surface an Implement action in their preview.
|
|
45
|
+
*/
|
|
46
|
+
readonly onImplementPlan?: () => void;
|
|
42
47
|
/**
|
|
43
48
|
* Session-level configuration for the Configure tab.
|
|
44
49
|
* Includes core config fields and optional interactive
|
|
@@ -79,6 +84,7 @@ export const SessionInspector = memo(function SessionInspector({
|
|
|
79
84
|
org,
|
|
80
85
|
selectedItem,
|
|
81
86
|
onApplied,
|
|
87
|
+
onImplementPlan,
|
|
82
88
|
sessionConfig,
|
|
83
89
|
workspaceConfig,
|
|
84
90
|
className,
|
|
@@ -138,6 +144,7 @@ export const SessionInspector = memo(function SessionInspector({
|
|
|
138
144
|
executions={allExecutions}
|
|
139
145
|
org={org}
|
|
140
146
|
onApplied={onApplied}
|
|
147
|
+
onImplementPlan={onImplementPlan}
|
|
141
148
|
/>
|
|
142
149
|
)}
|
|
143
150
|
{activeTab === "usage" && (
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
2
|
+
import { render, screen, fireEvent, cleanup, within } from "@testing-library/react";
|
|
3
|
+
import { create } from "@bufbuild/protobuf";
|
|
4
|
+
import type { Stigmer } from "@stigmer/sdk";
|
|
5
|
+
import { AgentExecutionSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
6
|
+
import { ExecutionArtifactSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/artifact_pb";
|
|
7
|
+
import {
|
|
8
|
+
ExecutionArtifactKind,
|
|
9
|
+
ExecutionPhase,
|
|
10
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
11
|
+
import { StigmerContext } from "../../../context";
|
|
12
|
+
import { ArtifactsTab } from "../ArtifactsTab";
|
|
13
|
+
|
|
14
|
+
function artifact(name: string) {
|
|
15
|
+
return create(ExecutionArtifactSchema, {
|
|
16
|
+
name,
|
|
17
|
+
kind: ExecutionArtifactKind.FILE,
|
|
18
|
+
sizeBytes: 1024n,
|
|
19
|
+
sandboxPath: `.stigmer/${name}`,
|
|
20
|
+
storageKey: `artifacts/aex_1/${name}`,
|
|
21
|
+
downloadUrl: `https://example.test/${name}`,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const execution = create(AgentExecutionSchema, {
|
|
26
|
+
metadata: { id: "aex_1" },
|
|
27
|
+
status: {
|
|
28
|
+
phase: ExecutionPhase.EXECUTION_COMPLETED,
|
|
29
|
+
artifacts: [artifact("plan.md"), artifact("notes.md")],
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
/** Modal content fetch — keep pending so nothing rejects during the test. */
|
|
34
|
+
function createStigmerMock(): Stigmer {
|
|
35
|
+
return {
|
|
36
|
+
agentExecution: {
|
|
37
|
+
getArtifactContent: vi.fn().mockReturnValue(new Promise(() => {})),
|
|
38
|
+
},
|
|
39
|
+
} as unknown as Stigmer;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function renderTab(onImplementPlan?: () => void) {
|
|
43
|
+
return render(
|
|
44
|
+
<StigmerContext.Provider value={createStigmerMock()}>
|
|
45
|
+
<ArtifactsTab
|
|
46
|
+
executions={[execution]}
|
|
47
|
+
org="acme"
|
|
48
|
+
onImplementPlan={onImplementPlan}
|
|
49
|
+
/>
|
|
50
|
+
</StigmerContext.Provider>,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function openPreviewFor(name: string) {
|
|
55
|
+
const row = screen.getByText(name).closest("[role='listitem']") as HTMLElement;
|
|
56
|
+
fireEvent.click(within(row).getByText("Preview"));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
afterEach(cleanup);
|
|
60
|
+
|
|
61
|
+
describe("ArtifactsTab — plan Implement wiring", () => {
|
|
62
|
+
it("shows Implement in the preview of a plan.md artifact", () => {
|
|
63
|
+
renderTab(vi.fn());
|
|
64
|
+
|
|
65
|
+
openPreviewFor("plan.md");
|
|
66
|
+
|
|
67
|
+
const dialog = document.querySelector("dialog")!;
|
|
68
|
+
expect(within(dialog).getByText("Implement")).toBeTruthy();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("does not show Implement in the preview of a non-plan artifact", () => {
|
|
72
|
+
renderTab(vi.fn());
|
|
73
|
+
|
|
74
|
+
openPreviewFor("notes.md");
|
|
75
|
+
|
|
76
|
+
const dialog = document.querySelector("dialog")!;
|
|
77
|
+
expect(within(dialog).queryByText("Implement")).toBeNull();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("invokes onImplementPlan when Implement is clicked for a plan", () => {
|
|
81
|
+
const onImplementPlan = vi.fn();
|
|
82
|
+
renderTab(onImplementPlan);
|
|
83
|
+
|
|
84
|
+
openPreviewFor("plan.md");
|
|
85
|
+
const dialog = document.querySelector("dialog")!;
|
|
86
|
+
fireEvent.click(within(dialog).getByText("Implement"));
|
|
87
|
+
|
|
88
|
+
expect(onImplementPlan).toHaveBeenCalledTimes(1);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -8,7 +8,8 @@ import { useDefaultAgent } from "../agent";
|
|
|
8
8
|
import { useStigmer } from "../hooks";
|
|
9
9
|
import { useWorkspaceEntries, type UseWorkspaceEntriesReturn } from "../workspace";
|
|
10
10
|
import { useSessionVariables, type UseSessionVariablesReturn } from "../execution/useSessionVariables";
|
|
11
|
-
import type { SessionComposerSubmitContext } from "../composer";
|
|
11
|
+
import type { SessionComposerSubmitContext, InteractionModeOption } from "../composer";
|
|
12
|
+
import { fromProtoInteractionMode } from "../composer";
|
|
12
13
|
import { fromProtoHarness, type HarnessOption } from "../models/harness";
|
|
13
14
|
import { Harness, ExecutionTarget } from "@stigmer/protos/ai/stigmer/agentic/session/v1/enum_pb";
|
|
14
15
|
import { fromProtoExecutionTarget, type ExecutionTargetOption } from "./execution-target";
|
|
@@ -59,6 +60,18 @@ export interface UseSessionPageFlowReturn {
|
|
|
59
60
|
/** Persisted model selection: `[modelId, setModelId]`. */
|
|
60
61
|
readonly model: UsePersistedModelReturn;
|
|
61
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Composer interaction mode: `[interactionMode, setInteractionMode]`.
|
|
65
|
+
*
|
|
66
|
+
* Derived like {@link model}: the user's explicit override wins, otherwise
|
|
67
|
+
* it reflects the latest execution's mode (so a completed Plan keeps the
|
|
68
|
+
* picker on "Plan" while it awaits review), falling back to `"agent"`.
|
|
69
|
+
*/
|
|
70
|
+
readonly interactionMode: readonly [
|
|
71
|
+
InteractionModeOption,
|
|
72
|
+
(mode: InteractionModeOption) => void,
|
|
73
|
+
];
|
|
74
|
+
|
|
62
75
|
/** Currently selected agent reference (derived from session, or overridden by user). */
|
|
63
76
|
readonly agentRef: ResourceRef | null;
|
|
64
77
|
/** Update the agent reference for future follow-ups. */
|
|
@@ -206,6 +219,24 @@ export function useSessionPageFlow(
|
|
|
206
219
|
const modelId = persistedModelId ?? lastExecModelId;
|
|
207
220
|
const model: UsePersistedModelReturn = [modelId, setPersistedModelId] as const;
|
|
208
221
|
|
|
222
|
+
// Interaction mode mirrors the model derivation: an explicit user override
|
|
223
|
+
// wins; otherwise reflect the latest execution's mode so a completed Plan
|
|
224
|
+
// keeps the composer on "Plan" until the user implements or switches. This
|
|
225
|
+
// is derived state (always consistent), never an effect-synced copy.
|
|
226
|
+
const lastExecInteractionMode = useMemo(
|
|
227
|
+
() =>
|
|
228
|
+
fromProtoInteractionMode(
|
|
229
|
+
conv.completedExecutions.at(-1)?.spec?.executionConfig?.interactionMode,
|
|
230
|
+
),
|
|
231
|
+
[conv.completedExecutions],
|
|
232
|
+
);
|
|
233
|
+
const [interactionModeOverride, setInteractionMode] =
|
|
234
|
+
useState<InteractionModeOption | null>(null);
|
|
235
|
+
const interactionMode: UseSessionPageFlowReturn["interactionMode"] = [
|
|
236
|
+
interactionModeOverride ?? lastExecInteractionMode ?? "agent",
|
|
237
|
+
setInteractionMode,
|
|
238
|
+
] as const;
|
|
239
|
+
|
|
209
240
|
const workspace = useWorkspaceEntries();
|
|
210
241
|
const sessionVariables = useSessionVariables();
|
|
211
242
|
const [mcpServerUsages, setMcpServerUsages] = useState<McpServerUsageInput[]>([]);
|
|
@@ -369,6 +400,7 @@ export function useSessionPageFlow(
|
|
|
369
400
|
harness,
|
|
370
401
|
executionTarget,
|
|
371
402
|
model,
|
|
403
|
+
interactionMode,
|
|
372
404
|
agentRef,
|
|
373
405
|
setAgentRef,
|
|
374
406
|
resolution,
|