@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
|
@@ -5790,6 +5790,7 @@ __export(project_exports, {
|
|
|
5790
5790
|
unplannedExecutionBlocks: () => unplannedExecutionBlocks,
|
|
5791
5791
|
usageEvents: () => usageEvents,
|
|
5792
5792
|
verificationCache: () => verificationCache,
|
|
5793
|
+
workflowAgentCapacityLeases: () => workflowAgentCapacityLeases,
|
|
5793
5794
|
workflowPromptOverrides: () => workflowPromptOverrides,
|
|
5794
5795
|
workflowRunBranches: () => workflowRunBranches,
|
|
5795
5796
|
workflowRunStepInstances: () => workflowRunStepInstances,
|
|
@@ -5898,6 +5899,8 @@ var tasks = projectSchema.table("tasks", {
|
|
|
5898
5899
|
nextRecoveryAt: text("next_recovery_at"),
|
|
5899
5900
|
error: text("error"),
|
|
5900
5901
|
summary: text("summary"),
|
|
5902
|
+
// FNXC:TaskRecommendations 2026-08-08-05:02: structured completion suggestions remain project-scoped JSONB beside the source task.
|
|
5903
|
+
recommendations: jsonb("recommendations"),
|
|
5901
5904
|
thinkingLevel: text("thinking_level"),
|
|
5902
5905
|
// FNXC:Settings-ThinkingLevel 2026-07-13 (merge port): validator/planning reasoning-effort overrides.
|
|
5903
5906
|
validatorThinkingLevel: text("validator_thinking_level"),
|
|
@@ -6478,7 +6481,9 @@ var agents = projectSchema.table("agents", {
|
|
|
6478
6481
|
projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
|
|
6479
6482
|
id: text("id").notNull(),
|
|
6480
6483
|
name: text("name").notNull(),
|
|
6484
|
+
/** Deprecated singular compatibility column; canonical roles live in roles JSONB. */
|
|
6481
6485
|
role: text("role").notNull(),
|
|
6486
|
+
roles: jsonb("roles").notNull().default([]),
|
|
6482
6487
|
state: text("state").notNull().default("idle"),
|
|
6483
6488
|
taskId: text("task_id"),
|
|
6484
6489
|
createdAt: text("created_at").notNull(),
|
|
@@ -6602,6 +6607,17 @@ var completionHandoffMarkers = projectSchema.table("completion_handoff_markers",
|
|
|
6602
6607
|
foreignKey({ columns: [t.projectId, t.taskId], foreignColumns: [tasks.projectId, tasks.id] }).onDelete("cascade"),
|
|
6603
6608
|
index("idx_completion_handoff_markers_acceptedAt").on(t.acceptedAt)
|
|
6604
6609
|
]);
|
|
6610
|
+
var workflowAgentCapacityLeases = projectSchema.table("workflow_agent_capacity_leases", {
|
|
6611
|
+
projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
|
|
6612
|
+
attemptId: text("attempt_id").notNull(),
|
|
6613
|
+
agentId: text("agent_id").notNull(),
|
|
6614
|
+
createdAt: text("created_at").notNull(),
|
|
6615
|
+
// 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.
|
|
6616
|
+
expiresAt: text("expires_at").notNull()
|
|
6617
|
+
}, (t) => [
|
|
6618
|
+
primaryKey({ columns: [t.projectId, t.attemptId] }),
|
|
6619
|
+
index("idx_workflow_agent_capacity_leases_agent").on(t.projectId, t.agentId)
|
|
6620
|
+
]);
|
|
6605
6621
|
var workflowWorkItems = projectSchema.table("workflow_work_items", {
|
|
6606
6622
|
projectId: text("project_id").notNull().default(sql`current_setting('fusion.project_id', true)`),
|
|
6607
6623
|
id: text("id").notNull(),
|
|
@@ -6622,6 +6638,13 @@ var workflowWorkItems = projectSchema.table("workflow_work_items", {
|
|
|
6622
6638
|
sourceColumn: text("source_column"),
|
|
6623
6639
|
targetColumn: text("target_column"),
|
|
6624
6640
|
irHash: text("ir_hash"),
|
|
6641
|
+
// FNXC:WorkflowAgentRouting 2026-08-07-03:25:
|
|
6642
|
+
// A workflow claim fences its durable principal and exact template instance.
|
|
6643
|
+
// Session retries must reuse this identity rather than re-routing a task.
|
|
6644
|
+
principalAgentId: text("principal_agent_id"),
|
|
6645
|
+
workflowRole: text("workflow_role"),
|
|
6646
|
+
authorityKind: text("authority_kind"),
|
|
6647
|
+
nodeInstanceId: text("node_instance_id"),
|
|
6625
6648
|
createdAt: text("created_at").notNull(),
|
|
6626
6649
|
updatedAt: text("updated_at").notNull()
|
|
6627
6650
|
}, (t) => [
|
|
@@ -8709,6 +8732,7 @@ var BLOCKING_TASK_STATUSES = /* @__PURE__ */ new Set([
|
|
|
8709
8732
|
// ../core/src/tasks/in-review-stall.ts
|
|
8710
8733
|
var DEFAULT_STALE_MERGING_MIN_AGE_MS = 5 * 6e4;
|
|
8711
8734
|
var DEFAULT_MAX_AUTO_MERGE_RETRIES = 3;
|
|
8735
|
+
var CONSECUTIVE_TOOL_FAILURE_RETRY_THRESHOLD = 1;
|
|
8712
8736
|
|
|
8713
8737
|
// ../core/src/config/settings-schema.ts
|
|
8714
8738
|
var DEFAULT_GLOBAL_SETTINGS = {
|
|
@@ -9049,6 +9073,8 @@ var DEFAULT_GLOBAL_SETTINGS = {
|
|
|
9049
9073
|
cliAgents: {}
|
|
9050
9074
|
};
|
|
9051
9075
|
var DEFAULT_PROJECT_SETTINGS = {
|
|
9076
|
+
// FNXC:TaskRecommendations 2026-08-08-05:02: completion follows-ups stay bounded by default; 0 disables writing them.
|
|
9077
|
+
maxRecommendationsPerTask: 3,
|
|
9052
9078
|
globalPause: false,
|
|
9053
9079
|
globalPauseReason: void 0,
|
|
9054
9080
|
defaultWorkflowId: void 0,
|
|
@@ -9217,9 +9243,15 @@ var DEFAULT_PROJECT_SETTINGS = {
|
|
|
9217
9243
|
* 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.
|
|
9218
9244
|
*/
|
|
9219
9245
|
maxAutoMergeRetries: DEFAULT_MAX_AUTO_MERGE_RETRIES,
|
|
9246
|
+
/*
|
|
9247
|
+
FNXC:ExecutorToolFailureRetry 2026-08-06-14:56:
|
|
9248
|
+
Fresh projects retry the existing bounded same-model continuation after one
|
|
9249
|
+
terminal tool error. Persisted project values are not migrated, so explicit
|
|
9250
|
+
operator thresholds remain authoritative.
|
|
9251
|
+
*/
|
|
9220
9252
|
executorToolFailureRetryCount: 2,
|
|
9221
9253
|
executorToolFailureRetryBackoffMs: 2e3,
|
|
9222
|
-
executorToolFailureThreshold:
|
|
9254
|
+
executorToolFailureThreshold: CONSECUTIVE_TOOL_FAILURE_RETRY_THRESHOLD,
|
|
9223
9255
|
executorModelEscalationEnabled: false,
|
|
9224
9256
|
executorEscalationProvider: void 0,
|
|
9225
9257
|
executorEscalationModelId: void 0,
|
|
@@ -9255,6 +9287,12 @@ var DEFAULT_PROJECT_SETTINGS = {
|
|
|
9255
9287
|
// proportional to the change; the thin merge gate carries cross-cutting
|
|
9256
9288
|
// coverage. Falls back to package/explicit command when no tests resolve.
|
|
9257
9289
|
scopeVerificationToChangedFiles: true,
|
|
9290
|
+
/*
|
|
9291
|
+
FNXC:WorkflowAgentRouting 2026-08-07-08:45:
|
|
9292
|
+
Keep the legacy project setting defaulted and persistable for existing clients and
|
|
9293
|
+
configuration records. Workflow-stage routing must ignore its value; durable role
|
|
9294
|
+
principals own scheduler, executor, and mission-stage authority.
|
|
9295
|
+
*/
|
|
9258
9296
|
ephemeralAgentsEnabled: true,
|
|
9259
9297
|
/*
|
|
9260
9298
|
FNXC:EphemeralAgentTaskCreation 2026-07-01-00:00:
|
|
@@ -9556,6 +9594,23 @@ function isEphemeralAgent(agent) {
|
|
|
9556
9594
|
}
|
|
9557
9595
|
|
|
9558
9596
|
// ../core/src/types/agents/agents.ts
|
|
9597
|
+
var AGENT_CAPABILITIES = [
|
|
9598
|
+
"triage",
|
|
9599
|
+
"executor",
|
|
9600
|
+
"reviewer",
|
|
9601
|
+
"merger",
|
|
9602
|
+
"scheduler",
|
|
9603
|
+
"engineer",
|
|
9604
|
+
"custom"
|
|
9605
|
+
];
|
|
9606
|
+
function normalizeAgentRoles(roles, legacyRole) {
|
|
9607
|
+
const values = roles?.length ? roles : legacyRole ? [legacyRole] : [];
|
|
9608
|
+
const unique2 = new Set(values);
|
|
9609
|
+
const normalized = AGENT_CAPABILITIES.filter((role) => unique2.has(role));
|
|
9610
|
+
if (normalized.length !== unique2.size) throw new Error("Agent roles contain an unknown capability");
|
|
9611
|
+
if (normalized.length === 0) throw new Error("Agent requires at least one role");
|
|
9612
|
+
return normalized;
|
|
9613
|
+
}
|
|
9559
9614
|
var AGENT_PERMISSIONS = [
|
|
9560
9615
|
"tasks:assign",
|
|
9561
9616
|
// Assign tasks to agents
|
|
@@ -9677,6 +9732,7 @@ function getDefaultHeartbeatProcedurePath(agentId, agentName) {
|
|
|
9677
9732
|
function agentToConfigSnapshot(agent) {
|
|
9678
9733
|
return {
|
|
9679
9734
|
name: agent.name,
|
|
9735
|
+
roles: [...agent.roles],
|
|
9680
9736
|
role: agent.role,
|
|
9681
9737
|
title: agent.title,
|
|
9682
9738
|
icon: agent.icon,
|
|
@@ -10011,6 +10067,41 @@ function resolveColumnFlags(column) {
|
|
|
10011
10067
|
return getTraitRegistry().resolveColumnFlags(column);
|
|
10012
10068
|
}
|
|
10013
10069
|
|
|
10070
|
+
// ../core/src/workflows/workflow-ir-types.ts
|
|
10071
|
+
var WORKFLOW_AGENT_ROLES = ["triage", "executor", "reviewer", "merger"];
|
|
10072
|
+
function isWorkflowAgentRole(value) {
|
|
10073
|
+
return typeof value === "string" && WORKFLOW_AGENT_ROLES.includes(value);
|
|
10074
|
+
}
|
|
10075
|
+
function classifyWorkflowAgentNode(node) {
|
|
10076
|
+
if (node.kind === "step-review") return "reviewer";
|
|
10077
|
+
if (node.kind !== "prompt" && node.kind !== "script") return void 0;
|
|
10078
|
+
switch (node.config?.seam) {
|
|
10079
|
+
/*
|
|
10080
|
+
* FNXC:WorkflowAgentRouting 2026-08-07-05:52:
|
|
10081
|
+
* Planning, independent review, and promotion seams each launch work under
|
|
10082
|
+
* their permanent stage owner. Classify those production seams here rather
|
|
10083
|
+
* than treating their handoff worker as an unowned lifecycle operation.
|
|
10084
|
+
* Only pure handoff/scheduling seams remain principal-free.
|
|
10085
|
+
*/
|
|
10086
|
+
case "planning":
|
|
10087
|
+
return "triage";
|
|
10088
|
+
case "execute":
|
|
10089
|
+
case "step-execute":
|
|
10090
|
+
return "executor";
|
|
10091
|
+
case "review":
|
|
10092
|
+
return "reviewer";
|
|
10093
|
+
case "merge":
|
|
10094
|
+
return "merger";
|
|
10095
|
+
case "review-handoff":
|
|
10096
|
+
case "schedule":
|
|
10097
|
+
return void 0;
|
|
10098
|
+
default: {
|
|
10099
|
+
const explicit = node.config?.workflowRole;
|
|
10100
|
+
return isWorkflowAgentRole(explicit) ? explicit : void 0;
|
|
10101
|
+
}
|
|
10102
|
+
}
|
|
10103
|
+
}
|
|
10104
|
+
|
|
10014
10105
|
// ../core/src/workflows/workflow-extension-types.ts
|
|
10015
10106
|
function workflowExtensionRegistryId(pluginId, extensionId) {
|
|
10016
10107
|
return `plugin:${pluginId}:${extensionId}`;
|
|
@@ -10999,6 +11090,20 @@ function validateCredentialInstanceIdConfig(nodes2) {
|
|
|
10999
11090
|
if (Array.isArray(templateNodes)) validateCredentialInstanceIdConfig(templateNodes);
|
|
11000
11091
|
}
|
|
11001
11092
|
}
|
|
11093
|
+
function validateReviewerAgentOverrides(nodes2) {
|
|
11094
|
+
for (const node of nodes2) {
|
|
11095
|
+
if (node.reviewerAgentId !== void 0) {
|
|
11096
|
+
if (typeof node.reviewerAgentId !== "string" || !node.reviewerAgentId.trim()) {
|
|
11097
|
+
throw new WorkflowIrError(`Workflow node '${node.id}' reviewerAgentId must be a non-empty string`);
|
|
11098
|
+
}
|
|
11099
|
+
if (classifyWorkflowAgentNode(node) !== "reviewer") {
|
|
11100
|
+
throw new WorkflowIrError(`Workflow node '${node.id}' reviewerAgentId is only legal on reviewer-session nodes`);
|
|
11101
|
+
}
|
|
11102
|
+
}
|
|
11103
|
+
const templateNodes = node.config?.template?.nodes;
|
|
11104
|
+
if (Array.isArray(templateNodes)) validateReviewerAgentOverrides(templateNodes);
|
|
11105
|
+
}
|
|
11106
|
+
}
|
|
11002
11107
|
function validateThinkingLevelConfig(nodes2) {
|
|
11003
11108
|
for (const node of nodes2) {
|
|
11004
11109
|
const value = node.config?.thinkingLevel;
|
|
@@ -11614,6 +11719,7 @@ function validateV2(ir) {
|
|
|
11614
11719
|
validateStepExecutePlacement(ir.nodes);
|
|
11615
11720
|
validateThinkingLevelConfig(ir.nodes);
|
|
11616
11721
|
validateCredentialInstanceIdConfig(ir.nodes);
|
|
11722
|
+
validateReviewerAgentOverrides(ir.nodes);
|
|
11617
11723
|
for (const node of ir.nodes) {
|
|
11618
11724
|
if (node.kind === "foreach") validateForeach(node, topLevelIds, columnIds);
|
|
11619
11725
|
if (node.kind === "loop") validateLoop(node, topLevelIds, columnIds);
|
|
@@ -15847,7 +15953,8 @@ function normalizePermissions(raw) {
|
|
|
15847
15953
|
return permissions;
|
|
15848
15954
|
}
|
|
15849
15955
|
function computeAccessState(agent) {
|
|
15850
|
-
const
|
|
15956
|
+
const roles = agent.roles?.length ? agent.roles : [agent.role];
|
|
15957
|
+
const roleDefaultPermissions = new Set(roles.flatMap((role) => ROLE_DEFAULT_PERMISSIONS[role] ?? []));
|
|
15851
15958
|
const explicitPermissions = normalizePermissions(agent.permissions ?? {});
|
|
15852
15959
|
const resolvedPermissions = new Set(roleDefaultPermissions);
|
|
15853
15960
|
for (const permission of explicitPermissions) {
|
|
@@ -15878,6 +15985,9 @@ var IMPLEMENTATION_TASK_COLUMNS = /* @__PURE__ */ new Set([
|
|
|
15878
15985
|
"in-progress",
|
|
15879
15986
|
"in-review"
|
|
15880
15987
|
]);
|
|
15988
|
+
function agentRoles(agent) {
|
|
15989
|
+
return agent.roles?.length ? agent.roles : agent.role ? [agent.role] : [];
|
|
15990
|
+
}
|
|
15881
15991
|
function getAgentAssignmentPolicy(agent) {
|
|
15882
15992
|
const raw = (agent.runtimeConfig ?? {})["assignmentPolicy"];
|
|
15883
15993
|
return raw === "explicit-only" || raw === "none" ? raw : "auto";
|
|
@@ -15892,10 +16002,10 @@ function isImplementationTask(task) {
|
|
|
15892
16002
|
return IMPLEMENTATION_TASK_COLUMNS.has(task.column);
|
|
15893
16003
|
}
|
|
15894
16004
|
function isExecutorRoleAgent(agent) {
|
|
15895
|
-
return agent.
|
|
16005
|
+
return agentRoles(agent).includes("executor");
|
|
15896
16006
|
}
|
|
15897
16007
|
function isEngineerRoleAgent(agent) {
|
|
15898
|
-
return agent.
|
|
16008
|
+
return agentRoles(agent).includes("engineer");
|
|
15899
16009
|
}
|
|
15900
16010
|
function canAgentTakeImplementationTaskForExplicitRouting(agent, task) {
|
|
15901
16011
|
if (!isImplementationTask(task)) return true;
|
|
@@ -15944,7 +16054,8 @@ function formatRoleMismatchReason(agent, task) {
|
|
|
15944
16054
|
if (policy !== "auto") {
|
|
15945
16055
|
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"}.`;
|
|
15946
16056
|
}
|
|
15947
|
-
|
|
16057
|
+
const roles = agentRoles(agent);
|
|
16058
|
+
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.`;
|
|
15948
16059
|
}
|
|
15949
16060
|
|
|
15950
16061
|
// ../core/src/agents/agent-permission-policy.ts
|
|
@@ -16066,6 +16177,7 @@ var agentColumns = {
|
|
|
16066
16177
|
id: project_exports.agents.id,
|
|
16067
16178
|
name: project_exports.agents.name,
|
|
16068
16179
|
role: project_exports.agents.role,
|
|
16180
|
+
roles: project_exports.agents.roles,
|
|
16069
16181
|
state: project_exports.agents.state,
|
|
16070
16182
|
taskId: project_exports.agents.taskId,
|
|
16071
16183
|
createdAt: project_exports.agents.createdAt,
|
|
@@ -16096,6 +16208,7 @@ function agentToData(agent) {
|
|
|
16096
16208
|
return {
|
|
16097
16209
|
id: agent.id,
|
|
16098
16210
|
name: agent.name,
|
|
16211
|
+
roles: agent.roles,
|
|
16099
16212
|
role: agent.role,
|
|
16100
16213
|
state: agent.state,
|
|
16101
16214
|
taskId: agent.taskId,
|
|
@@ -16130,6 +16243,7 @@ async function writeAgent(handle, agent, projectId) {
|
|
|
16130
16243
|
id: agent.id,
|
|
16131
16244
|
name: agent.name,
|
|
16132
16245
|
role: agent.role,
|
|
16246
|
+
roles: agent.roles,
|
|
16133
16247
|
state: agent.state,
|
|
16134
16248
|
taskId: agent.taskId ?? null,
|
|
16135
16249
|
createdAt: agent.createdAt,
|
|
@@ -16142,6 +16256,7 @@ async function writeAgent(handle, agent, projectId) {
|
|
|
16142
16256
|
set: {
|
|
16143
16257
|
name: agent.name,
|
|
16144
16258
|
role: agent.role,
|
|
16259
|
+
roles: agent.roles,
|
|
16145
16260
|
state: agent.state,
|
|
16146
16261
|
taskId: agent.taskId ?? null,
|
|
16147
16262
|
updatedAt: agent.updatedAt,
|
|
@@ -16163,6 +16278,7 @@ function mergeAgentRow(row) {
|
|
|
16163
16278
|
...data,
|
|
16164
16279
|
id: row.id,
|
|
16165
16280
|
name: row.name,
|
|
16281
|
+
roles: row.roles?.length ? row.roles : [row.role],
|
|
16166
16282
|
role: row.role,
|
|
16167
16283
|
state: row.state,
|
|
16168
16284
|
taskId: row.taskId ?? void 0,
|
|
@@ -16492,8 +16608,62 @@ var FsWatchPollController = class {
|
|
|
16492
16608
|
}
|
|
16493
16609
|
};
|
|
16494
16610
|
|
|
16611
|
+
// ../core/src/agents/workflow-role-agent-defaults.ts
|
|
16612
|
+
var BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG = {
|
|
16613
|
+
mode: "managed",
|
|
16614
|
+
entryFile: "AGENTS.md",
|
|
16615
|
+
files: ["AGENTS.md", "soul.md"]
|
|
16616
|
+
};
|
|
16617
|
+
var BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULTS = {
|
|
16618
|
+
triage: {
|
|
16619
|
+
role: "triage",
|
|
16620
|
+
name: "Workflow Planner",
|
|
16621
|
+
title: "Built-in workflow planning owner",
|
|
16622
|
+
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.",
|
|
16623
|
+
soul: "Calm, structured, and curious. You reduce ambiguity without adding ceremony, respect operator intent, and prefer concrete evidence over confident guesses."
|
|
16624
|
+
},
|
|
16625
|
+
executor: {
|
|
16626
|
+
role: "executor",
|
|
16627
|
+
name: "Workflow Executor",
|
|
16628
|
+
title: "Built-in workflow execution owner",
|
|
16629
|
+
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.",
|
|
16630
|
+
soul: "Methodical, practical, and accountable. You favor small safe steps, protect user work, and let tests and observable behavior guide your confidence."
|
|
16631
|
+
},
|
|
16632
|
+
reviewer: {
|
|
16633
|
+
role: "reviewer",
|
|
16634
|
+
name: "Workflow Reviewer",
|
|
16635
|
+
title: "Built-in workflow review owner",
|
|
16636
|
+
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.",
|
|
16637
|
+
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."
|
|
16638
|
+
},
|
|
16639
|
+
merger: {
|
|
16640
|
+
role: "merger",
|
|
16641
|
+
name: "Workflow Merger",
|
|
16642
|
+
title: "Built-in workflow merge owner",
|
|
16643
|
+
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.",
|
|
16644
|
+
soul: "Deliberate, conservative, and transparent. You optimize for repository integrity, treat uncertainty as a reason to stop, and communicate operational state plainly."
|
|
16645
|
+
}
|
|
16646
|
+
};
|
|
16647
|
+
var BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULT_LIST = Object.values(BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULTS);
|
|
16648
|
+
|
|
16495
16649
|
// ../core/src/agents/agent-store.ts
|
|
16496
16650
|
var agentStoreLog = createLogger("agent-store");
|
|
16651
|
+
function isCanonicalOrPartialBuiltinWorkflowBundle(config2) {
|
|
16652
|
+
if (config2?.mode !== "managed" || config2.entryFile !== "AGENTS.md") return false;
|
|
16653
|
+
return config2.files.every((file) => (file === "AGENTS.md" || file === "soul.md") && config2.files.indexOf(file) === config2.files.lastIndexOf(file));
|
|
16654
|
+
}
|
|
16655
|
+
function isCompleteBuiltinWorkflowBundle(config2) {
|
|
16656
|
+
return isCanonicalOrPartialBuiltinWorkflowBundle(config2) && config2.files.length === BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG.files.length;
|
|
16657
|
+
}
|
|
16658
|
+
function compareBuiltinWorkflowOwnerAge(a, b) {
|
|
16659
|
+
const aTime = Date.parse(a.createdAt);
|
|
16660
|
+
const bTime = Date.parse(b.createdAt);
|
|
16661
|
+
const aValid = Number.isFinite(aTime);
|
|
16662
|
+
const bValid = Number.isFinite(bTime);
|
|
16663
|
+
if (aValid && bValid && aTime !== bTime) return aTime - bTime;
|
|
16664
|
+
if (aValid !== bValid) return aValid ? -1 : 1;
|
|
16665
|
+
return a.id.localeCompare(b.id);
|
|
16666
|
+
}
|
|
16497
16667
|
var DEFAULT_AGENT_HEARTBEAT_INTERVAL_MS = 36e5;
|
|
16498
16668
|
function normalizeStoredPermissions(raw) {
|
|
16499
16669
|
if (!raw) return void 0;
|
|
@@ -16604,6 +16774,73 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
16604
16774
|
}
|
|
16605
16775
|
return projectId;
|
|
16606
16776
|
}
|
|
16777
|
+
/** The durable project partition used by workflow routing and capacity leases. */
|
|
16778
|
+
get workflowProjectId() {
|
|
16779
|
+
return this.asyncLayer?.projectId;
|
|
16780
|
+
}
|
|
16781
|
+
/**
|
|
16782
|
+
* FNXC:WorkflowAgentRouting 2026-08-07-05:52:
|
|
16783
|
+
* Workflow capacity is a PostgreSQL lease rather than a process-local count.
|
|
16784
|
+
* The project advisory lock serializes project then agent admission across
|
|
16785
|
+
* engine processes; model work starts only after this short transaction ends.
|
|
16786
|
+
*/
|
|
16787
|
+
async acquireWorkflowSessionCapacity(input) {
|
|
16788
|
+
if (!this.asyncLayer) return "acquired";
|
|
16789
|
+
const projectId = this.backendProjectId;
|
|
16790
|
+
const now = /* @__PURE__ */ new Date();
|
|
16791
|
+
const expiresAt = new Date(now.getTime() + (input.leaseDurationMs ?? 10 * 6e4)).toISOString();
|
|
16792
|
+
return this.asyncLayer.transactionImmediate(async (tx) => {
|
|
16793
|
+
await tx.execute(sql`SELECT pg_advisory_xact_lock(hashtext(${projectId}), hashtext('workflow-agent-capacity'))`);
|
|
16794
|
+
await tx.delete(project_exports.workflowAgentCapacityLeases).where(and(
|
|
16795
|
+
eq(project_exports.workflowAgentCapacityLeases.projectId, projectId),
|
|
16796
|
+
lte(project_exports.workflowAgentCapacityLeases.expiresAt, now.toISOString())
|
|
16797
|
+
));
|
|
16798
|
+
const existing = await tx.select({ attemptId: project_exports.workflowAgentCapacityLeases.attemptId }).from(project_exports.workflowAgentCapacityLeases).where(and(
|
|
16799
|
+
eq(project_exports.workflowAgentCapacityLeases.projectId, projectId),
|
|
16800
|
+
eq(project_exports.workflowAgentCapacityLeases.attemptId, input.attemptId)
|
|
16801
|
+
)).limit(1);
|
|
16802
|
+
if (existing[0]) return "acquired";
|
|
16803
|
+
const projectCount = await tx.select({ count: sql`count(*)::int` }).from(project_exports.workflowAgentCapacityLeases).where(eq(project_exports.workflowAgentCapacityLeases.projectId, projectId));
|
|
16804
|
+
if (input.maxProjectSessions !== void 0 && (projectCount[0]?.count ?? 0) >= input.maxProjectSessions) return "project-capacity";
|
|
16805
|
+
const agentCount = await tx.select({ count: sql`count(*)::int` }).from(project_exports.workflowAgentCapacityLeases).where(and(
|
|
16806
|
+
eq(project_exports.workflowAgentCapacityLeases.projectId, projectId),
|
|
16807
|
+
eq(project_exports.workflowAgentCapacityLeases.agentId, input.agentId)
|
|
16808
|
+
));
|
|
16809
|
+
if (input.maxAgentSessions !== void 0 && (agentCount[0]?.count ?? 0) >= input.maxAgentSessions) return "agent-capacity";
|
|
16810
|
+
await tx.insert(project_exports.workflowAgentCapacityLeases).values({
|
|
16811
|
+
projectId,
|
|
16812
|
+
agentId: input.agentId,
|
|
16813
|
+
attemptId: input.attemptId,
|
|
16814
|
+
createdAt: now.toISOString(),
|
|
16815
|
+
expiresAt
|
|
16816
|
+
});
|
|
16817
|
+
return "acquired";
|
|
16818
|
+
});
|
|
16819
|
+
}
|
|
16820
|
+
/** Renew only the caller's project-scoped attempt; a reclaimed lease never resurrects. */
|
|
16821
|
+
async renewWorkflowSessionCapacity(attemptId, leaseDurationMs = 10 * 6e4) {
|
|
16822
|
+
if (!this.asyncLayer) return true;
|
|
16823
|
+
const now = /* @__PURE__ */ new Date();
|
|
16824
|
+
const result = await this.asyncLayer.db.update(project_exports.workflowAgentCapacityLeases).set({ expiresAt: new Date(now.getTime() + leaseDurationMs).toISOString() }).where(and(
|
|
16825
|
+
eq(project_exports.workflowAgentCapacityLeases.projectId, this.backendProjectId),
|
|
16826
|
+
eq(project_exports.workflowAgentCapacityLeases.attemptId, attemptId),
|
|
16827
|
+
/*
|
|
16828
|
+
* FNXC:WorkflowAgentRouting 2026-08-07-07:30:
|
|
16829
|
+
* Renew a currently live lease only. An expired row may already have
|
|
16830
|
+
* been reclaimed by another engine's admission transaction.
|
|
16831
|
+
*/
|
|
16832
|
+
gt(project_exports.workflowAgentCapacityLeases.expiresAt, now.toISOString())
|
|
16833
|
+
)).returning({ attemptId: project_exports.workflowAgentCapacityLeases.attemptId });
|
|
16834
|
+
return result.length > 0;
|
|
16835
|
+
}
|
|
16836
|
+
/** Release is project-scoped and idempotent so cancellation and recovery may race safely. */
|
|
16837
|
+
async releaseWorkflowSessionCapacity(attemptId) {
|
|
16838
|
+
if (!this.asyncLayer) return;
|
|
16839
|
+
await this.asyncLayer.db.delete(project_exports.workflowAgentCapacityLeases).where(and(
|
|
16840
|
+
eq(project_exports.workflowAgentCapacityLeases.projectId, this.backendProjectId),
|
|
16841
|
+
eq(project_exports.workflowAgentCapacityLeases.attemptId, attemptId)
|
|
16842
|
+
));
|
|
16843
|
+
}
|
|
16607
16844
|
get db() {
|
|
16608
16845
|
throw new Error("SQLite Database is not available in backend mode (asyncLayer injected)");
|
|
16609
16846
|
}
|
|
@@ -16618,7 +16855,7 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
16618
16855
|
*/
|
|
16619
16856
|
async init() {
|
|
16620
16857
|
await mkdir(this.agentsDir, { recursive: true });
|
|
16621
|
-
|
|
16858
|
+
await this.provisionBuiltinWorkflowRoleAgents();
|
|
16622
16859
|
}
|
|
16623
16860
|
/**
|
|
16624
16861
|
* One-shot migration that re-points every non-ephemeral agent off the
|
|
@@ -16780,8 +17017,8 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
16780
17017
|
* @param name - Agent name to match exactly
|
|
16781
17018
|
* @returns Matching non-ephemeral agent, or null when none exists
|
|
16782
17019
|
*/
|
|
16783
|
-
async findAgentByName(name) {
|
|
16784
|
-
const agents2 = await findAgentRowsByName(this.asyncLayer.db, name);
|
|
17020
|
+
async findAgentByName(name, executor) {
|
|
17021
|
+
const agents2 = await findAgentRowsByName(executor ?? this.asyncLayer.db, name);
|
|
16785
17022
|
for (const agent of agents2) {
|
|
16786
17023
|
if (!isEphemeralAgent(agent)) {
|
|
16787
17024
|
return this.parseAgent(agent);
|
|
@@ -16816,18 +17053,16 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
16816
17053
|
* @returns The created agent
|
|
16817
17054
|
* @throws Error if input is invalid or a duplicate non-ephemeral name exists
|
|
16818
17055
|
*/
|
|
16819
|
-
async createAgent(input) {
|
|
17056
|
+
async createAgent(input, executor) {
|
|
16820
17057
|
if (!input.name?.trim()) {
|
|
16821
17058
|
throw new Error("Agent name is required");
|
|
16822
17059
|
}
|
|
16823
|
-
|
|
16824
|
-
throw new Error("Agent role is required");
|
|
16825
|
-
}
|
|
17060
|
+
const roles = normalizeAgentRoles(input.roles, input.role);
|
|
16826
17061
|
const normalizedName = input.name.trim();
|
|
16827
17062
|
const metadata = input.metadata ?? {};
|
|
16828
|
-
const ephemeral = isEphemeralAgent({ metadata, name: input.name, role:
|
|
17063
|
+
const ephemeral = isEphemeralAgent({ metadata, name: input.name, role: roles[0], reportsTo: input.reportsTo });
|
|
16829
17064
|
if (!ephemeral) {
|
|
16830
|
-
const existing = await this.findAgentByName(normalizedName);
|
|
17065
|
+
const existing = await this.findAgentByName(normalizedName, executor);
|
|
16831
17066
|
if (existing) {
|
|
16832
17067
|
throw new Error(`Agent with name "${normalizedName}" already exists (agentId: ${existing.id})`);
|
|
16833
17068
|
}
|
|
@@ -16840,7 +17075,8 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
16840
17075
|
const agent = {
|
|
16841
17076
|
id: agentId,
|
|
16842
17077
|
name: normalizedName,
|
|
16843
|
-
|
|
17078
|
+
roles,
|
|
17079
|
+
role: roles[0],
|
|
16844
17080
|
// Non-ephemeral agents start active so they immediately participate in
|
|
16845
17081
|
// heartbeat scheduling; ephemeral/task-worker agents start idle and are
|
|
16846
17082
|
// activated by the engine when work is assigned.
|
|
@@ -16862,7 +17098,7 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
16862
17098
|
...input.bundleConfig && { bundleConfig: input.bundleConfig },
|
|
16863
17099
|
...resolvedHeartbeatProcedurePath && { heartbeatProcedurePath: resolvedHeartbeatProcedurePath }
|
|
16864
17100
|
};
|
|
16865
|
-
await this.writeAgent(agent);
|
|
17101
|
+
await this.writeAgent(agent, executor);
|
|
16866
17102
|
this.emit("agent:created", agent);
|
|
16867
17103
|
return agent;
|
|
16868
17104
|
}
|
|
@@ -17205,10 +17441,12 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
17205
17441
|
const beforeSnapshot = agentToConfigSnapshot(agent);
|
|
17206
17442
|
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
17207
17443
|
const normalizedUpdatedPermissionPolicy = "permissionPolicy" in updates && updates.permissionPolicy !== void 0 ? normalizeAgentPermissionPolicy(updates.permissionPolicy) : updates.permissionPolicy;
|
|
17444
|
+
const roles = updates.roles !== void 0 || updates.role !== void 0 ? normalizeAgentRoles(updates.roles, updates.role) : agent.roles;
|
|
17208
17445
|
const updated = {
|
|
17209
17446
|
...agent,
|
|
17210
17447
|
name: nextName ?? agent.name,
|
|
17211
|
-
|
|
17448
|
+
roles,
|
|
17449
|
+
role: roles[0],
|
|
17212
17450
|
metadata: updates.metadata !== void 0 ? updates.metadata : agent.metadata,
|
|
17213
17451
|
updatedAt,
|
|
17214
17452
|
..."title" in updates && { title: updates.title },
|
|
@@ -17743,12 +17981,81 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
17743
17981
|
* @param filter - Optional filter criteria
|
|
17744
17982
|
* @returns Array of agents
|
|
17745
17983
|
*/
|
|
17746
|
-
async listAgents(filter) {
|
|
17747
|
-
const agents2 = await listAgentRows(this.asyncLayer.db, {
|
|
17748
|
-
|
|
17749
|
-
|
|
17984
|
+
async listAgents(filter, executor) {
|
|
17985
|
+
const agents2 = await listAgentRows(executor ?? this.asyncLayer.db, { state: filter?.state });
|
|
17986
|
+
return agents2.map((a) => this.parseAgent(a)).filter((agent) => !filter?.role || agent.roles.includes(filter.role)).filter((agent) => filter?.includeEphemeral === true || !isEphemeralAgent(agent));
|
|
17987
|
+
}
|
|
17988
|
+
/**
|
|
17989
|
+
* Idempotently seed the permanent owners that route built-in workflow stages.
|
|
17990
|
+
* Matching operator-created agents are intentionally not reused: provenance
|
|
17991
|
+
* makes upgrade repair deterministic while operators remain free to add pool
|
|
17992
|
+
* members with the same tag.
|
|
17993
|
+
*/
|
|
17994
|
+
async provisionBuiltinWorkflowRoleAgents() {
|
|
17995
|
+
const provision = async (executor) => {
|
|
17996
|
+
const existing = await this.listAgents({ includeEphemeral: true }, executor);
|
|
17997
|
+
const supportedRoles = new Set(BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULT_LIST.map(({ role }) => role));
|
|
17998
|
+
const groups = /* @__PURE__ */ new Map();
|
|
17999
|
+
for (const agent of existing) {
|
|
18000
|
+
const role = agent.metadata?.workflowRole;
|
|
18001
|
+
if (agent.metadata?.builtInWorkflowRole === true && typeof role === "string" && supportedRoles.has(role)) {
|
|
18002
|
+
const group = groups.get(role) ?? [];
|
|
18003
|
+
group.push(agent);
|
|
18004
|
+
groups.set(role, group);
|
|
18005
|
+
}
|
|
18006
|
+
}
|
|
18007
|
+
const winners = /* @__PURE__ */ new Map();
|
|
18008
|
+
for (const [role, group] of groups) {
|
|
18009
|
+
group.sort(compareBuiltinWorkflowOwnerAge);
|
|
18010
|
+
winners.set(role, group[0]);
|
|
18011
|
+
for (const loser of group.slice(1)) {
|
|
18012
|
+
const { builtInWorkflowRole: _builtIn, workflowRole: _role, ...metadata } = loser.metadata;
|
|
18013
|
+
await this.writeAgent({ ...loser, metadata, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, executor);
|
|
18014
|
+
}
|
|
18015
|
+
}
|
|
18016
|
+
const result = [];
|
|
18017
|
+
for (const definition of BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULT_LIST) {
|
|
18018
|
+
let agent = winners.get(definition.role);
|
|
18019
|
+
if (!agent) {
|
|
18020
|
+
agent = await this.createAgent({
|
|
18021
|
+
name: definition.name,
|
|
18022
|
+
roles: [definition.role],
|
|
18023
|
+
title: definition.title,
|
|
18024
|
+
metadata: { builtInWorkflowRole: true, workflowRole: definition.role },
|
|
18025
|
+
runtimeConfig: { enabled: false },
|
|
18026
|
+
instructionsText: definition.instructionsText,
|
|
18027
|
+
soul: definition.soul,
|
|
18028
|
+
bundleConfig: { ...BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG, files: [...BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG.files] }
|
|
18029
|
+
}, executor);
|
|
18030
|
+
} else {
|
|
18031
|
+
const canonicalOrPartialBundle = isCanonicalOrPartialBuiltinWorkflowBundle(agent.bundleConfig);
|
|
18032
|
+
const customInstructions = Boolean(agent.instructionsPath?.trim()) || agent.bundleConfig?.mode === "external" || agent.bundleConfig !== void 0 && !canonicalOrPartialBundle || Boolean(agent.instructionsText?.trim()) && agent.instructionsText !== definition.instructionsText;
|
|
18033
|
+
const updates = {};
|
|
18034
|
+
if (!customInstructions && !agent.instructionsText?.trim()) updates.instructionsText = definition.instructionsText;
|
|
18035
|
+
if (!agent.soul?.trim()) updates.soul = definition.soul;
|
|
18036
|
+
if (!customInstructions && !isCompleteBuiltinWorkflowBundle(agent.bundleConfig)) {
|
|
18037
|
+
updates.bundleConfig = { ...BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG, files: [...BUILTIN_WORKFLOW_AGENT_BUNDLE_CONFIG.files] };
|
|
18038
|
+
}
|
|
18039
|
+
if (Object.keys(updates).length > 0) {
|
|
18040
|
+
agent = { ...agent, ...updates, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
18041
|
+
await this.writeAgent(agent, executor);
|
|
18042
|
+
}
|
|
18043
|
+
}
|
|
18044
|
+
result.push(agent);
|
|
18045
|
+
}
|
|
18046
|
+
return result;
|
|
18047
|
+
};
|
|
18048
|
+
if (!this.asyncLayer) {
|
|
18049
|
+
const agents3 = await provision();
|
|
18050
|
+
await Promise.all(agents3.map((agent) => this.materializeBuiltinWorkflowRoleBundle(agent)));
|
|
18051
|
+
return agents3;
|
|
18052
|
+
}
|
|
18053
|
+
const agents2 = await this.asyncLayer.transactionImmediate(async (tx) => {
|
|
18054
|
+
await tx.execute(sql`SELECT pg_advisory_xact_lock(hashtext(${this.backendProjectId}), hashtext('builtin-workflow-role-provisioning'))`);
|
|
18055
|
+
return provision(tx);
|
|
17750
18056
|
});
|
|
17751
|
-
|
|
18057
|
+
await Promise.all(agents2.map((agent) => this.materializeBuiltinWorkflowRoleBundle(agent)));
|
|
18058
|
+
return agents2;
|
|
17752
18059
|
}
|
|
17753
18060
|
/**
|
|
17754
18061
|
* Create an API key for an agent.
|
|
@@ -18316,6 +18623,29 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
18316
18623
|
}
|
|
18317
18624
|
return null;
|
|
18318
18625
|
}
|
|
18626
|
+
/** Seed only missing or blank default-owned mirror files after the provisioning transaction commits. */
|
|
18627
|
+
async materializeBuiltinWorkflowRoleBundle(agent) {
|
|
18628
|
+
return this.withLock(agent.id, async () => {
|
|
18629
|
+
const role = agent.metadata?.workflowRole;
|
|
18630
|
+
const definition = BUILTIN_WORKFLOW_ROLE_AGENT_DEFAULT_LIST.find((candidate) => candidate.role === role);
|
|
18631
|
+
if (!definition || agent.metadata?.builtInWorkflowRole !== true) return;
|
|
18632
|
+
const config2 = agent.bundleConfig;
|
|
18633
|
+
if (!isCanonicalOrPartialBuiltinWorkflowBundle(config2)) return;
|
|
18634
|
+
if (agent.instructionsPath?.trim() || agent.instructionsText?.trim() && agent.instructionsText !== definition.instructionsText) return;
|
|
18635
|
+
const bundleDir = await this.resolveCompatibleBundleDir(agent.id, true);
|
|
18636
|
+
await mkdir(bundleDir, { recursive: true });
|
|
18637
|
+
for (const [file, content] of [["AGENTS.md", definition.instructionsText], ["soul.md", definition.soul]]) {
|
|
18638
|
+
const path = join(bundleDir, file);
|
|
18639
|
+
let current = "";
|
|
18640
|
+
try {
|
|
18641
|
+
current = await readFile(path, "utf-8");
|
|
18642
|
+
} catch (error) {
|
|
18643
|
+
if (error.code !== "ENOENT") throw error;
|
|
18644
|
+
}
|
|
18645
|
+
if (!current.trim()) await writeFile(path, content, "utf-8");
|
|
18646
|
+
}
|
|
18647
|
+
});
|
|
18648
|
+
}
|
|
18319
18649
|
getCanonicalBundleDir(agent) {
|
|
18320
18650
|
return join(this.agentsDir, getCanonicalAgentInstructionsBundleDirName(agent.name, agent.id));
|
|
18321
18651
|
}
|
|
@@ -18438,6 +18768,7 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
18438
18768
|
...data,
|
|
18439
18769
|
id: row.id,
|
|
18440
18770
|
name: row.name,
|
|
18771
|
+
roles: data.roles?.length ? data.roles : [row.role],
|
|
18441
18772
|
role: row.role,
|
|
18442
18773
|
state: row.state,
|
|
18443
18774
|
taskId: row.taskId ?? void 0,
|
|
@@ -18471,7 +18802,8 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
18471
18802
|
return {
|
|
18472
18803
|
id: data.id,
|
|
18473
18804
|
name: data.name,
|
|
18474
|
-
|
|
18805
|
+
roles: normalizeAgentRoles(data.roles, data.role),
|
|
18806
|
+
role: normalizeAgentRoles(data.roles, data.role)[0],
|
|
18475
18807
|
state: data.state,
|
|
18476
18808
|
taskId: data.taskId,
|
|
18477
18809
|
createdAt: data.createdAt,
|
|
@@ -18497,8 +18829,8 @@ var AgentStore = class _AgentStore extends EventEmitter {
|
|
|
18497
18829
|
heartbeatProcedurePath: data.heartbeatProcedurePath
|
|
18498
18830
|
};
|
|
18499
18831
|
}
|
|
18500
|
-
async writeAgent(agent) {
|
|
18501
|
-
await writeAgent(this.asyncLayer.db, agent, this.asyncLayer.projectId);
|
|
18832
|
+
async writeAgent(agent, executor) {
|
|
18833
|
+
await writeAgent(executor ?? this.asyncLayer.db, agent, this.asyncLayer.projectId);
|
|
18502
18834
|
return;
|
|
18503
18835
|
}
|
|
18504
18836
|
/**
|