@runfusion/fusion 0.75.1-beta.2 → 0.76.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +2883 -1203
- package/dist/child-process-worker.js +2345 -1000
- package/dist/client/.vite/manifest.json +260 -260
- package/dist/client/assets/AgentDetailView-Czb9wfC3.css +1 -0
- package/dist/client/assets/AgentDetailView-nsriz_Mw.js +13 -0
- package/dist/client/assets/{AgentPermissionPolicyEditor-CC_rrbbG.js → AgentPermissionPolicyEditor-TKuT5ryJ.js} +1 -1
- package/dist/client/assets/AgentsView-BBLq03lO.js +24 -0
- package/dist/client/assets/{ChatView-mFKfoegS.js → ChatView-BEc5SVfU.js} +1 -1
- package/dist/client/assets/{CommandCenter-C2wvDq_N.js → CommandCenter-DBCxo_tI.js} +1 -1
- package/dist/client/assets/{DevServerView-0MWEJM7o.js → DevServerView-BaLUl9De.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-BjMPhCdh.js → DirectoryPicker-CKj8CfUJ.js} +1 -1
- package/dist/client/assets/{DocumentsView-ckH3rjmd.js → DocumentsView-Cu8vlXJT.js} +1 -1
- package/dist/client/assets/{EvalsView-NyA644VF.js → EvalsView-CbdRWRjR.js} +1 -1
- package/dist/client/assets/{ExperimentalAgentOnboardingModal-D0ZfbBRN.js → ExperimentalAgentOnboardingModal-BVdW0xJ3.js} +1 -1
- package/dist/client/assets/{GoalsView-Ctt5P_yo.js → GoalsView-CCbeAvBm.js} +1 -1
- package/dist/client/assets/{InsightsView-NDp0bdNB.js → InsightsView-B46ZemSm.js} +1 -1
- package/dist/client/assets/{MemoryView-BKbA4Bm4.js → MemoryView-JM1fFU2D.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-V3AFWMHm.js → PiExtensionsManager-KHsw2af9.js} +1 -1
- package/dist/client/assets/{PluginManager-D88G_TZh.js → PluginManager-BeKLun0C.js} +1 -1
- package/dist/client/assets/{PullRequestView-DQQmFFYC.js → PullRequestView-CwQW2pe4.js} +1 -1
- package/dist/client/assets/{ReportModal-Clh1S8RF.js → ReportModal-BC9DhvX4.js} +1 -1
- package/dist/client/assets/{ResearchView-kxvq4zp5.js → ResearchView-CuB7IbEB.js} +1 -1
- package/dist/client/assets/{SecretsView-Dm0Vk8SA.js → SecretsView-DD8uA6G7.js} +1 -1
- package/dist/client/assets/{SessionTerminal-DJO4JTEX.js → SessionTerminal-B0S1e8qf.js} +1 -1
- package/dist/client/assets/SettingsModal-BHNEJWD4.js +21 -0
- package/dist/client/assets/{SettingsModal-BN5mL_ym.js → SettingsModal-BxrdR4-f.js} +1 -1
- package/dist/client/assets/{SettingsTextareaRow-CvKszqO-.js → SettingsTextareaRow-CpJOy-rv.js} +1 -1
- package/dist/client/assets/{SetupWizardModal-D5hVZh1G.js → SetupWizardModal-DviAPDlf.js} +2 -2
- package/dist/client/assets/{SkillsView-DQVz21w2.js → SkillsView-DeoEN_K0.js} +1 -1
- package/dist/client/assets/{WorkflowNodeEditor-CpKIi6Tb.js → WorkflowNodeEditor-CPk8plo1.js} +3 -3
- package/dist/client/assets/{agent-import-generation-DGUNF3nd.js → agent-import-generation-D74gC46R.js} +1 -1
- package/dist/client/assets/app-64JrFN7_.js +13 -0
- package/dist/client/assets/app-BxMYvSHk.js +13 -0
- package/dist/client/assets/app-CCgIvcFy.js +5 -0
- package/dist/client/assets/app-CVFYXkbM.js +13 -0
- package/dist/client/assets/app-Cl4NLAGd.js +13 -0
- package/dist/client/assets/app-_gHuSFBp.js +13 -0
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-CV83wubP.js → architectureDiagram-3BPJPVTR-DUNXxw_k.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-CNQsA3dD.js → blockDiagram-GPEHLZMM-CTxWMFSZ.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-CXe7ii5H.js → c4Diagram-AAUBKEIU-DI15zO_G.js} +1 -1
- package/dist/client/assets/channel-DDHw4Vcu.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-BS6W9bWv.js → chunk-2J33WTMH-D1X3sNvd.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-DbehRhdW.js → chunk-4BX2VUAB-DYz990rf.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-DMI5SR42.js → chunk-55IACEB6-BQshFTjv.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-D1d-GBVj.js → chunk-727SXJPM-DiVH9Rd6.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-BzO6K3Uf.js → chunk-AQP2D5EJ-Bx8LBEvE.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-BVRLV3lK.js → chunk-FMBD7UC4-7rsNdXy-.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-DdWByQfn.js → chunk-ND2GUHAM-COOZ3ff5.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-1ia12cRV.js → chunk-QZHKN3VN-oL-CU1gN.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-C5tnFtxH.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-C5tnFtxH.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-BPDT41HR.js → cose-bilkent-S5V4N54A-CHXhlVh-.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-Cktn26EH.js → dagre-BM42HDAG-BJVXidIK.js} +1 -1
- package/dist/client/assets/{dashboard-view-zd8GxC49.js → dashboard-view-B8OjndkD.js} +1 -1
- package/dist/client/assets/{dashboard-view-8o0EUcRX.js → dashboard-view-BOqEOmsR.js} +1 -1
- package/dist/client/assets/{dashboard-view-CkCerlVJ.js → dashboard-view-CCg2wkwd.js} +1 -1
- package/dist/client/assets/{dashboard-view-D_HC4lnr.js → dashboard-view-DOWuByTe.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-But4Lvf-.js → diagram-2AECGRRQ-CBS8IBP_.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-2bpQAGFc.js → diagram-5GNKFQAL-Dmyxol5P.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-Ckn9Qd1u.js → diagram-KO2AKTUF-DVF_5fDu.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-Bjn8h84N.js → diagram-LMA3HP47-DqbOTb-t.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-BI6541hx.js → diagram-OG6HWLK6-BIhwB3ji.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-BqMiwHiM.js → erDiagram-TEJ5UH35-BcYtj6RH.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-Q_SAB10M.js → flowDiagram-I6XJVG4X-CBYrhc7v.js} +1 -1
- package/dist/client/assets/{folder-open-CHiAJbTN.js → folder-open-DBW3UhL6.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-CqWFSfPF.js → ganttDiagram-6RSMTGT7-BtFaOg92.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-B1o4uEjO.js → gitGraphDiagram-PVQCEYII-BE5E5ctQ.js} +1 -1
- package/dist/client/assets/{index-kwRywd9M.js → index-D6J9uyUx.js} +306 -306
- package/dist/client/assets/index-DDdZ20Cz.css +1 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-BzaMa0c2.js → infoDiagram-5YYISTIA-BBpwj0Ir.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-I38yge00.js → ishikawaDiagram-YF4QCWOH-CwjmjYav.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-WVQSgx4P.js → journeyDiagram-JHISSGLW-ChnMZl1o.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-BWtPQ3AH.js → kanban-definition-UN3LZRKU-BA_bNZa3.js} +1 -1
- package/dist/client/assets/{mermaid.core-D1hnBtbi.js → mermaid.core-CNMqiN_t.js} +4 -4
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-DqiSLkW7.js → mindmap-definition-RKZ34NQL-D-8_lEGp.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-CcgwbdIN.js → pieDiagram-4H26LBE5-Cz_dYW8h.js} +1 -1
- package/dist/client/assets/{puzzle-BhXUaTQl.js → puzzle-Bbsv6mZh.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-MsqQQfDN.js → quadrantDiagram-W4KKPZXB-BtXzvuUT.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-CILGjMMA.js → requirementDiagram-4Y6WPE33-CK29bQbQ.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-c14fWbtR.js → sankeyDiagram-5OEKKPKP-DGxLW8W6.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-BD5bxMom.js → sequenceDiagram-3UESZ5HK-CPOz7wfR.js} +1 -1
- package/dist/client/assets/{shield-alert-C9W2WywS.js → shield-alert-BnpS5qUu.js} +1 -1
- package/dist/client/assets/{standing-instructions-template-CJdbDSyg.js → standing-instructions-template-jeWwq3Zh.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-CZ5YqeOn.js → stateDiagram-AJRCARHV-CxpQ3AE6.js} +1 -1
- package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-mdMN-PNX.js → stateDiagram-v2-BHNVJYJU-BSImQHUw.js} +1 -1
- package/dist/client/assets/{timeline-definition-PNZ67QCA-DHKay9uj.js → timeline-definition-PNZ67QCA-jt74--XQ.js} +1 -1
- package/dist/client/assets/{upload-DxEwowRU.js → upload-D2j8N8vC.js} +1 -1
- package/dist/client/assets/{users-mNFgW_Zd.js → users-BtzATmoI.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-X29K9hWM.js → vennDiagram-CIIHVFJN-3vOGHb8-.js} +1 -1
- package/dist/client/assets/{wardley-L42UT6IY-yEoTrfmN.js → wardley-L42UT6IY-DNuWuyr8.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-ny4k3kG0.js → wardleyDiagram-YWT4CUSO-D-kFe8Uz.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-DC8ODtRJ.js → xychartDiagram-2RQKCTM6-DvDQ30sY.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/version.json +1 -1
- package/dist/droid-cli/package.json +1 -1
- package/dist/extension.js +2702 -1117
- package/dist/migrations/0045_fn_8764_multi_role_workflow_agents.sql +20 -0
- package/dist/migrations/0046_fn_8764_workflow_principal_fence.sql +49 -0
- package/dist/migrations/0047_fn_8829_task_recommendations.sql +3 -0
- package/dist/pi-claude-cli/package.json +1 -1
- package/dist/plugin-sdk/index.d.ts +43 -0
- package/dist/plugin-sdk/index.js +357 -25
- package/dist/plugins/fusion-plugin-acp-runtime/bundled.js +357 -25
- package/dist/plugins/fusion-plugin-acp-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-claude-runtime/bundled.js +357 -25
- package/dist/plugins/fusion-plugin-claude-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-cli-printing-press/bundled.js +357 -25
- package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
- package/dist/plugins/fusion-plugin-compound-engineering/bundled.js +357 -25
- package/dist/plugins/fusion-plugin-compound-engineering/package.json +1 -1
- package/dist/plugins/fusion-plugin-cursor-runtime/bundled.js +357 -25
- package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-dependency-graph/bundled.js +357 -25
- package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
- package/dist/plugins/fusion-plugin-droid-runtime/bundled.js +357 -25
- package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-grok-runtime/bundled.js +357 -25
- package/dist/plugins/fusion-plugin-grok-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-hermes-runtime/bundled.js +357 -25
- package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-linear-import/bundled.js +357 -25
- package/dist/plugins/fusion-plugin-linear-import/package.json +1 -1
- package/dist/plugins/fusion-plugin-omp-runtime/bundled.js +357 -25
- package/dist/plugins/fusion-plugin-omp-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-openclaw-runtime/bundled.js +357 -25
- package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-paperclip-runtime/bundled.js +357 -25
- package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-quality/bundled.js +357 -25
- package/dist/plugins/fusion-plugin-quality/package.json +1 -1
- package/dist/plugins/fusion-plugin-reports/bundled.js +357 -25
- package/dist/plugins/fusion-plugin-reports/package.json +1 -1
- package/dist/plugins/fusion-plugin-roadmap/bundled.js +357 -25
- package/dist/plugins/fusion-plugin-roadmap/manifest.json +11 -1
- package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
- package/dist/plugins/fusion-plugin-todos/bundled.js +357 -25
- package/dist/plugins/fusion-plugin-todos/manifest.json +11 -1
- package/dist/plugins/fusion-plugin-todos/package.json +1 -1
- package/dist/plugins/fusion-plugin-whatsapp-chat/bundled.js +357 -25
- package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
- package/package.json +1 -1
- package/skill/fusion/SKILL.md +1 -1
- package/skill/fusion/references/extension-tools.md +16 -2
- package/skill/fusion/references/fusion-capabilities.md +1 -0
- package/dist/client/assets/AgentDetailView-C8-9_SOC.css +0 -1
- package/dist/client/assets/AgentDetailView-ltOG8f0-.js +0 -13
- package/dist/client/assets/AgentsView-DWUdi27N.js +0 -24
- package/dist/client/assets/SettingsModal-CTf5voEW.js +0 -21
- package/dist/client/assets/app-B4gkbXNS.js +0 -13
- package/dist/client/assets/app-BXLZYY_A.js +0 -13
- package/dist/client/assets/app-CNFmVt1q.js +0 -13
- package/dist/client/assets/app-DCERHstn.js +0 -13
- package/dist/client/assets/app-iKAA6v9i.js +0 -5
- package/dist/client/assets/app-p_Hq0vGS.js +0 -13
- package/dist/client/assets/channel-DoX862zl.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-mb2IeAGN.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-mb2IeAGN.js +0 -1
- package/dist/client/assets/index-Bdw5eNKV.css +0 -1
|
@@ -163063,6 +163063,7 @@ __export(project_exports, {
|
|
|
163063
163063
|
unplannedExecutionBlocks: () => unplannedExecutionBlocks,
|
|
163064
163064
|
usageEvents: () => usageEvents,
|
|
163065
163065
|
verificationCache: () => verificationCache,
|
|
163066
|
+
workflowAgentCapacityLeases: () => workflowAgentCapacityLeases,
|
|
163066
163067
|
workflowPromptOverrides: () => workflowPromptOverrides,
|
|
163067
163068
|
workflowRunBranches: () => workflowRunBranches,
|
|
163068
163069
|
workflowRunStepInstances: () => workflowRunStepInstances,
|
|
@@ -163171,6 +163172,8 @@ var tasks = projectSchema.table("tasks", {
|
|
|
163171
163172
|
nextRecoveryAt: text("next_recovery_at"),
|
|
163172
163173
|
error: text("error"),
|
|
163173
163174
|
summary: text("summary"),
|
|
163175
|
+
// FNXC:TaskRecommendations 2026-08-08-05:02: structured completion suggestions remain project-scoped JSONB beside the source task.
|
|
163176
|
+
recommendations: jsonb("recommendations"),
|
|
163174
163177
|
thinkingLevel: text("thinking_level"),
|
|
163175
163178
|
// FNXC:Settings-ThinkingLevel 2026-07-13 (merge port): validator/planning reasoning-effort overrides.
|
|
163176
163179
|
validatorThinkingLevel: text("validator_thinking_level"),
|
|
@@ -163751,7 +163754,9 @@ var agents = projectSchema.table("agents", {
|
|
|
163751
163754
|
projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
|
|
163752
163755
|
id: text("id").notNull(),
|
|
163753
163756
|
name: text("name").notNull(),
|
|
163757
|
+
/** Deprecated singular compatibility column; canonical roles live in roles JSONB. */
|
|
163754
163758
|
role: text("role").notNull(),
|
|
163759
|
+
roles: jsonb("roles").notNull().default([]),
|
|
163755
163760
|
state: text("state").notNull().default("idle"),
|
|
163756
163761
|
taskId: text("task_id"),
|
|
163757
163762
|
createdAt: text("created_at").notNull(),
|
|
@@ -163875,6 +163880,17 @@ var completionHandoffMarkers = projectSchema.table("completion_handoff_markers",
|
|
|
163875
163880
|
foreignKey({ columns: [t.projectId, t.taskId], foreignColumns: [tasks.projectId, tasks.id] }).onDelete("cascade"),
|
|
163876
163881
|
index("idx_completion_handoff_markers_acceptedAt").on(t.acceptedAt)
|
|
163877
163882
|
]);
|
|
163883
|
+
var workflowAgentCapacityLeases = projectSchema.table("workflow_agent_capacity_leases", {
|
|
163884
|
+
projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
|
|
163885
|
+
attemptId: text("attempt_id").notNull(),
|
|
163886
|
+
agentId: text("agent_id").notNull(),
|
|
163887
|
+
createdAt: text("created_at").notNull(),
|
|
163888
|
+
// FNXC:WorkflowAgentRouting 2026-08-07-07:16: A crashed engine cannot run its finally cleanup, so durable workflow capacity leases expire and are reclaimed on the next admission.
|
|
163889
|
+
expiresAt: text("expires_at").notNull()
|
|
163890
|
+
}, (t) => [
|
|
163891
|
+
primaryKey({ columns: [t.projectId, t.attemptId] }),
|
|
163892
|
+
index("idx_workflow_agent_capacity_leases_agent").on(t.projectId, t.agentId)
|
|
163893
|
+
]);
|
|
163878
163894
|
var workflowWorkItems = projectSchema.table("workflow_work_items", {
|
|
163879
163895
|
projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
|
|
163880
163896
|
id: text("id").notNull(),
|
|
@@ -163895,6 +163911,13 @@ var workflowWorkItems = projectSchema.table("workflow_work_items", {
|
|
|
163895
163911
|
sourceColumn: text("source_column"),
|
|
163896
163912
|
targetColumn: text("target_column"),
|
|
163897
163913
|
irHash: text("ir_hash"),
|
|
163914
|
+
// FNXC:WorkflowAgentRouting 2026-08-07-03:25:
|
|
163915
|
+
// A workflow claim fences its durable principal and exact template instance.
|
|
163916
|
+
// Session retries must reuse this identity rather than re-routing a task.
|
|
163917
|
+
principalAgentId: text("principal_agent_id"),
|
|
163918
|
+
workflowRole: text("workflow_role"),
|
|
163919
|
+
authorityKind: text("authority_kind"),
|
|
163920
|
+
nodeInstanceId: text("node_instance_id"),
|
|
163898
163921
|
createdAt: text("created_at").notNull(),
|
|
163899
163922
|
updatedAt: text("updated_at").notNull()
|
|
163900
163923
|
}, (t) => [
|
|
@@ -165890,6 +165913,7 @@ var BLOCKING_TASK_STATUSES = /* @__PURE__ */ new Set([
|
|
|
165890
165913
|
// ../core/src/tasks/in-review-stall.ts
|
|
165891
165914
|
var DEFAULT_STALE_MERGING_MIN_AGE_MS = 5 * 6e4;
|
|
165892
165915
|
var DEFAULT_MAX_AUTO_MERGE_RETRIES = 3;
|
|
165916
|
+
var CONSECUTIVE_TOOL_FAILURE_RETRY_THRESHOLD = 1;
|
|
165893
165917
|
|
|
165894
165918
|
// ../core/src/config/settings-schema.ts
|
|
165895
165919
|
var DEFAULT_GLOBAL_SETTINGS = {
|
|
@@ -166230,6 +166254,8 @@ var DEFAULT_GLOBAL_SETTINGS = {
|
|
|
166230
166254
|
cliAgents: {}
|
|
166231
166255
|
};
|
|
166232
166256
|
var DEFAULT_PROJECT_SETTINGS = {
|
|
166257
|
+
// FNXC:TaskRecommendations 2026-08-08-05:02: completion follows-ups stay bounded by default; 0 disables writing them.
|
|
166258
|
+
maxRecommendationsPerTask: 3,
|
|
166233
166259
|
globalPause: false,
|
|
166234
166260
|
globalPauseReason: void 0,
|
|
166235
166261
|
defaultWorkflowId: void 0,
|
|
@@ -166398,9 +166424,15 @@ var DEFAULT_PROJECT_SETTINGS = {
|
|
|
166398
166424
|
* Project settings own the auto-merge conflict retry cap because existing engine/dashboard consumers already resolve project settings; the default imports core's stall-detection fallback to keep every surface on the historical value of 3.
|
|
166399
166425
|
*/
|
|
166400
166426
|
maxAutoMergeRetries: DEFAULT_MAX_AUTO_MERGE_RETRIES,
|
|
166427
|
+
/*
|
|
166428
|
+
FNXC:ExecutorToolFailureRetry 2026-08-06-14:56:
|
|
166429
|
+
Fresh projects retry the existing bounded same-model continuation after one
|
|
166430
|
+
terminal tool error. Persisted project values are not migrated, so explicit
|
|
166431
|
+
operator thresholds remain authoritative.
|
|
166432
|
+
*/
|
|
166401
166433
|
executorToolFailureRetryCount: 2,
|
|
166402
166434
|
executorToolFailureRetryBackoffMs: 2e3,
|
|
166403
|
-
executorToolFailureThreshold:
|
|
166435
|
+
executorToolFailureThreshold: CONSECUTIVE_TOOL_FAILURE_RETRY_THRESHOLD,
|
|
166404
166436
|
executorModelEscalationEnabled: false,
|
|
166405
166437
|
executorEscalationProvider: void 0,
|
|
166406
166438
|
executorEscalationModelId: void 0,
|
|
@@ -166436,6 +166468,12 @@ var DEFAULT_PROJECT_SETTINGS = {
|
|
|
166436
166468
|
// proportional to the change; the thin merge gate carries cross-cutting
|
|
166437
166469
|
// coverage. Falls back to package/explicit command when no tests resolve.
|
|
166438
166470
|
scopeVerificationToChangedFiles: true,
|
|
166471
|
+
/*
|
|
166472
|
+
FNXC:WorkflowAgentRouting 2026-08-07-08:45:
|
|
166473
|
+
Keep the legacy project setting defaulted and persistable for existing clients and
|
|
166474
|
+
configuration records. Workflow-stage routing must ignore its value; durable role
|
|
166475
|
+
principals own scheduler, executor, and mission-stage authority.
|
|
166476
|
+
*/
|
|
166439
166477
|
ephemeralAgentsEnabled: true,
|
|
166440
166478
|
/*
|
|
166441
166479
|
FNXC:EphemeralAgentTaskCreation 2026-07-01-00:00:
|
|
@@ -166737,6 +166775,23 @@ function isEphemeralAgent(agent) {
|
|
|
166737
166775
|
}
|
|
166738
166776
|
|
|
166739
166777
|
// ../core/src/types/agents/agents.ts
|
|
166778
|
+
var AGENT_CAPABILITIES = [
|
|
166779
|
+
"triage",
|
|
166780
|
+
"executor",
|
|
166781
|
+
"reviewer",
|
|
166782
|
+
"merger",
|
|
166783
|
+
"scheduler",
|
|
166784
|
+
"engineer",
|
|
166785
|
+
"custom"
|
|
166786
|
+
];
|
|
166787
|
+
function normalizeAgentRoles(roles, legacyRole) {
|
|
166788
|
+
const values = roles?.length ? roles : legacyRole ? [legacyRole] : [];
|
|
166789
|
+
const unique2 = new Set(values);
|
|
166790
|
+
const normalized = AGENT_CAPABILITIES.filter((role) => unique2.has(role));
|
|
166791
|
+
if (normalized.length !== unique2.size) throw new Error("Agent roles contain an unknown capability");
|
|
166792
|
+
if (normalized.length === 0) throw new Error("Agent requires at least one role");
|
|
166793
|
+
return normalized;
|
|
166794
|
+
}
|
|
166740
166795
|
var AGENT_PERMISSIONS = [
|
|
166741
166796
|
"tasks:assign",
|
|
166742
166797
|
// Assign tasks to agents
|
|
@@ -166858,6 +166913,7 @@ function getDefaultHeartbeatProcedurePath(agentId, agentName) {
|
|
|
166858
166913
|
function agentToConfigSnapshot(agent) {
|
|
166859
166914
|
return {
|
|
166860
166915
|
name: agent.name,
|
|
166916
|
+
roles: [...agent.roles],
|
|
166861
166917
|
role: agent.role,
|
|
166862
166918
|
title: agent.title,
|
|
166863
166919
|
icon: agent.icon,
|
|
@@ -167192,6 +167248,41 @@ function resolveColumnFlags(column) {
|
|
|
167192
167248
|
return getTraitRegistry().resolveColumnFlags(column);
|
|
167193
167249
|
}
|
|
167194
167250
|
|
|
167251
|
+
// ../core/src/workflows/workflow-ir-types.ts
|
|
167252
|
+
var WORKFLOW_AGENT_ROLES = ["triage", "executor", "reviewer", "merger"];
|
|
167253
|
+
function isWorkflowAgentRole(value) {
|
|
167254
|
+
return typeof value === "string" && WORKFLOW_AGENT_ROLES.includes(value);
|
|
167255
|
+
}
|
|
167256
|
+
function classifyWorkflowAgentNode(node) {
|
|
167257
|
+
if (node.kind === "step-review") return "reviewer";
|
|
167258
|
+
if (node.kind !== "prompt" && node.kind !== "script") return void 0;
|
|
167259
|
+
switch (node.config?.seam) {
|
|
167260
|
+
/*
|
|
167261
|
+
* FNXC:WorkflowAgentRouting 2026-08-07-05:52:
|
|
167262
|
+
* Planning, independent review, and promotion seams each launch work under
|
|
167263
|
+
* their permanent stage owner. Classify those production seams here rather
|
|
167264
|
+
* than treating their handoff worker as an unowned lifecycle operation.
|
|
167265
|
+
* Only pure handoff/scheduling seams remain principal-free.
|
|
167266
|
+
*/
|
|
167267
|
+
case "planning":
|
|
167268
|
+
return "triage";
|
|
167269
|
+
case "execute":
|
|
167270
|
+
case "step-execute":
|
|
167271
|
+
return "executor";
|
|
167272
|
+
case "review":
|
|
167273
|
+
return "reviewer";
|
|
167274
|
+
case "merge":
|
|
167275
|
+
return "merger";
|
|
167276
|
+
case "review-handoff":
|
|
167277
|
+
case "schedule":
|
|
167278
|
+
return void 0;
|
|
167279
|
+
default: {
|
|
167280
|
+
const explicit = node.config?.workflowRole;
|
|
167281
|
+
return isWorkflowAgentRole(explicit) ? explicit : void 0;
|
|
167282
|
+
}
|
|
167283
|
+
}
|
|
167284
|
+
}
|
|
167285
|
+
|
|
167195
167286
|
// ../core/src/workflows/workflow-extension-types.ts
|
|
167196
167287
|
function workflowExtensionRegistryId(pluginId, extensionId) {
|
|
167197
167288
|
return `plugin:${pluginId}:${extensionId}`;
|
|
@@ -168180,6 +168271,20 @@ function validateCredentialInstanceIdConfig(nodes2) {
|
|
|
168180
168271
|
if (Array.isArray(templateNodes)) validateCredentialInstanceIdConfig(templateNodes);
|
|
168181
168272
|
}
|
|
168182
168273
|
}
|
|
168274
|
+
function validateReviewerAgentOverrides(nodes2) {
|
|
168275
|
+
for (const node of nodes2) {
|
|
168276
|
+
if (node.reviewerAgentId !== void 0) {
|
|
168277
|
+
if (typeof node.reviewerAgentId !== "string" || !node.reviewerAgentId.trim()) {
|
|
168278
|
+
throw new WorkflowIrError(`Workflow node '${node.id}' reviewerAgentId must be a non-empty string`);
|
|
168279
|
+
}
|
|
168280
|
+
if (classifyWorkflowAgentNode(node) !== "reviewer") {
|
|
168281
|
+
throw new WorkflowIrError(`Workflow node '${node.id}' reviewerAgentId is only legal on reviewer-session nodes`);
|
|
168282
|
+
}
|
|
168283
|
+
}
|
|
168284
|
+
const templateNodes = node.config?.template?.nodes;
|
|
168285
|
+
if (Array.isArray(templateNodes)) validateReviewerAgentOverrides(templateNodes);
|
|
168286
|
+
}
|
|
168287
|
+
}
|
|
168183
168288
|
function validateThinkingLevelConfig(nodes2) {
|
|
168184
168289
|
for (const node of nodes2) {
|
|
168185
168290
|
const value = node.config?.thinkingLevel;
|
|
@@ -168795,6 +168900,7 @@ function validateV2(ir) {
|
|
|
168795
168900
|
validateStepExecutePlacement(ir.nodes);
|
|
168796
168901
|
validateThinkingLevelConfig(ir.nodes);
|
|
168797
168902
|
validateCredentialInstanceIdConfig(ir.nodes);
|
|
168903
|
+
validateReviewerAgentOverrides(ir.nodes);
|
|
168798
168904
|
for (const node of ir.nodes) {
|
|
168799
168905
|
if (node.kind === "foreach") validateForeach(node, topLevelIds, columnIds);
|
|
168800
168906
|
if (node.kind === "loop") validateLoop(node, topLevelIds, columnIds);
|
|
@@ -173028,7 +173134,8 @@ function normalizePermissions(raw) {
|
|
|
173028
173134
|
return permissions;
|
|
173029
173135
|
}
|
|
173030
173136
|
function computeAccessState(agent) {
|
|
173031
|
-
const
|
|
173137
|
+
const roles = agent.roles?.length ? agent.roles : [agent.role];
|
|
173138
|
+
const roleDefaultPermissions = new Set(roles.flatMap((role) => ROLE_DEFAULT_PERMISSIONS[role] ?? []));
|
|
173032
173139
|
const explicitPermissions = normalizePermissions(agent.permissions ?? {});
|
|
173033
173140
|
const resolvedPermissions = new Set(roleDefaultPermissions);
|
|
173034
173141
|
for (const permission of explicitPermissions) {
|
|
@@ -173059,6 +173166,9 @@ var IMPLEMENTATION_TASK_COLUMNS = /* @__PURE__ */ new Set([
|
|
|
173059
173166
|
"in-progress",
|
|
173060
173167
|
"in-review"
|
|
173061
173168
|
]);
|
|
173169
|
+
function agentRoles(agent) {
|
|
173170
|
+
return agent.roles?.length ? agent.roles : agent.role ? [agent.role] : [];
|
|
173171
|
+
}
|
|
173062
173172
|
function getAgentAssignmentPolicy(agent) {
|
|
173063
173173
|
const raw = (agent.runtimeConfig ?? {})["assignmentPolicy"];
|
|
173064
173174
|
return raw === "explicit-only" || raw === "none" ? raw : "auto";
|
|
@@ -173073,10 +173183,10 @@ function isImplementationTask(task) {
|
|
|
173073
173183
|
return IMPLEMENTATION_TASK_COLUMNS.has(task.column);
|
|
173074
173184
|
}
|
|
173075
173185
|
function isExecutorRoleAgent(agent) {
|
|
173076
|
-
return agent.
|
|
173186
|
+
return agentRoles(agent).includes("executor");
|
|
173077
173187
|
}
|
|
173078
173188
|
function isEngineerRoleAgent(agent) {
|
|
173079
|
-
return agent.
|
|
173189
|
+
return agentRoles(agent).includes("engineer");
|
|
173080
173190
|
}
|
|
173081
173191
|
function canAgentTakeImplementationTaskForExplicitRouting(agent, task) {
|
|
173082
173192
|
if (!isImplementationTask(task)) return true;
|
|
@@ -173125,7 +173235,8 @@ function formatRoleMismatchReason(agent, task) {
|
|
|
173125
173235
|
if (policy !== "auto") {
|
|
173126
173236
|
return `Agent ${agent.id} has assignmentPolicy "${policy}"; implementation task ${task.id} cannot be routed to it${policy === "none" ? " by any path (no override supported)" : " automatically \u2014 explicit routing only"}.`;
|
|
173127
173237
|
}
|
|
173128
|
-
|
|
173238
|
+
const roles = agentRoles(agent);
|
|
173239
|
+
return `Agent ${agent.id} has roles "${roles.join(", ") || "none"}"; implementation task ${task.id} requires an "executor"-role agent by default, with durable "engineer" supported only for explicit routing. Pass override=true to bypass.`;
|
|
173129
173240
|
}
|
|
173130
173241
|
|
|
173131
173242
|
// ../core/src/agents/agent-permission-policy.ts
|
|
@@ -173247,6 +173358,7 @@ var agentColumns = {
|
|
|
173247
173358
|
id: project_exports.agents.id,
|
|
173248
173359
|
name: project_exports.agents.name,
|
|
173249
173360
|
role: project_exports.agents.role,
|
|
173361
|
+
roles: project_exports.agents.roles,
|
|
173250
173362
|
state: project_exports.agents.state,
|
|
173251
173363
|
taskId: project_exports.agents.taskId,
|
|
173252
173364
|
createdAt: project_exports.agents.createdAt,
|
|
@@ -173277,6 +173389,7 @@ function agentToData(agent) {
|
|
|
173277
173389
|
return {
|
|
173278
173390
|
id: agent.id,
|
|
173279
173391
|
name: agent.name,
|
|
173392
|
+
roles: agent.roles,
|
|
173280
173393
|
role: agent.role,
|
|
173281
173394
|
state: agent.state,
|
|
173282
173395
|
taskId: agent.taskId,
|
|
@@ -173311,6 +173424,7 @@ async function writeAgent(handle, agent, projectId) {
|
|
|
173311
173424
|
id: agent.id,
|
|
173312
173425
|
name: agent.name,
|
|
173313
173426
|
role: agent.role,
|
|
173427
|
+
roles: agent.roles,
|
|
173314
173428
|
state: agent.state,
|
|
173315
173429
|
taskId: agent.taskId ?? null,
|
|
173316
173430
|
createdAt: agent.createdAt,
|
|
@@ -173323,6 +173437,7 @@ async function writeAgent(handle, agent, projectId) {
|
|
|
173323
173437
|
set: {
|
|
173324
173438
|
name: agent.name,
|
|
173325
173439
|
role: agent.role,
|
|
173440
|
+
roles: agent.roles,
|
|
173326
173441
|
state: agent.state,
|
|
173327
173442
|
taskId: agent.taskId ?? null,
|
|
173328
173443
|
updatedAt: agent.updatedAt,
|
|
@@ -173344,6 +173459,7 @@ function mergeAgentRow(row) {
|
|
|
173344
173459
|
...data,
|
|
173345
173460
|
id: row.id,
|
|
173346
173461
|
name: row.name,
|
|
173462
|
+
roles: row.roles?.length ? row.roles : [row.role],
|
|
173347
173463
|
role: row.role,
|
|
173348
173464
|
state: row.state,
|
|
173349
173465
|
taskId: row.taskId ?? void 0,
|
|
@@ -173673,8 +173789,62 @@ var FsWatchPollController = class {
|
|
|
173673
173789
|
}
|
|
173674
173790
|
};
|
|
173675
173791
|
|
|
173792
|
+
// ../core/src/agents/workflow-role-agent-defaults.ts
|
|
173793
|
+
var BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG = {
|
|
173794
|
+
mode: "managed",
|
|
173795
|
+
entryFile: "AGENTS.md",
|
|
173796
|
+
files: ["AGENTS.md", "soul.md"]
|
|
173797
|
+
};
|
|
173798
|
+
var BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULTS = {
|
|
173799
|
+
triage: {
|
|
173800
|
+
role: "triage",
|
|
173801
|
+
name: "Workflow Planner",
|
|
173802
|
+
title: "Built-in workflow planning owner",
|
|
173803
|
+
instructionsText: "You are the Workflow Planner. Turn incoming work into clear, bounded, testable plans. Identify the user outcome, constraints, affected surfaces, and acceptance evidence before release. Keep planning distinct from implementation: do not claim code is complete, invent decisions, or bypass approval gates. Communicate concisely, make uncertainty explicit, and leave an executable plan that another agent can follow.",
|
|
173804
|
+
soul: "Calm, structured, and curious. You reduce ambiguity without adding ceremony, respect operator intent, and prefer concrete evidence over confident guesses."
|
|
173805
|
+
},
|
|
173806
|
+
executor: {
|
|
173807
|
+
role: "executor",
|
|
173808
|
+
name: "Workflow Executor",
|
|
173809
|
+
title: "Built-in workflow execution owner",
|
|
173810
|
+
instructionsText: "You are the Workflow Executor. Implement the approved scope with production-quality, maintainable changes. Read the task specification, preserve existing behavior outside scope, and verify the smallest relevant checks before reporting completion. Do not redesign workflow policy, broaden permissions, or conceal blockers; record concrete evidence and surface a genuine dependency when work cannot proceed. Communicate changes and verification precisely.",
|
|
173811
|
+
soul: "Methodical, practical, and accountable. You favor small safe steps, protect user work, and let tests and observable behavior guide your confidence."
|
|
173812
|
+
},
|
|
173813
|
+
reviewer: {
|
|
173814
|
+
role: "reviewer",
|
|
173815
|
+
name: "Workflow Reviewer",
|
|
173816
|
+
title: "Built-in workflow review owner",
|
|
173817
|
+
instructionsText: "You are the Workflow Reviewer. Independently assess whether the submitted work satisfies its specification, preserves system invariants, and has credible verification. Focus on correctness, regressions, security, maintainability, and missing evidence rather than rewriting the implementation. State actionable findings with severity and rationale; approve only when the evidence supports it. Do not perform implementation or override workflow gates.",
|
|
173818
|
+
soul: "Independent, exacting, and fair. You are skeptical of unsupported claims, respectful in feedback, and clear about what would make the work safe to accept."
|
|
173819
|
+
},
|
|
173820
|
+
merger: {
|
|
173821
|
+
role: "merger",
|
|
173822
|
+
name: "Workflow Merger",
|
|
173823
|
+
title: "Built-in workflow merge owner",
|
|
173824
|
+
instructionsText: "You are the Workflow Merger. Integrate only work that has met the project\u2019s configured completion and review requirements. Protect the target branch, inspect merge state and conflicts carefully, and preserve unrelated work. Do not substitute judgment for missing approval, bypass safeguards, or silently discard changes. Report the integration result, any conflicts, and the evidence used to make the decision.",
|
|
173825
|
+
soul: "Deliberate, conservative, and transparent. You optimize for repository integrity, treat uncertainty as a reason to stop, and communicate operational state plainly."
|
|
173826
|
+
}
|
|
173827
|
+
};
|
|
173828
|
+
var BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULT_LIST = Object.values(BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULTS);
|
|
173829
|
+
|
|
173676
173830
|
// ../core/src/agents/agent-store.ts
|
|
173677
173831
|
var agentStoreLog = createLogger("agent-store");
|
|
173832
|
+
function isCanonicalOrPartialBuiltinWorkflowBundle(config2) {
|
|
173833
|
+
if (config2?.mode !== "managed" || config2.entryFile !== "AGENTS.md") return false;
|
|
173834
|
+
return config2.files.every((file) => (file === "AGENTS.md" || file === "soul.md") && config2.files.indexOf(file) === config2.files.lastIndexOf(file));
|
|
173835
|
+
}
|
|
173836
|
+
function isCompleteBuiltinWorkflowBundle(config2) {
|
|
173837
|
+
return isCanonicalOrPartialBuiltinWorkflowBundle(config2) && config2.files.length === BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG.files.length;
|
|
173838
|
+
}
|
|
173839
|
+
function compareBuiltinWorkflowOwnerAge(a, b) {
|
|
173840
|
+
const aTime = Date.parse(a.createdAt);
|
|
173841
|
+
const bTime = Date.parse(b.createdAt);
|
|
173842
|
+
const aValid = Number.isFinite(aTime);
|
|
173843
|
+
const bValid = Number.isFinite(bTime);
|
|
173844
|
+
if (aValid && bValid && aTime !== bTime) return aTime - bTime;
|
|
173845
|
+
if (aValid !== bValid) return aValid ? -1 : 1;
|
|
173846
|
+
return a.id.localeCompare(b.id);
|
|
173847
|
+
}
|
|
173678
173848
|
var DEFAULT_AGENT_HEARTBEAT_INTERVAL_MS = 36e5;
|
|
173679
173849
|
function normalizeStoredPermissions(raw) {
|
|
173680
173850
|
if (!raw) return void 0;
|
|
@@ -173785,6 +173955,73 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
173785
173955
|
}
|
|
173786
173956
|
return projectId;
|
|
173787
173957
|
}
|
|
173958
|
+
/** The durable project partition used by workflow routing and capacity leases. */
|
|
173959
|
+
get workflowProjectId() {
|
|
173960
|
+
return this.asyncLayer?.projectId;
|
|
173961
|
+
}
|
|
173962
|
+
/**
|
|
173963
|
+
* FNXC:WorkflowAgentRouting 2026-08-07-05:52:
|
|
173964
|
+
* Workflow capacity is a PostgreSQL lease rather than a process-local count.
|
|
173965
|
+
* The project advisory lock serializes project then agent admission across
|
|
173966
|
+
* engine processes; model work starts only after this short transaction ends.
|
|
173967
|
+
*/
|
|
173968
|
+
async acquireWorkflowSessionCapacity(input) {
|
|
173969
|
+
if (!this.asyncLayer) return "acquired";
|
|
173970
|
+
const projectId = this.backendProjectId;
|
|
173971
|
+
const now = /* @__PURE__ */ new Date();
|
|
173972
|
+
const expiresAt = new Date(now.getTime() + (input.leaseDurationMs ?? 10 * 6e4)).toISOString();
|
|
173973
|
+
return this.asyncLayer.transactionImmediate(async (tx) => {
|
|
173974
|
+
await tx.execute(sql`SELECT pg_advisory_xact_lock(hashtext(${projectId}), hashtext('workflow-agent-capacity'))`);
|
|
173975
|
+
await tx.delete(project_exports.workflowAgentCapacityLeases).where(and(
|
|
173976
|
+
eq(project_exports.workflowAgentCapacityLeases.projectId, projectId),
|
|
173977
|
+
lte(project_exports.workflowAgentCapacityLeases.expiresAt, now.toISOString())
|
|
173978
|
+
));
|
|
173979
|
+
const existing = await tx.select({ attemptId: project_exports.workflowAgentCapacityLeases.attemptId }).from(project_exports.workflowAgentCapacityLeases).where(and(
|
|
173980
|
+
eq(project_exports.workflowAgentCapacityLeases.projectId, projectId),
|
|
173981
|
+
eq(project_exports.workflowAgentCapacityLeases.attemptId, input.attemptId)
|
|
173982
|
+
)).limit(1);
|
|
173983
|
+
if (existing[0]) return "acquired";
|
|
173984
|
+
const projectCount = await tx.select({ count: sql`count(*)::int` }).from(project_exports.workflowAgentCapacityLeases).where(eq(project_exports.workflowAgentCapacityLeases.projectId, projectId));
|
|
173985
|
+
if (input.maxProjectSessions !== void 0 && (projectCount[0]?.count ?? 0) >= input.maxProjectSessions) return "project-capacity";
|
|
173986
|
+
const agentCount = await tx.select({ count: sql`count(*)::int` }).from(project_exports.workflowAgentCapacityLeases).where(and(
|
|
173987
|
+
eq(project_exports.workflowAgentCapacityLeases.projectId, projectId),
|
|
173988
|
+
eq(project_exports.workflowAgentCapacityLeases.agentId, input.agentId)
|
|
173989
|
+
));
|
|
173990
|
+
if (input.maxAgentSessions !== void 0 && (agentCount[0]?.count ?? 0) >= input.maxAgentSessions) return "agent-capacity";
|
|
173991
|
+
await tx.insert(project_exports.workflowAgentCapacityLeases).values({
|
|
173992
|
+
projectId,
|
|
173993
|
+
agentId: input.agentId,
|
|
173994
|
+
attemptId: input.attemptId,
|
|
173995
|
+
createdAt: now.toISOString(),
|
|
173996
|
+
expiresAt
|
|
173997
|
+
});
|
|
173998
|
+
return "acquired";
|
|
173999
|
+
});
|
|
174000
|
+
}
|
|
174001
|
+
/** Renew only the caller's project-scoped attempt; a reclaimed lease never resurrects. */
|
|
174002
|
+
async renewWorkflowSessionCapacity(attemptId, leaseDurationMs = 10 * 6e4) {
|
|
174003
|
+
if (!this.asyncLayer) return true;
|
|
174004
|
+
const now = /* @__PURE__ */ new Date();
|
|
174005
|
+
const result = await this.asyncLayer.db.update(project_exports.workflowAgentCapacityLeases).set({ expiresAt: new Date(now.getTime() + leaseDurationMs).toISOString() }).where(and(
|
|
174006
|
+
eq(project_exports.workflowAgentCapacityLeases.projectId, this.backendProjectId),
|
|
174007
|
+
eq(project_exports.workflowAgentCapacityLeases.attemptId, attemptId),
|
|
174008
|
+
/*
|
|
174009
|
+
* FNXC:WorkflowAgentRouting 2026-08-07-07:30:
|
|
174010
|
+
* Renew a currently live lease only. An expired row may already have
|
|
174011
|
+
* been reclaimed by another engine's admission transaction.
|
|
174012
|
+
*/
|
|
174013
|
+
gt(project_exports.workflowAgentCapacityLeases.expiresAt, now.toISOString())
|
|
174014
|
+
)).returning({ attemptId: project_exports.workflowAgentCapacityLeases.attemptId });
|
|
174015
|
+
return result.length > 0;
|
|
174016
|
+
}
|
|
174017
|
+
/** Release is project-scoped and idempotent so cancellation and recovery may race safely. */
|
|
174018
|
+
async releaseWorkflowSessionCapacity(attemptId) {
|
|
174019
|
+
if (!this.asyncLayer) return;
|
|
174020
|
+
await this.asyncLayer.db.delete(project_exports.workflowAgentCapacityLeases).where(and(
|
|
174021
|
+
eq(project_exports.workflowAgentCapacityLeases.projectId, this.backendProjectId),
|
|
174022
|
+
eq(project_exports.workflowAgentCapacityLeases.attemptId, attemptId)
|
|
174023
|
+
));
|
|
174024
|
+
}
|
|
173788
174025
|
get db() {
|
|
173789
174026
|
throw new Error("SQLite Database is not available in backend mode (asyncLayer injected)");
|
|
173790
174027
|
}
|
|
@@ -173799,7 +174036,7 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
173799
174036
|
*/
|
|
173800
174037
|
async init() {
|
|
173801
174038
|
await mkdir(this.agentsDir, { recursive: true });
|
|
173802
|
-
|
|
174039
|
+
await this.provisionBuiltinWorkflowRoleAgents();
|
|
173803
174040
|
}
|
|
173804
174041
|
/**
|
|
173805
174042
|
* One-shot migration that re-points every non-ephemeral agent off the
|
|
@@ -173961,8 +174198,8 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
173961
174198
|
* @param name - Agent name to match exactly
|
|
173962
174199
|
* @returns Matching non-ephemeral agent, or null when none exists
|
|
173963
174200
|
*/
|
|
173964
|
-
async findAgentByName(name) {
|
|
173965
|
-
const agents2 = await findAgentRowsByName(this.asyncLayer.db, name);
|
|
174201
|
+
async findAgentByName(name, executor) {
|
|
174202
|
+
const agents2 = await findAgentRowsByName(executor ?? this.asyncLayer.db, name);
|
|
173966
174203
|
for (const agent of agents2) {
|
|
173967
174204
|
if (!isEphemeralAgent(agent)) {
|
|
173968
174205
|
return this.parseAgent(agent);
|
|
@@ -173997,18 +174234,16 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
173997
174234
|
* @returns The created agent
|
|
173998
174235
|
* @throws Error if input is invalid or a duplicate non-ephemeral name exists
|
|
173999
174236
|
*/
|
|
174000
|
-
async createAgent(input) {
|
|
174237
|
+
async createAgent(input, executor) {
|
|
174001
174238
|
if (!input.name?.trim()) {
|
|
174002
174239
|
throw new Error("Agent name is required");
|
|
174003
174240
|
}
|
|
174004
|
-
|
|
174005
|
-
throw new Error("Agent role is required");
|
|
174006
|
-
}
|
|
174241
|
+
const roles = normalizeAgentRoles(input.roles, input.role);
|
|
174007
174242
|
const normalizedName = input.name.trim();
|
|
174008
174243
|
const metadata = input.metadata ?? {};
|
|
174009
|
-
const ephemeral = isEphemeralAgent({ metadata, name: input.name, role:
|
|
174244
|
+
const ephemeral = isEphemeralAgent({ metadata, name: input.name, role: roles[0], reportsTo: input.reportsTo });
|
|
174010
174245
|
if (!ephemeral) {
|
|
174011
|
-
const existing = await this.findAgentByName(normalizedName);
|
|
174246
|
+
const existing = await this.findAgentByName(normalizedName, executor);
|
|
174012
174247
|
if (existing) {
|
|
174013
174248
|
throw new Error(`Agent with name "${normalizedName}" already exists (agentId: ${existing.id})`);
|
|
174014
174249
|
}
|
|
@@ -174021,7 +174256,8 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
174021
174256
|
const agent = {
|
|
174022
174257
|
id: agentId,
|
|
174023
174258
|
name: normalizedName,
|
|
174024
|
-
|
|
174259
|
+
roles,
|
|
174260
|
+
role: roles[0],
|
|
174025
174261
|
// Non-ephemeral agents start active so they immediately participate in
|
|
174026
174262
|
// heartbeat scheduling; ephemeral/task-worker agents start idle and are
|
|
174027
174263
|
// activated by the engine when work is assigned.
|
|
@@ -174043,7 +174279,7 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
174043
174279
|
...input.bundleConfig && { bundleConfig: input.bundleConfig },
|
|
174044
174280
|
...resolvedHeartbeatProcedurePath && { heartbeatProcedurePath: resolvedHeartbeatProcedurePath }
|
|
174045
174281
|
};
|
|
174046
|
-
await this.writeAgent(agent);
|
|
174282
|
+
await this.writeAgent(agent, executor);
|
|
174047
174283
|
this.emit("agent:created", agent);
|
|
174048
174284
|
return agent;
|
|
174049
174285
|
}
|
|
@@ -174386,10 +174622,12 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
174386
174622
|
const beforeSnapshot = agentToConfigSnapshot(agent);
|
|
174387
174623
|
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
174388
174624
|
const normalizedUpdatedPermissionPolicy = "permissionPolicy" in updates && updates.permissionPolicy !== void 0 ? normalizeAgentPermissionPolicy(updates.permissionPolicy) : updates.permissionPolicy;
|
|
174625
|
+
const roles = updates.roles !== void 0 || updates.role !== void 0 ? normalizeAgentRoles(updates.roles, updates.role) : agent.roles;
|
|
174389
174626
|
const updated = {
|
|
174390
174627
|
...agent,
|
|
174391
174628
|
name: nextName ?? agent.name,
|
|
174392
|
-
|
|
174629
|
+
roles,
|
|
174630
|
+
role: roles[0],
|
|
174393
174631
|
metadata: updates.metadata !== void 0 ? updates.metadata : agent.metadata,
|
|
174394
174632
|
updatedAt,
|
|
174395
174633
|
..."title" in updates && { title: updates.title },
|
|
@@ -174924,12 +175162,81 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
174924
175162
|
* @param filter - Optional filter criteria
|
|
174925
175163
|
* @returns Array of agents
|
|
174926
175164
|
*/
|
|
174927
|
-
async listAgents(filter) {
|
|
174928
|
-
const agents2 = await listAgentRows(this.asyncLayer.db, {
|
|
174929
|
-
|
|
174930
|
-
|
|
175165
|
+
async listAgents(filter, executor) {
|
|
175166
|
+
const agents2 = await listAgentRows(executor ?? this.asyncLayer.db, { state: filter?.state });
|
|
175167
|
+
return agents2.map((a) => this.parseAgent(a)).filter((agent) => !filter?.role || agent.roles.includes(filter.role)).filter((agent) => filter?.includeEphemeral === true || !isEphemeralAgent(agent));
|
|
175168
|
+
}
|
|
175169
|
+
/**
|
|
175170
|
+
* Idempotently seed the permanent owners that route built-in workflow stages.
|
|
175171
|
+
* Matching operator-created agents are intentionally not reused: provenance
|
|
175172
|
+
* makes upgrade repair deterministic while operators remain free to add pool
|
|
175173
|
+
* members with the same tag.
|
|
175174
|
+
*/
|
|
175175
|
+
async provisionBuiltinWorkflowRoleAgents() {
|
|
175176
|
+
const provision = async (executor) => {
|
|
175177
|
+
const existing = await this.listAgents({ includeEphemeral: true }, executor);
|
|
175178
|
+
const supportedRoles = new Set(BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULT_LIST.map(({ role }) => role));
|
|
175179
|
+
const groups = /* @__PURE__ */ new Map();
|
|
175180
|
+
for (const agent of existing) {
|
|
175181
|
+
const role = agent.metadata?.workflowRole;
|
|
175182
|
+
if (agent.metadata?.builtInWorkflowRole === true && typeof role === "string" && supportedRoles.has(role)) {
|
|
175183
|
+
const group = groups.get(role) ?? [];
|
|
175184
|
+
group.push(agent);
|
|
175185
|
+
groups.set(role, group);
|
|
175186
|
+
}
|
|
175187
|
+
}
|
|
175188
|
+
const winners = /* @__PURE__ */ new Map();
|
|
175189
|
+
for (const [role, group] of groups) {
|
|
175190
|
+
group.sort(compareBuiltinWorkflowOwnerAge);
|
|
175191
|
+
winners.set(role, group[0]);
|
|
175192
|
+
for (const loser of group.slice(1)) {
|
|
175193
|
+
const { builtInWorkflowRole: _builtIn, workflowRole: _role, ...metadata } = loser.metadata;
|
|
175194
|
+
await this.writeAgent({ ...loser, metadata, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, executor);
|
|
175195
|
+
}
|
|
175196
|
+
}
|
|
175197
|
+
const result = [];
|
|
175198
|
+
for (const definition of BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULT_LIST) {
|
|
175199
|
+
let agent = winners.get(definition.role);
|
|
175200
|
+
if (!agent) {
|
|
175201
|
+
agent = await this.createAgent({
|
|
175202
|
+
name: definition.name,
|
|
175203
|
+
roles: [definition.role],
|
|
175204
|
+
title: definition.title,
|
|
175205
|
+
metadata: { builtInWorkflowRole: true, workflowRole: definition.role },
|
|
175206
|
+
runtimeConfig: { enabled: false },
|
|
175207
|
+
instructionsText: definition.instructionsText,
|
|
175208
|
+
soul: definition.soul,
|
|
175209
|
+
bundleConfig: { ...BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG, files: [...BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG.files] }
|
|
175210
|
+
}, executor);
|
|
175211
|
+
} else {
|
|
175212
|
+
const canonicalOrPartialBundle = isCanonicalOrPartialBuiltinWorkflowBundle(agent.bundleConfig);
|
|
175213
|
+
const customInstructions = Boolean(agent.instructionsPath?.trim()) || agent.bundleConfig?.mode === "external" || agent.bundleConfig !== void 0 && !canonicalOrPartialBundle || Boolean(agent.instructionsText?.trim()) && agent.instructionsText !== definition.instructionsText;
|
|
175214
|
+
const updates = {};
|
|
175215
|
+
if (!customInstructions && !agent.instructionsText?.trim()) updates.instructionsText = definition.instructionsText;
|
|
175216
|
+
if (!agent.soul?.trim()) updates.soul = definition.soul;
|
|
175217
|
+
if (!customInstructions && !isCompleteBuiltinWorkflowBundle(agent.bundleConfig)) {
|
|
175218
|
+
updates.bundleConfig = { ...BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG, files: [...BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG.files] };
|
|
175219
|
+
}
|
|
175220
|
+
if (Object.keys(updates).length > 0) {
|
|
175221
|
+
agent = { ...agent, ...updates, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
175222
|
+
await this.writeAgent(agent, executor);
|
|
175223
|
+
}
|
|
175224
|
+
}
|
|
175225
|
+
result.push(agent);
|
|
175226
|
+
}
|
|
175227
|
+
return result;
|
|
175228
|
+
};
|
|
175229
|
+
if (!this.asyncLayer) {
|
|
175230
|
+
const agents3 = await provision();
|
|
175231
|
+
await Promise.all(agents3.map((agent) => this.materializeBuiltinWorkflowRoleBundle(agent)));
|
|
175232
|
+
return agents3;
|
|
175233
|
+
}
|
|
175234
|
+
const agents2 = await this.asyncLayer.transactionImmediate(async (tx) => {
|
|
175235
|
+
await tx.execute(sql`SELECT pg_advisory_xact_lock(hashtext(${this.backendProjectId}), hashtext('builtin-workflow-role-provisioning'))`);
|
|
175236
|
+
return provision(tx);
|
|
174931
175237
|
});
|
|
174932
|
-
|
|
175238
|
+
await Promise.all(agents2.map((agent) => this.materializeBuiltinWorkflowRoleBundle(agent)));
|
|
175239
|
+
return agents2;
|
|
174933
175240
|
}
|
|
174934
175241
|
/**
|
|
174935
175242
|
* Create an API key for an agent.
|
|
@@ -175497,6 +175804,29 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
175497
175804
|
}
|
|
175498
175805
|
return null;
|
|
175499
175806
|
}
|
|
175807
|
+
/** Seed only missing or blank default-owned mirror files after the provisioning transaction commits. */
|
|
175808
|
+
async materializeBuiltinWorkflowRoleBundle(agent) {
|
|
175809
|
+
return this.withLock(agent.id, async () => {
|
|
175810
|
+
const role = agent.metadata?.workflowRole;
|
|
175811
|
+
const definition = BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULT_LIST.find((candidate) => candidate.role === role);
|
|
175812
|
+
if (!definition || agent.metadata?.builtInWorkflowRole !== true) return;
|
|
175813
|
+
const config2 = agent.bundleConfig;
|
|
175814
|
+
if (!isCanonicalOrPartialBuiltinWorkflowBundle(config2)) return;
|
|
175815
|
+
if (agent.instructionsPath?.trim() || agent.instructionsText?.trim() && agent.instructionsText !== definition.instructionsText) return;
|
|
175816
|
+
const bundleDir = await this.resolveCompatibleBundleDir(agent.id, true);
|
|
175817
|
+
await mkdir(bundleDir, { recursive: true });
|
|
175818
|
+
for (const [file, content] of [["AGENTS.md", definition.instructionsText], ["soul.md", definition.soul]]) {
|
|
175819
|
+
const path = join(bundleDir, file);
|
|
175820
|
+
let current = "";
|
|
175821
|
+
try {
|
|
175822
|
+
current = await readFile(path, "utf-8");
|
|
175823
|
+
} catch (error) {
|
|
175824
|
+
if (error.code !== "ENOENT") throw error;
|
|
175825
|
+
}
|
|
175826
|
+
if (!current.trim()) await writeFile(path, content, "utf-8");
|
|
175827
|
+
}
|
|
175828
|
+
});
|
|
175829
|
+
}
|
|
175500
175830
|
getCanonicalBundleDir(agent) {
|
|
175501
175831
|
return join(this.agentsDir, getCanonicalAgentInstructionsBundleDirName(agent.name, agent.id));
|
|
175502
175832
|
}
|
|
@@ -175619,6 +175949,7 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
175619
175949
|
...data,
|
|
175620
175950
|
id: row.id,
|
|
175621
175951
|
name: row.name,
|
|
175952
|
+
roles: data.roles?.length ? data.roles : [row.role],
|
|
175622
175953
|
role: row.role,
|
|
175623
175954
|
state: row.state,
|
|
175624
175955
|
taskId: row.taskId ?? void 0,
|
|
@@ -175652,7 +175983,8 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
175652
175983
|
return {
|
|
175653
175984
|
id: data.id,
|
|
175654
175985
|
name: data.name,
|
|
175655
|
-
|
|
175986
|
+
roles: normalizeAgentRoles(data.roles, data.role),
|
|
175987
|
+
role: normalizeAgentRoles(data.roles, data.role)[0],
|
|
175656
175988
|
state: data.state,
|
|
175657
175989
|
taskId: data.taskId,
|
|
175658
175990
|
createdAt: data.createdAt,
|
|
@@ -175678,8 +176010,8 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
175678
176010
|
heartbeatProcedurePath: data.heartbeatProcedurePath
|
|
175679
176011
|
};
|
|
175680
176012
|
}
|
|
175681
|
-
async writeAgent(agent) {
|
|
175682
|
-
await writeAgent(this.asyncLayer.db, agent, this.asyncLayer.projectId);
|
|
176013
|
+
async writeAgent(agent, executor) {
|
|
176014
|
+
await writeAgent(executor ?? this.asyncLayer.db, agent, this.asyncLayer.projectId);
|
|
175683
176015
|
return;
|
|
175684
176016
|
}
|
|
175685
176017
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runfusion/fusion",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.76.0-beta.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Fusion CLI: HTTP API server, daemon, dashboard launcher, and task tooling for the Fusion AI coding agent.",
|
|
6
6
|
"homepage": "https://github.com/Runfusion/Fusion#readme",
|