@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
|
@@ -5765,6 +5765,7 @@ __export(project_exports, {
|
|
|
5765
5765
|
unplannedExecutionBlocks: () => unplannedExecutionBlocks,
|
|
5766
5766
|
usageEvents: () => usageEvents,
|
|
5767
5767
|
verificationCache: () => verificationCache,
|
|
5768
|
+
workflowAgentCapacityLeases: () => workflowAgentCapacityLeases,
|
|
5768
5769
|
workflowPromptOverrides: () => workflowPromptOverrides,
|
|
5769
5770
|
workflowRunBranches: () => workflowRunBranches,
|
|
5770
5771
|
workflowRunStepInstances: () => workflowRunStepInstances,
|
|
@@ -5873,6 +5874,8 @@ var tasks = projectSchema.table("tasks", {
|
|
|
5873
5874
|
nextRecoveryAt: text("next_recovery_at"),
|
|
5874
5875
|
error: text("error"),
|
|
5875
5876
|
summary: text("summary"),
|
|
5877
|
+
// FNXC:TaskRecommendations 2026-08-08-05:02: structured completion suggestions remain project-scoped JSONB beside the source task.
|
|
5878
|
+
recommendations: jsonb("recommendations"),
|
|
5876
5879
|
thinkingLevel: text("thinking_level"),
|
|
5877
5880
|
// FNXC:Settings-ThinkingLevel 2026-07-13 (merge port): validator/planning reasoning-effort overrides.
|
|
5878
5881
|
validatorThinkingLevel: text("validator_thinking_level"),
|
|
@@ -6453,7 +6456,9 @@ var agents = projectSchema.table("agents", {
|
|
|
6453
6456
|
projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
|
|
6454
6457
|
id: text("id").notNull(),
|
|
6455
6458
|
name: text("name").notNull(),
|
|
6459
|
+
/** Deprecated singular compatibility column; canonical roles live in roles JSONB. */
|
|
6456
6460
|
role: text("role").notNull(),
|
|
6461
|
+
roles: jsonb("roles").notNull().default([]),
|
|
6457
6462
|
state: text("state").notNull().default("idle"),
|
|
6458
6463
|
taskId: text("task_id"),
|
|
6459
6464
|
createdAt: text("created_at").notNull(),
|
|
@@ -6577,6 +6582,17 @@ var completionHandoffMarkers = projectSchema.table("completion_handoff_markers",
|
|
|
6577
6582
|
foreignKey({ columns: [t.projectId, t.taskId], foreignColumns: [tasks.projectId, tasks.id] }).onDelete("cascade"),
|
|
6578
6583
|
index("idx_completion_handoff_markers_acceptedAt").on(t.acceptedAt)
|
|
6579
6584
|
]);
|
|
6585
|
+
var workflowAgentCapacityLeases = projectSchema.table("workflow_agent_capacity_leases", {
|
|
6586
|
+
projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
|
|
6587
|
+
attemptId: text("attempt_id").notNull(),
|
|
6588
|
+
agentId: text("agent_id").notNull(),
|
|
6589
|
+
createdAt: text("created_at").notNull(),
|
|
6590
|
+
// 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.
|
|
6591
|
+
expiresAt: text("expires_at").notNull()
|
|
6592
|
+
}, (t) => [
|
|
6593
|
+
primaryKey({ columns: [t.projectId, t.attemptId] }),
|
|
6594
|
+
index("idx_workflow_agent_capacity_leases_agent").on(t.projectId, t.agentId)
|
|
6595
|
+
]);
|
|
6580
6596
|
var workflowWorkItems = projectSchema.table("workflow_work_items", {
|
|
6581
6597
|
projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
|
|
6582
6598
|
id: text("id").notNull(),
|
|
@@ -6597,6 +6613,13 @@ var workflowWorkItems = projectSchema.table("workflow_work_items", {
|
|
|
6597
6613
|
sourceColumn: text("source_column"),
|
|
6598
6614
|
targetColumn: text("target_column"),
|
|
6599
6615
|
irHash: text("ir_hash"),
|
|
6616
|
+
// FNXC:WorkflowAgentRouting 2026-08-07-03:25:
|
|
6617
|
+
// A workflow claim fences its durable principal and exact template instance.
|
|
6618
|
+
// Session retries must reuse this identity rather than re-routing a task.
|
|
6619
|
+
principalAgentId: text("principal_agent_id"),
|
|
6620
|
+
workflowRole: text("workflow_role"),
|
|
6621
|
+
authorityKind: text("authority_kind"),
|
|
6622
|
+
nodeInstanceId: text("node_instance_id"),
|
|
6600
6623
|
createdAt: text("created_at").notNull(),
|
|
6601
6624
|
updatedAt: text("updated_at").notNull()
|
|
6602
6625
|
}, (t) => [
|
|
@@ -8592,6 +8615,7 @@ var BLOCKING_TASK_STATUSES = /* @__PURE__ */ new Set([
|
|
|
8592
8615
|
// ../core/src/tasks/in-review-stall.ts
|
|
8593
8616
|
var DEFAULT_STALE_MERGING_MIN_AGE_MS = 5 * 6e4;
|
|
8594
8617
|
var DEFAULT_MAX_AUTO_MERGE_RETRIES = 3;
|
|
8618
|
+
var CONSECUTIVE_TOOL_FAILURE_RETRY_THRESHOLD = 1;
|
|
8595
8619
|
|
|
8596
8620
|
// ../core/src/config/settings-schema.ts
|
|
8597
8621
|
var DEFAULT_GLOBAL_SETTINGS = {
|
|
@@ -8932,6 +8956,8 @@ var DEFAULT_GLOBAL_SETTINGS = {
|
|
|
8932
8956
|
cliAgents: {}
|
|
8933
8957
|
};
|
|
8934
8958
|
var DEFAULT_PROJECT_SETTINGS = {
|
|
8959
|
+
// FNXC:TaskRecommendations 2026-08-08-05:02: completion follows-ups stay bounded by default; 0 disables writing them.
|
|
8960
|
+
maxRecommendationsPerTask: 3,
|
|
8935
8961
|
globalPause: false,
|
|
8936
8962
|
globalPauseReason: void 0,
|
|
8937
8963
|
defaultWorkflowId: void 0,
|
|
@@ -9100,9 +9126,15 @@ var DEFAULT_PROJECT_SETTINGS = {
|
|
|
9100
9126
|
* 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.
|
|
9101
9127
|
*/
|
|
9102
9128
|
maxAutoMergeRetries: DEFAULT_MAX_AUTO_MERGE_RETRIES,
|
|
9129
|
+
/*
|
|
9130
|
+
FNXC:ExecutorToolFailureRetry 2026-08-06-14:56:
|
|
9131
|
+
Fresh projects retry the existing bounded same-model continuation after one
|
|
9132
|
+
terminal tool error. Persisted project values are not migrated, so explicit
|
|
9133
|
+
operator thresholds remain authoritative.
|
|
9134
|
+
*/
|
|
9103
9135
|
executorToolFailureRetryCount: 2,
|
|
9104
9136
|
executorToolFailureRetryBackoffMs: 2e3,
|
|
9105
|
-
executorToolFailureThreshold:
|
|
9137
|
+
executorToolFailureThreshold: CONSECUTIVE_TOOL_FAILURE_RETRY_THRESHOLD,
|
|
9106
9138
|
executorModelEscalationEnabled: false,
|
|
9107
9139
|
executorEscalationProvider: void 0,
|
|
9108
9140
|
executorEscalationModelId: void 0,
|
|
@@ -9138,6 +9170,12 @@ var DEFAULT_PROJECT_SETTINGS = {
|
|
|
9138
9170
|
// proportional to the change; the thin merge gate carries cross-cutting
|
|
9139
9171
|
// coverage. Falls back to package/explicit command when no tests resolve.
|
|
9140
9172
|
scopeVerificationToChangedFiles: true,
|
|
9173
|
+
/*
|
|
9174
|
+
FNXC:WorkflowAgentRouting 2026-08-07-08:45:
|
|
9175
|
+
Keep the legacy project setting defaulted and persistable for existing clients and
|
|
9176
|
+
configuration records. Workflow-stage routing must ignore its value; durable role
|
|
9177
|
+
principals own scheduler, executor, and mission-stage authority.
|
|
9178
|
+
*/
|
|
9141
9179
|
ephemeralAgentsEnabled: true,
|
|
9142
9180
|
/*
|
|
9143
9181
|
FNXC:EphemeralAgentTaskCreation 2026-07-01-00:00:
|
|
@@ -9439,6 +9477,23 @@ function isEphemeralAgent(agent) {
|
|
|
9439
9477
|
}
|
|
9440
9478
|
|
|
9441
9479
|
// ../core/src/types/agents/agents.ts
|
|
9480
|
+
var AGENT_CAPABILITIES = [
|
|
9481
|
+
"triage",
|
|
9482
|
+
"executor",
|
|
9483
|
+
"reviewer",
|
|
9484
|
+
"merger",
|
|
9485
|
+
"scheduler",
|
|
9486
|
+
"engineer",
|
|
9487
|
+
"custom"
|
|
9488
|
+
];
|
|
9489
|
+
function normalizeAgentRoles(roles, legacyRole) {
|
|
9490
|
+
const values = roles?.length ? roles : legacyRole ? [legacyRole] : [];
|
|
9491
|
+
const unique2 = new Set(values);
|
|
9492
|
+
const normalized = AGENT_CAPABILITIES.filter((role) => unique2.has(role));
|
|
9493
|
+
if (normalized.length !== unique2.size) throw new Error("Agent roles contain an unknown capability");
|
|
9494
|
+
if (normalized.length === 0) throw new Error("Agent requires at least one role");
|
|
9495
|
+
return normalized;
|
|
9496
|
+
}
|
|
9442
9497
|
var AGENT_PERMISSIONS = [
|
|
9443
9498
|
"tasks:assign",
|
|
9444
9499
|
// Assign tasks to agents
|
|
@@ -9560,6 +9615,7 @@ function getDefaultHeartbeatProcedurePath(agentId, agentName) {
|
|
|
9560
9615
|
function agentToConfigSnapshot(agent) {
|
|
9561
9616
|
return {
|
|
9562
9617
|
name: agent.name,
|
|
9618
|
+
roles: [...agent.roles],
|
|
9563
9619
|
role: agent.role,
|
|
9564
9620
|
title: agent.title,
|
|
9565
9621
|
icon: agent.icon,
|
|
@@ -9894,6 +9950,41 @@ function resolveColumnFlags(column) {
|
|
|
9894
9950
|
return getTraitRegistry().resolveColumnFlags(column);
|
|
9895
9951
|
}
|
|
9896
9952
|
|
|
9953
|
+
// ../core/src/workflows/workflow-ir-types.ts
|
|
9954
|
+
var WORKFLOW_AGENT_ROLES = ["triage", "executor", "reviewer", "merger"];
|
|
9955
|
+
function isWorkflowAgentRole(value) {
|
|
9956
|
+
return typeof value === "string" && WORKFLOW_AGENT_ROLES.includes(value);
|
|
9957
|
+
}
|
|
9958
|
+
function classifyWorkflowAgentNode(node) {
|
|
9959
|
+
if (node.kind === "step-review") return "reviewer";
|
|
9960
|
+
if (node.kind !== "prompt" && node.kind !== "script") return void 0;
|
|
9961
|
+
switch (node.config?.seam) {
|
|
9962
|
+
/*
|
|
9963
|
+
* FNXC:WorkflowAgentRouting 2026-08-07-05:52:
|
|
9964
|
+
* Planning, independent review, and promotion seams each launch work under
|
|
9965
|
+
* their permanent stage owner. Classify those production seams here rather
|
|
9966
|
+
* than treating their handoff worker as an unowned lifecycle operation.
|
|
9967
|
+
* Only pure handoff/scheduling seams remain principal-free.
|
|
9968
|
+
*/
|
|
9969
|
+
case "planning":
|
|
9970
|
+
return "triage";
|
|
9971
|
+
case "execute":
|
|
9972
|
+
case "step-execute":
|
|
9973
|
+
return "executor";
|
|
9974
|
+
case "review":
|
|
9975
|
+
return "reviewer";
|
|
9976
|
+
case "merge":
|
|
9977
|
+
return "merger";
|
|
9978
|
+
case "review-handoff":
|
|
9979
|
+
case "schedule":
|
|
9980
|
+
return void 0;
|
|
9981
|
+
default: {
|
|
9982
|
+
const explicit = node.config?.workflowRole;
|
|
9983
|
+
return isWorkflowAgentRole(explicit) ? explicit : void 0;
|
|
9984
|
+
}
|
|
9985
|
+
}
|
|
9986
|
+
}
|
|
9987
|
+
|
|
9897
9988
|
// ../core/src/workflows/workflow-extension-types.ts
|
|
9898
9989
|
function workflowExtensionRegistryId(pluginId, extensionId) {
|
|
9899
9990
|
return `plugin:${pluginId}:${extensionId}`;
|
|
@@ -10882,6 +10973,20 @@ function validateCredentialInstanceIdConfig(nodes2) {
|
|
|
10882
10973
|
if (Array.isArray(templateNodes)) validateCredentialInstanceIdConfig(templateNodes);
|
|
10883
10974
|
}
|
|
10884
10975
|
}
|
|
10976
|
+
function validateReviewerAgentOverrides(nodes2) {
|
|
10977
|
+
for (const node of nodes2) {
|
|
10978
|
+
if (node.reviewerAgentId !== void 0) {
|
|
10979
|
+
if (typeof node.reviewerAgentId !== "string" || !node.reviewerAgentId.trim()) {
|
|
10980
|
+
throw new WorkflowIrError(`Workflow node '${node.id}' reviewerAgentId must be a non-empty string`);
|
|
10981
|
+
}
|
|
10982
|
+
if (classifyWorkflowAgentNode(node) !== "reviewer") {
|
|
10983
|
+
throw new WorkflowIrError(`Workflow node '${node.id}' reviewerAgentId is only legal on reviewer-session nodes`);
|
|
10984
|
+
}
|
|
10985
|
+
}
|
|
10986
|
+
const templateNodes = node.config?.template?.nodes;
|
|
10987
|
+
if (Array.isArray(templateNodes)) validateReviewerAgentOverrides(templateNodes);
|
|
10988
|
+
}
|
|
10989
|
+
}
|
|
10885
10990
|
function validateThinkingLevelConfig(nodes2) {
|
|
10886
10991
|
for (const node of nodes2) {
|
|
10887
10992
|
const value = node.config?.thinkingLevel;
|
|
@@ -11497,6 +11602,7 @@ function validateV2(ir) {
|
|
|
11497
11602
|
validateStepExecutePlacement(ir.nodes);
|
|
11498
11603
|
validateThinkingLevelConfig(ir.nodes);
|
|
11499
11604
|
validateCredentialInstanceIdConfig(ir.nodes);
|
|
11605
|
+
validateReviewerAgentOverrides(ir.nodes);
|
|
11500
11606
|
for (const node of ir.nodes) {
|
|
11501
11607
|
if (node.kind === "foreach") validateForeach(node, topLevelIds, columnIds);
|
|
11502
11608
|
if (node.kind === "loop") validateLoop(node, topLevelIds, columnIds);
|
|
@@ -15730,7 +15836,8 @@ function normalizePermissions(raw) {
|
|
|
15730
15836
|
return permissions;
|
|
15731
15837
|
}
|
|
15732
15838
|
function computeAccessState(agent) {
|
|
15733
|
-
const
|
|
15839
|
+
const roles = agent.roles?.length ? agent.roles : [agent.role];
|
|
15840
|
+
const roleDefaultPermissions = new Set(roles.flatMap((role) => ROLE_DEFAULT_PERMISSIONS[role] ?? []));
|
|
15734
15841
|
const explicitPermissions = normalizePermissions(agent.permissions ?? {});
|
|
15735
15842
|
const resolvedPermissions = new Set(roleDefaultPermissions);
|
|
15736
15843
|
for (const permission of explicitPermissions) {
|
|
@@ -15761,6 +15868,9 @@ var IMPLEMENTATION_TASK_COLUMNS = /* @__PURE__ */ new Set([
|
|
|
15761
15868
|
"in-progress",
|
|
15762
15869
|
"in-review"
|
|
15763
15870
|
]);
|
|
15871
|
+
function agentRoles(agent) {
|
|
15872
|
+
return agent.roles?.length ? agent.roles : agent.role ? [agent.role] : [];
|
|
15873
|
+
}
|
|
15764
15874
|
function getAgentAssignmentPolicy(agent) {
|
|
15765
15875
|
const raw = (agent.runtimeConfig ?? {})["assignmentPolicy"];
|
|
15766
15876
|
return raw === "explicit-only" || raw === "none" ? raw : "auto";
|
|
@@ -15775,10 +15885,10 @@ function isImplementationTask(task) {
|
|
|
15775
15885
|
return IMPLEMENTATION_TASK_COLUMNS.has(task.column);
|
|
15776
15886
|
}
|
|
15777
15887
|
function isExecutorRoleAgent(agent) {
|
|
15778
|
-
return agent.
|
|
15888
|
+
return agentRoles(agent).includes("executor");
|
|
15779
15889
|
}
|
|
15780
15890
|
function isEngineerRoleAgent(agent) {
|
|
15781
|
-
return agent.
|
|
15891
|
+
return agentRoles(agent).includes("engineer");
|
|
15782
15892
|
}
|
|
15783
15893
|
function canAgentTakeImplementationTaskForExplicitRouting(agent, task) {
|
|
15784
15894
|
if (!isImplementationTask(task)) return true;
|
|
@@ -15827,7 +15937,8 @@ function formatRoleMismatchReason(agent, task) {
|
|
|
15827
15937
|
if (policy !== "auto") {
|
|
15828
15938
|
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"}.`;
|
|
15829
15939
|
}
|
|
15830
|
-
|
|
15940
|
+
const roles = agentRoles(agent);
|
|
15941
|
+
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.`;
|
|
15831
15942
|
}
|
|
15832
15943
|
|
|
15833
15944
|
// ../core/src/agents/agent-permission-policy.ts
|
|
@@ -15949,6 +16060,7 @@ var agentColumns = {
|
|
|
15949
16060
|
id: project_exports.agents.id,
|
|
15950
16061
|
name: project_exports.agents.name,
|
|
15951
16062
|
role: project_exports.agents.role,
|
|
16063
|
+
roles: project_exports.agents.roles,
|
|
15952
16064
|
state: project_exports.agents.state,
|
|
15953
16065
|
taskId: project_exports.agents.taskId,
|
|
15954
16066
|
createdAt: project_exports.agents.createdAt,
|
|
@@ -15979,6 +16091,7 @@ function agentToData(agent) {
|
|
|
15979
16091
|
return {
|
|
15980
16092
|
id: agent.id,
|
|
15981
16093
|
name: agent.name,
|
|
16094
|
+
roles: agent.roles,
|
|
15982
16095
|
role: agent.role,
|
|
15983
16096
|
state: agent.state,
|
|
15984
16097
|
taskId: agent.taskId,
|
|
@@ -16013,6 +16126,7 @@ async function writeAgent(handle, agent, projectId) {
|
|
|
16013
16126
|
id: agent.id,
|
|
16014
16127
|
name: agent.name,
|
|
16015
16128
|
role: agent.role,
|
|
16129
|
+
roles: agent.roles,
|
|
16016
16130
|
state: agent.state,
|
|
16017
16131
|
taskId: agent.taskId ?? null,
|
|
16018
16132
|
createdAt: agent.createdAt,
|
|
@@ -16025,6 +16139,7 @@ async function writeAgent(handle, agent, projectId) {
|
|
|
16025
16139
|
set: {
|
|
16026
16140
|
name: agent.name,
|
|
16027
16141
|
role: agent.role,
|
|
16142
|
+
roles: agent.roles,
|
|
16028
16143
|
state: agent.state,
|
|
16029
16144
|
taskId: agent.taskId ?? null,
|
|
16030
16145
|
updatedAt: agent.updatedAt,
|
|
@@ -16046,6 +16161,7 @@ function mergeAgentRow(row) {
|
|
|
16046
16161
|
...data,
|
|
16047
16162
|
id: row.id,
|
|
16048
16163
|
name: row.name,
|
|
16164
|
+
roles: row.roles?.length ? row.roles : [row.role],
|
|
16049
16165
|
role: row.role,
|
|
16050
16166
|
state: row.state,
|
|
16051
16167
|
taskId: row.taskId ?? void 0,
|
|
@@ -16375,8 +16491,62 @@ var FsWatchPollController = class {
|
|
|
16375
16491
|
}
|
|
16376
16492
|
};
|
|
16377
16493
|
|
|
16494
|
+
// ../core/src/agents/workflow-role-agent-defaults.ts
|
|
16495
|
+
var BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG = {
|
|
16496
|
+
mode: "managed",
|
|
16497
|
+
entryFile: "AGENTS.md",
|
|
16498
|
+
files: ["AGENTS.md", "soul.md"]
|
|
16499
|
+
};
|
|
16500
|
+
var BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULTS = {
|
|
16501
|
+
triage: {
|
|
16502
|
+
role: "triage",
|
|
16503
|
+
name: "Workflow Planner",
|
|
16504
|
+
title: "Built-in workflow planning owner",
|
|
16505
|
+
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.",
|
|
16506
|
+
soul: "Calm, structured, and curious. You reduce ambiguity without adding ceremony, respect operator intent, and prefer concrete evidence over confident guesses."
|
|
16507
|
+
},
|
|
16508
|
+
executor: {
|
|
16509
|
+
role: "executor",
|
|
16510
|
+
name: "Workflow Executor",
|
|
16511
|
+
title: "Built-in workflow execution owner",
|
|
16512
|
+
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.",
|
|
16513
|
+
soul: "Methodical, practical, and accountable. You favor small safe steps, protect user work, and let tests and observable behavior guide your confidence."
|
|
16514
|
+
},
|
|
16515
|
+
reviewer: {
|
|
16516
|
+
role: "reviewer",
|
|
16517
|
+
name: "Workflow Reviewer",
|
|
16518
|
+
title: "Built-in workflow review owner",
|
|
16519
|
+
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.",
|
|
16520
|
+
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."
|
|
16521
|
+
},
|
|
16522
|
+
merger: {
|
|
16523
|
+
role: "merger",
|
|
16524
|
+
name: "Workflow Merger",
|
|
16525
|
+
title: "Built-in workflow merge owner",
|
|
16526
|
+
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.",
|
|
16527
|
+
soul: "Deliberate, conservative, and transparent. You optimize for repository integrity, treat uncertainty as a reason to stop, and communicate operational state plainly."
|
|
16528
|
+
}
|
|
16529
|
+
};
|
|
16530
|
+
var BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULT_LIST = Object.values(BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULTS);
|
|
16531
|
+
|
|
16378
16532
|
// ../core/src/agents/agent-store.ts
|
|
16379
16533
|
var agentStoreLog = createLogger("agent-store");
|
|
16534
|
+
function isCanonicalOrPartialBuiltinWorkflowBundle(config2) {
|
|
16535
|
+
if (config2?.mode !== "managed" || config2.entryFile !== "AGENTS.md") return false;
|
|
16536
|
+
return config2.files.every((file) => (file === "AGENTS.md" || file === "soul.md") && config2.files.indexOf(file) === config2.files.lastIndexOf(file));
|
|
16537
|
+
}
|
|
16538
|
+
function isCompleteBuiltinWorkflowBundle(config2) {
|
|
16539
|
+
return isCanonicalOrPartialBuiltinWorkflowBundle(config2) && config2.files.length === BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG.files.length;
|
|
16540
|
+
}
|
|
16541
|
+
function compareBuiltinWorkflowOwnerAge(a, b) {
|
|
16542
|
+
const aTime = Date.parse(a.createdAt);
|
|
16543
|
+
const bTime = Date.parse(b.createdAt);
|
|
16544
|
+
const aValid = Number.isFinite(aTime);
|
|
16545
|
+
const bValid = Number.isFinite(bTime);
|
|
16546
|
+
if (aValid && bValid && aTime !== bTime) return aTime - bTime;
|
|
16547
|
+
if (aValid !== bValid) return aValid ? -1 : 1;
|
|
16548
|
+
return a.id.localeCompare(b.id);
|
|
16549
|
+
}
|
|
16380
16550
|
var DEFAULT_AGENT_HEARTBEAT_INTERVAL_MS = 36e5;
|
|
16381
16551
|
function normalizeStoredPermissions(raw) {
|
|
16382
16552
|
if (!raw) return void 0;
|
|
@@ -16487,6 +16657,73 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
16487
16657
|
}
|
|
16488
16658
|
return projectId;
|
|
16489
16659
|
}
|
|
16660
|
+
/** The durable project partition used by workflow routing and capacity leases. */
|
|
16661
|
+
get workflowProjectId() {
|
|
16662
|
+
return this.asyncLayer?.projectId;
|
|
16663
|
+
}
|
|
16664
|
+
/**
|
|
16665
|
+
* FNXC:WorkflowAgentRouting 2026-08-07-05:52:
|
|
16666
|
+
* Workflow capacity is a PostgreSQL lease rather than a process-local count.
|
|
16667
|
+
* The project advisory lock serializes project then agent admission across
|
|
16668
|
+
* engine processes; model work starts only after this short transaction ends.
|
|
16669
|
+
*/
|
|
16670
|
+
async acquireWorkflowSessionCapacity(input) {
|
|
16671
|
+
if (!this.asyncLayer) return "acquired";
|
|
16672
|
+
const projectId = this.backendProjectId;
|
|
16673
|
+
const now = /* @__PURE__ */ new Date();
|
|
16674
|
+
const expiresAt = new Date(now.getTime() + (input.leaseDurationMs ?? 10 * 6e4)).toISOString();
|
|
16675
|
+
return this.asyncLayer.transactionImmediate(async (tx) => {
|
|
16676
|
+
await tx.execute(sql`SELECT pg_advisory_xact_lock(hashtext(${projectId}), hashtext('workflow-agent-capacity'))`);
|
|
16677
|
+
await tx.delete(project_exports.workflowAgentCapacityLeases).where(and(
|
|
16678
|
+
eq(project_exports.workflowAgentCapacityLeases.projectId, projectId),
|
|
16679
|
+
lte(project_exports.workflowAgentCapacityLeases.expiresAt, now.toISOString())
|
|
16680
|
+
));
|
|
16681
|
+
const existing = await tx.select({ attemptId: project_exports.workflowAgentCapacityLeases.attemptId }).from(project_exports.workflowAgentCapacityLeases).where(and(
|
|
16682
|
+
eq(project_exports.workflowAgentCapacityLeases.projectId, projectId),
|
|
16683
|
+
eq(project_exports.workflowAgentCapacityLeases.attemptId, input.attemptId)
|
|
16684
|
+
)).limit(1);
|
|
16685
|
+
if (existing[0]) return "acquired";
|
|
16686
|
+
const projectCount = await tx.select({ count: sql`count(*)::int` }).from(project_exports.workflowAgentCapacityLeases).where(eq(project_exports.workflowAgentCapacityLeases.projectId, projectId));
|
|
16687
|
+
if (input.maxProjectSessions !== void 0 && (projectCount[0]?.count ?? 0) >= input.maxProjectSessions) return "project-capacity";
|
|
16688
|
+
const agentCount = await tx.select({ count: sql`count(*)::int` }).from(project_exports.workflowAgentCapacityLeases).where(and(
|
|
16689
|
+
eq(project_exports.workflowAgentCapacityLeases.projectId, projectId),
|
|
16690
|
+
eq(project_exports.workflowAgentCapacityLeases.agentId, input.agentId)
|
|
16691
|
+
));
|
|
16692
|
+
if (input.maxAgentSessions !== void 0 && (agentCount[0]?.count ?? 0) >= input.maxAgentSessions) return "agent-capacity";
|
|
16693
|
+
await tx.insert(project_exports.workflowAgentCapacityLeases).values({
|
|
16694
|
+
projectId,
|
|
16695
|
+
agentId: input.agentId,
|
|
16696
|
+
attemptId: input.attemptId,
|
|
16697
|
+
createdAt: now.toISOString(),
|
|
16698
|
+
expiresAt
|
|
16699
|
+
});
|
|
16700
|
+
return "acquired";
|
|
16701
|
+
});
|
|
16702
|
+
}
|
|
16703
|
+
/** Renew only the caller's project-scoped attempt; a reclaimed lease never resurrects. */
|
|
16704
|
+
async renewWorkflowSessionCapacity(attemptId, leaseDurationMs = 10 * 6e4) {
|
|
16705
|
+
if (!this.asyncLayer) return true;
|
|
16706
|
+
const now = /* @__PURE__ */ new Date();
|
|
16707
|
+
const result = await this.asyncLayer.db.update(project_exports.workflowAgentCapacityLeases).set({ expiresAt: new Date(now.getTime() + leaseDurationMs).toISOString() }).where(and(
|
|
16708
|
+
eq(project_exports.workflowAgentCapacityLeases.projectId, this.backendProjectId),
|
|
16709
|
+
eq(project_exports.workflowAgentCapacityLeases.attemptId, attemptId),
|
|
16710
|
+
/*
|
|
16711
|
+
* FNXC:WorkflowAgentRouting 2026-08-07-07:30:
|
|
16712
|
+
* Renew a currently live lease only. An expired row may already have
|
|
16713
|
+
* been reclaimed by another engine's admission transaction.
|
|
16714
|
+
*/
|
|
16715
|
+
gt(project_exports.workflowAgentCapacityLeases.expiresAt, now.toISOString())
|
|
16716
|
+
)).returning({ attemptId: project_exports.workflowAgentCapacityLeases.attemptId });
|
|
16717
|
+
return result.length > 0;
|
|
16718
|
+
}
|
|
16719
|
+
/** Release is project-scoped and idempotent so cancellation and recovery may race safely. */
|
|
16720
|
+
async releaseWorkflowSessionCapacity(attemptId) {
|
|
16721
|
+
if (!this.asyncLayer) return;
|
|
16722
|
+
await this.asyncLayer.db.delete(project_exports.workflowAgentCapacityLeases).where(and(
|
|
16723
|
+
eq(project_exports.workflowAgentCapacityLeases.projectId, this.backendProjectId),
|
|
16724
|
+
eq(project_exports.workflowAgentCapacityLeases.attemptId, attemptId)
|
|
16725
|
+
));
|
|
16726
|
+
}
|
|
16490
16727
|
get db() {
|
|
16491
16728
|
throw new Error("SQLite Database is not available in backend mode (asyncLayer injected)");
|
|
16492
16729
|
}
|
|
@@ -16501,7 +16738,7 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
16501
16738
|
*/
|
|
16502
16739
|
async init() {
|
|
16503
16740
|
await mkdir(this.agentsDir, { recursive: true });
|
|
16504
|
-
|
|
16741
|
+
await this.provisionBuiltinWorkflowRoleAgents();
|
|
16505
16742
|
}
|
|
16506
16743
|
/**
|
|
16507
16744
|
* One-shot migration that re-points every non-ephemeral agent off the
|
|
@@ -16663,8 +16900,8 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
16663
16900
|
* @param name - Agent name to match exactly
|
|
16664
16901
|
* @returns Matching non-ephemeral agent, or null when none exists
|
|
16665
16902
|
*/
|
|
16666
|
-
async findAgentByName(name) {
|
|
16667
|
-
const agents2 = await findAgentRowsByName(this.asyncLayer.db, name);
|
|
16903
|
+
async findAgentByName(name, executor) {
|
|
16904
|
+
const agents2 = await findAgentRowsByName(executor ?? this.asyncLayer.db, name);
|
|
16668
16905
|
for (const agent of agents2) {
|
|
16669
16906
|
if (!isEphemeralAgent(agent)) {
|
|
16670
16907
|
return this.parseAgent(agent);
|
|
@@ -16699,18 +16936,16 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
16699
16936
|
* @returns The created agent
|
|
16700
16937
|
* @throws Error if input is invalid or a duplicate non-ephemeral name exists
|
|
16701
16938
|
*/
|
|
16702
|
-
async createAgent(input) {
|
|
16939
|
+
async createAgent(input, executor) {
|
|
16703
16940
|
if (!input.name?.trim()) {
|
|
16704
16941
|
throw new Error("Agent name is required");
|
|
16705
16942
|
}
|
|
16706
|
-
|
|
16707
|
-
throw new Error("Agent role is required");
|
|
16708
|
-
}
|
|
16943
|
+
const roles = normalizeAgentRoles(input.roles, input.role);
|
|
16709
16944
|
const normalizedName = input.name.trim();
|
|
16710
16945
|
const metadata = input.metadata ?? {};
|
|
16711
|
-
const ephemeral = isEphemeralAgent({ metadata, name: input.name, role:
|
|
16946
|
+
const ephemeral = isEphemeralAgent({ metadata, name: input.name, role: roles[0], reportsTo: input.reportsTo });
|
|
16712
16947
|
if (!ephemeral) {
|
|
16713
|
-
const existing = await this.findAgentByName(normalizedName);
|
|
16948
|
+
const existing = await this.findAgentByName(normalizedName, executor);
|
|
16714
16949
|
if (existing) {
|
|
16715
16950
|
throw new Error(`Agent with name "${normalizedName}" already exists (agentId: ${existing.id})`);
|
|
16716
16951
|
}
|
|
@@ -16723,7 +16958,8 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
16723
16958
|
const agent = {
|
|
16724
16959
|
id: agentId,
|
|
16725
16960
|
name: normalizedName,
|
|
16726
|
-
|
|
16961
|
+
roles,
|
|
16962
|
+
role: roles[0],
|
|
16727
16963
|
// Non-ephemeral agents start active so they immediately participate in
|
|
16728
16964
|
// heartbeat scheduling; ephemeral/task-worker agents start idle and are
|
|
16729
16965
|
// activated by the engine when work is assigned.
|
|
@@ -16745,7 +16981,7 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
16745
16981
|
...input.bundleConfig && { bundleConfig: input.bundleConfig },
|
|
16746
16982
|
...resolvedHeartbeatProcedurePath && { heartbeatProcedurePath: resolvedHeartbeatProcedurePath }
|
|
16747
16983
|
};
|
|
16748
|
-
await this.writeAgent(agent);
|
|
16984
|
+
await this.writeAgent(agent, executor);
|
|
16749
16985
|
this.emit("agent:created", agent);
|
|
16750
16986
|
return agent;
|
|
16751
16987
|
}
|
|
@@ -17088,10 +17324,12 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
17088
17324
|
const beforeSnapshot = agentToConfigSnapshot(agent);
|
|
17089
17325
|
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
17090
17326
|
const normalizedUpdatedPermissionPolicy = "permissionPolicy" in updates && updates.permissionPolicy !== void 0 ? normalizeAgentPermissionPolicy(updates.permissionPolicy) : updates.permissionPolicy;
|
|
17327
|
+
const roles = updates.roles !== void 0 || updates.role !== void 0 ? normalizeAgentRoles(updates.roles, updates.role) : agent.roles;
|
|
17091
17328
|
const updated = {
|
|
17092
17329
|
...agent,
|
|
17093
17330
|
name: nextName ?? agent.name,
|
|
17094
|
-
|
|
17331
|
+
roles,
|
|
17332
|
+
role: roles[0],
|
|
17095
17333
|
metadata: updates.metadata !== void 0 ? updates.metadata : agent.metadata,
|
|
17096
17334
|
updatedAt,
|
|
17097
17335
|
..."title" in updates && { title: updates.title },
|
|
@@ -17626,12 +17864,81 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
17626
17864
|
* @param filter - Optional filter criteria
|
|
17627
17865
|
* @returns Array of agents
|
|
17628
17866
|
*/
|
|
17629
|
-
async listAgents(filter) {
|
|
17630
|
-
const agents2 = await listAgentRows(this.asyncLayer.db, {
|
|
17631
|
-
|
|
17632
|
-
|
|
17867
|
+
async listAgents(filter, executor) {
|
|
17868
|
+
const agents2 = await listAgentRows(executor ?? this.asyncLayer.db, { state: filter?.state });
|
|
17869
|
+
return agents2.map((a) => this.parseAgent(a)).filter((agent) => !filter?.role || agent.roles.includes(filter.role)).filter((agent) => filter?.includeEphemeral === true || !isEphemeralAgent(agent));
|
|
17870
|
+
}
|
|
17871
|
+
/**
|
|
17872
|
+
* Idempotently seed the permanent owners that route built-in workflow stages.
|
|
17873
|
+
* Matching operator-created agents are intentionally not reused: provenance
|
|
17874
|
+
* makes upgrade repair deterministic while operators remain free to add pool
|
|
17875
|
+
* members with the same tag.
|
|
17876
|
+
*/
|
|
17877
|
+
async provisionBuiltinWorkflowRoleAgents() {
|
|
17878
|
+
const provision = async (executor) => {
|
|
17879
|
+
const existing = await this.listAgents({ includeEphemeral: true }, executor);
|
|
17880
|
+
const supportedRoles = new Set(BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULT_LIST.map(({ role }) => role));
|
|
17881
|
+
const groups = /* @__PURE__ */ new Map();
|
|
17882
|
+
for (const agent of existing) {
|
|
17883
|
+
const role = agent.metadata?.workflowRole;
|
|
17884
|
+
if (agent.metadata?.builtInWorkflowRole === true && typeof role === "string" && supportedRoles.has(role)) {
|
|
17885
|
+
const group = groups.get(role) ?? [];
|
|
17886
|
+
group.push(agent);
|
|
17887
|
+
groups.set(role, group);
|
|
17888
|
+
}
|
|
17889
|
+
}
|
|
17890
|
+
const winners = /* @__PURE__ */ new Map();
|
|
17891
|
+
for (const [role, group] of groups) {
|
|
17892
|
+
group.sort(compareBuiltinWorkflowOwnerAge);
|
|
17893
|
+
winners.set(role, group[0]);
|
|
17894
|
+
for (const loser of group.slice(1)) {
|
|
17895
|
+
const { builtInWorkflowRole: _builtIn, workflowRole: _role, ...metadata } = loser.metadata;
|
|
17896
|
+
await this.writeAgent({ ...loser, metadata, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, executor);
|
|
17897
|
+
}
|
|
17898
|
+
}
|
|
17899
|
+
const result = [];
|
|
17900
|
+
for (const definition of BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULT_LIST) {
|
|
17901
|
+
let agent = winners.get(definition.role);
|
|
17902
|
+
if (!agent) {
|
|
17903
|
+
agent = await this.createAgent({
|
|
17904
|
+
name: definition.name,
|
|
17905
|
+
roles: [definition.role],
|
|
17906
|
+
title: definition.title,
|
|
17907
|
+
metadata: { builtInWorkflowRole: true, workflowRole: definition.role },
|
|
17908
|
+
runtimeConfig: { enabled: false },
|
|
17909
|
+
instructionsText: definition.instructionsText,
|
|
17910
|
+
soul: definition.soul,
|
|
17911
|
+
bundleConfig: { ...BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG, files: [...BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG.files] }
|
|
17912
|
+
}, executor);
|
|
17913
|
+
} else {
|
|
17914
|
+
const canonicalOrPartialBundle = isCanonicalOrPartialBuiltinWorkflowBundle(agent.bundleConfig);
|
|
17915
|
+
const customInstructions = Boolean(agent.instructionsPath?.trim()) || agent.bundleConfig?.mode === "external" || agent.bundleConfig !== void 0 && !canonicalOrPartialBundle || Boolean(agent.instructionsText?.trim()) && agent.instructionsText !== definition.instructionsText;
|
|
17916
|
+
const updates = {};
|
|
17917
|
+
if (!customInstructions && !agent.instructionsText?.trim()) updates.instructionsText = definition.instructionsText;
|
|
17918
|
+
if (!agent.soul?.trim()) updates.soul = definition.soul;
|
|
17919
|
+
if (!customInstructions && !isCompleteBuiltinWorkflowBundle(agent.bundleConfig)) {
|
|
17920
|
+
updates.bundleConfig = { ...BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG, files: [...BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG.files] };
|
|
17921
|
+
}
|
|
17922
|
+
if (Object.keys(updates).length > 0) {
|
|
17923
|
+
agent = { ...agent, ...updates, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
17924
|
+
await this.writeAgent(agent, executor);
|
|
17925
|
+
}
|
|
17926
|
+
}
|
|
17927
|
+
result.push(agent);
|
|
17928
|
+
}
|
|
17929
|
+
return result;
|
|
17930
|
+
};
|
|
17931
|
+
if (!this.asyncLayer) {
|
|
17932
|
+
const agents3 = await provision();
|
|
17933
|
+
await Promise.all(agents3.map((agent) => this.materializeBuiltinWorkflowRoleBundle(agent)));
|
|
17934
|
+
return agents3;
|
|
17935
|
+
}
|
|
17936
|
+
const agents2 = await this.asyncLayer.transactionImmediate(async (tx) => {
|
|
17937
|
+
await tx.execute(sql`SELECT pg_advisory_xact_lock(hashtext(${this.backendProjectId}), hashtext('builtin-workflow-role-provisioning'))`);
|
|
17938
|
+
return provision(tx);
|
|
17633
17939
|
});
|
|
17634
|
-
|
|
17940
|
+
await Promise.all(agents2.map((agent) => this.materializeBuiltinWorkflowRoleBundle(agent)));
|
|
17941
|
+
return agents2;
|
|
17635
17942
|
}
|
|
17636
17943
|
/**
|
|
17637
17944
|
* Create an API key for an agent.
|
|
@@ -18199,6 +18506,29 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
18199
18506
|
}
|
|
18200
18507
|
return null;
|
|
18201
18508
|
}
|
|
18509
|
+
/** Seed only missing or blank default-owned mirror files after the provisioning transaction commits. */
|
|
18510
|
+
async materializeBuiltinWorkflowRoleBundle(agent) {
|
|
18511
|
+
return this.withLock(agent.id, async () => {
|
|
18512
|
+
const role = agent.metadata?.workflowRole;
|
|
18513
|
+
const definition = BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULT_LIST.find((candidate) => candidate.role === role);
|
|
18514
|
+
if (!definition || agent.metadata?.builtInWorkflowRole !== true) return;
|
|
18515
|
+
const config2 = agent.bundleConfig;
|
|
18516
|
+
if (!isCanonicalOrPartialBuiltinWorkflowBundle(config2)) return;
|
|
18517
|
+
if (agent.instructionsPath?.trim() || agent.instructionsText?.trim() && agent.instructionsText !== definition.instructionsText) return;
|
|
18518
|
+
const bundleDir = await this.resolveCompatibleBundleDir(agent.id, true);
|
|
18519
|
+
await mkdir(bundleDir, { recursive: true });
|
|
18520
|
+
for (const [file, content] of [["AGENTS.md", definition.instructionsText], ["soul.md", definition.soul]]) {
|
|
18521
|
+
const path = join(bundleDir, file);
|
|
18522
|
+
let current = "";
|
|
18523
|
+
try {
|
|
18524
|
+
current = await readFile(path, "utf-8");
|
|
18525
|
+
} catch (error) {
|
|
18526
|
+
if (error.code !== "ENOENT") throw error;
|
|
18527
|
+
}
|
|
18528
|
+
if (!current.trim()) await writeFile(path, content, "utf-8");
|
|
18529
|
+
}
|
|
18530
|
+
});
|
|
18531
|
+
}
|
|
18202
18532
|
getCanonicalBundleDir(agent) {
|
|
18203
18533
|
return join(this.agentsDir, getCanonicalAgentInstructionsBundleDirName(agent.name, agent.id));
|
|
18204
18534
|
}
|
|
@@ -18321,6 +18651,7 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
18321
18651
|
...data,
|
|
18322
18652
|
id: row.id,
|
|
18323
18653
|
name: row.name,
|
|
18654
|
+
roles: data.roles?.length ? data.roles : [row.role],
|
|
18324
18655
|
role: row.role,
|
|
18325
18656
|
state: row.state,
|
|
18326
18657
|
taskId: row.taskId ?? void 0,
|
|
@@ -18354,7 +18685,8 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
18354
18685
|
return {
|
|
18355
18686
|
id: data.id,
|
|
18356
18687
|
name: data.name,
|
|
18357
|
-
|
|
18688
|
+
roles: normalizeAgentRoles(data.roles, data.role),
|
|
18689
|
+
role: normalizeAgentRoles(data.roles, data.role)[0],
|
|
18358
18690
|
state: data.state,
|
|
18359
18691
|
taskId: data.taskId,
|
|
18360
18692
|
createdAt: data.createdAt,
|
|
@@ -18380,8 +18712,8 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
18380
18712
|
heartbeatProcedurePath: data.heartbeatProcedurePath
|
|
18381
18713
|
};
|
|
18382
18714
|
}
|
|
18383
|
-
async writeAgent(agent) {
|
|
18384
|
-
await writeAgent(this.asyncLayer.db, agent, this.asyncLayer.projectId);
|
|
18715
|
+
async writeAgent(agent, executor) {
|
|
18716
|
+
await writeAgent(executor ?? this.asyncLayer.db, agent, this.asyncLayer.projectId);
|
|
18385
18717
|
return;
|
|
18386
18718
|
}
|
|
18387
18719
|
/**
|