Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 94 additions & 5 deletions dashboard/src/v2/NodesPage.tsx

Large diffs are not rendered by default.

25 changes: 20 additions & 5 deletions dashboard/src/v2/components/nodes/NodeFlowInspector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ interface NodeFlowInspectorProps {
attachments: NodeFlowSkillAttachment[];
attachAgentId: string;
attaching?: boolean;
attachmentsLoading?: boolean;
attachmentError?: string | null;
onAttachAgentIdChange: (agentPresetId: string) => void;
onAttachAgent: () => void;
onDetachAgent: (agentPresetId: string) => void;
onRetryAttachments?: () => void;
onNodeChange: (nodeId: string, update: Partial<NodeFlowNode>) => void;
definition?: NodeDefinitionManifest | null;
requiredCredentials?: NodeFlowRequiredCredential[];
Expand All @@ -41,9 +44,12 @@ export const NodeFlowInspector: FunctionComponent<NodeFlowInspectorProps> = ({
attachments,
attachAgentId,
attaching = false,
attachmentsLoading = false,
attachmentError = null,
onAttachAgentIdChange,
onAttachAgent,
onDetachAgent,
onRetryAttachments,
onNodeChange,
definition = null,
requiredCredentials = [],
Expand Down Expand Up @@ -133,13 +139,14 @@ export const NodeFlowInspector: FunctionComponent<NodeFlowInspectorProps> = ({
))}
</section>

<section className="flex flex-col gap-3 border-t border-black/[0.06] pt-4 dark:border-white/[0.06]" aria-labelledby="node-agent-attachments-heading">
<section className="flex flex-col gap-3 border-t border-black/[0.06] pt-4 dark:border-white/[0.06]" aria-labelledby="node-agent-attachments-heading" aria-busy={attachmentsLoading || attaching}>
<h3 id="node-agent-attachments-heading" className="text-xs font-bold uppercase tracking-[0.16em] text-slate-500 dark:text-slate-400">Agent Attachments</h3>
<div className="flex gap-2">
<select
aria-label="Agent preset"
className={inputClass}
value={attachAgentId}
disabled={attachmentsLoading || attaching}
onChange={(event) => onAttachAgentIdChange(event.currentTarget.value)}
>
<option value="">Select agent</option>
Expand All @@ -150,16 +157,23 @@ export const NodeFlowInspector: FunctionComponent<NodeFlowInspectorProps> = ({
<button
type="button"
aria-label="Attach node flow to agent"
disabled={!attachAgentId || attaching}
disabled={!attachAgentId || attachmentsLoading || attaching}
onClick={onAttachAgent}
className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-signal-500 text-white transition hover:bg-signal-400 focus:outline-none focus-visible:ring-2 focus-visible:ring-signal-500/40 disabled:opacity-50 dark:text-void-900"
>
<Link2 className="h-4 w-4" aria-hidden="true" />
</button>
</div>
{attachments.length === 0 ? (
{attachmentsLoading ? <p role="status" className="text-xs leading-relaxed text-slate-500 dark:text-slate-400">Loading agent attachments…</p> : null}
{attachmentError ? (
<div role="alert" className="rounded-xl border border-status-red/20 bg-status-red/[0.06] p-3 text-xs text-status-red">
<p>{attachmentError}</p>
{onRetryAttachments ? <button type="button" className="mt-2 font-bold underline focus:outline-none focus-visible:ring-2 focus-visible:ring-signal-500/40" onClick={onRetryAttachments}>Retry attachments</button> : null}
</div>
) : null}
{!attachmentsLoading && !attachmentError && attachments.length === 0 ? (
<p className="text-xs leading-relaxed text-slate-500 dark:text-slate-400">No agents attached.</p>
) : (
) : !attachmentsLoading && !attachmentError ? (
<div className="flex flex-col gap-2">
{attachments.map((attachment) => {
const agent = agents.find((entry) => entry.id === attachment.agentPresetId);
Expand All @@ -172,6 +186,7 @@ export const NodeFlowInspector: FunctionComponent<NodeFlowInspectorProps> = ({
<button
type="button"
aria-label={`Detach ${agent?.name ?? attachment.agentPresetId}`}
disabled={attaching}
className="flex h-8 w-8 items-center justify-center rounded-lg text-slate-400 transition hover:bg-status-red/[0.08] hover:text-status-red focus:outline-none focus-visible:ring-2 focus-visible:ring-signal-500/40"
onClick={() => onDetachAgent(attachment.agentPresetId)}
>
Expand All @@ -181,7 +196,7 @@ export const NodeFlowInspector: FunctionComponent<NodeFlowInspectorProps> = ({
);
})}
</div>
)}
) : null}
</section>
</aside>
);
Expand Down
15 changes: 15 additions & 0 deletions dashboard/src/v2/lib/__tests__/agent-preset-api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
import {
applyBaseAgentUpdate,
createAgentPreset,
fetchAgentPresets,
fetchBaseAgentUpdateNotices,
pushAgentPresetsToRepository,
updateAgentPreset,
Expand Down Expand Up @@ -62,6 +63,20 @@ describe("createAgentPreset", () => {
});
});

describe("fetchAgentPresets", () => {
it("forwards the project-transition abort signal", async () => {
const controller = new AbortController();
vi.mocked(fetchJson).mockResolvedValueOnce([]);

await expect(fetchAgentPresets("project/one", controller.signal)).resolves.toEqual([]);

expect(fetchJson).toHaveBeenCalledWith(
"/api/projects/project%2Fone/agent-presets",
{ signal: controller.signal },
);
});
});

describe("base-agent updates", () => {
it("fetches route-aware notices without changing the preset list contract", async () => {
const notices = [{
Expand Down
4 changes: 2 additions & 2 deletions dashboard/src/v2/lib/agent-preset-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import type {
} from "../../../../src/contracts/skill-types.js";
import { fetchJson } from "../../lib/api/fetch-json.js";

export const fetchAgentPresets = async (projectId: string): Promise<AgentPreset[]> => {
return fetchJson<AgentPreset[]>(`/api/projects/${encodeURIComponent(projectId)}/agent-presets`);
export const fetchAgentPresets = async (projectId: string, signal?: AbortSignal): Promise<AgentPreset[]> => {
return fetchJson<AgentPreset[]>(`/api/projects/${encodeURIComponent(projectId)}/agent-presets`, { signal });
};

export const fetchBaseAgentUpdateNotices = async (projectId: string): Promise<BaseAgentUpdateNotice[]> => {
Expand Down
4 changes: 4 additions & 0 deletions docs-web/content/docs/user-dashboard-node-flows.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ Foreach executes the selected downstream branch once per deterministic logical i

## Agent Attachment

A selected project loads its agent presets, and selecting a flow loads that flow's current bindings. The inspector exposes only agent names and attachment skill metadata; it never renders agent instructions, custom source, credential values, or decrypted material.

Attaching and detaching use the governed node-flow attachment routes, then refresh the selected flow's bindings. Project or flow changes clear the prior selection and visible bindings, abort in-flight reads where possible, and ignore stale responses. Loading, failure, retry, empty, and mutation states remain keyboard accessible. The backend independently enforces project ownership and the attached-flow capability boundary; dashboard state does not grant authorization.

A flow can be attached to a project agent preset as a repeatable skill with a name and description. Detaching removes only that binding; the flow, its graph, schedules, and run history remain in the project.

## Scheduling
Expand Down
2 changes: 2 additions & 0 deletions docs-web/content/docs/user-dashboard-nodes-canvas.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ Validated custom definitions can also execute after their immutable artifact and

Credential slots display metadata-only status such as bound, missing, or denied and can submit a binding request. Secret values remain behind the credential broker and are not returned to the graph or browser.

Agent attachments are also metadata-only. The selected project supplies the available preset names, and the selected flow supplies its current skill names and descriptions. Attach and detach refresh that governed backend state; project or flow transitions clear previous bindings and ignore obsolete requests. Agent instructions, custom source, credentials, and decrypted values are never rendered by the attachment controls.

Validation and dry run report graph issues, requested capabilities, credential requirements, side-effect differences, and policy findings. Dry run does not execute nodes. Publication requires the current draft revision, a valid graph and policy review, and all required credentials bound. Published snapshots are immutable; the workspace can compare versions or restore a prior version into a new draft revision.

## Runs and schedules
Expand Down
2 changes: 2 additions & 0 deletions docs-web/content/docs/user-dashboard-nodes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Validation and dry run report structural errors, requested capabilities, credent

The debugger reads persisted flow runs, node runs, attempts, retry decisions, approvals, invocation links, timing, and redacted input and output. Cancellation, safe retry, and approval decisions update the same durable pinned run rather than creating an unrelated execution. Scheduling targets a pinned or latest-published version from the [Scheduler](/docs/user-dashboard-scheduler).

The inspector loads project-owned agent presets and the selected flow's current skill attachments. Attach and detach operations refresh those bindings through the governed backend routes, while project and flow transitions clear stale UI state and cancel or ignore obsolete reads. Only agent and skill metadata is rendered; agent instructions, custom source, credentials, and decrypted values remain outside this surface.

Outside development builds, the workspace requires the Nodes feature flag plus the node-flow backend and automation-security prerequisites. A definition can also require a configured provider, credential broker, allowed egress, approval/outbox services, webhook ingress, or custom-node runtime. Registry presence and feature visibility do not assert that an integration is configured or production-ready.

For the detailed editing contract, see [Nodes Canvas](/docs/user-dashboard-nodes-canvas). For API behavior, publication, execution, and scheduling, see [Node Flows](/docs/user-dashboard-node-flows).
4 changes: 4 additions & 0 deletions docs-web/user/dashboard/node-flows.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ Foreach executes the selected downstream branch once per deterministic logical i

## Agent Attachment

A selected project loads its agent presets, and selecting a flow loads that flow's current bindings. The inspector exposes only agent names and attachment skill metadata; it never renders agent instructions, custom source, credential values, or decrypted material.

Attaching and detaching use the governed node-flow attachment routes, then refresh the selected flow's bindings. Project or flow changes clear the prior selection and visible bindings, abort in-flight reads where possible, and ignore stale responses. Loading, failure, retry, empty, and mutation states remain keyboard accessible. The backend independently enforces project ownership and the attached-flow capability boundary; dashboard state does not grant authorization.

A flow can be attached to a project agent preset as a repeatable skill with a name and description. Detaching removes only that binding; the flow, its graph, schedules, and run history remain in the project.

## Scheduling
Expand Down
2 changes: 2 additions & 0 deletions docs-web/user/dashboard/nodes-canvas.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ Validated custom definitions can also execute after their immutable artifact and

Credential slots display metadata-only status such as bound, missing, or denied and can submit a binding request. Secret values remain behind the credential broker and are not returned to the graph or browser.

Agent attachments are also metadata-only. The selected project supplies the available preset names, and the selected flow supplies its current skill names and descriptions. Attach and detach refresh that governed backend state; project or flow transitions clear previous bindings and ignore obsolete requests. Agent instructions, custom source, credentials, and decrypted values are never rendered by the attachment controls.

Validation and dry run report graph issues, requested capabilities, credential requirements, side-effect differences, and policy findings. Dry run does not execute nodes. Publication requires the current draft revision, a valid graph and policy review, and all required credentials bound. Published snapshots are immutable; the workspace can compare versions or restore a prior version into a new draft revision.

## Runs and schedules
Expand Down
2 changes: 2 additions & 0 deletions docs-web/user/dashboard/nodes.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Validation and dry run report structural errors, requested capabilities, credent

The debugger reads persisted flow runs, node runs, attempts, retry decisions, approvals, invocation links, timing, and redacted input and output. Cancellation, safe retry, and approval decisions update the same durable pinned run rather than creating an unrelated execution. Scheduling targets a pinned or latest-published version from the [Scheduler](./scheduler.md).

The inspector loads project-owned agent presets and the selected flow's current skill attachments. Attach and detach operations refresh those bindings through the governed backend routes, while project and flow transitions clear stale UI state and cancel or ignore obsolete reads. Only agent and skill metadata is rendered; agent instructions, custom source, credentials, and decrypted values remain outside this surface.

Outside development builds, the workspace requires the Nodes feature flag plus the node-flow backend and automation-security prerequisites. A definition can also require a configured provider, credential broker, allowed egress, approval/outbox services, webhook ingress, or custom-node runtime. Registry presence and feature visibility do not assert that an integration is configured or production-ready.

For the detailed editing contract, see [Nodes Canvas](./nodes-canvas.md). For API behavior, publication, execution, and scheduling, see [Node Flows](./node-flows.md).
4 changes: 4 additions & 0 deletions docs/dashboard/node-flows.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ The run debugger lists durable approvals beside node attempts. A pending item of

## Agent attachment

A selected project loads its agent presets, and selecting a flow loads that flow's current bindings. The inspector exposes only agent names and attachment skill metadata; it never renders agent instructions, custom source, credential values, or decrypted material.

Attaching and detaching use the governed node-flow attachment routes, then refresh the selected flow's bindings. Project or flow changes clear the prior selection and visible bindings, abort in-flight reads where possible, and ignore stale responses. Loading, failure, retry, empty, and mutation states remain keyboard accessible. The backend independently enforces project ownership and the attached-flow capability boundary; dashboard state does not grant authorization.

A flow can be attached to a project agent preset as a repeatable skill with a name and description. Detaching removes only that binding; the flow, its graph, schedules, and run history remain in the project.

Scheduling is entered through `/scheduler` and targets a pinned or latest-published version. A flow can also be attached to a project agent preset as a reusable skill; removing the attachment does not remove the flow, publications, schedules, or run history.
Expand Down
2 changes: 2 additions & 0 deletions docs/dashboard/nodes.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Validation and dry run report structural errors, requested capabilities, credent

The debugger reads persisted flow runs, node runs, attempts, retry decisions, approvals, invocation links, timing, and redacted input and output. Cancellation, safe retry, and approval decisions update the same durable pinned run rather than creating an unrelated execution. Scheduling targets a pinned or latest-published version from the [Scheduler](./scheduler.md).

The inspector loads project-owned agent presets and the selected flow's current skill attachments. Attach and detach operations refresh those bindings through the governed backend routes, while project and flow transitions clear stale UI state and cancel or ignore obsolete reads. Only agent and skill metadata is rendered; agent instructions, custom source, credentials, and decrypted values remain outside this surface.

Outside development builds, the workspace requires the Nodes feature flag plus the node-flow backend and automation-security prerequisites. A definition can also require a configured provider, credential broker, allowed egress, approval/outbox services, webhook ingress, or custom-node runtime. Registry presence and feature visibility do not assert that an integration is configured or production-ready.

For the detailed editing contract, see [Nodes Canvas](./nodes-canvas.md). For API behavior, publication, execution, and scheduling, see [Node Flows Dashboard](./node-flows.md).
Loading
Loading