refactor: name presentation evidence states
This commit is contained in:
@@ -5,7 +5,6 @@ export type AgentRole = "user" | "assistant";
|
||||
export type PresentationToolAction =
|
||||
| { readonly type: "selectWorkflowNode"; readonly nodeId: string }
|
||||
| { readonly type: "focusOperation"; readonly eventId: string }
|
||||
| { readonly type: "openEvidence"; readonly eventId: string }
|
||||
| { readonly type: "showTraceFrame"; readonly frameIndex: number };
|
||||
|
||||
export type AgentToolCall = {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { loadCanonicalDemoRecording } from "../timeline/replay.js";
|
||||
import { runPreparedRecipeReplay } from "./preparedRecipeDriver.js";
|
||||
import { isAllowedAgentToolName } from "./tools.js";
|
||||
|
||||
const collect = async <T>(events: AsyncIterable<T>): Promise<ReadonlyArray<T>> => {
|
||||
const collected: T[] = [];
|
||||
@@ -38,8 +39,9 @@ describe("prepared recipe driver", () => {
|
||||
"selectWorkflowNode",
|
||||
"resumeIssueReview",
|
||||
"readRunTrace",
|
||||
"openEvidence",
|
||||
]);
|
||||
|
||||
expect(isAllowedAgentToolName("openEvidence")).toBe(false);
|
||||
});
|
||||
|
||||
it("emits approval-request at resumeIssueReview and waits for decision", async () => {
|
||||
|
||||
@@ -112,17 +112,6 @@ export async function* runPreparedRecipeReplay(
|
||||
eventId: trace?.id ?? null,
|
||||
});
|
||||
break;
|
||||
case "openEvidence":
|
||||
yield {
|
||||
id: step.id,
|
||||
role: "assistant",
|
||||
parts: [
|
||||
agentToolCallPart(`${step.id}-call`, step.toolName, { eventId: trace?.id ?? "trace" }),
|
||||
presentationActionPart({ type: "openEvidence", eventId: trace?.id ?? "trace" }),
|
||||
agentToolResultPart(`${step.id}-call`, step.toolName, "success", { eventId: trace?.id ?? "trace" }),
|
||||
],
|
||||
};
|
||||
break;
|
||||
default:
|
||||
assertNever(step.toolName);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,7 @@ export type RecipeTool =
|
||||
| "startPreparedReportRun"
|
||||
| "selectWorkflowNode"
|
||||
| "resumeIssueReview"
|
||||
| "readRunTrace"
|
||||
| "openEvidence";
|
||||
| "readRunTrace";
|
||||
|
||||
export type PreparedRecipeStep = {
|
||||
readonly id: string;
|
||||
@@ -58,10 +57,5 @@ export const PREPARE_THESIS_REPORT_RECIPE: PreparedRecipe = {
|
||||
narration: "I will read the run trace as evidence.",
|
||||
toolName: "readRunTrace",
|
||||
},
|
||||
{
|
||||
id: "open-evidence",
|
||||
narration: "I will open the evidence linked to the trace call.",
|
||||
toolName: "openEvidence",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -8,7 +8,6 @@ describe("agent tools", () => {
|
||||
expect(AGENT_TOOLS.resumeIssueReview.kind).toBe("workflow");
|
||||
expect(AGENT_TOOLS.readRunTrace.kind).toBe("workflow");
|
||||
expect(AGENT_TOOLS.selectWorkflowNode.kind).toBe("presentation");
|
||||
expect(AGENT_TOOLS.openEvidence.kind).toBe("presentation");
|
||||
});
|
||||
|
||||
it("rejects unknown tool names", () => {
|
||||
|
||||
@@ -7,7 +7,6 @@ export type WorkflowToolName =
|
||||
export type PresentationToolName =
|
||||
| "selectWorkflowNode"
|
||||
| "focusOperation"
|
||||
| "openEvidence"
|
||||
| "showTraceFrame";
|
||||
|
||||
export type AgentToolName = WorkflowToolName | PresentationToolName;
|
||||
@@ -49,11 +48,6 @@ export const AGENT_TOOLS = {
|
||||
kind: "presentation",
|
||||
description: "Focus an operation event in the presentation.",
|
||||
},
|
||||
openEvidence: {
|
||||
name: "openEvidence",
|
||||
kind: "presentation",
|
||||
description: "Open evidence for an operation event.",
|
||||
},
|
||||
showTraceFrame: {
|
||||
name: "showTraceFrame",
|
||||
kind: "presentation",
|
||||
|
||||
@@ -18,7 +18,7 @@ const record: EvidenceRecord = {
|
||||
describe("EvidenceDrawer", () => {
|
||||
it("renders current evidence records and can close", () => {
|
||||
const close = vi.fn();
|
||||
render(<EvidenceDrawer records={[record]} mode="open" close={close} />);
|
||||
render(<EvidenceDrawer records={[record]} mode="receipt" close={close} />);
|
||||
|
||||
expect(screen.getByRole("complementary", { name: /presentation evidence/i })).toBeInTheDocument();
|
||||
expect(screen.getByText(/workflow.runs.start/i)).toBeInTheDocument();
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { EvidenceRecord } from "../app/state.js";
|
||||
import { formatJson } from "./format.js";
|
||||
import type { EvidenceMode } from "./storyboard.js";
|
||||
import type { EvidencePresentation } from "./storyboard.js";
|
||||
|
||||
type EvidenceDrawerProps = {
|
||||
readonly records: readonly EvidenceRecord[];
|
||||
readonly mode: EvidenceMode;
|
||||
readonly mode: EvidencePresentation;
|
||||
readonly close: () => void;
|
||||
};
|
||||
|
||||
|
||||
@@ -117,14 +117,6 @@ export const PresentationRoute = () => {
|
||||
case "selectWorkflowNode":
|
||||
dispatch({ type: "select_node", nodeId: action.nodeId });
|
||||
break;
|
||||
case "openEvidence": {
|
||||
const hasLiveEvidence = evidence.length > 0;
|
||||
if (!hasLiveEvidence) {
|
||||
setEvidence(replayEvidence);
|
||||
}
|
||||
dispatch({ type: "set_evidence_mode", mode: "open" });
|
||||
break;
|
||||
}
|
||||
case "focusOperation":
|
||||
case "showTraceFrame":
|
||||
break;
|
||||
@@ -164,7 +156,7 @@ export const PresentationRoute = () => {
|
||||
onDeny={agent.phase === "awaiting-approval" ? handleDeny : undefined}
|
||||
jump={handleJump}
|
||||
selectNode={(nodeId) => dispatch({ type: "select_node", nodeId })}
|
||||
openEvidence={() => dispatch({ type: "set_evidence_mode", mode: "open" })}
|
||||
openEvidence={() => dispatch({ type: "set_evidence_presentation", presentation: "inspector" })}
|
||||
closeOverlay={() => dispatch({ type: "close_overlay" })}
|
||||
openDiscussion={handleOpenDiscussion}
|
||||
closeDiscussion={handleCloseDiscussion}
|
||||
|
||||
@@ -53,7 +53,7 @@ export const PresentationStage = ({
|
||||
<div
|
||||
className="presentation-stage"
|
||||
data-chat-mode={composition.chatMode}
|
||||
data-evidence-mode={composition.evidenceMode}
|
||||
data-evidence-presentation={composition.evidencePresentation}
|
||||
data-scene-view={activeSceneView}
|
||||
>
|
||||
<aside className="presentation-stage__chat" aria-label="agent chat region">
|
||||
@@ -79,7 +79,7 @@ export const PresentationStage = ({
|
||||
)}
|
||||
</section>
|
||||
<aside className="presentation-stage__evidence" aria-label="evidence region">
|
||||
<EvidenceDrawer records={evidence} mode={composition.evidenceMode} close={closeOverlay} />
|
||||
<EvidenceDrawer records={evidence} mode={composition.evidencePresentation} close={closeOverlay} />
|
||||
</aside>
|
||||
{state.location.kind === "main" && (
|
||||
<SceneProgress location={state.location} />
|
||||
|
||||
@@ -72,27 +72,27 @@ describe("presentationReducer", () => {
|
||||
expect(state.selectedNodeId).toBe("review_issues");
|
||||
});
|
||||
|
||||
it("closes overlays in priority order: node, evidence, discussion", () => {
|
||||
it("closes overlays in priority order: inspector, node, discussion", () => {
|
||||
const withNode = presentationReducer(initialPresentationState, {
|
||||
type: "select_node",
|
||||
nodeId: "review_issues",
|
||||
});
|
||||
const withEvidence = presentationReducer(withNode, {
|
||||
type: "set_evidence_mode",
|
||||
mode: "open",
|
||||
const withInspector = presentationReducer(withNode, {
|
||||
type: "set_evidence_presentation",
|
||||
presentation: "inspector",
|
||||
});
|
||||
const opened = presentationReducer(withEvidence, {
|
||||
const opened = presentationReducer(withInspector, {
|
||||
type: "open_discussion",
|
||||
branchId: "hosted-automation",
|
||||
});
|
||||
|
||||
const closed1 = presentationReducer(opened, { type: "close_overlay" });
|
||||
expect(closed1.selectedNodeId).toBeNull();
|
||||
expect(closed1.evidenceModeOverride).toBe("open");
|
||||
expect(closed1.evidencePresentationOverride).toBe("hidden");
|
||||
expect(closed1.selectedNodeId).toBe("review_issues");
|
||||
expect(closed1.location.kind).toBe("discussion");
|
||||
|
||||
const closed2 = presentationReducer(closed1, { type: "close_overlay" });
|
||||
expect(closed2.evidenceModeOverride).toBe("hidden");
|
||||
expect(closed2.selectedNodeId).toBeNull();
|
||||
|
||||
const closed3 = presentationReducer(closed2, { type: "close_overlay" });
|
||||
expect(closed3.location.kind).toBe("main");
|
||||
@@ -123,11 +123,11 @@ describe("presentationReducer", () => {
|
||||
|
||||
it("closes overlays before rewinding content", () => {
|
||||
const opened = presentationReducer(initialPresentationState, {
|
||||
type: "set_evidence_mode",
|
||||
mode: "open",
|
||||
type: "set_evidence_presentation",
|
||||
presentation: "inspector",
|
||||
});
|
||||
const closed = presentationReducer(opened, { type: "close_overlay" });
|
||||
expect(closed.evidenceModeOverride).toBe("hidden");
|
||||
expect(closed.evidencePresentationOverride).toBe("hidden");
|
||||
expect(closed.location).toEqual(initialPresentationState.location);
|
||||
});
|
||||
|
||||
@@ -136,13 +136,14 @@ describe("presentationReducer", () => {
|
||||
type: "jump",
|
||||
location: { kind: "main", sceneId: "interrupt-evidence", beatId: "trace", focusPath: [] },
|
||||
});
|
||||
expect(stateAtTrace.evidenceModeOverride).toBeNull();
|
||||
expect(stateAtTrace.evidencePresentationOverride).toBeNull();
|
||||
|
||||
const closed = presentationReducer(stateAtTrace, { type: "close_overlay" });
|
||||
expect(closed.evidenceModeOverride).toBe("hidden");
|
||||
expect(closed.evidencePresentationOverride).toBeNull();
|
||||
expect(closed.location.kind).toBe("main");
|
||||
|
||||
const secondEscape = presentationReducer(closed, { type: "close_overlay" });
|
||||
expect(secondEscape.evidenceModeOverride).toBe("hidden");
|
||||
expect(secondEscape.evidencePresentationOverride).toBeNull();
|
||||
expect(secondEscape.location.kind).toBe("main");
|
||||
});
|
||||
|
||||
@@ -183,4 +184,51 @@ describe("presentationReducer", () => {
|
||||
expect(presentationReducer(opened, { type: "close_discussion" }).location)
|
||||
.toEqual(deepRuntimeState.location);
|
||||
});
|
||||
|
||||
it("derives a receipt from beat metadata without opening an inspector", () => {
|
||||
const state = presentationReducer(initialPresentationState, {
|
||||
type: "jump",
|
||||
location: { kind: "main", sceneId: "architecture", beatId: "node-use", focusPath: ["node-use"] },
|
||||
});
|
||||
expect(compositionForState(state).evidencePresentation).toBe("receipt");
|
||||
expect(state.evidencePresentationOverride).toBeNull();
|
||||
});
|
||||
|
||||
it("closes an explicit inspector before the node spotlight", () => {
|
||||
const withNode = presentationReducer(initialPresentationState, {
|
||||
type: "select_node",
|
||||
nodeId: "review_issues",
|
||||
});
|
||||
const withInspector = presentationReducer(withNode, {
|
||||
type: "set_evidence_presentation",
|
||||
presentation: "inspector",
|
||||
});
|
||||
const firstEscape = presentationReducer(withInspector, { type: "close_overlay" });
|
||||
expect(firstEscape.evidencePresentationOverride).toBe("hidden");
|
||||
expect(firstEscape.selectedNodeId).toBe("review_issues");
|
||||
const secondEscape = presentationReducer(firstEscape, { type: "close_overlay" });
|
||||
expect(secondEscape.selectedNodeId).toBeNull();
|
||||
});
|
||||
|
||||
it("closes the inspector and recomputes receipt state when the beat changes", () => {
|
||||
const atReceiptBeat = presentationReducer(initialPresentationState, {
|
||||
type: "jump",
|
||||
location: { kind: "main", sceneId: "architecture", beatId: "node-use", focusPath: ["node-use"] },
|
||||
});
|
||||
const opened = presentationReducer(atReceiptBeat, {
|
||||
type: "set_evidence_presentation",
|
||||
presentation: "inspector",
|
||||
});
|
||||
const advanced = presentationReducer(opened, { type: "next" });
|
||||
expect(advanced.evidencePresentationOverride).toBeNull();
|
||||
expect(compositionForState(advanced).evidencePresentation).not.toBe("inspector");
|
||||
});
|
||||
|
||||
it("does not treat a receipt as an Escape-closeable overlay", () => {
|
||||
const receipt = presentationReducer(initialPresentationState, {
|
||||
type: "jump",
|
||||
location: { kind: "main", sceneId: "authoring", beatId: "diagnose", focusPath: [] },
|
||||
});
|
||||
expect(presentationReducer(receipt, { type: "close_overlay" })).toEqual(receipt);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,9 +2,10 @@ import {
|
||||
defaultMainLocation,
|
||||
findDiscussionBranch,
|
||||
findScene,
|
||||
type BeatEvidencePresentation,
|
||||
type ChatMode,
|
||||
type DiscussionBranchId,
|
||||
type EvidenceMode,
|
||||
type EvidencePresentation,
|
||||
type MainLocation,
|
||||
type PresentationLocation,
|
||||
} from "./storyboard.js";
|
||||
@@ -19,7 +20,7 @@ export type PresentationState = {
|
||||
readonly location: PresentationLocation;
|
||||
readonly discussionReturn: MainLocation | null;
|
||||
readonly selectedNodeId: string | null;
|
||||
readonly evidenceModeOverride: EvidenceMode | null;
|
||||
readonly evidencePresentationOverride: EvidencePresentation | null;
|
||||
readonly playbackMode: "replay" | "live";
|
||||
readonly motionDisabled: boolean;
|
||||
readonly startedAt: number;
|
||||
@@ -34,7 +35,7 @@ export type PresentationAction =
|
||||
| { readonly type: "close_discussion" }
|
||||
| { readonly type: "select_node"; readonly nodeId: string }
|
||||
| { readonly type: "clear_node" }
|
||||
| { readonly type: "set_evidence_mode"; readonly mode: EvidenceMode }
|
||||
| { readonly type: "set_evidence_presentation"; readonly presentation: EvidencePresentation }
|
||||
| { readonly type: "close_overlay" }
|
||||
| { readonly type: "set_playback_mode"; readonly mode: PresentationState["playbackMode"] }
|
||||
| { readonly type: "set_focus_path"; readonly path: readonly string[] }
|
||||
@@ -44,7 +45,7 @@ export const initialPresentationState: PresentationState = {
|
||||
location: defaultMainLocation,
|
||||
discussionReturn: null,
|
||||
selectedNodeId: null,
|
||||
evidenceModeOverride: null,
|
||||
evidencePresentationOverride: null,
|
||||
playbackMode: "replay",
|
||||
motionDisabled: false,
|
||||
startedAt: Date.now(),
|
||||
@@ -52,29 +53,29 @@ export const initialPresentationState: PresentationState = {
|
||||
|
||||
const compositionForLocation = (
|
||||
location: PresentationLocation,
|
||||
evidenceOverride: EvidenceMode | null,
|
||||
evidenceOverride: EvidencePresentation | null,
|
||||
): {
|
||||
readonly chatMode: ChatMode;
|
||||
readonly evidenceMode: EvidenceMode;
|
||||
readonly evidencePresentation: EvidencePresentation;
|
||||
} => {
|
||||
if (location.kind === "discussion") {
|
||||
return {
|
||||
chatMode: "hidden",
|
||||
evidenceMode: evidenceOverride ?? "hidden",
|
||||
evidencePresentation: evidenceOverride ?? "hidden",
|
||||
};
|
||||
}
|
||||
const scene = findScene(location.sceneId);
|
||||
const beat = scene?.beats.find((b) => b.id === location.beatId);
|
||||
return {
|
||||
chatMode: beat?.chatMode ?? "hidden",
|
||||
evidenceMode: evidenceOverride ?? beat?.evidenceMode ?? "hidden",
|
||||
evidencePresentation: evidenceOverride ?? beat?.evidencePresentation ?? "hidden",
|
||||
};
|
||||
};
|
||||
|
||||
export const compositionForState = (state: PresentationState) =>
|
||||
compositionForLocation(
|
||||
state.location,
|
||||
state.evidenceModeOverride,
|
||||
state.evidencePresentationOverride,
|
||||
);
|
||||
|
||||
const isValidMainLocation = (location: PresentationLocation): location is MainLocation =>
|
||||
@@ -93,6 +94,14 @@ const clampMainLocation = (location: MainLocation): MainLocation => {
|
||||
return defaultMainLocation;
|
||||
};
|
||||
|
||||
const moveToLocation = (
|
||||
state: PresentationState,
|
||||
location: PresentationLocation,
|
||||
): PresentationState => ({
|
||||
...state,
|
||||
location,
|
||||
});
|
||||
|
||||
export const presentationReducer = (
|
||||
state: PresentationState,
|
||||
action: PresentationAction,
|
||||
@@ -101,27 +110,27 @@ export const presentationReducer = (
|
||||
case "next": {
|
||||
if (!isValidMainLocation(state.location)) return state;
|
||||
const next = nextMainLocation(state.location);
|
||||
return { ...state, location: next };
|
||||
return { ...moveToLocation(state, next), evidencePresentationOverride: null };
|
||||
}
|
||||
case "previous": {
|
||||
if (!isValidMainLocation(state.location)) return state;
|
||||
const prev = previousMainLocation(state.location);
|
||||
return { ...state, location: prev };
|
||||
return { ...moveToLocation(state, prev), evidencePresentationOverride: null };
|
||||
}
|
||||
case "jump": {
|
||||
if (state.location.kind === "discussion") return state;
|
||||
return { ...state, location: action.location };
|
||||
return { ...moveToLocation(state, action.location), evidencePresentationOverride: null };
|
||||
}
|
||||
case "jump_hash": {
|
||||
const parsed = locationFromHash(action.hash);
|
||||
if (parsed.kind === "main") {
|
||||
return { ...state, location: clampMainLocation(parsed), discussionReturn: null };
|
||||
return { ...moveToLocation(state, clampMainLocation(parsed)), discussionReturn: null, evidencePresentationOverride: null };
|
||||
}
|
||||
const branch = findDiscussionBranch(parsed.branchId);
|
||||
const returnLoc = branch
|
||||
? firstBeatOfScene(branch.parentSceneId) ?? defaultMainLocation
|
||||
: defaultMainLocation;
|
||||
return { ...state, location: parsed, discussionReturn: returnLoc };
|
||||
return { ...moveToLocation(state, parsed), discussionReturn: returnLoc };
|
||||
}
|
||||
case "open_discussion": {
|
||||
const branch = findDiscussionBranch(action.branchId);
|
||||
@@ -138,8 +147,7 @@ export const presentationReducer = (
|
||||
case "close_discussion": {
|
||||
if (state.location.kind !== "discussion") return state;
|
||||
return {
|
||||
...state,
|
||||
location: state.discussionReturn ?? defaultMainLocation,
|
||||
...moveToLocation(state, state.discussionReturn ?? defaultMainLocation),
|
||||
discussionReturn: null,
|
||||
};
|
||||
}
|
||||
@@ -147,18 +155,11 @@ export const presentationReducer = (
|
||||
return { ...state, selectedNodeId: action.nodeId };
|
||||
case "clear_node":
|
||||
return { ...state, selectedNodeId: null };
|
||||
case "set_evidence_mode":
|
||||
return { ...state, evidenceModeOverride: action.mode };
|
||||
case "set_evidence_presentation":
|
||||
return { ...state, evidencePresentationOverride: action.presentation };
|
||||
case "close_overlay": {
|
||||
if (state.evidencePresentationOverride === "inspector") return { ...state, evidencePresentationOverride: "hidden" };
|
||||
if (state.selectedNodeId !== null) return { ...state, selectedNodeId: null };
|
||||
const isEvidenceVisible = (() => {
|
||||
if (state.evidenceModeOverride !== null) return state.evidenceModeOverride !== "hidden";
|
||||
if (state.location.kind === "discussion") return false;
|
||||
const scene = findScene(state.location.sceneId);
|
||||
const beat = scene?.beats.find((b) => b.id === (state.location as MainLocation).beatId);
|
||||
return beat?.evidenceMode === "open" || beat?.evidenceMode === "peek";
|
||||
})();
|
||||
if (isEvidenceVisible) return { ...state, evidenceModeOverride: "hidden" };
|
||||
if (state.location.kind === "discussion") {
|
||||
return {
|
||||
...state,
|
||||
|
||||
@@ -48,7 +48,7 @@ const mockBeat = {
|
||||
caption: "Human and agent clients use the same public lifecycle surface.",
|
||||
chatMode: "rail" as const,
|
||||
chatTheme: "light" as const,
|
||||
evidenceMode: "hidden" as const,
|
||||
evidencePresentation: "hidden" as const,
|
||||
figure: { catalogId: "system-architecture", focusPath: [] as readonly string[], activeNodeId: "client-operations" },
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@ export type ClaimClass = "motivation" | "implemented" | "evaluated" | "external-
|
||||
export type StageTheme = "paper" | "night";
|
||||
export type ChatTheme = "light" | "dark";
|
||||
export type ChatMode = "hidden" | "full" | "rail" | "dock";
|
||||
export type EvidenceMode = "hidden" | "peek" | "open";
|
||||
export type EvidencePresentation = "hidden" | "receipt" | "inspector";
|
||||
export type BeatEvidencePresentation = Exclude<EvidencePresentation, "inspector">;
|
||||
export type SceneView =
|
||||
| "narrative"
|
||||
| "positioning"
|
||||
@@ -27,7 +28,7 @@ export type SceneBeatDefinition = {
|
||||
readonly caption: string;
|
||||
readonly chatMode: ChatMode;
|
||||
readonly chatTheme: ChatTheme;
|
||||
readonly evidenceMode: EvidenceMode;
|
||||
readonly evidencePresentation: BeatEvidencePresentation;
|
||||
readonly figure: FigureBeatDefinition | null;
|
||||
};
|
||||
|
||||
@@ -48,14 +49,14 @@ const sceneBeat = (
|
||||
id: string,
|
||||
title: string,
|
||||
caption: string,
|
||||
options: Partial<Pick<SceneBeatDefinition, "chatMode" | "chatTheme" | "evidenceMode" | "figure">> = {},
|
||||
options: Partial<Pick<SceneBeatDefinition, "chatMode" | "chatTheme" | "evidencePresentation" | "figure">> = {},
|
||||
): SceneBeatDefinition => ({
|
||||
id,
|
||||
title,
|
||||
caption,
|
||||
chatMode: options.chatMode ?? "hidden",
|
||||
chatTheme: options.chatTheme ?? "dark",
|
||||
evidenceMode: options.evidenceMode ?? "hidden",
|
||||
evidencePresentation: options.evidencePresentation ?? "hidden",
|
||||
figure: options.figure ?? null,
|
||||
});
|
||||
|
||||
@@ -140,7 +141,7 @@ export const mainScenes = defineScenes([
|
||||
sceneBeat("client", "Client operations", "Human and agent clients use the same public lifecycle surface.", { figure: { catalogId: "system-architecture", focusPath: [], activeNodeId: "client-operations" } }),
|
||||
sceneBeat("api", "Transport and API", "JSON-RPC reaches WorkflowApi without owning domain behavior.", { figure: { catalogId: "system-architecture", focusPath: [], activeNodeId: "application-lifecycle" } }),
|
||||
sceneBeat("runtime", "Runtime and providers", "The runtime resolves provider-neutral capabilities and stores lifecycle records.", { figure: { catalogId: "system-architecture", focusPath: ["runtime-providers"], activeNodeId: "configured-providers" } }),
|
||||
sceneBeat("node-use", "NodeUse", "One callable node validates input, invokes a capability, and reduces output into state.", { evidenceMode: "peek", figure: { catalogId: "system-architecture", focusPath: ["node-use"], activeNodeId: "invoke-handler" } }),
|
||||
sceneBeat("node-use", "NodeUse", "One callable node validates input, invokes a capability, and reduces output into state.", { evidencePresentation: "receipt", figure: { catalogId: "system-architecture", focusPath: ["node-use"], activeNodeId: "invoke-handler" } }),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -154,7 +155,7 @@ export const mainScenes = defineScenes([
|
||||
beats: [
|
||||
sceneBeat("discover", "Discover", "Inspect capabilities and schemas before authoring."),
|
||||
sceneBeat("author", "Author", "Focused operations build and connect the draft."),
|
||||
sceneBeat("diagnose", "Diagnose", "Structured diagnostics identify invalid state.", { evidenceMode: "peek" }),
|
||||
sceneBeat("diagnose", "Diagnose", "Structured diagnostics identify invalid state.", { evidencePresentation: "receipt" }),
|
||||
sceneBeat("repair", "Repair", "Repair hints lead to a valid compiled workflow."),
|
||||
],
|
||||
},
|
||||
@@ -197,7 +198,7 @@ export const mainScenes = defineScenes([
|
||||
sceneBeat("approval", "Approval", "The operator reviews a schema-backed resume request.", { chatMode: "rail", chatTheme: "light" }),
|
||||
sceneBeat("resume", "Resume", "The approved payload resumes the same persisted run.", { chatMode: "rail", chatTheme: "light" }),
|
||||
sceneBeat("output", "Output", "The workflow produces the report and issue-board changes.", { chatMode: "dock", chatTheme: "light" }),
|
||||
sceneBeat("trace", "Evidence", "Trace frames and protocol evidence remain inspectable.", { chatMode: "dock", chatTheme: "light", evidenceMode: "open" }),
|
||||
sceneBeat("trace", "Evidence", "Trace frames and protocol evidence remain inspectable.", { chatMode: "dock", chatTheme: "light", evidencePresentation: "receipt" }),
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user