Files
fusion/packages/dashboard/app/components/agent-presets/agentCreatePayload.ts
gsxdsm eaadd153b1 FN-8764: route workflow stages through durable role agents
Route workflow stages through task-scoped durable role agents.

- Persist normalized multi-role agents and workflow principal fences with migrations.
- Route planning, execution, review, and merge workflow nodes through authorized permanent principals with capacity leasing and recovery.
- Retire ephemeral workflow-stage workers and expose role-aware agent configuration, workflow editing, and documentation.
- Preserve lifecycle-column ratchet coverage by centralizing workflow-role classification rather than adding test exemptions.

Files changed:
 .changeset/fn-8764-workflow-role-agents.md         |   7 +
 CONCEPTS.md                                        |   3 +
 docs/agents.md                                     |   6 +
 docs/architecture.md                               |   6 +
 docs/cli-reference.md                              |   2 +
 docs/dashboard-guide.md                            |   4 +
 docs/settings-reference.md                         |   6 +-
 docs/storage.md                                    |   2 +
 docs/workflow-steps.md                             |   6 +
 .../src/__tests__/extension-agent-update.test.ts   |  11 +-
 packages/cli/src/__tests__/extension.test.ts       |  18 +-
 packages/cli/src/extension.ts                      |  41 +-
 .../core/src/__tests__/agent-permissions.test.ts   |  12 +
 .../core/src/__tests__/agent-role-policy.test.ts   |   7 +
 packages/core/src/__tests__/agent-roles.test.ts    |  21 +
 .../legacy-column-collection-gating-ledger.test.ts |  19 +-
 .../src/__tests__/postgres/schema-applier.test.ts  |  16 +-
 .../core/src/__tests__/settings-parity.test.ts     |   9 +-
 .../workflow-agent-node-classification.test.ts     |  25 +
 .../src/__tests__/workflow-work-item-cas.test.ts   |  38 ++
 packages/core/src/agents/agent-permissions.ts      |  11 +-
 packages/core/src/agents/agent-role-policy.ts      |  39 +-
 packages/core/src/agents/agent-store.ts            | 190 ++++++-
 .../core/src/async-stores/async-agent-store.ts     |   6 +
 packages/core/src/config/settings-schema.ts        |   5 +-
 packages/core/src/index.gate.ts                    |   2 +-
 packages/core/src/index.ts                         |   7 +-
 .../0045_fn_8764_multi_role_workflow_agents.sql    |  20 +
 .../0046_fn_8764_workflow_principal_fence.sql      |  49 ++
 packages/core/src/postgres/schema-applier.ts       |  22 +-
 packages/core/src/postgres/schema/project.ts       |  21 +
 packages/core/src/store.ts                         |   2 +-
 .../task-store/async/async-workflow-workitems.ts   |  49 +-
 packages/core/src/task-store/row-types.ts          |   4 +
 packages/core/src/task-store/settings-helpers.ts   |  16 +-
 packages/core/src/task-store/settings-ops-2.ts     |  13 +-
 packages/core/src/task-store/settings-ops.ts       |  16 +-
 packages/core/src/task-store/task-row-mappers.ts   |   4 +
 .../src/task-store/workflow-task-create-ops.ts     |   6 +-
 .../src/task-store/workflow-workitems-ops-2.ts     |  25 +-
 packages/core/src/types.ts                         |   2 +
 packages/core/src/types/agents/agents.ts           |  45 +-
 packages/core/src/types/merge/merge-queue.ts       |  17 +
 packages/core/src/types/settings/settings-scope.ts |   9 +-
 packages/core/src/workflows/workflow-ir-types.ts   |  58 +++
 packages/core/src/workflows/workflow-ir.ts         |  19 +
 .../dashboard/app/components/AgentDetailView.css   |  14 +
 .../dashboard/app/components/AgentDetailView.tsx   |  34 +-
 .../dashboard/app/components/NewAgentDialog.tsx    |  28 +-
 .../app/components/WorkflowNodeEditor.tsx          |  19 +
 .../__tests__/AgentDetailView.core.test.tsx        |   4 +-
 .../app/components/__tests__/AgentsView.test.tsx   |   2 +-
 .../__tests__/SettingsModal.general.test.tsx       |  86 ---
 .../__tests__/SettingsModal.test-harness.tsx       |   1 -
 .../components/agent-presets/agentCreatePayload.ts |   9 +-
 .../app/components/settings/section-keys.ts        |   1 -
 .../settings/sections/GeneralSection.tsx           |   8 -
 .../settings-default-descriptions.test.tsx         |   1 -
 .../app/components/workflow-flow-mapping.ts        |   7 +
 packages/dashboard/src/mission-routes.ts           |  26 +-
 .../src/routes/__tests__/agent-core-routes.test.ts |  23 +-
 .../src/routes/register-agent-core-routes.ts       |  42 +-
 ...gister-agent-import-export-generation-routes.ts |  21 -
 .../engine/src/__tests__/agent-action-gate.test.ts |  33 ++
 .../engine/src/__tests__/agent-assignment.test.ts  | 370 -------------
 .../src/__tests__/ephemeral-worker-manager.test.ts | 575 ---------------------
 ...ecutor-ephemeral-disabled-dispatch-gate.test.ts | 223 --------
 .../__tests__/executor-fast-mode-workflows.test.ts |  58 +++
 .../engine/src/__tests__/log-severity-manifest.ts  |   1 -
 .../__tests__/log-severity-spam-contract.test.ts   |   3 -
 .../resolved-read-with-literal-filter.test.ts      |   4 -
 .../__tests__/scheduler-ephemeral-toggle.test.ts   | 175 -------
 .../__tests__/scheduler-workflow-cutover.test.ts   |  19 -
 .../src/__tests__/workflow-agent-capacity.test.ts  |  47 ++
 .../src/__tests__/workflow-agent-routing.test.ts   | 137 +++++
 .../src/__tests__/workflow-graph-foreach.test.ts   |  15 +
 .../__tests__/workflow-graph-task-runner.test.ts   |  73 +++
 .../src/__tests__/workflow-task-runtime.test.ts    |  95 ++++
 .../src/__tests__/workflow-work-scheduler.test.ts  |  20 +
 packages/engine/src/agents/agent-action-gate.ts    |  64 +++
 packages/engine/src/agents/agent-assignment.ts     | 135 -----
 packages/engine/src/agents/agent-reflection.ts     |   1 +
 .../engine/src/agents/ephemeral-worker-manager.ts  | 429 ---------------
 .../engine/src/agents/workflow-agent-capacity.ts   | 113 ++++
 .../engine/src/agents/workflow-agent-router.ts     | 185 +++++++
 packages/engine/src/execution/reviewer.ts          |  26 +-
 packages/engine/src/executor.ts                    | 501 +++++++++++++++---
 packages/engine/src/index.ts                       |   1 -
 packages/engine/src/merger.ts                      |  20 +-
 packages/engine/src/pi.ts                          |  11 +
 packages/engine/src/runtimes/in-process-runtime.ts |  37 --
 packages/engine/src/scheduler.ts                   | 114 +---
 packages/engine/src/triage.ts                      | 196 ++++++-
 .../src/workflows/workflow-graph-executor.ts       | 109 +++-
 .../engine/src/workflows/workflow-graph-loop.ts    |  13 +-
 .../src/workflows/workflow-graph-task-runner.ts    |  12 +
 .../engine/src/workflows/workflow-task-runtime.ts  | 125 ++++-
 .../src/workflows/workflow-work-scheduler.ts       |   8 +-
 98 files changed, 2722 insertions(+), 2468 deletions(-)

Fusion-Task-Id: FN-8764
Fusion-Task-Lineage: 5527fccb-342d-46f6-8108-bbf89142efec
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-07 01:36:34 -07:00

91 lines
3.7 KiB
TypeScript

import type { AgentCapability, AgentCreateInput, AgentOnboardingSummary } from "../../api";
import type { AgentPreset } from "./index";
export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
export const VALID_AGENT_CAPABILITIES = new Set<string>(["triage", "executor", "reviewer", "merger", "scheduler", "engineer", "custom"]);
export interface AgentDraftValues {
name: string;
role: AgentCapability;
/** Canonical multi-role tags; `role` remains the compatibility primary. */
roles?: AgentCapability[];
title?: string;
icon?: string;
reportsTo?: string;
instructionsPath?: string;
instructionsText?: string;
heartbeatProcedurePath?: string;
soul?: string;
memory?: string;
model?: string;
runtimeHint?: string;
thinkingLevel?: ThinkingLevel;
maxTurns?: number;
skills?: string[];
}
export function mapPresetToAgentDraft(preset: AgentPreset): AgentDraftValues {
return {
name: preset.name,
role: preset.role,
title: preset.description ?? preset.title,
icon: preset.icon,
soul: preset.soul ?? "",
instructionsText: preset.instructionsText ?? "",
};
}
export function mapOnboardingSummaryToAgentDraft(draft: AgentOnboardingSummary): AgentDraftValues {
const runtimeHint = draft.runtimeHint?.trim() ?? "";
const modelSelection = draft.model?.trim() || draft.modelHint?.trim() || "";
return {
name: draft.name ?? "",
title: draft.title ?? "",
icon: draft.icon ?? "",
role: (VALID_AGENT_CAPABILITIES.has(draft.role) ? draft.role : "custom") as AgentCapability,
reportsTo: draft.reportsTo ?? "",
instructionsText: draft.instructionsText ?? "",
heartbeatProcedurePath: draft.heartbeatProcedurePath ?? "",
soul: draft.soul ?? "",
memory: draft.memory ?? "",
skills: Array.isArray(draft.skills) ? draft.skills : [],
model: runtimeHint ? "" : modelSelection,
runtimeHint,
thinkingLevel: draft.thinkingLevel ?? undefined,
maxTurns: draft.maxTurns ?? undefined,
};
}
export function buildAgentCreatePayload(values: AgentDraftValues): AgentCreateInput {
const runtimeCfg: Record<string, unknown> = {};
if (values.runtimeHint?.trim()) {
runtimeCfg.runtimeHint = values.runtimeHint.trim();
} else if (values.model?.trim()) {
runtimeCfg.model = values.model.trim();
}
if (values.thinkingLevel && values.thinkingLevel !== "off") runtimeCfg.thinkingLevel = values.thinkingLevel;
if (values.maxTurns !== undefined && values.maxTurns !== 1000) runtimeCfg.maxTurns = values.maxTurns;
return {
name: values.name.trim(),
/*
* FNXC:WorkflowAgentRouting 2026-08-07-03:46:
* New permanent agents submit canonical role tags. The deprecated singular
* field remains an API compatibility seam, never the source of new data.
*/
roles: values.roles?.length ? values.roles : [values.role],
...(values.title?.trim() ? { title: values.title.trim() } : {}),
...(values.icon?.trim() ? { icon: values.icon.trim() } : {}),
...(values.reportsTo?.trim() ? { reportsTo: values.reportsTo.trim() } : {}),
...(values.instructionsPath?.trim() ? { instructionsPath: values.instructionsPath.trim() } : {}),
...(values.instructionsText?.trim() ? { instructionsText: values.instructionsText.trim() } : {}),
...(values.heartbeatProcedurePath?.trim() ? { heartbeatProcedurePath: values.heartbeatProcedurePath.trim() } : {}),
...(values.soul?.trim() ? { soul: values.soul.trim() } : {}),
...(values.memory?.trim() ? { memory: values.memory.trim() } : {}),
...(Object.keys(runtimeCfg).length > 0 ? { runtimeConfig: runtimeCfg } : {}),
...(values.skills && values.skills.length > 0 ? { metadata: { skills: values.skills } } : {}),
};
}