feat: finish continuous authoring workspace
This commit is contained in:
@@ -173,7 +173,7 @@ Implementation order:
|
|||||||
Astro remains an option, not the default next surface.
|
Astro remains an option, not the default next surface.
|
||||||
25. Completed: presentation agent authoring story creates a canonical prepared
|
25. Completed: presentation agent authoring story creates a canonical prepared
|
||||||
authoring recording, Scene 8 as an authentic full-screen conversation,
|
authoring recording, Scene 8 as an authentic full-screen conversation,
|
||||||
Scene 9 as a 5-phase lifecycle with trace panel, and route-level coverage
|
Scene 9 as a 5-phase lifecycle with synchronized chat dock, and route-level coverage
|
||||||
that never calls workflow authoring RPC operations during Scene 9
|
that never calls workflow authoring RPC operations during Scene 9
|
||||||
navigation. Implementation:
|
navigation. Implementation:
|
||||||
[`presentation agent authoring story`](historical/superpowers/plans/2026-07-11-presentation-agent-authoring-story.md).
|
[`presentation agent authoring story`](historical/superpowers/plans/2026-07-11-presentation-agent-authoring-story.md).
|
||||||
@@ -200,8 +200,12 @@ Presentation visual audit, July 10:
|
|||||||
|
|
||||||
Scenes 8 and 9 are now implemented as authentic external-agent handoff and
|
Scenes 8 and 9 are now implemented as authentic external-agent handoff and
|
||||||
factual prepared-authoring proof. The canonical recording, full-screen Scene 8,
|
factual prepared-authoring proof. The canonical recording, full-screen Scene 8,
|
||||||
Scene 9 lifecycle with trace panel, and route coverage are all in place. Plan:
|
Scene 9 lifecycle with a synchronized chat dock, five factual phase visuals,
|
||||||
[`presentation agent authoring story`](historical/superpowers/plans/2026-07-11-presentation-agent-authoring-story.md).
|
and route coverage are all in place. The obsolete receipt/trace modal was
|
||||||
|
removed. Plans:
|
||||||
|
[`presentation agent authoring story`](historical/superpowers/plans/2026-07-11-presentation-agent-authoring-story.md)
|
||||||
|
and
|
||||||
|
[`continuous authoring workspace`](historical/superpowers/plans/2026-07-11-continuous-authoring-workspace.md).
|
||||||
|
|
||||||
Recommended next visual slices:
|
Recommended next visual slices:
|
||||||
|
|
||||||
|
|||||||
+2
@@ -1,5 +1,7 @@
|
|||||||
# Continuous Authoring Workspace Implementation Plan
|
# Continuous Authoring Workspace Implementation Plan
|
||||||
|
|
||||||
|
Status: Completed on 2026-07-11.
|
||||||
|
|
||||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
**Goal:** Rebuild Scenes 8 and 9 as one continuous assistant conversation that synchronizes prepared tool activity with five factual workflow-authoring projections.
|
**Goal:** Rebuild Scenes 8 and 9 as one continuous assistant conversation that synchronizes prepared tool activity with five factual workflow-authoring projections.
|
||||||
+22
-13
@@ -170,8 +170,10 @@ both modes. Replay is visibly labeled and does not create real issues.
|
|||||||
|
|
||||||
The console exposes `/present`, a 720p no-scroll defense compositor for the
|
The console exposes `/present`, a 720p no-scroll defense compositor for the
|
||||||
prepared `lda_report_workflow` story. It renders a 14-scene, multi-beat
|
prepared `lda_report_workflow` story. It renders a 14-scene, multi-beat
|
||||||
storyboard with an adaptive aspect-ratio canvas, stable stage regions, discussion
|
storyboard (expanded from the original 12-scene defense plan; items 13 and 14
|
||||||
branches, act themes, a chat dock, and keyboard navigation.
|
now cover evaluation and closing) with an adaptive aspect-ratio canvas, stable
|
||||||
|
stage regions, discussion branches, act themes, a chat dock, and keyboard
|
||||||
|
navigation.
|
||||||
|
|
||||||
The final presentation beats frame the evaluation as bounded evidence, make
|
The final presentation beats frame the evaluation as bounded evidence, make
|
||||||
claim boundaries and future work explicit, and end on the canonical defense
|
claim boundaries and future work explicit, and end on the canonical defense
|
||||||
@@ -180,8 +182,11 @@ discussion index rather than a benchmark or generic conclusion.
|
|||||||
Scenes 8 and 9 use the canonical prepared-authoring recording as their only
|
Scenes 8 and 9 use the canonical prepared-authoring recording as their only
|
||||||
execution evidence. Scene 8 is a full-screen conversation that establishes the
|
execution evidence. Scene 8 is a full-screen conversation that establishes the
|
||||||
external agent handoff; Scene 9 breaks the prepared authoring into five phases
|
external agent handoff; Scene 9 breaks the prepared authoring into five phases
|
||||||
with an overlay trace panel. Neither scene calls workflow authoring RPC
|
with the same conversation contracted into a synchronized bottom dock. Each
|
||||||
operations — they consume deterministic prepared data. Scenes 10 through 12 use
|
phase opens its matching prepared tool group beneath a factual source, graph,
|
||||||
|
repair, artifact, or deployment view. Neither scene calls workflow authoring
|
||||||
|
RPC operations — they consume deterministic prepared data. Scenes 10 through
|
||||||
|
12 use
|
||||||
the canonical replay to show run, interrupt, approval, resume, output, and
|
the canonical replay to show run, interrupt, approval, resume, output, and
|
||||||
trace. Raw protocol payloads are available through the evidence receipt and
|
trace. Raw protocol payloads are available through the evidence receipt and
|
||||||
inspector.
|
inspector.
|
||||||
@@ -192,17 +197,20 @@ prepared timeline agent and approval flow; a future AI SDK driver should target
|
|||||||
`AgentMessagePart` / `TimelineAgent`-compatible events instead of replacing the
|
`AgentMessagePart` / `TimelineAgent`-compatible events instead of replacing the
|
||||||
presentation timeline.
|
presentation timeline.
|
||||||
|
|
||||||
The key defense states are directly addressable:
|
The key deep-link-addressable defense states include:
|
||||||
|
|
||||||
- `/present#scene/workflow-demo/operation`
|
|
||||||
- `/present#scene/workflow-demo/graph`
|
|
||||||
- `/present#scene/workflow-demo/interrupt`
|
|
||||||
- `/present#scene/interrupt-evidence/approval`
|
|
||||||
- `/present#scene/agent-handoff/request` — Scene 8, prepared handoff request
|
- `/present#scene/agent-handoff/request` — Scene 8, prepared handoff request
|
||||||
- `/present#scene/agent-handoff/handoff` — Scene 8, agent accepts
|
- `/present#scene/agent-handoff/handoff` — Scene 8, agent accepts
|
||||||
- `/present#scene/prepared-lifecycle/discover` — Scene 9, discover phase
|
- `/present#scene/prepared-lifecycle/discover` — Scene 9, discover phase
|
||||||
- `/present#scene/prepared-lifecycle/draft` — Scene 9, draft phase
|
- `/present#scene/prepared-lifecycle/draft` — Scene 9, draft phase
|
||||||
- `/present#scene/prepared-lifecycle/deployment` — Scene 9, deployment phase
|
- `/present#scene/prepared-lifecycle/deployment` — Scene 9, deployment phase
|
||||||
|
- `/present#scene/run-from-deployment/operation` — Scene 10, run operation
|
||||||
|
- `/present#scene/run-from-deployment/graph` — Scene 10, workflow graph
|
||||||
|
- `/present#scene/typed-human-boundary/approval` — Scene 11, typed approval
|
||||||
|
- `/present#scene/resume-output-evidence/resume` — Scene 12, resume proof
|
||||||
|
|
||||||
|
Legacy aliases from the earlier 12-scene plan (such as `workflow-demo` and
|
||||||
|
`interrupt-evidence`) are replaced by the IDs above and no longer resolve.
|
||||||
|
|
||||||
Scene 12 is factual by design. It projects the reviewed/live run into visible
|
Scene 12 is factual by design. It projects the reviewed/live run into visible
|
||||||
workflow input, interrupt payload, resume decision, output, and trace facts.
|
workflow input, interrupt payload, resume decision, output, and trace facts.
|
||||||
@@ -324,12 +332,13 @@ workflow authoring RPC operations.
|
|||||||
- **Scene 8 (Agent Handoff)**: a full-screen prepared conversation that
|
- **Scene 8 (Agent Handoff)**: a full-screen prepared conversation that
|
||||||
establishes the external agent interface. The `request` beat shows the
|
establishes the external agent interface. The `request` beat shows the
|
||||||
operator requesting a report workflow; the `handoff` beat reveals the full
|
operator requesting a report workflow; the `handoff` beat reveals the full
|
||||||
completed conversation across all authoring phases.
|
completed conversation with prepared `wf` tool groups across all authoring
|
||||||
|
phases.
|
||||||
- **Scene 9 (Prepared Workflow Lifecycle)**: a five-phase lifecycle
|
- **Scene 9 (Prepared Workflow Lifecycle)**: a five-phase lifecycle
|
||||||
(discover, draft, validate, artifact, deployment) with a compact phase rail
|
(discover, draft, validate, artifact, deployment) with a compact phase rail
|
||||||
and one dominant command/result projection per beat. An overlay
|
and one dominant factual product projection per beat. The Scene 8 thread
|
||||||
`AuthoringTracePanel` surfaces the full agent trace for the active phase
|
remains visible as a compact bottom dock; its active tool group follows the
|
||||||
without competing with the lifecycle projection.
|
current beat. There is no detached trace modal or second transcript.
|
||||||
|
|
||||||
The authoring scenes consume deterministic prepared data and never call
|
The authoring scenes consume deterministic prepared data and never call
|
||||||
workflow authoring RPCs — only `workflow.health` infrastructure pings are
|
workflow authoring RPCs — only `workflow.health` infrastructure pings are
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import type { AgentToolName } from "./tools.js";
|
import type { AgentToolName, PreparedAuthoringToolName } from "./tools.js";
|
||||||
|
|
||||||
|
export type AgentMessageToolName = AgentToolName | PreparedAuthoringToolName;
|
||||||
|
|
||||||
export type AgentRole = "user" | "assistant";
|
export type AgentRole = "user" | "assistant";
|
||||||
|
|
||||||
@@ -9,13 +11,13 @@ export type PresentationToolAction =
|
|||||||
|
|
||||||
export type AgentToolCall = {
|
export type AgentToolCall = {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
readonly name: AgentToolName;
|
readonly name: AgentMessageToolName;
|
||||||
readonly input: unknown;
|
readonly input: unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AgentToolResult = {
|
export type AgentToolResult = {
|
||||||
readonly callId: string;
|
readonly callId: string;
|
||||||
readonly name: AgentToolName;
|
readonly name: AgentMessageToolName;
|
||||||
readonly status: "success" | "failure";
|
readonly status: "success" | "failure";
|
||||||
readonly output: unknown;
|
readonly output: unknown;
|
||||||
};
|
};
|
||||||
@@ -56,7 +58,7 @@ export const agentTextMessage = (id: string, role: AgentRole, text: string): Age
|
|||||||
|
|
||||||
export const agentToolCallPart = (
|
export const agentToolCallPart = (
|
||||||
id: string,
|
id: string,
|
||||||
name: AgentToolName,
|
name: AgentMessageToolName,
|
||||||
input: unknown,
|
input: unknown,
|
||||||
): AgentMessagePart => ({
|
): AgentMessagePart => ({
|
||||||
type: "tool-call",
|
type: "tool-call",
|
||||||
@@ -65,7 +67,7 @@ export const agentToolCallPart = (
|
|||||||
|
|
||||||
export const agentToolResultPart = (
|
export const agentToolResultPart = (
|
||||||
callId: string,
|
callId: string,
|
||||||
name: AgentToolName,
|
name: AgentMessageToolName,
|
||||||
status: "success" | "failure",
|
status: "success" | "failure",
|
||||||
output: unknown,
|
output: unknown,
|
||||||
): AgentMessagePart => ({
|
): AgentMessagePart => ({
|
||||||
|
|||||||
@@ -5,6 +5,23 @@ export type WorkflowToolName =
|
|||||||
| "readRunTrace"
|
| "readRunTrace"
|
||||||
| "runWorkflowCommand";
|
| "runWorkflowCommand";
|
||||||
|
|
||||||
|
/** Names emitted by the prepared authoring recording for its operation trace. */
|
||||||
|
export type PreparedAuthoringToolName =
|
||||||
|
| "workflow.sources.list"
|
||||||
|
| "workflow.capabilities.list"
|
||||||
|
| "workflow.capabilities.inspect"
|
||||||
|
| "wf schema"
|
||||||
|
| "workflow.draft_workspaces.create_from_capability"
|
||||||
|
| "workflow.draft_workspaces.add_step_from_capability"
|
||||||
|
| "workflow.draft_workspaces.get"
|
||||||
|
| "workflow.draft_workspaces.validate"
|
||||||
|
| "workflow.draft_workspaces.set_step_output_map"
|
||||||
|
| "workflow.draft_workspaces.compile"
|
||||||
|
| "workflow.draft_workspaces.create_artifact"
|
||||||
|
| "workflow.artifacts.inspect"
|
||||||
|
| "workflow.deployments.save"
|
||||||
|
| "workflow.deployments.validate";
|
||||||
|
|
||||||
export type PresentationToolName =
|
export type PresentationToolName =
|
||||||
| "selectWorkflowNode"
|
| "selectWorkflowNode"
|
||||||
| "focusOperation"
|
| "focusOperation"
|
||||||
|
|||||||
@@ -66,9 +66,9 @@ describe("PresentationRoute", () => {
|
|||||||
window.location.hash = "#scene/agent-handoff/request";
|
window.location.hash = "#scene/agent-handoff/request";
|
||||||
const { PresentationRoute } = await import("./PresentationRoute.js");
|
const { PresentationRoute } = await import("./PresentationRoute.js");
|
||||||
render(<PresentationRoute />);
|
render(<PresentationRoute />);
|
||||||
expect(screen.getByRole("heading", { name: /Agent Handoff/i })).toBeInTheDocument();
|
expect(screen.getByRole("log", { name: "prepared authoring conversation" })).toBeInTheDocument();
|
||||||
await userEvent.keyboard("{ArrowRight}");
|
await userEvent.keyboard("{ArrowRight}");
|
||||||
expect(await screen.findByText(/The interface delegates durable work to lda\.chat/i)).toBeInTheDocument();
|
expect(await screen.findByRole("button", { name: /deployment.*2 tool calls/i })).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders audience progress chrome without rail or mode label", async () => {
|
it("renders audience progress chrome without rail or mode label", async () => {
|
||||||
@@ -373,8 +373,8 @@ describe("PresentationRoute", () => {
|
|||||||
const { PresentationRoute } = await import("./PresentationRoute.js");
|
const { PresentationRoute } = await import("./PresentationRoute.js");
|
||||||
render(<PresentationRoute />);
|
render(<PresentationRoute />);
|
||||||
|
|
||||||
expect(await screen.findByRole("heading", { name: "Agent Handoff" })).toBeInTheDocument();
|
expect(await screen.findByRole("log", { name: "prepared authoring conversation" })).toBeInTheDocument();
|
||||||
expect(screen.getByText(/A thin agent interface receives the report request/)).toBeInTheDocument();
|
expect(screen.getByRole("button", { name: /discover.*4 tool calls/i })).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("navigates to Scene 8 handoff beat via hash", async () => {
|
it("navigates to Scene 8 handoff beat via hash", async () => {
|
||||||
@@ -382,8 +382,8 @@ describe("PresentationRoute", () => {
|
|||||||
const { PresentationRoute } = await import("./PresentationRoute.js");
|
const { PresentationRoute } = await import("./PresentationRoute.js");
|
||||||
render(<PresentationRoute />);
|
render(<PresentationRoute />);
|
||||||
|
|
||||||
expect(await screen.findByRole("heading", { name: "Agent Handoff" })).toBeInTheDocument();
|
expect(await screen.findByRole("log", { name: "prepared authoring conversation" })).toBeInTheDocument();
|
||||||
expect(screen.getByText(/The interface delegates durable work to lda\.chat/)).toBeInTheDocument();
|
expect(screen.getByRole("button", { name: /deployment.*2 tool calls/i })).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ describe("AgentHandoffScene", () => {
|
|||||||
|
|
||||||
it("interleaves prepared workflow tool groups with the handoff conversation", () => {
|
it("interleaves prepared workflow tool groups with the handoff conversation", () => {
|
||||||
renderBeat("handoff");
|
renderBeat("handoff");
|
||||||
expect(screen.getAllByText(/runWorkflowCommand/i).length).toBeGreaterThan(0);
|
expect(screen.getAllByText(/workflow\.deployments\.save/i).length).toBeGreaterThan(0);
|
||||||
expect(screen.getByRole("button", { name: /deployment.*2 tool calls/i }))
|
expect(screen.getByRole("button", { name: /deployment.*2 tool calls/i }))
|
||||||
.toHaveAttribute("aria-expanded", "true");
|
.toHaveAttribute("aria-expanded", "true");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ describe("AuthoringConversation", () => {
|
|||||||
|
|
||||||
expect(screen.getByRole("log", { name: "prepared authoring conversation" }))
|
expect(screen.getByRole("log", { name: "prepared authoring conversation" }))
|
||||||
.toHaveAttribute("data-surface", "stage");
|
.toHaveAttribute("data-surface", "stage");
|
||||||
expect(screen.getAllByText(/runWorkflowCommand/i).length).toBeGreaterThan(0);
|
expect(screen.getAllByText(/workflow\.deployments\.save/i).length).toBeGreaterThan(0);
|
||||||
expect(screen.getAllByText(/deployment/i).length).toBeGreaterThan(0);
|
expect(screen.getAllByText(/deployment/i).length).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ type AuthoringPhaseVisualProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const InventoryVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["visual"], { kind: "inventory" }> }) => (
|
const InventoryVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["visual"], { kind: "inventory" }> }) => (
|
||||||
<section className="authoring-visual authoring-visual--inventory" role="region" aria-label="discovery evidence">
|
<section className="authoring-visual authoring-visual--inventory" aria-label="discovery evidence">
|
||||||
<div className="authoring-inventory__sources">
|
<div className="authoring-inventory__sources">
|
||||||
{visual.sources.map((source) => (
|
{visual.sources.map((source) => (
|
||||||
<div key={source}><Database aria-hidden="true" /><code>{source}</code></div>
|
<div key={source}><Database aria-hidden="true" /><code>{source}</code></div>
|
||||||
@@ -31,7 +31,7 @@ const InventoryVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection[
|
|||||||
);
|
);
|
||||||
|
|
||||||
const GraphVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["visual"], { kind: "graph" }> }) => (
|
const GraphVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["visual"], { kind: "graph" }> }) => (
|
||||||
<section className="authoring-visual authoring-visual--graph" role="region" aria-label="draft graph evidence">
|
<section className="authoring-visual authoring-visual--graph" aria-label="draft graph evidence">
|
||||||
<div className="authoring-graph__node"><Database aria-hidden="true" /><strong>{visual.nodes[0]}</strong></div>
|
<div className="authoring-graph__node"><Database aria-hidden="true" /><strong>{visual.nodes[0]}</strong></div>
|
||||||
<div className="authoring-graph__edge"><span>{visual.inputBinding}</span><ArrowRight aria-hidden="true" /></div>
|
<div className="authoring-graph__edge"><span>{visual.inputBinding}</span><ArrowRight aria-hidden="true" /></div>
|
||||||
<div className="authoring-graph__node"><Workflow aria-hidden="true" /><strong>{visual.nodes[1]}</strong></div>
|
<div className="authoring-graph__node"><Workflow aria-hidden="true" /><strong>{visual.nodes[1]}</strong></div>
|
||||||
@@ -41,7 +41,7 @@ const GraphVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["vis
|
|||||||
);
|
);
|
||||||
|
|
||||||
const RepairVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["visual"], { kind: "repair" }> }) => (
|
const RepairVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["visual"], { kind: "repair" }> }) => (
|
||||||
<section className="authoring-visual authoring-visual--repair" role="region" aria-label="validation repair evidence">
|
<section className="authoring-visual authoring-visual--repair" aria-label="validation repair evidence">
|
||||||
<div className="authoring-repair__diagnostic"><AlertTriangle aria-hidden="true" /><span>Diagnostic</span><strong>{visual.diagnostic}</strong></div>
|
<div className="authoring-repair__diagnostic"><AlertTriangle aria-hidden="true" /><span>Diagnostic</span><strong>{visual.diagnostic}</strong></div>
|
||||||
<ArrowRight aria-hidden="true" />
|
<ArrowRight aria-hidden="true" />
|
||||||
<div className="authoring-repair__correction"><Link2 aria-hidden="true" /><span>Repair</span><code>{visual.correction}</code></div>
|
<div className="authoring-repair__correction"><Link2 aria-hidden="true" /><span>Repair</span><code>{visual.correction}</code></div>
|
||||||
@@ -50,7 +50,7 @@ const RepairVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["vi
|
|||||||
);
|
);
|
||||||
|
|
||||||
const ArtifactVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["visual"], { kind: "artifact" }> }) => (
|
const ArtifactVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["visual"], { kind: "artifact" }> }) => (
|
||||||
<section className="authoring-visual authoring-visual--artifact" role="region" aria-label="artifact evidence">
|
<section className="authoring-visual authoring-visual--artifact" aria-label="artifact evidence">
|
||||||
<LockKeyhole aria-hidden="true" />
|
<LockKeyhole aria-hidden="true" />
|
||||||
<div><span>Immutable workflow artifact</span><strong>{visual.artifactId}</strong></div>
|
<div><span>Immutable workflow artifact</span><strong>{visual.artifactId}</strong></div>
|
||||||
<dl><div><dt>Version</dt><dd>Version {visual.version}</dd></div><div><dt>Requirements</dt><dd>{visual.requiredSources} local sources</dd></div></dl>
|
<dl><div><dt>Version</dt><dd>Version {visual.version}</dd></div><div><dt>Requirements</dt><dd>{visual.requiredSources} local sources</dd></div></dl>
|
||||||
@@ -58,7 +58,7 @@ const ArtifactVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["
|
|||||||
);
|
);
|
||||||
|
|
||||||
const BindingsVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["visual"], { kind: "bindings" }> }) => (
|
const BindingsVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["visual"], { kind: "bindings" }> }) => (
|
||||||
<section className="authoring-visual authoring-visual--bindings" role="region" aria-label="deployment binding evidence">
|
<section className="authoring-visual authoring-visual--bindings" aria-label="deployment binding evidence">
|
||||||
<header><Link2 aria-hidden="true" /><div><span>Deployment</span><strong>{visual.deploymentId}</strong></div><b><CheckCircle2 aria-hidden="true" />{visual.status}</b></header>
|
<header><Link2 aria-hidden="true" /><div><span>Deployment</span><strong>{visual.deploymentId}</strong></div><b><CheckCircle2 aria-hidden="true" />{visual.status}</b></header>
|
||||||
<div className="authoring-bindings__rows">
|
<div className="authoring-bindings__rows">
|
||||||
{visual.bindings.map((binding) => (
|
{visual.bindings.map((binding) => (
|
||||||
|
|||||||
@@ -67,6 +67,29 @@ describe("projectPreparedAuthoring", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("labels each prepared command with its actual operation name", () => {
|
||||||
|
const titles = projectPreparedAuthoring()
|
||||||
|
.flatMap((phase) => phase.commands)
|
||||||
|
.map((command) => command.title);
|
||||||
|
|
||||||
|
expect(titles).toEqual([
|
||||||
|
"workflow.sources.list",
|
||||||
|
"workflow.capabilities.list",
|
||||||
|
"workflow.capabilities.inspect",
|
||||||
|
"wf schema",
|
||||||
|
"workflow.draft_workspaces.create_from_capability",
|
||||||
|
"workflow.draft_workspaces.add_step_from_capability",
|
||||||
|
"workflow.draft_workspaces.get",
|
||||||
|
"workflow.draft_workspaces.validate",
|
||||||
|
"workflow.draft_workspaces.set_step_output_map",
|
||||||
|
"workflow.draft_workspaces.compile",
|
||||||
|
"workflow.draft_workspaces.create_artifact",
|
||||||
|
"workflow.artifacts.inspect",
|
||||||
|
"workflow.deployments.save",
|
||||||
|
"workflow.deployments.validate",
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it("starts every command with wf or uv run wf", () => {
|
it("starts every command with wf or uv run wf", () => {
|
||||||
const phases = projectPreparedAuthoring();
|
const phases = projectPreparedAuthoring();
|
||||||
for (const phase of phases) {
|
for (const phase of phases) {
|
||||||
@@ -118,7 +141,12 @@ describe("projectPreparedAuthoringThread", () => {
|
|||||||
|
|
||||||
expect(calls).toHaveLength(projectPreparedAuthoring()[0]!.commands.length);
|
expect(calls).toHaveLength(projectPreparedAuthoring()[0]!.commands.length);
|
||||||
expect(results).toHaveLength(calls.length);
|
expect(results).toHaveLength(calls.length);
|
||||||
expect(calls.every((part) => part.call.name === "runWorkflowCommand")).toBe(true);
|
expect(calls.map((part) => part.call.name)).toEqual([
|
||||||
|
"workflow.sources.list",
|
||||||
|
"workflow.capabilities.list",
|
||||||
|
"workflow.capabilities.inspect",
|
||||||
|
"wf schema",
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses a stable phase tool-group id", () => {
|
it("uses a stable phase tool-group id", () => {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
agentToolResultPart,
|
agentToolResultPart,
|
||||||
type AgentMessage,
|
type AgentMessage,
|
||||||
} from "../../demo/agent/events.js";
|
} from "../../demo/agent/events.js";
|
||||||
|
import type { PreparedAuthoringToolName } from "../../demo/agent/tools.js";
|
||||||
|
|
||||||
export type AuthoringPhaseId =
|
export type AuthoringPhaseId =
|
||||||
| "discover"
|
| "discover"
|
||||||
@@ -24,6 +25,8 @@ export type AuthoringPhaseId =
|
|||||||
export type CommandResult = "success" | "diagnostic";
|
export type CommandResult = "success" | "diagnostic";
|
||||||
|
|
||||||
export type PreparedAuthoringCommand = {
|
export type PreparedAuthoringCommand = {
|
||||||
|
/** JSON-RPC method, or the explicit local CLI label for non-RPC commands. */
|
||||||
|
readonly title: PreparedAuthoringToolName;
|
||||||
readonly command: string;
|
readonly command: string;
|
||||||
readonly summary: string;
|
readonly summary: string;
|
||||||
readonly result: CommandResult;
|
readonly result: CommandResult;
|
||||||
@@ -70,24 +73,28 @@ const recording: readonly PreparedAuthoringPhase[] = [
|
|||||||
label: "Discover",
|
label: "Discover",
|
||||||
commands: [
|
commands: [
|
||||||
{
|
{
|
||||||
|
title: "workflow.sources.list",
|
||||||
command: "wf source list",
|
command: "wf source list",
|
||||||
summary: "List available capability sources",
|
summary: "List available capability sources",
|
||||||
result: "success",
|
result: "success",
|
||||||
detail: "6 sources: local.lda_docs, local.lda_report, local.issue_board, and platform helpers.",
|
detail: "6 sources: local.lda_docs, local.lda_report, local.issue_board, and platform helpers.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
title: "workflow.capabilities.list",
|
||||||
command: "wf cap list --source local.lda_report --format ids",
|
command: "wf cap list --source local.lda_report --format ids",
|
||||||
summary: "List report workflow capabilities",
|
summary: "List report workflow capabilities",
|
||||||
result: "success",
|
result: "success",
|
||||||
detail: "5 capabilities: analyze_documents, build_report, create_issue_drafts, finalise_report, record_revision_request.",
|
detail: "5 capabilities: analyze_documents, build_report, create_issue_drafts, finalise_report, record_revision_request.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
title: "workflow.capabilities.inspect",
|
||||||
command: "wf cap inspect local.lda_report.analyze_documents",
|
command: "wf cap inspect local.lda_report.analyze_documents",
|
||||||
summary: "Inspect the report-analysis contract",
|
summary: "Inspect the report-analysis contract",
|
||||||
result: "success",
|
result: "success",
|
||||||
detail: "Input: documents. Output: analysis. Declared outcome: ok.",
|
detail: "Input: documents. Output: analysis. Declared outcome: ok.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
title: "wf schema",
|
||||||
command: "wf schema",
|
command: "wf schema",
|
||||||
summary: "List registered workflow schemas",
|
summary: "List registered workflow schemas",
|
||||||
result: "success",
|
result: "success",
|
||||||
@@ -116,17 +123,20 @@ const recording: readonly PreparedAuthoringPhase[] = [
|
|||||||
label: "Draft",
|
label: "Draft",
|
||||||
commands: [
|
commands: [
|
||||||
{
|
{
|
||||||
|
title: "workflow.draft_workspaces.create_from_capability",
|
||||||
command: "wf draft create lda_report_workflow --capability local.lda_docs.read_documents",
|
command: "wf draft create lda_report_workflow --capability local.lda_docs.read_documents",
|
||||||
summary: "Create a new workflow draft",
|
summary: "Create a new workflow draft",
|
||||||
result: "success",
|
result: "success",
|
||||||
detail: "Draft 'lda_report_workflow' created with ID draft_a1b2c3",
|
detail: "Draft 'lda_report_workflow' created with ID draft_a1b2c3",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
title: "workflow.draft_workspaces.add_step_from_capability",
|
||||||
command: "wf draft add-step lda_report_workflow --revision 1 --step analyze --capability local.lda_report.analyze_documents --from-step read_documents --from-outcome ok --route ok=__end__ --input state.documents=documents",
|
command: "wf draft add-step lda_report_workflow --revision 1 --step analyze --capability local.lda_report.analyze_documents --from-step read_documents --from-outcome ok --route ok=__end__ --input state.documents=documents",
|
||||||
summary: "Add the report-analysis step with a declared route",
|
summary: "Add the report-analysis step with a declared route",
|
||||||
result: "success",
|
result: "success",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
title: "workflow.draft_workspaces.get",
|
||||||
command: "wf draft inspect lda_report_workflow --include-draft",
|
command: "wf draft inspect lda_report_workflow --include-draft",
|
||||||
summary: "Inspect the prepared draft graph",
|
summary: "Inspect the prepared draft graph",
|
||||||
result: "success",
|
result: "success",
|
||||||
@@ -151,12 +161,14 @@ const recording: readonly PreparedAuthoringPhase[] = [
|
|||||||
label: "Validate",
|
label: "Validate",
|
||||||
commands: [
|
commands: [
|
||||||
{
|
{
|
||||||
|
title: "workflow.draft_workspaces.validate",
|
||||||
command: "wf draft validate lda_report_workflow",
|
command: "wf draft validate lda_report_workflow",
|
||||||
summary: "Validate the workflow draft",
|
summary: "Validate the workflow draft",
|
||||||
result: "diagnostic",
|
result: "diagnostic",
|
||||||
detail: "Diagnostic: analyze output 'analysis' has no state projection.",
|
detail: "Diagnostic: analyze output 'analysis' has no state projection.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
title: "workflow.draft_workspaces.set_step_output_map",
|
||||||
command: "wf draft set-output lda_report_workflow --revision 2 --step analyze --map analysis=state.analysis",
|
command: "wf draft set-output lda_report_workflow --revision 2 --step analyze --map analysis=state.analysis",
|
||||||
summary: "Repair the missing output binding",
|
summary: "Repair the missing output binding",
|
||||||
result: "success",
|
result: "success",
|
||||||
@@ -185,18 +197,21 @@ const recording: readonly PreparedAuthoringPhase[] = [
|
|||||||
label: "Artifact",
|
label: "Artifact",
|
||||||
commands: [
|
commands: [
|
||||||
{
|
{
|
||||||
|
title: "workflow.draft_workspaces.compile",
|
||||||
command: "wf draft compile lda_report_workflow",
|
command: "wf draft compile lda_report_workflow",
|
||||||
summary: "Compile the validated draft without mutating it",
|
summary: "Compile the validated draft without mutating it",
|
||||||
result: "success",
|
result: "success",
|
||||||
detail: "Compiled raw plan requires local.lda_docs, local.lda_report, and local.issue_board.",
|
detail: "Compiled raw plan requires local.lda_docs, local.lda_report, and local.issue_board.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
title: "workflow.draft_workspaces.create_artifact",
|
||||||
command: "wf draft save lda_report_workflow --artifact lda_report_case_study --version 1 --title \"lda.chat Report Case Study\" --outcome completed --outcome cancelled --binding local.lda_docs=local.lda_docs --binding local.lda_report=local.lda_report --binding local.issue_board=local.issue_board",
|
command: "wf draft save lda_report_workflow --artifact lda_report_case_study --version 1 --title \"lda.chat Report Case Study\" --outcome completed --outcome cancelled --binding local.lda_docs=local.lda_docs --binding local.lda_report=local.lda_report --binding local.issue_board=local.issue_board",
|
||||||
summary: "Save immutable artifact version 1",
|
summary: "Save immutable artifact version 1",
|
||||||
result: "success",
|
result: "success",
|
||||||
detail: "Artifact lda_report_case_study v1 saved with report-workflow source requirements.",
|
detail: "Artifact lda_report_case_study v1 saved with report-workflow source requirements.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
title: "workflow.artifacts.inspect",
|
||||||
command: "wf artifact inspect lda_report_case_study --version 1",
|
command: "wf artifact inspect lda_report_case_study --version 1",
|
||||||
summary: "Inspect the compiled artifact",
|
summary: "Inspect the compiled artifact",
|
||||||
result: "success",
|
result: "success",
|
||||||
@@ -221,12 +236,14 @@ const recording: readonly PreparedAuthoringPhase[] = [
|
|||||||
label: "Deployment",
|
label: "Deployment",
|
||||||
commands: [
|
commands: [
|
||||||
{
|
{
|
||||||
|
title: "workflow.deployments.save",
|
||||||
command: "wf deploy save lda_report_case_study.default --artifact lda_report_case_study --version 1 --binding local.lda_docs=local.lda_docs --binding local.lda_report=local.lda_report --binding local.issue_board=local.issue_board",
|
command: "wf deploy save lda_report_case_study.default --artifact lda_report_case_study --version 1 --binding local.lda_docs=local.lda_docs --binding local.lda_report=local.lda_report --binding local.issue_board=local.issue_board",
|
||||||
summary: "Save deployment with source bindings",
|
summary: "Save deployment with source bindings",
|
||||||
result: "success",
|
result: "success",
|
||||||
detail: "Deployment lda_report_case_study.default saved with all three local source bindings.",
|
detail: "Deployment lda_report_case_study.default saved with all three local source bindings.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
title: "workflow.deployments.validate",
|
||||||
command: "wf deploy validate lda_report_case_study.default",
|
command: "wf deploy validate lda_report_case_study.default",
|
||||||
summary: "Validate deployment bindings",
|
summary: "Validate deployment bindings",
|
||||||
result: "success",
|
result: "success",
|
||||||
@@ -275,12 +292,12 @@ export const projectPreparedAuthoringThread = (
|
|||||||
const toolParts = phase.commands.flatMap((command, index) => {
|
const toolParts = phase.commands.flatMap((command, index) => {
|
||||||
const callId = `${groupId}-command-${index}`;
|
const callId = `${groupId}-command-${index}`;
|
||||||
return [
|
return [
|
||||||
agentToolCallPart(callId, "runWorkflowCommand", {
|
agentToolCallPart(callId, command.title, {
|
||||||
phase: phase.phase,
|
phase: phase.phase,
|
||||||
command: command.command,
|
command: command.command,
|
||||||
summary: command.summary,
|
summary: command.summary,
|
||||||
}),
|
}),
|
||||||
agentToolResultPart(callId, "runWorkflowCommand", "success", {
|
agentToolResultPart(callId, command.title, "success", {
|
||||||
status: command.result,
|
status: command.result,
|
||||||
detail: command.detail ?? null,
|
detail: command.detail ?? null,
|
||||||
}),
|
}),
|
||||||
@@ -298,27 +315,5 @@ export const projectPreparedAuthoringThread = (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handoffConversation: readonly AuthoringConversationTurn[] = [
|
|
||||||
{
|
|
||||||
role: "user",
|
|
||||||
text: "We need to prepare a report workflow for the lda_report scenario. What sources and capabilities are available?",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
role: "assistant",
|
|
||||||
text: "I found the local document, report, and issue-board capabilities. I can author the reusable workflow through the public wf CLI.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
role: "user",
|
|
||||||
text: "Keep the workflow inspectable and validate the source bindings before it runs.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
role: "assistant",
|
|
||||||
text: "The prepared workflow is ready for deployment: I will create the draft, repair validation diagnostics, save artifact lda_report_case_study v1, then validate its deployment bindings.",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
/** Returns the full prepared authoring recording. */
|
/** Returns the full prepared authoring recording. */
|
||||||
export const projectPreparedAuthoring = (): readonly PreparedAuthoringPhase[] => recording;
|
export const projectPreparedAuthoring = (): readonly PreparedAuthoringPhase[] => recording;
|
||||||
|
|
||||||
/** Returns the compact Scene 8 handoff derived from the prepared authoring evidence. */
|
|
||||||
export const projectPreparedAuthoringHandoff = (): readonly AuthoringConversationTurn[] => handoffConversation;
|
|
||||||
|
|||||||
@@ -157,13 +157,12 @@ const AssistantMessageBody = ({
|
|||||||
const part = parts[index]!;
|
const part = parts[index]!;
|
||||||
if (part.type === "text") {
|
if (part.type === "text") {
|
||||||
rendered.push(
|
rendered.push(
|
||||||
<p key={`text-${index}`} style={{ whiteSpace: "pre-line" }}>{part.text}</p>,
|
<p key={`text-${part.text}`} style={{ whiteSpace: "pre-line" }}>{part.text}</p>,
|
||||||
);
|
);
|
||||||
index += 1;
|
index += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const toolRunStart = index;
|
|
||||||
const toolRun: ToolRenderPart[] = [];
|
const toolRun: ToolRenderPart[] = [];
|
||||||
while (index < parts.length && parts[index]!.type !== "text") {
|
while (index < parts.length && parts[index]!.type !== "text") {
|
||||||
toolRun.push(parts[index]! as ToolRenderPart);
|
toolRun.push(parts[index]! as ToolRenderPart);
|
||||||
@@ -176,7 +175,7 @@ const AssistantMessageBody = ({
|
|||||||
|
|
||||||
if (logicalTools.length === 1 && !messageId.startsWith("authoring-")) {
|
if (logicalTools.length === 1 && !messageId.startsWith("authoring-")) {
|
||||||
rendered.push(
|
rendered.push(
|
||||||
<div key={`tool-${toolRunStart}`}>
|
<div key={`tool-${logicalTools[0]!.toolCallId ?? logicalTools[0]!.toolName}`}>
|
||||||
{renderContentPart(logicalTools[0]!, submitApproval, cancelApproval)}
|
{renderContentPart(logicalTools[0]!, submitApproval, cancelApproval)}
|
||||||
</div>,
|
</div>,
|
||||||
);
|
);
|
||||||
@@ -189,7 +188,7 @@ const AssistantMessageBody = ({
|
|||||||
const count = logicalTools.length;
|
const count = logicalTools.length;
|
||||||
rendered.push(
|
rendered.push(
|
||||||
<ToolGroupRoot
|
<ToolGroupRoot
|
||||||
key={`tool-group-${toolRunStart}`}
|
key={`tool-group-${groupId}`}
|
||||||
data-tool-group-id={groupId}
|
data-tool-group-id={groupId}
|
||||||
{...(phaseLabel
|
{...(phaseLabel
|
||||||
? {
|
? {
|
||||||
@@ -203,13 +202,13 @@ const AssistantMessageBody = ({
|
|||||||
{...(phaseLabel ? { label: `${phaseLabel} · ${count} tool ${count === 1 ? "call" : "calls"}` } : {})}
|
{...(phaseLabel ? { label: `${phaseLabel} · ${count} tool ${count === 1 ? "call" : "calls"}` } : {})}
|
||||||
/>
|
/>
|
||||||
<ToolGroupContent>
|
<ToolGroupContent>
|
||||||
{logicalTools.map((tool, toolIndex) => {
|
{logicalTools.map((tool) => {
|
||||||
const pairedResult = tool.type === "tool-call"
|
const pairedResult = tool.type === "tool-call"
|
||||||
? toolRun.find((candidate): candidate is Extract<ToolRenderPart, { readonly type: "tool-result" }> =>
|
? toolRun.find((candidate): candidate is Extract<ToolRenderPart, { readonly type: "tool-result" }> =>
|
||||||
candidate.type === "tool-result" && candidate.toolCallId === tool.toolCallId)
|
candidate.type === "tool-result" && candidate.toolCallId === tool.toolCallId)
|
||||||
: undefined;
|
: undefined;
|
||||||
return (
|
return (
|
||||||
<div key={`${tool.type}-${tool.toolName}-${tool.toolCallId ?? "no-id"}-${toolIndex}`}>
|
<div key={`${tool.type}-${tool.toolName}-${tool.toolCallId ?? "no-id"}`}>
|
||||||
{renderContentPart(tool, submitApproval, cancelApproval, false, pairedResult)}
|
{renderContentPart(tool, submitApproval, cancelApproval, false, pairedResult)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -254,13 +253,19 @@ export const AssistantOperatorThread = ({
|
|||||||
}: AssistantOperatorThreadProps) => {
|
}: AssistantOperatorThreadProps) => {
|
||||||
const projected = useMemo(() => projectAgentMessagesForAssistant(messages), [messages]);
|
const projected = useMemo(() => projectAgentMessagesForAssistant(messages), [messages]);
|
||||||
const viewportRef = useRef<HTMLDivElement>(null);
|
const viewportRef = useRef<HTMLDivElement>(null);
|
||||||
const [openToolGroups, setOpenToolGroups] = useState<ReadonlySet<string>>(
|
const [toolGroupOverrides, setToolGroupOverrides] = useState<ReadonlyMap<string, boolean>>(
|
||||||
() => new Set(activeToolGroupId ? [activeToolGroupId] : []),
|
() => new Map(),
|
||||||
);
|
);
|
||||||
|
const openToolGroups = useMemo(() => {
|
||||||
useEffect(() => {
|
const open = new Set<string>();
|
||||||
if (activeToolGroupId) setOpenToolGroups(new Set([activeToolGroupId]));
|
if (activeToolGroupId && toolGroupOverrides.get(activeToolGroupId) !== false) {
|
||||||
}, [activeToolGroupId]);
|
open.add(activeToolGroupId);
|
||||||
|
}
|
||||||
|
for (const [groupId, isOpen] of toolGroupOverrides) {
|
||||||
|
if (isOpen) open.add(groupId);
|
||||||
|
}
|
||||||
|
return open;
|
||||||
|
}, [activeToolGroupId, toolGroupOverrides]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!activeToolGroupId) return;
|
if (!activeToolGroupId) return;
|
||||||
@@ -278,10 +283,9 @@ export const AssistantOperatorThread = ({
|
|||||||
}, [activeToolGroupId, projected]);
|
}, [activeToolGroupId, projected]);
|
||||||
|
|
||||||
const setToolGroupOpen = useCallback((groupId: string, open: boolean) => {
|
const setToolGroupOpen = useCallback((groupId: string, open: boolean) => {
|
||||||
setOpenToolGroups((current) => {
|
setToolGroupOverrides((current) => {
|
||||||
const next = new Set(current);
|
const next = new Map(current);
|
||||||
if (open) next.add(groupId);
|
next.set(groupId, open);
|
||||||
else next.delete(groupId);
|
|
||||||
return next;
|
return next;
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
@@ -305,8 +309,8 @@ export const AssistantOperatorThread = ({
|
|||||||
if (message.role === "user") {
|
if (message.role === "user") {
|
||||||
return (
|
return (
|
||||||
<MessageBubble key={message.id} role="user">
|
<MessageBubble key={message.id} role="user">
|
||||||
{message.content.filter((p) => p.type === "text").map((p, i) => (
|
{message.content.filter((p) => p.type === "text").map((p) => (
|
||||||
<p key={`text-${i}`} style={{ whiteSpace: "pre-line" }}>{p.text}</p>
|
<p key={`text-${p.text}`} style={{ whiteSpace: "pre-line" }}>{p.text}</p>
|
||||||
))}
|
))}
|
||||||
</MessageBubble>
|
</MessageBubble>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -137,4 +137,34 @@ describe("assistantRuntimeProjection", () => {
|
|||||||
{ type: "text", text: "provider failed" },
|
{ type: "text", text: "provider failed" },
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("assigns sequential indexes for same-type presentation actions", () => {
|
||||||
|
const messages: ReadonlyArray<AgentMessage> = [
|
||||||
|
{
|
||||||
|
id: "dual",
|
||||||
|
role: "assistant",
|
||||||
|
parts: [
|
||||||
|
{ type: "presentation-action", action: { type: "selectWorkflowNode", nodeId: "review_issues" } },
|
||||||
|
{ type: "presentation-action", action: { type: "selectWorkflowNode", nodeId: "start_step" } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const projected = projectAgentMessagesForAssistant(messages);
|
||||||
|
|
||||||
|
expect(projected[0]?.content).toEqual([
|
||||||
|
{
|
||||||
|
type: "tool-call",
|
||||||
|
toolCallId: "presentation-dual-0-selectWorkflowNode",
|
||||||
|
toolName: "presentation.selectWorkflowNode",
|
||||||
|
args: { type: "selectWorkflowNode", nodeId: "review_issues" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "tool-call",
|
||||||
|
toolCallId: "presentation-dual-1-selectWorkflowNode",
|
||||||
|
toolName: "presentation.selectWorkflowNode",
|
||||||
|
args: { type: "selectWorkflowNode", nodeId: "start_step" },
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user