@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
|
@@ -5762,6 +5762,7 @@ __export(project_exports, {
|
|
|
5762
5762
|
unplannedExecutionBlocks: () => unplannedExecutionBlocks,
|
|
5763
5763
|
usageEvents: () => usageEvents,
|
|
5764
5764
|
verificationCache: () => verificationCache,
|
|
5765
|
+
workflowAgentCapacityLeases: () => workflowAgentCapacityLeases,
|
|
5765
5766
|
workflowPromptOverrides: () => workflowPromptOverrides,
|
|
5766
5767
|
workflowRunBranches: () => workflowRunBranches,
|
|
5767
5768
|
workflowRunStepInstances: () => workflowRunStepInstances,
|
|
@@ -5870,6 +5871,8 @@ var tasks = projectSchema.table("tasks", {
|
|
|
5870
5871
|
nextRecoveryAt: text("next_recovery_at"),
|
|
5871
5872
|
error: text("error"),
|
|
5872
5873
|
summary: text("summary"),
|
|
5874
|
+
// FNXC:TaskRecommendations 2026-08-08-05:02: structured completion suggestions remain project-scoped JSONB beside the source task.
|
|
5875
|
+
recommendations: jsonb("recommendations"),
|
|
5873
5876
|
thinkingLevel: text("thinking_level"),
|
|
5874
5877
|
// FNXC:Settings-ThinkingLevel 2026-07-13 (merge port): validator/planning reasoning-effort overrides.
|
|
5875
5878
|
validatorThinkingLevel: text("validator_thinking_level"),
|
|
@@ -6450,7 +6453,9 @@ var agents = projectSchema.table("agents", {
|
|
|
6450
6453
|
projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
|
|
6451
6454
|
id: text("id").notNull(),
|
|
6452
6455
|
name: text("name").notNull(),
|
|
6456
|
+
/** Deprecated singular compatibility column; canonical roles live in roles JSONB. */
|
|
6453
6457
|
role: text("role").notNull(),
|
|
6458
|
+
roles: jsonb("roles").notNull().default([]),
|
|
6454
6459
|
state: text("state").notNull().default("idle"),
|
|
6455
6460
|
taskId: text("task_id"),
|
|
6456
6461
|
createdAt: text("created_at").notNull(),
|
|
@@ -6574,6 +6579,17 @@ var completionHandoffMarkers = projectSchema.table("completion_handoff_markers",
|
|
|
6574
6579
|
foreignKey({ columns: [t.projectId, t.taskId], foreignColumns: [tasks.projectId, tasks.id] }).onDelete("cascade"),
|
|
6575
6580
|
index("idx_completion_handoff_markers_acceptedAt").on(t.acceptedAt)
|
|
6576
6581
|
]);
|
|
6582
|
+
var workflowAgentCapacityLeases = projectSchema.table("workflow_agent_capacity_leases", {
|
|
6583
|
+
projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
|
|
6584
|
+
attemptId: text("attempt_id").notNull(),
|
|
6585
|
+
agentId: text("agent_id").notNull(),
|
|
6586
|
+
createdAt: text("created_at").notNull(),
|
|
6587
|
+
// 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.
|
|
6588
|
+
expiresAt: text("expires_at").notNull()
|
|
6589
|
+
}, (t) => [
|
|
6590
|
+
primaryKey({ columns: [t.projectId, t.attemptId] }),
|
|
6591
|
+
index("idx_workflow_agent_capacity_leases_agent").on(t.projectId, t.agentId)
|
|
6592
|
+
]);
|
|
6577
6593
|
var workflowWorkItems = projectSchema.table("workflow_work_items", {
|
|
6578
6594
|
projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
|
|
6579
6595
|
id: text("id").notNull(),
|
|
@@ -6594,6 +6610,13 @@ var workflowWorkItems = projectSchema.table("workflow_work_items", {
|
|
|
6594
6610
|
sourceColumn: text("source_column"),
|
|
6595
6611
|
targetColumn: text("target_column"),
|
|
6596
6612
|
irHash: text("ir_hash"),
|
|
6613
|
+
// FNXC:WorkflowAgentRouting 2026-08-07-03:25:
|
|
6614
|
+
// A workflow claim fences its durable principal and exact template instance.
|
|
6615
|
+
// Session retries must reuse this identity rather than re-routing a task.
|
|
6616
|
+
principalAgentId: text("principal_agent_id"),
|
|
6617
|
+
workflowRole: text("workflow_role"),
|
|
6618
|
+
authorityKind: text("authority_kind"),
|
|
6619
|
+
nodeInstanceId: text("node_instance_id"),
|
|
6597
6620
|
createdAt: text("created_at").notNull(),
|
|
6598
6621
|
updatedAt: text("updated_at").notNull()
|
|
6599
6622
|
}, (t) => [
|
|
@@ -8589,6 +8612,7 @@ var BLOCKING_TASK_STATUSES = /* @__PURE__ */ new Set([
|
|
|
8589
8612
|
// ../core/src/tasks/in-review-stall.ts
|
|
8590
8613
|
var DEFAULT_STALE_MERGING_MIN_AGE_MS = 5 * 6e4;
|
|
8591
8614
|
var DEFAULT_MAX_AUTO_MERGE_RETRIES = 3;
|
|
8615
|
+
var CONSECUTIVE_TOOL_FAILURE_RETRY_THRESHOLD = 1;
|
|
8592
8616
|
|
|
8593
8617
|
// ../core/src/config/settings-schema.ts
|
|
8594
8618
|
var DEFAULT_GLOBAL_SETTINGS = {
|
|
@@ -8929,6 +8953,8 @@ var DEFAULT_GLOBAL_SETTINGS = {
|
|
|
8929
8953
|
cliAgents: {}
|
|
8930
8954
|
};
|
|
8931
8955
|
var DEFAULT_PROJECT_SETTINGS = {
|
|
8956
|
+
// FNXC:TaskRecommendations 2026-08-08-05:02: completion follows-ups stay bounded by default; 0 disables writing them.
|
|
8957
|
+
maxRecommendationsPerTask: 3,
|
|
8932
8958
|
globalPause: false,
|
|
8933
8959
|
globalPauseReason: void 0,
|
|
8934
8960
|
defaultWorkflowId: void 0,
|
|
@@ -9097,9 +9123,15 @@ var DEFAULT_PROJECT_SETTINGS = {
|
|
|
9097
9123
|
* 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.
|
|
9098
9124
|
*/
|
|
9099
9125
|
maxAutoMergeRetries: DEFAULT_MAX_AUTO_MERGE_RETRIES,
|
|
9126
|
+
/*
|
|
9127
|
+
FNXC:ExecutorToolFailureRetry 2026-08-06-14:56:
|
|
9128
|
+
Fresh projects retry the existing bounded same-model continuation after one
|
|
9129
|
+
terminal tool error. Persisted project values are not migrated, so explicit
|
|
9130
|
+
operator thresholds remain authoritative.
|
|
9131
|
+
*/
|
|
9100
9132
|
executorToolFailureRetryCount: 2,
|
|
9101
9133
|
executorToolFailureRetryBackoffMs: 2e3,
|
|
9102
|
-
executorToolFailureThreshold:
|
|
9134
|
+
executorToolFailureThreshold: CONSECUTIVE_TOOL_FAILURE_RETRY_THRESHOLD,
|
|
9103
9135
|
executorModelEscalationEnabled: false,
|
|
9104
9136
|
executorEscalationProvider: void 0,
|
|
9105
9137
|
executorEscalationModelId: void 0,
|
|
@@ -9135,6 +9167,12 @@ var DEFAULT_PROJECT_SETTINGS = {
|
|
|
9135
9167
|
// proportional to the change; the thin merge gate carries cross-cutting
|
|
9136
9168
|
// coverage. Falls back to package/explicit command when no tests resolve.
|
|
9137
9169
|
scopeVerificationToChangedFiles: true,
|
|
9170
|
+
/*
|
|
9171
|
+
FNXC:WorkflowAgentRouting 2026-08-07-08:45:
|
|
9172
|
+
Keep the legacy project setting defaulted and persistable for existing clients and
|
|
9173
|
+
configuration records. Workflow-stage routing must ignore its value; durable role
|
|
9174
|
+
principals own scheduler, executor, and mission-stage authority.
|
|
9175
|
+
*/
|
|
9138
9176
|
ephemeralAgentsEnabled: true,
|
|
9139
9177
|
/*
|
|
9140
9178
|
FNXC:EphemeralAgentTaskCreation 2026-07-01-00:00:
|
|
@@ -9436,6 +9474,23 @@ function isEphemeralAgent(agent) {
|
|
|
9436
9474
|
}
|
|
9437
9475
|
|
|
9438
9476
|
// ../core/src/types/agents/agents.ts
|
|
9477
|
+
var AGENT_CAPABILITIES = [
|
|
9478
|
+
"triage",
|
|
9479
|
+
"executor",
|
|
9480
|
+
"reviewer",
|
|
9481
|
+
"merger",
|
|
9482
|
+
"scheduler",
|
|
9483
|
+
"engineer",
|
|
9484
|
+
"custom"
|
|
9485
|
+
];
|
|
9486
|
+
function normalizeAgentRoles(roles, legacyRole) {
|
|
9487
|
+
const values = roles?.length ? roles : legacyRole ? [legacyRole] : [];
|
|
9488
|
+
const unique2 = new Set(values);
|
|
9489
|
+
const normalized = AGENT_CAPABILITIES.filter((role) => unique2.has(role));
|
|
9490
|
+
if (normalized.length !== unique2.size) throw new Error("Agent roles contain an unknown capability");
|
|
9491
|
+
if (normalized.length === 0) throw new Error("Agent requires at least one role");
|
|
9492
|
+
return normalized;
|
|
9493
|
+
}
|
|
9439
9494
|
var AGENT_PERMISSIONS = [
|
|
9440
9495
|
"tasks:assign",
|
|
9441
9496
|
// Assign tasks to agents
|
|
@@ -9557,6 +9612,7 @@ function getDefaultHeartbeatProcedurePath(agentId, agentName) {
|
|
|
9557
9612
|
function agentToConfigSnapshot(agent) {
|
|
9558
9613
|
return {
|
|
9559
9614
|
name: agent.name,
|
|
9615
|
+
roles: [...agent.roles],
|
|
9560
9616
|
role: agent.role,
|
|
9561
9617
|
title: agent.title,
|
|
9562
9618
|
icon: agent.icon,
|
|
@@ -9891,6 +9947,41 @@ function resolveColumnFlags(column) {
|
|
|
9891
9947
|
return getTraitRegistry().resolveColumnFlags(column);
|
|
9892
9948
|
}
|
|
9893
9949
|
|
|
9950
|
+
// ../core/src/workflows/workflow-ir-types.ts
|
|
9951
|
+
var WORKFLOW_AGENT_ROLES = ["triage", "executor", "reviewer", "merger"];
|
|
9952
|
+
function isWorkflowAgentRole(value) {
|
|
9953
|
+
return typeof value === "string" && WORKFLOW_AGENT_ROLES.includes(value);
|
|
9954
|
+
}
|
|
9955
|
+
function classifyWorkflowAgentNode(node) {
|
|
9956
|
+
if (node.kind === "step-review") return "reviewer";
|
|
9957
|
+
if (node.kind !== "prompt" && node.kind !== "script") return void 0;
|
|
9958
|
+
switch (node.config?.seam) {
|
|
9959
|
+
/*
|
|
9960
|
+
* FNXC:WorkflowAgentRouting 2026-08-07-05:52:
|
|
9961
|
+
* Planning, independent review, and promotion seams each launch work under
|
|
9962
|
+
* their permanent stage owner. Classify those production seams here rather
|
|
9963
|
+
* than treating their handoff worker as an unowned lifecycle operation.
|
|
9964
|
+
* Only pure handoff/scheduling seams remain principal-free.
|
|
9965
|
+
*/
|
|
9966
|
+
case "planning":
|
|
9967
|
+
return "triage";
|
|
9968
|
+
case "execute":
|
|
9969
|
+
case "step-execute":
|
|
9970
|
+
return "executor";
|
|
9971
|
+
case "review":
|
|
9972
|
+
return "reviewer";
|
|
9973
|
+
case "merge":
|
|
9974
|
+
return "merger";
|
|
9975
|
+
case "review-handoff":
|
|
9976
|
+
case "schedule":
|
|
9977
|
+
return void 0;
|
|
9978
|
+
default: {
|
|
9979
|
+
const explicit = node.config?.workflowRole;
|
|
9980
|
+
return isWorkflowAgentRole(explicit) ? explicit : void 0;
|
|
9981
|
+
}
|
|
9982
|
+
}
|
|
9983
|
+
}
|
|
9984
|
+
|
|
9894
9985
|
// ../core/src/workflows/workflow-extension-types.ts
|
|
9895
9986
|
function workflowExtensionRegistryId(pluginId, extensionId) {
|
|
9896
9987
|
return `plugin:${pluginId}:${extensionId}`;
|
|
@@ -10879,6 +10970,20 @@ function validateCredentialInstanceIdConfig(nodes2) {
|
|
|
10879
10970
|
if (Array.isArray(templateNodes)) validateCredentialInstanceIdConfig(templateNodes);
|
|
10880
10971
|
}
|
|
10881
10972
|
}
|
|
10973
|
+
function validateReviewerAgentOverrides(nodes2) {
|
|
10974
|
+
for (const node of nodes2) {
|
|
10975
|
+
if (node.reviewerAgentId !== void 0) {
|
|
10976
|
+
if (typeof node.reviewerAgentId !== "string" || !node.reviewerAgentId.trim()) {
|
|
10977
|
+
throw new WorkflowIrError(`Workflow node '${node.id}' reviewerAgentId must be a non-empty string`);
|
|
10978
|
+
}
|
|
10979
|
+
if (classifyWorkflowAgentNode(node) !== "reviewer") {
|
|
10980
|
+
throw new WorkflowIrError(`Workflow node '${node.id}' reviewerAgentId is only legal on reviewer-session nodes`);
|
|
10981
|
+
}
|
|
10982
|
+
}
|
|
10983
|
+
const templateNodes = node.config?.template?.nodes;
|
|
10984
|
+
if (Array.isArray(templateNodes)) validateReviewerAgentOverrides(templateNodes);
|
|
10985
|
+
}
|
|
10986
|
+
}
|
|
10882
10987
|
function validateThinkingLevelConfig(nodes2) {
|
|
10883
10988
|
for (const node of nodes2) {
|
|
10884
10989
|
const value = node.config?.thinkingLevel;
|
|
@@ -11494,6 +11599,7 @@ function validateV2(ir) {
|
|
|
11494
11599
|
validateStepExecutePlacement(ir.nodes);
|
|
11495
11600
|
validateThinkingLevelConfig(ir.nodes);
|
|
11496
11601
|
validateCredentialInstanceIdConfig(ir.nodes);
|
|
11602
|
+
validateReviewerAgentOverrides(ir.nodes);
|
|
11497
11603
|
for (const node of ir.nodes) {
|
|
11498
11604
|
if (node.kind === "foreach") validateForeach(node, topLevelIds, columnIds);
|
|
11499
11605
|
if (node.kind === "loop") validateLoop(node, topLevelIds, columnIds);
|
|
@@ -15738,7 +15844,8 @@ function normalizePermissions(raw) {
|
|
|
15738
15844
|
return permissions;
|
|
15739
15845
|
}
|
|
15740
15846
|
function computeAccessState(agent) {
|
|
15741
|
-
const
|
|
15847
|
+
const roles = agent.roles?.length ? agent.roles : [agent.role];
|
|
15848
|
+
const roleDefaultPermissions = new Set(roles.flatMap((role) => ROLE_DEFAULT_PERMISSIONS[role] ?? []));
|
|
15742
15849
|
const explicitPermissions = normalizePermissions(agent.permissions ?? {});
|
|
15743
15850
|
const resolvedPermissions = new Set(roleDefaultPermissions);
|
|
15744
15851
|
for (const permission of explicitPermissions) {
|
|
@@ -15769,6 +15876,9 @@ var IMPLEMENTATION_TASK_COLUMNS = /* @__PURE__ */ new Set([
|
|
|
15769
15876
|
"in-progress",
|
|
15770
15877
|
"in-review"
|
|
15771
15878
|
]);
|
|
15879
|
+
function agentRoles(agent) {
|
|
15880
|
+
return agent.roles?.length ? agent.roles : agent.role ? [agent.role] : [];
|
|
15881
|
+
}
|
|
15772
15882
|
function getAgentAssignmentPolicy(agent) {
|
|
15773
15883
|
const raw = (agent.runtimeConfig ?? {})["assignmentPolicy"];
|
|
15774
15884
|
return raw === "explicit-only" || raw === "none" ? raw : "auto";
|
|
@@ -15783,10 +15893,10 @@ function isImplementationTask(task) {
|
|
|
15783
15893
|
return IMPLEMENTATION_TASK_COLUMNS.has(task.column);
|
|
15784
15894
|
}
|
|
15785
15895
|
function isExecutorRoleAgent(agent) {
|
|
15786
|
-
return agent.
|
|
15896
|
+
return agentRoles(agent).includes("executor");
|
|
15787
15897
|
}
|
|
15788
15898
|
function isEngineerRoleAgent(agent) {
|
|
15789
|
-
return agent.
|
|
15899
|
+
return agentRoles(agent).includes("engineer");
|
|
15790
15900
|
}
|
|
15791
15901
|
function canAgentTakeImplementationTaskForExplicitRouting(agent, task) {
|
|
15792
15902
|
if (!isImplementationTask(task)) return true;
|
|
@@ -15835,7 +15945,8 @@ function formatRoleMismatchReason(agent, task) {
|
|
|
15835
15945
|
if (policy !== "auto") {
|
|
15836
15946
|
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"}.`;
|
|
15837
15947
|
}
|
|
15838
|
-
|
|
15948
|
+
const roles = agentRoles(agent);
|
|
15949
|
+
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.`;
|
|
15839
15950
|
}
|
|
15840
15951
|
|
|
15841
15952
|
// ../core/src/agents/agent-permission-policy.ts
|
|
@@ -15957,6 +16068,7 @@ var agentColumns = {
|
|
|
15957
16068
|
id: project_exports.agents.id,
|
|
15958
16069
|
name: project_exports.agents.name,
|
|
15959
16070
|
role: project_exports.agents.role,
|
|
16071
|
+
roles: project_exports.agents.roles,
|
|
15960
16072
|
state: project_exports.agents.state,
|
|
15961
16073
|
taskId: project_exports.agents.taskId,
|
|
15962
16074
|
createdAt: project_exports.agents.createdAt,
|
|
@@ -15987,6 +16099,7 @@ function agentToData(agent) {
|
|
|
15987
16099
|
return {
|
|
15988
16100
|
id: agent.id,
|
|
15989
16101
|
name: agent.name,
|
|
16102
|
+
roles: agent.roles,
|
|
15990
16103
|
role: agent.role,
|
|
15991
16104
|
state: agent.state,
|
|
15992
16105
|
taskId: agent.taskId,
|
|
@@ -16021,6 +16134,7 @@ async function writeAgent(handle, agent, projectId) {
|
|
|
16021
16134
|
id: agent.id,
|
|
16022
16135
|
name: agent.name,
|
|
16023
16136
|
role: agent.role,
|
|
16137
|
+
roles: agent.roles,
|
|
16024
16138
|
state: agent.state,
|
|
16025
16139
|
taskId: agent.taskId ?? null,
|
|
16026
16140
|
createdAt: agent.createdAt,
|
|
@@ -16033,6 +16147,7 @@ async function writeAgent(handle, agent, projectId) {
|
|
|
16033
16147
|
set: {
|
|
16034
16148
|
name: agent.name,
|
|
16035
16149
|
role: agent.role,
|
|
16150
|
+
roles: agent.roles,
|
|
16036
16151
|
state: agent.state,
|
|
16037
16152
|
taskId: agent.taskId ?? null,
|
|
16038
16153
|
updatedAt: agent.updatedAt,
|
|
@@ -16054,6 +16169,7 @@ function mergeAgentRow(row) {
|
|
|
16054
16169
|
...data,
|
|
16055
16170
|
id: row.id,
|
|
16056
16171
|
name: row.name,
|
|
16172
|
+
roles: row.roles?.length ? row.roles : [row.role],
|
|
16057
16173
|
role: row.role,
|
|
16058
16174
|
state: row.state,
|
|
16059
16175
|
taskId: row.taskId ?? void 0,
|
|
@@ -16383,8 +16499,62 @@ var FsWatchPollController = class {
|
|
|
16383
16499
|
}
|
|
16384
16500
|
};
|
|
16385
16501
|
|
|
16502
|
+
// ../core/src/agents/workflow-role-agent-defaults.ts
|
|
16503
|
+
var BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG = {
|
|
16504
|
+
mode: "managed",
|
|
16505
|
+
entryFile: "AGENTS.md",
|
|
16506
|
+
files: ["AGENTS.md", "soul.md"]
|
|
16507
|
+
};
|
|
16508
|
+
var BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULTS = {
|
|
16509
|
+
triage: {
|
|
16510
|
+
role: "triage",
|
|
16511
|
+
name: "Workflow Planner",
|
|
16512
|
+
title: "Built-in workflow planning owner",
|
|
16513
|
+
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.",
|
|
16514
|
+
soul: "Calm, structured, and curious. You reduce ambiguity without adding ceremony, respect operator intent, and prefer concrete evidence over confident guesses."
|
|
16515
|
+
},
|
|
16516
|
+
executor: {
|
|
16517
|
+
role: "executor",
|
|
16518
|
+
name: "Workflow Executor",
|
|
16519
|
+
title: "Built-in workflow execution owner",
|
|
16520
|
+
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.",
|
|
16521
|
+
soul: "Methodical, practical, and accountable. You favor small safe steps, protect user work, and let tests and observable behavior guide your confidence."
|
|
16522
|
+
},
|
|
16523
|
+
reviewer: {
|
|
16524
|
+
role: "reviewer",
|
|
16525
|
+
name: "Workflow Reviewer",
|
|
16526
|
+
title: "Built-in workflow review owner",
|
|
16527
|
+
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.",
|
|
16528
|
+
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."
|
|
16529
|
+
},
|
|
16530
|
+
merger: {
|
|
16531
|
+
role: "merger",
|
|
16532
|
+
name: "Workflow Merger",
|
|
16533
|
+
title: "Built-in workflow merge owner",
|
|
16534
|
+
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.",
|
|
16535
|
+
soul: "Deliberate, conservative, and transparent. You optimize for repository integrity, treat uncertainty as a reason to stop, and communicate operational state plainly."
|
|
16536
|
+
}
|
|
16537
|
+
};
|
|
16538
|
+
var BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULT_LIST = Object.values(BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULTS);
|
|
16539
|
+
|
|
16386
16540
|
// ../core/src/agents/agent-store.ts
|
|
16387
16541
|
var agentStoreLog = createLogger("agent-store");
|
|
16542
|
+
function isCanonicalOrPartialBuiltinWorkflowBundle(config2) {
|
|
16543
|
+
if (config2?.mode !== "managed" || config2.entryFile !== "AGENTS.md") return false;
|
|
16544
|
+
return config2.files.every((file) => (file === "AGENTS.md" || file === "soul.md") && config2.files.indexOf(file) === config2.files.lastIndexOf(file));
|
|
16545
|
+
}
|
|
16546
|
+
function isCompleteBuiltinWorkflowBundle(config2) {
|
|
16547
|
+
return isCanonicalOrPartialBuiltinWorkflowBundle(config2) && config2.files.length === BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG.files.length;
|
|
16548
|
+
}
|
|
16549
|
+
function compareBuiltinWorkflowOwnerAge(a, b) {
|
|
16550
|
+
const aTime = Date.parse(a.createdAt);
|
|
16551
|
+
const bTime = Date.parse(b.createdAt);
|
|
16552
|
+
const aValid = Number.isFinite(aTime);
|
|
16553
|
+
const bValid = Number.isFinite(bTime);
|
|
16554
|
+
if (aValid && bValid && aTime !== bTime) return aTime - bTime;
|
|
16555
|
+
if (aValid !== bValid) return aValid ? -1 : 1;
|
|
16556
|
+
return a.id.localeCompare(b.id);
|
|
16557
|
+
}
|
|
16388
16558
|
var DEFAULT_AGENT_HEARTBEAT_INTERVAL_MS = 36e5;
|
|
16389
16559
|
function normalizeStoredPermissions(raw) {
|
|
16390
16560
|
if (!raw) return void 0;
|
|
@@ -16495,6 +16665,73 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
16495
16665
|
}
|
|
16496
16666
|
return projectId;
|
|
16497
16667
|
}
|
|
16668
|
+
/** The durable project partition used by workflow routing and capacity leases. */
|
|
16669
|
+
get workflowProjectId() {
|
|
16670
|
+
return this.asyncLayer?.projectId;
|
|
16671
|
+
}
|
|
16672
|
+
/**
|
|
16673
|
+
* FNXC:WorkflowAgentRouting 2026-08-07-05:52:
|
|
16674
|
+
* Workflow capacity is a PostgreSQL lease rather than a process-local count.
|
|
16675
|
+
* The project advisory lock serializes project then agent admission across
|
|
16676
|
+
* engine processes; model work starts only after this short transaction ends.
|
|
16677
|
+
*/
|
|
16678
|
+
async acquireWorkflowSessionCapacity(input) {
|
|
16679
|
+
if (!this.asyncLayer) return "acquired";
|
|
16680
|
+
const projectId = this.backendProjectId;
|
|
16681
|
+
const now = /* @__PURE__ */ new Date();
|
|
16682
|
+
const expiresAt = new Date(now.getTime() + (input.leaseDurationMs ?? 10 * 6e4)).toISOString();
|
|
16683
|
+
return this.asyncLayer.transactionImmediate(async (tx) => {
|
|
16684
|
+
await tx.execute(sql`SELECT pg_advisory_xact_lock(hashtext(${projectId}), hashtext('workflow-agent-capacity'))`);
|
|
16685
|
+
await tx.delete(project_exports.workflowAgentCapacityLeases).where(and(
|
|
16686
|
+
eq(project_exports.workflowAgentCapacityLeases.projectId, projectId),
|
|
16687
|
+
lte(project_exports.workflowAgentCapacityLeases.expiresAt, now.toISOString())
|
|
16688
|
+
));
|
|
16689
|
+
const existing = await tx.select({ attemptId: project_exports.workflowAgentCapacityLeases.attemptId }).from(project_exports.workflowAgentCapacityLeases).where(and(
|
|
16690
|
+
eq(project_exports.workflowAgentCapacityLeases.projectId, projectId),
|
|
16691
|
+
eq(project_exports.workflowAgentCapacityLeases.attemptId, input.attemptId)
|
|
16692
|
+
)).limit(1);
|
|
16693
|
+
if (existing[0]) return "acquired";
|
|
16694
|
+
const projectCount = await tx.select({ count: sql`count(*)::int` }).from(project_exports.workflowAgentCapacityLeases).where(eq(project_exports.workflowAgentCapacityLeases.projectId, projectId));
|
|
16695
|
+
if (input.maxProjectSessions !== void 0 && (projectCount[0]?.count ?? 0) >= input.maxProjectSessions) return "project-capacity";
|
|
16696
|
+
const agentCount = await tx.select({ count: sql`count(*)::int` }).from(project_exports.workflowAgentCapacityLeases).where(and(
|
|
16697
|
+
eq(project_exports.workflowAgentCapacityLeases.projectId, projectId),
|
|
16698
|
+
eq(project_exports.workflowAgentCapacityLeases.agentId, input.agentId)
|
|
16699
|
+
));
|
|
16700
|
+
if (input.maxAgentSessions !== void 0 && (agentCount[0]?.count ?? 0) >= input.maxAgentSessions) return "agent-capacity";
|
|
16701
|
+
await tx.insert(project_exports.workflowAgentCapacityLeases).values({
|
|
16702
|
+
projectId,
|
|
16703
|
+
agentId: input.agentId,
|
|
16704
|
+
attemptId: input.attemptId,
|
|
16705
|
+
createdAt: now.toISOString(),
|
|
16706
|
+
expiresAt
|
|
16707
|
+
});
|
|
16708
|
+
return "acquired";
|
|
16709
|
+
});
|
|
16710
|
+
}
|
|
16711
|
+
/** Renew only the caller's project-scoped attempt; a reclaimed lease never resurrects. */
|
|
16712
|
+
async renewWorkflowSessionCapacity(attemptId, leaseDurationMs = 10 * 6e4) {
|
|
16713
|
+
if (!this.asyncLayer) return true;
|
|
16714
|
+
const now = /* @__PURE__ */ new Date();
|
|
16715
|
+
const result = await this.asyncLayer.db.update(project_exports.workflowAgentCapacityLeases).set({ expiresAt: new Date(now.getTime() + leaseDurationMs).toISOString() }).where(and(
|
|
16716
|
+
eq(project_exports.workflowAgentCapacityLeases.projectId, this.backendProjectId),
|
|
16717
|
+
eq(project_exports.workflowAgentCapacityLeases.attemptId, attemptId),
|
|
16718
|
+
/*
|
|
16719
|
+
* FNXC:WorkflowAgentRouting 2026-08-07-07:30:
|
|
16720
|
+
* Renew a currently live lease only. An expired row may already have
|
|
16721
|
+
* been reclaimed by another engine's admission transaction.
|
|
16722
|
+
*/
|
|
16723
|
+
gt(project_exports.workflowAgentCapacityLeases.expiresAt, now.toISOString())
|
|
16724
|
+
)).returning({ attemptId: project_exports.workflowAgentCapacityLeases.attemptId });
|
|
16725
|
+
return result.length > 0;
|
|
16726
|
+
}
|
|
16727
|
+
/** Release is project-scoped and idempotent so cancellation and recovery may race safely. */
|
|
16728
|
+
async releaseWorkflowSessionCapacity(attemptId) {
|
|
16729
|
+
if (!this.asyncLayer) return;
|
|
16730
|
+
await this.asyncLayer.db.delete(project_exports.workflowAgentCapacityLeases).where(and(
|
|
16731
|
+
eq(project_exports.workflowAgentCapacityLeases.projectId, this.backendProjectId),
|
|
16732
|
+
eq(project_exports.workflowAgentCapacityLeases.attemptId, attemptId)
|
|
16733
|
+
));
|
|
16734
|
+
}
|
|
16498
16735
|
get db() {
|
|
16499
16736
|
throw new Error("SQLite Database is not available in backend mode (asyncLayer injected)");
|
|
16500
16737
|
}
|
|
@@ -16509,7 +16746,7 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
16509
16746
|
*/
|
|
16510
16747
|
async init() {
|
|
16511
16748
|
await mkdir(this.agentsDir, { recursive: true });
|
|
16512
|
-
|
|
16749
|
+
await this.provisionBuiltinWorkflowRoleAgents();
|
|
16513
16750
|
}
|
|
16514
16751
|
/**
|
|
16515
16752
|
* One-shot migration that re-points every non-ephemeral agent off the
|
|
@@ -16671,8 +16908,8 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
16671
16908
|
* @param name - Agent name to match exactly
|
|
16672
16909
|
* @returns Matching non-ephemeral agent, or null when none exists
|
|
16673
16910
|
*/
|
|
16674
|
-
async findAgentByName(name) {
|
|
16675
|
-
const agents2 = await findAgentRowsByName(this.asyncLayer.db, name);
|
|
16911
|
+
async findAgentByName(name, executor) {
|
|
16912
|
+
const agents2 = await findAgentRowsByName(executor ?? this.asyncLayer.db, name);
|
|
16676
16913
|
for (const agent of agents2) {
|
|
16677
16914
|
if (!isEphemeralAgent(agent)) {
|
|
16678
16915
|
return this.parseAgent(agent);
|
|
@@ -16707,18 +16944,16 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
16707
16944
|
* @returns The created agent
|
|
16708
16945
|
* @throws Error if input is invalid or a duplicate non-ephemeral name exists
|
|
16709
16946
|
*/
|
|
16710
|
-
async createAgent(input) {
|
|
16947
|
+
async createAgent(input, executor) {
|
|
16711
16948
|
if (!input.name?.trim()) {
|
|
16712
16949
|
throw new Error("Agent name is required");
|
|
16713
16950
|
}
|
|
16714
|
-
|
|
16715
|
-
throw new Error("Agent role is required");
|
|
16716
|
-
}
|
|
16951
|
+
const roles = normalizeAgentRoles(input.roles, input.role);
|
|
16717
16952
|
const normalizedName = input.name.trim();
|
|
16718
16953
|
const metadata = input.metadata ?? {};
|
|
16719
|
-
const ephemeral = isEphemeralAgent({ metadata, name: input.name, role:
|
|
16954
|
+
const ephemeral = isEphemeralAgent({ metadata, name: input.name, role: roles[0], reportsTo: input.reportsTo });
|
|
16720
16955
|
if (!ephemeral) {
|
|
16721
|
-
const existing = await this.findAgentByName(normalizedName);
|
|
16956
|
+
const existing = await this.findAgentByName(normalizedName, executor);
|
|
16722
16957
|
if (existing) {
|
|
16723
16958
|
throw new Error(`Agent with name "${normalizedName}" already exists (agentId: ${existing.id})`);
|
|
16724
16959
|
}
|
|
@@ -16731,7 +16966,8 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
16731
16966
|
const agent = {
|
|
16732
16967
|
id: agentId,
|
|
16733
16968
|
name: normalizedName,
|
|
16734
|
-
|
|
16969
|
+
roles,
|
|
16970
|
+
role: roles[0],
|
|
16735
16971
|
// Non-ephemeral agents start active so they immediately participate in
|
|
16736
16972
|
// heartbeat scheduling; ephemeral/task-worker agents start idle and are
|
|
16737
16973
|
// activated by the engine when work is assigned.
|
|
@@ -16753,7 +16989,7 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
16753
16989
|
...input.bundleConfig && { bundleConfig: input.bundleConfig },
|
|
16754
16990
|
...resolvedHeartbeatProcedurePath && { heartbeatProcedurePath: resolvedHeartbeatProcedurePath }
|
|
16755
16991
|
};
|
|
16756
|
-
await this.writeAgent(agent);
|
|
16992
|
+
await this.writeAgent(agent, executor);
|
|
16757
16993
|
this.emit("agent:created", agent);
|
|
16758
16994
|
return agent;
|
|
16759
16995
|
}
|
|
@@ -17096,10 +17332,12 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
17096
17332
|
const beforeSnapshot = agentToConfigSnapshot(agent);
|
|
17097
17333
|
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
17098
17334
|
const normalizedUpdatedPermissionPolicy = "permissionPolicy" in updates && updates.permissionPolicy !== void 0 ? normalizeAgentPermissionPolicy(updates.permissionPolicy) : updates.permissionPolicy;
|
|
17335
|
+
const roles = updates.roles !== void 0 || updates.role !== void 0 ? normalizeAgentRoles(updates.roles, updates.role) : agent.roles;
|
|
17099
17336
|
const updated = {
|
|
17100
17337
|
...agent,
|
|
17101
17338
|
name: nextName ?? agent.name,
|
|
17102
|
-
|
|
17339
|
+
roles,
|
|
17340
|
+
role: roles[0],
|
|
17103
17341
|
metadata: updates.metadata !== void 0 ? updates.metadata : agent.metadata,
|
|
17104
17342
|
updatedAt,
|
|
17105
17343
|
..."title" in updates && { title: updates.title },
|
|
@@ -17634,12 +17872,81 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
17634
17872
|
* @param filter - Optional filter criteria
|
|
17635
17873
|
* @returns Array of agents
|
|
17636
17874
|
*/
|
|
17637
|
-
async listAgents(filter) {
|
|
17638
|
-
const agents2 = await listAgentRows(this.asyncLayer.db, {
|
|
17639
|
-
|
|
17640
|
-
|
|
17875
|
+
async listAgents(filter, executor) {
|
|
17876
|
+
const agents2 = await listAgentRows(executor ?? this.asyncLayer.db, { state: filter?.state });
|
|
17877
|
+
return agents2.map((a) => this.parseAgent(a)).filter((agent) => !filter?.role || agent.roles.includes(filter.role)).filter((agent) => filter?.includeEphemeral === true || !isEphemeralAgent(agent));
|
|
17878
|
+
}
|
|
17879
|
+
/**
|
|
17880
|
+
* Idempotently seed the permanent owners that route built-in workflow stages.
|
|
17881
|
+
* Matching operator-created agents are intentionally not reused: provenance
|
|
17882
|
+
* makes upgrade repair deterministic while operators remain free to add pool
|
|
17883
|
+
* members with the same tag.
|
|
17884
|
+
*/
|
|
17885
|
+
async provisionBuiltinWorkflowRoleAgents() {
|
|
17886
|
+
const provision = async (executor) => {
|
|
17887
|
+
const existing = await this.listAgents({ includeEphemeral: true }, executor);
|
|
17888
|
+
const supportedRoles = new Set(BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULT_LIST.map(({ role }) => role));
|
|
17889
|
+
const groups = /* @__PURE__ */ new Map();
|
|
17890
|
+
for (const agent of existing) {
|
|
17891
|
+
const role = agent.metadata?.workflowRole;
|
|
17892
|
+
if (agent.metadata?.builtInWorkflowRole === true && typeof role === "string" && supportedRoles.has(role)) {
|
|
17893
|
+
const group = groups.get(role) ?? [];
|
|
17894
|
+
group.push(agent);
|
|
17895
|
+
groups.set(role, group);
|
|
17896
|
+
}
|
|
17897
|
+
}
|
|
17898
|
+
const winners = /* @__PURE__ */ new Map();
|
|
17899
|
+
for (const [role, group] of groups) {
|
|
17900
|
+
group.sort(compareBuiltinWorkflowOwnerAge);
|
|
17901
|
+
winners.set(role, group[0]);
|
|
17902
|
+
for (const loser of group.slice(1)) {
|
|
17903
|
+
const { builtInWorkflowRole: _builtIn, workflowRole: _role, ...metadata } = loser.metadata;
|
|
17904
|
+
await this.writeAgent({ ...loser, metadata, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, executor);
|
|
17905
|
+
}
|
|
17906
|
+
}
|
|
17907
|
+
const result = [];
|
|
17908
|
+
for (const definition of BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULT_LIST) {
|
|
17909
|
+
let agent = winners.get(definition.role);
|
|
17910
|
+
if (!agent) {
|
|
17911
|
+
agent = await this.createAgent({
|
|
17912
|
+
name: definition.name,
|
|
17913
|
+
roles: [definition.role],
|
|
17914
|
+
title: definition.title,
|
|
17915
|
+
metadata: { builtInWorkflowRole: true, workflowRole: definition.role },
|
|
17916
|
+
runtimeConfig: { enabled: false },
|
|
17917
|
+
instructionsText: definition.instructionsText,
|
|
17918
|
+
soul: definition.soul,
|
|
17919
|
+
bundleConfig: { ...BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG, files: [...BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG.files] }
|
|
17920
|
+
}, executor);
|
|
17921
|
+
} else {
|
|
17922
|
+
const canonicalOrPartialBundle = isCanonicalOrPartialBuiltinWorkflowBundle(agent.bundleConfig);
|
|
17923
|
+
const customInstructions = Boolean(agent.instructionsPath?.trim()) || agent.bundleConfig?.mode === "external" || agent.bundleConfig !== void 0 && !canonicalOrPartialBundle || Boolean(agent.instructionsText?.trim()) && agent.instructionsText !== definition.instructionsText;
|
|
17924
|
+
const updates = {};
|
|
17925
|
+
if (!customInstructions && !agent.instructionsText?.trim()) updates.instructionsText = definition.instructionsText;
|
|
17926
|
+
if (!agent.soul?.trim()) updates.soul = definition.soul;
|
|
17927
|
+
if (!customInstructions && !isCompleteBuiltinWorkflowBundle(agent.bundleConfig)) {
|
|
17928
|
+
updates.bundleConfig = { ...BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG, files: [...BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG.files] };
|
|
17929
|
+
}
|
|
17930
|
+
if (Object.keys(updates).length > 0) {
|
|
17931
|
+
agent = { ...agent, ...updates, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
17932
|
+
await this.writeAgent(agent, executor);
|
|
17933
|
+
}
|
|
17934
|
+
}
|
|
17935
|
+
result.push(agent);
|
|
17936
|
+
}
|
|
17937
|
+
return result;
|
|
17938
|
+
};
|
|
17939
|
+
if (!this.asyncLayer) {
|
|
17940
|
+
const agents3 = await provision();
|
|
17941
|
+
await Promise.all(agents3.map((agent) => this.materializeBuiltinWorkflowRoleBundle(agent)));
|
|
17942
|
+
return agents3;
|
|
17943
|
+
}
|
|
17944
|
+
const agents2 = await this.asyncLayer.transactionImmediate(async (tx) => {
|
|
17945
|
+
await tx.execute(sql`SELECT pg_advisory_xact_lock(hashtext(${this.backendProjectId}), hashtext('builtin-workflow-role-provisioning'))`);
|
|
17946
|
+
return provision(tx);
|
|
17641
17947
|
});
|
|
17642
|
-
|
|
17948
|
+
await Promise.all(agents2.map((agent) => this.materializeBuiltinWorkflowRoleBundle(agent)));
|
|
17949
|
+
return agents2;
|
|
17643
17950
|
}
|
|
17644
17951
|
/**
|
|
17645
17952
|
* Create an API key for an agent.
|
|
@@ -18207,6 +18514,29 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
18207
18514
|
}
|
|
18208
18515
|
return null;
|
|
18209
18516
|
}
|
|
18517
|
+
/** Seed only missing or blank default-owned mirror files after the provisioning transaction commits. */
|
|
18518
|
+
async materializeBuiltinWorkflowRoleBundle(agent) {
|
|
18519
|
+
return this.withLock(agent.id, async () => {
|
|
18520
|
+
const role = agent.metadata?.workflowRole;
|
|
18521
|
+
const definition = BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULT_LIST.find((candidate) => candidate.role === role);
|
|
18522
|
+
if (!definition || agent.metadata?.builtInWorkflowRole !== true) return;
|
|
18523
|
+
const config2 = agent.bundleConfig;
|
|
18524
|
+
if (!isCanonicalOrPartialBuiltinWorkflowBundle(config2)) return;
|
|
18525
|
+
if (agent.instructionsPath?.trim() || agent.instructionsText?.trim() && agent.instructionsText !== definition.instructionsText) return;
|
|
18526
|
+
const bundleDir = await this.resolveCompatibleBundleDir(agent.id, true);
|
|
18527
|
+
await mkdir(bundleDir, { recursive: true });
|
|
18528
|
+
for (const [file, content] of [["AGENTS.md", definition.instructionsText], ["soul.md", definition.soul]]) {
|
|
18529
|
+
const path = join(bundleDir, file);
|
|
18530
|
+
let current = "";
|
|
18531
|
+
try {
|
|
18532
|
+
current = await readFile(path, "utf-8");
|
|
18533
|
+
} catch (error) {
|
|
18534
|
+
if (error.code !== "ENOENT") throw error;
|
|
18535
|
+
}
|
|
18536
|
+
if (!current.trim()) await writeFile(path, content, "utf-8");
|
|
18537
|
+
}
|
|
18538
|
+
});
|
|
18539
|
+
}
|
|
18210
18540
|
getCanonicalBundleDir(agent) {
|
|
18211
18541
|
return join(this.agentsDir, getCanonicalAgentInstructionsBundleDirName(agent.name, agent.id));
|
|
18212
18542
|
}
|
|
@@ -18329,6 +18659,7 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
18329
18659
|
...data,
|
|
18330
18660
|
id: row.id,
|
|
18331
18661
|
name: row.name,
|
|
18662
|
+
roles: data.roles?.length ? data.roles : [row.role],
|
|
18332
18663
|
role: row.role,
|
|
18333
18664
|
state: row.state,
|
|
18334
18665
|
taskId: row.taskId ?? void 0,
|
|
@@ -18362,7 +18693,8 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
18362
18693
|
return {
|
|
18363
18694
|
id: data.id,
|
|
18364
18695
|
name: data.name,
|
|
18365
|
-
|
|
18696
|
+
roles: normalizeAgentRoles(data.roles, data.role),
|
|
18697
|
+
role: normalizeAgentRoles(data.roles, data.role)[0],
|
|
18366
18698
|
state: data.state,
|
|
18367
18699
|
taskId: data.taskId,
|
|
18368
18700
|
createdAt: data.createdAt,
|
|
@@ -18388,8 +18720,8 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
18388
18720
|
heartbeatProcedurePath: data.heartbeatProcedurePath
|
|
18389
18721
|
};
|
|
18390
18722
|
}
|
|
18391
|
-
async writeAgent(agent) {
|
|
18392
|
-
await writeAgent(this.asyncLayer.db, agent, this.asyncLayer.projectId);
|
|
18723
|
+
async writeAgent(agent, executor) {
|
|
18724
|
+
await writeAgent(executor ?? this.asyncLayer.db, agent, this.asyncLayer.projectId);
|
|
18393
18725
|
return;
|
|
18394
18726
|
}
|
|
18395
18727
|
/**
|