@runfusion/fusion 0.41.0 → 0.43.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.
Files changed (86) hide show
  1. package/dist/bin.js +45728 -42714
  2. package/dist/client/.vite/manifest.json +76 -76
  3. package/dist/client/assets/AgentDetailView-DiWELtb1.css +1 -0
  4. package/dist/client/assets/AgentDetailView-FUKwQxBP.js +18 -0
  5. package/dist/client/assets/{AgentPermissionPolicyEditor-VDXL4XQc.js → AgentPermissionPolicyEditor-DMDUhF1n.js} +2 -2
  6. package/dist/client/assets/{AgentsView-ChXccELt.js → AgentsView-BrQLd-yI.js} +3 -3
  7. package/dist/client/assets/ChatView-Wz_0goif.js +6 -0
  8. package/dist/client/assets/{DevServerView-DGOahw1o.js → DevServerView-DRqQpHsc.js} +1 -1
  9. package/dist/client/assets/{DirectoryPicker-DgqHlxyh.js → DirectoryPicker-BFot4CVM.js} +1 -1
  10. package/dist/client/assets/{DocumentsView-CbbZwzbH.js → DocumentsView-hamhUcvL.js} +1 -1
  11. package/dist/client/assets/{EvalsView-BL_ue7fJ.js → EvalsView-DAXqDqn2.js} +1 -1
  12. package/dist/client/assets/{ExperimentalAgentOnboardingModal-8H4XAu4O.js → ExperimentalAgentOnboardingModal-B4aZ1y0n.js} +1 -1
  13. package/dist/client/assets/{GoalsView-CO4FVL3E.js → GoalsView-2QOp9DA0.js} +1 -1
  14. package/dist/client/assets/{InsightsView-C5vLvWVE.js → InsightsView-D5fzFy-F.js} +1 -1
  15. package/dist/client/assets/{MemoryView-BL3W3yOv.js → MemoryView-B082KoR-.js} +2 -2
  16. package/dist/client/assets/NodesView-BA3pKMZu.js +14 -0
  17. package/dist/client/assets/{PiExtensionsManager-HvSiVeDN.js → PiExtensionsManager-C4I11iKm.js} +2 -2
  18. package/dist/client/assets/{PluginManager-DIjrlhnU.js → PluginManager-BzTzTMXZ.js} +1 -1
  19. package/dist/client/assets/{PullRequestView-C2jQX8xZ.js → PullRequestView-11xJ565q.js} +2 -2
  20. package/dist/client/assets/{ReliabilityView-DbqEwoRs.js → ReliabilityView-CpELpGqw.js} +1 -1
  21. package/dist/client/assets/{ResearchView-C1QghmLI.js → ResearchView-BHiXfjzE.js} +1 -1
  22. package/dist/client/assets/{SecretsView-Y2MQ-9n5.js → SecretsView-CC5OYoCn.js} +1 -1
  23. package/dist/client/assets/SessionTerminal-G70Jwkzs.js +2 -0
  24. package/dist/client/assets/{SettingsModal-DabcZZ6S.js → SettingsModal-BKEbtdqR.js} +1 -1
  25. package/dist/client/assets/SettingsModal-C0M1CqOu.js +31 -0
  26. package/dist/client/assets/SettingsModal-DpAxDq6M.css +1 -0
  27. package/dist/client/assets/{SetupWizardModal-Db-Rxinm.js → SetupWizardModal-CBfItlP6.js} +1 -1
  28. package/dist/client/assets/{SkillsView-CIEY1-DE.js → SkillsView-DbcW87-E.js} +1 -1
  29. package/dist/client/assets/{StashRecoveryView-B6iDu5tI.js → StashRecoveryView-CH9RPtzX.js} +1 -1
  30. package/dist/client/assets/{TodoView-B8IK_BPM.js → TodoView-ToviJ0SV.js} +2 -2
  31. package/dist/client/assets/WorkflowNodeEditor-BTu0D9c5.js +8 -0
  32. package/dist/client/assets/WorkflowNodeEditor-BokhW3de.css +1 -0
  33. package/dist/client/assets/app-B2ZxMfJT.js +13 -0
  34. package/dist/client/assets/app-BffGU_HQ.js +13 -0
  35. package/dist/client/assets/app-C8AHhs5N.js +13 -0
  36. package/dist/client/assets/app-CUu1sm3x.js +13 -0
  37. package/dist/client/assets/app-Ri9Te2Dv.js +13 -0
  38. package/dist/client/assets/app-sEkTyT-w.js +13 -0
  39. package/dist/client/assets/{dashboard-view-O5j3PSGY.js → dashboard-view-C3SsjXo1.js} +1621 -1621
  40. package/dist/client/assets/{dashboard-view-Dn4CztR6.js → dashboard-view-DtgLZ_LM.js} +1 -1
  41. package/dist/client/assets/dashboard-view-Du53JbmQ.css +1 -0
  42. package/dist/client/assets/{folder-open-C9XAaduE.js → folder-open-BlPyIWyf.js} +1 -1
  43. package/dist/client/assets/index-DqhCiU35.css +1 -0
  44. package/dist/client/assets/index-bZCpHtGq.js +793 -0
  45. package/dist/client/assets/{upload-BRAEugYV.js → upload-B-qmm1NH.js} +1 -1
  46. package/dist/client/assets/{users-BlflbMZ3.js → users-D6C5TXf2.js} +1 -1
  47. package/dist/client/index.html +2 -2
  48. package/dist/client/version.json +1 -1
  49. package/dist/droid-cli/package.json +1 -1
  50. package/dist/extension.js +44655 -41906
  51. package/dist/pi-claude-cli/package.json +1 -1
  52. package/dist/plugin-sdk/index.d.ts +6258 -86
  53. package/dist/plugins/fusion-plugin-acp-runtime/package.json +1 -1
  54. package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
  55. package/dist/plugins/fusion-plugin-compound-engineering/bundled.js +236 -26
  56. package/dist/plugins/fusion-plugin-compound-engineering/package.json +1 -1
  57. package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
  58. package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
  59. package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
  60. package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
  61. package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
  62. package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
  63. package/dist/plugins/fusion-plugin-reports/package.json +1 -1
  64. package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
  65. package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
  66. package/package.json +2 -9
  67. package/skill/fusion/references/extension-tools.md +3 -3
  68. package/skill/fusion/references/fusion-capabilities.md +2 -2
  69. package/dist/client/assets/AgentDetailView-CY-O6VkV.css +0 -1
  70. package/dist/client/assets/AgentDetailView-krC0B2O0.js +0 -18
  71. package/dist/client/assets/ChatView-Dz_xBKit.js +0 -6
  72. package/dist/client/assets/NodesView-DfBNNm9c.js +0 -14
  73. package/dist/client/assets/SessionTerminal-DaTXNT3A.js +0 -2
  74. package/dist/client/assets/SettingsModal-BAzm2kYd.js +0 -31
  75. package/dist/client/assets/SettingsModal-bJvc9aHl.css +0 -1
  76. package/dist/client/assets/WorkflowNodeEditor-DaPyvD_R.js +0 -8
  77. package/dist/client/assets/WorkflowNodeEditor-Po5uv_dN.css +0 -1
  78. package/dist/client/assets/app-CTRT_hQh.js +0 -13
  79. package/dist/client/assets/app-CVg6W-HU.js +0 -13
  80. package/dist/client/assets/app-CY3pQXOr.js +0 -13
  81. package/dist/client/assets/app-DNh04p5I.js +0 -13
  82. package/dist/client/assets/app-DhHgtB9j.js +0 -13
  83. package/dist/client/assets/app-NL_hPGDS.js +0 -13
  84. package/dist/client/assets/dashboard-view-BgiLoG1G.css +0 -1
  85. package/dist/client/assets/index-BHGM5W8a.css +0 -1
  86. package/dist/client/assets/index-CgfrecpF.js +0 -793
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusion-plugin-examples/acp-runtime",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusion-plugin-examples/cli-printing-press",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "type": "module",
5
5
  "description": "CLI Printing Press plugin package for Fusion",
6
6
  "private": true,
@@ -65,6 +65,30 @@ var COMPOUND_ENGINEERING_SKILLS = [
65
65
  skillFiles: ["skills/ce-compound/SKILL.md"],
66
66
  enabled: true,
67
67
  triggerPatterns: ["compound this", "document this learning", "capture this solution"]
68
+ },
69
+ {
70
+ skillId: "ce-commit",
71
+ name: "ce-commit",
72
+ description: "Create a git commit with a clear, value-communicating message following repo conventions.",
73
+ skillFiles: ["skills/ce-commit/SKILL.md"],
74
+ enabled: true,
75
+ triggerPatterns: ["commit", "commit this", "save my changes", "create a commit"]
76
+ },
77
+ {
78
+ skillId: "ce-commit-push-pr",
79
+ name: "ce-commit-push-pr",
80
+ description: "Commit, push, and open a PR with an adaptive, value-first description that scales with the change.",
81
+ skillFiles: ["skills/ce-commit-push-pr/SKILL.md"],
82
+ enabled: true,
83
+ triggerPatterns: ["commit and PR", "ship this", "create a PR", "open a pull request"]
84
+ },
85
+ {
86
+ skillId: "ce-resolve-pr-feedback",
87
+ name: "ce-resolve-pr-feedback",
88
+ description: "Resolve PR review feedback by evaluating validity and fixing issues in parallel.",
89
+ skillFiles: ["skills/ce-resolve-pr-feedback/SKILL.md"],
90
+ enabled: true,
91
+ triggerPatterns: ["resolve PR feedback", "address review comments", "fix review feedback"]
68
92
  }
69
93
  ];
70
94
 
@@ -138,6 +162,72 @@ function isPluginLocalPath(p) {
138
162
  return isAbsolute(p) && !GLOBAL_SKILL_DIR_PATTERN.test(resolve(p) + sep);
139
163
  }
140
164
 
165
+ // ../../plugins/fusion-plugin-compound-engineering/src/agent-installation.ts
166
+ import { cpSync as cpSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2 } from "node:fs";
167
+ import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2, resolve as resolve2, sep as sep2 } from "node:path";
168
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
169
+ function resolveBundledAgentsRoot() {
170
+ const here = fileURLToPath2(import.meta.url);
171
+ const dir = dirname2(here);
172
+ const local = resolve2(dir, "agents");
173
+ if (existsSync2(local)) return local;
174
+ return resolve2(dir, "..", "src", "agents");
175
+ }
176
+ function resolveDefaultAgentsInstallTargetRoot() {
177
+ const here = fileURLToPath2(import.meta.url);
178
+ return resolve2(dirname2(here), "..", ".fusion-ce-agents");
179
+ }
180
+ var GLOBAL_AGENT_DIR_PATTERN = /[\\/]\.(claude|codex|gemini)[\\/]agents([\\/]|$)/;
181
+ function assertPluginLocalAgentsTarget(targetRoot) {
182
+ const normalized = resolve2(targetRoot);
183
+ if (GLOBAL_AGENT_DIR_PATTERN.test(normalized + sep2)) {
184
+ throw new Error(
185
+ `Refusing to install Compound Engineering agents into a global client agents directory: ${normalized}. Install target MUST be plugin-local (never <home>/.claude|.codex|.gemini/agents).`
186
+ );
187
+ }
188
+ }
189
+ function assertValidAgentSource(agentId, sourceFile) {
190
+ if (!existsSync2(sourceFile)) {
191
+ throw new Error(`Bundled agent def missing for '${agentId}': ${sourceFile}`);
192
+ }
193
+ const content = readFileSync2(sourceFile, "utf-8");
194
+ if (!/^---[\s\S]*?\bname\s*:\s*\S/m.test(content)) {
195
+ throw new Error(`Bundled agent def '${agentId}' at ${sourceFile} is missing a frontmatter 'name:' field`);
196
+ }
197
+ }
198
+ function installBundledCeAgents(options = {}) {
199
+ const targetRoot = options.targetRoot ? resolve2(options.targetRoot) : resolveDefaultAgentsInstallTargetRoot();
200
+ assertPluginLocalAgentsTarget(targetRoot);
201
+ const sourceRoot = options.sourceRoot ? resolve2(options.sourceRoot) : resolveBundledAgentsRoot();
202
+ const sourceFiles = existsSync2(sourceRoot) ? readdirSync(sourceRoot).filter((f) => f.endsWith(".md")) : [];
203
+ const results = sourceFiles.map((file) => {
204
+ const agentId = file.replace(/\.md$/, "");
205
+ const sourceFile = join2(sourceRoot, file);
206
+ const targetFile = join2(targetRoot, file);
207
+ try {
208
+ assertValidAgentSource(agentId, sourceFile);
209
+ if (existsSync2(targetFile)) {
210
+ return { agentId, sourceFile, targetFile, outcome: "skipped", reason: "existing install preserved" };
211
+ }
212
+ mkdirSync2(targetRoot, { recursive: true });
213
+ cpSync2(sourceFile, targetFile);
214
+ return { agentId, sourceFile, targetFile, outcome: "installed" };
215
+ } catch (error) {
216
+ return {
217
+ agentId,
218
+ sourceFile,
219
+ targetFile,
220
+ outcome: "error",
221
+ reason: error instanceof Error ? error.message : String(error)
222
+ };
223
+ }
224
+ });
225
+ return { targetRoot, results };
226
+ }
227
+ function isPluginLocalAgentsPath(p) {
228
+ return isAbsolute2(p) && !GLOBAL_AGENT_DIR_PATTERN.test(resolve2(p) + sep2);
229
+ }
230
+
141
231
  // ../../plugins/fusion-plugin-compound-engineering/src/schema.ts
142
232
  function ensureCeSchema(db) {
143
233
  db.exec(`
@@ -216,8 +306,8 @@ function ensureCeSchema(db) {
216
306
  }
217
307
 
218
308
  // ../../plugins/fusion-plugin-compound-engineering/src/session/orchestrator.ts
219
- import { mkdirSync as mkdirSync2, writeFileSync } from "node:fs";
220
- import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2 } from "node:path";
309
+ import { mkdirSync as mkdirSync3, writeFileSync } from "node:fs";
310
+ import { dirname as dirname3, isAbsolute as isAbsolute3, join as join3 } from "node:path";
221
311
 
222
312
  // ../../plugins/fusion-plugin-compound-engineering/src/sync/pipeline-store.ts
223
313
  import { randomUUID } from "node:crypto";
@@ -755,6 +845,7 @@ var MAX_ACTIVITY_TURNS = 200;
755
845
  var MAX_ACTIVITY_TURN_CHARS = 16e3;
756
846
  var MAX_PERSISTED_ACTIVITY_TURNS = 50;
757
847
  var MAX_PERSISTED_ACTIVITY_TURN_CHARS = 4e3;
848
+ var INTERACTIVE_AI_UNAVAILABLE_MESSAGE = "Session cannot be continued in this process: interactive AI sessions are unavailable (no factory on this context). Resume from a route context with the engine loaded.";
758
849
  var CE_EVENTS = {
759
850
  turn: "compound-engineering:session-turn",
760
851
  question: "compound-engineering:session-question",
@@ -1000,8 +1091,36 @@ var CeOrchestrator = class {
1000
1091
  );
1001
1092
  }
1002
1093
  const live = this.live.get(sessionId);
1094
+ if (!live && !this.factory) {
1095
+ throw new Error(INTERACTIVE_AI_UNAVAILABLE_MESSAGE);
1096
+ }
1097
+ const turn = this.runAnswerTurn(session, questionId, response);
1098
+ if (opts.detach) {
1099
+ if (!live) {
1100
+ this.store.update(sessionId, { status: "active", error: null });
1101
+ }
1102
+ void turn;
1103
+ return { session: this.requireSession(sessionId) };
1104
+ }
1105
+ return turn;
1106
+ }
1107
+ async runAnswerTurn(session, questionId, response) {
1108
+ const sessionId = session.id;
1109
+ let live = this.live.get(sessionId);
1003
1110
  if (!live) {
1004
- throw new Error(`Session ${sessionId} has no live handle in this process; call resume() first.`);
1111
+ try {
1112
+ await this.rehydrate(session);
1113
+ live = this.live.get(sessionId);
1114
+ if (!live) {
1115
+ throw new Error(`Session ${sessionId} could not be rehydrated with a live handle.`);
1116
+ }
1117
+ } catch (err) {
1118
+ const interrupted = this.interruptSession(sessionId, err);
1119
+ return {
1120
+ session: interrupted,
1121
+ event: { type: "error", data: { message: interrupted.error ?? "interrupted", cause: err } }
1122
+ };
1123
+ }
1005
1124
  }
1006
1125
  this.store.appendHistory(sessionId, {
1007
1126
  role: "user",
@@ -1009,12 +1128,7 @@ var CeOrchestrator = class {
1009
1128
  at: (/* @__PURE__ */ new Date()).toISOString()
1010
1129
  });
1011
1130
  this.store.update(sessionId, { status: "active", currentQuestion: null });
1012
- const turn = this.runTurn(sessionId, () => live.answer(questionId, response), live);
1013
- if (opts.detach) {
1014
- void turn;
1015
- return { session: this.requireSession(sessionId) };
1016
- }
1017
- return turn;
1131
+ return this.runTurn(sessionId, () => live.answer(questionId, response), live);
1018
1132
  }
1019
1133
  /**
1020
1134
  * Resume an `awaiting_input`, `interrupted`, or `error` session — and, crucially,
@@ -1051,7 +1165,7 @@ var CeOrchestrator = class {
1051
1165
  if (!this.factory) {
1052
1166
  const next = this.store.update(sessionId, {
1053
1167
  status: "interrupted",
1054
- error: "Session cannot be continued in this process: interactive AI sessions are unavailable (no factory on this context). Resume from a route context with the engine loaded."
1168
+ error: INTERACTIVE_AI_UNAVAILABLE_MESSAGE
1055
1169
  }) ?? session;
1056
1170
  return { session: next };
1057
1171
  }
@@ -1128,6 +1242,22 @@ var CeOrchestrator = class {
1128
1242
  getState(sessionId) {
1129
1243
  return this.store.get(sessionId);
1130
1244
  }
1245
+ /**
1246
+ * Cancel a session: stop any live in-process handle but keep the persisted row
1247
+ * for inspection/resume by marking it `interrupted`. Unlike discard(), cancel
1248
+ * preserves the conversation and progress; discard stops the handle AND deletes
1249
+ * the row. Terminal sessions are idempotent no-ops.
1250
+ */
1251
+ cancel(sessionId) {
1252
+ const session = this.store.get(sessionId);
1253
+ if (!session) return void 0;
1254
+ if (session.status === "completed" || session.status === "error" || session.status === "interrupted") {
1255
+ return session;
1256
+ }
1257
+ const interrupted = this.interruptSession(sessionId, new Error("Cancelled by user"));
1258
+ this.disposeLive(sessionId);
1259
+ return interrupted;
1260
+ }
1131
1261
  /**
1132
1262
  * Discard a session: dispose any live in-process handle (so an in-flight
1133
1263
  * agent doesn't keep running unobserved) and delete the persisted row.
@@ -1288,9 +1418,9 @@ var CeOrchestrator = class {
1288
1418
  const stage = getStage(session.stage);
1289
1419
  const location = stage?.artifactLocation ?? `docs/ce/${session.stage}/`;
1290
1420
  const content = this.extractArtifactContent(data);
1291
- const target = location.endsWith("/") ? join2(location, `${session.stage}-${session.id}.md`) : location;
1292
- const abs = isAbsolute2(target) ? target : join2(this.projectRoot, target);
1293
- mkdirSync2(dirname2(abs), { recursive: true });
1421
+ const target = location.endsWith("/") ? join3(location, `${session.stage}-${session.id}.md`) : location;
1422
+ const abs = isAbsolute3(target) ? target : join3(this.projectRoot, target);
1423
+ mkdirSync3(dirname3(abs), { recursive: true });
1294
1424
  writeFileSync(abs, content, "utf-8");
1295
1425
  return abs;
1296
1426
  }
@@ -1322,6 +1452,35 @@ var CeOrchestrator = class {
1322
1452
  }
1323
1453
  };
1324
1454
 
1455
+ // ../../plugins/fusion-plugin-compound-engineering/src/session/session-recovery.ts
1456
+ var DEFAULT_RECOVERY_SCAN_TTL_MS = 12e4;
1457
+ var lastRecoveryScanAt = /* @__PURE__ */ new WeakMap();
1458
+ function recoverStaleSessionsForContext(ctx, options) {
1459
+ const key = ctx.taskStore;
1460
+ const now = options.now ?? Date.now();
1461
+ const ttlMs = options.ttlMs ?? DEFAULT_RECOVERY_SCAN_TTL_MS;
1462
+ if (!options.force) {
1463
+ const last = lastRecoveryScanAt.get(key) ?? 0;
1464
+ if (now - last < ttlMs) return [];
1465
+ }
1466
+ lastRecoveryScanAt.set(key, now);
1467
+ try {
1468
+ const recovered = getCeSessionStore(ctx).recoverStaleSessions(now);
1469
+ if (recovered.length > 0) {
1470
+ ctx.logger.info(`Compound Engineering recovered stale session(s) during ${options.reason}: ${recovered.join(", ")}`);
1471
+ if (options.emitEvent) {
1472
+ ctx.emitEvent("compound-engineering:sessions-recovered", { sessionIds: recovered, reason: options.reason });
1473
+ }
1474
+ }
1475
+ return recovered;
1476
+ } catch (err) {
1477
+ ctx.logger.warn(
1478
+ `Compound Engineering stale-session recovery skipped during ${options.reason}: ${err instanceof Error ? err.message : String(err)}`
1479
+ );
1480
+ return [];
1481
+ }
1482
+ }
1483
+
1325
1484
  // ../../plugins/fusion-plugin-compound-engineering/src/routes/route-helpers.ts
1326
1485
  function asString2(v) {
1327
1486
  return typeof v === "string" && v.length > 0 ? v : void 0;
@@ -1401,12 +1560,24 @@ function createSessionRoutes() {
1401
1560
  }
1402
1561
  }
1403
1562
  },
1563
+ {
1564
+ method: "POST",
1565
+ path: "/sessions/:id/cancel",
1566
+ description: "Cancel an in-flight CE session (stops the agent, keeps the row as interrupted).",
1567
+ handler: async (req, ctx) => {
1568
+ const id = req.params.id;
1569
+ const session = getOrchestrator(ctx).cancel(id);
1570
+ if (!session) return { status: 404, body: { error: `Session ${id} not found` } };
1571
+ return { status: 200, body: { session } };
1572
+ }
1573
+ },
1404
1574
  {
1405
1575
  method: "GET",
1406
1576
  path: "/sessions/:id",
1407
1577
  description: "Get current session state, including in-flight working output (liveActivity).",
1408
1578
  handler: async (req, ctx) => {
1409
1579
  const id = req.params.id;
1580
+ recoverStaleSessionsForContext(ctx, { reason: "route" });
1410
1581
  const session = getCeSessionStore(ctx).get(id);
1411
1582
  if (!session) return { status: 404, body: { error: `Session ${id} not found` } };
1412
1583
  const liveActivity = getOrchestrator(ctx).getLiveActivity(id);
@@ -1421,6 +1592,7 @@ function createSessionRoutes() {
1421
1592
  path: "/sessions",
1422
1593
  description: "List CE sessions (optionally filtered by status/stage).",
1423
1594
  handler: async (req, ctx) => {
1595
+ recoverStaleSessionsForContext(ctx, { reason: "route" });
1424
1596
  const query = req.query ?? {};
1425
1597
  const status = asCeSessionStatus(typeof query.status === "string" ? query.status : void 0);
1426
1598
  const stage = typeof query.stage === "string" ? query.stage : void 0;
@@ -1456,8 +1628,8 @@ function createSessionRoutes() {
1456
1628
  }
1457
1629
 
1458
1630
  // ../../plugins/fusion-plugin-compound-engineering/src/artifacts/discovery.ts
1459
- import { accessSync, constants, readdirSync, readFileSync as readFileSync2, statSync } from "node:fs";
1460
- import { isAbsolute as isAbsolute3, join as join3, relative, sep as sep2 } from "node:path";
1631
+ import { accessSync, constants, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync } from "node:fs";
1632
+ import { isAbsolute as isAbsolute4, join as join4, relative, sep as sep3 } from "node:path";
1461
1633
  var CONVENTIONAL_LOCATIONS = [
1462
1634
  { stage: "strategy", label: "Strategy", path: "STRATEGY.md", kind: "file" },
1463
1635
  { stage: "ideation", label: "Ideation", path: "docs/ideation", kind: "directory" },
@@ -1468,13 +1640,13 @@ var CONVENTIONAL_LOCATIONS = [
1468
1640
  ];
1469
1641
  var MAX_ARTIFACT_BYTES = 2e6;
1470
1642
  function toPosix(p) {
1471
- return p.split(sep2).join("/");
1643
+ return p.split(sep3).join("/");
1472
1644
  }
1473
1645
  function isWithin(root, locationAbs, candidate) {
1474
1646
  const relToLocation = relative(locationAbs, candidate);
1475
- if (relToLocation.startsWith("..") || isAbsolute3(relToLocation)) return false;
1647
+ if (relToLocation.startsWith("..") || isAbsolute4(relToLocation)) return false;
1476
1648
  const relToRoot = relative(root, candidate);
1477
- if (relToRoot.startsWith("..") || isAbsolute3(relToRoot)) return false;
1649
+ if (relToRoot.startsWith("..") || isAbsolute4(relToRoot)) return false;
1478
1650
  return true;
1479
1651
  }
1480
1652
  function makeId(stage, relPath) {
@@ -1522,7 +1694,7 @@ function sortEntries(entries) {
1522
1694
  });
1523
1695
  }
1524
1696
  function discoverLocation(root, loc) {
1525
- const locationAbs = join3(root, loc.path);
1697
+ const locationAbs = join4(root, loc.path);
1526
1698
  const entries = [];
1527
1699
  let present = false;
1528
1700
  let st;
@@ -1558,7 +1730,7 @@ function discoverLocation(root, loc) {
1558
1730
  );
1559
1731
  return { stage: loc.stage, label: loc.label, present, entries: sortEntries(entries) };
1560
1732
  }
1561
- names = readdirSync(locationAbs);
1733
+ names = readdirSync2(locationAbs);
1562
1734
  } catch (err) {
1563
1735
  entries.push(makeError(loc.stage, toPosix(loc.path), err instanceof Error ? err.message : String(err)));
1564
1736
  return { stage: loc.stage, label: loc.label, present, entries: sortEntries(entries) };
@@ -1566,8 +1738,8 @@ function discoverLocation(root, loc) {
1566
1738
  for (const childName of names) {
1567
1739
  if (childName.startsWith(".")) continue;
1568
1740
  if (!childName.toLowerCase().endsWith(".md")) continue;
1569
- const abs = join3(locationAbs, childName);
1570
- const relPath = toPosix(join3(loc.path, childName));
1741
+ const abs = join4(locationAbs, childName);
1742
+ const relPath = toPosix(join4(loc.path, childName));
1571
1743
  let childStat;
1572
1744
  try {
1573
1745
  childStat = statSync(abs);
@@ -1600,8 +1772,8 @@ function readArtifactById(projectRoot, id) {
1600
1772
  const relPath = id.slice(sepIdx + 1);
1601
1773
  const loc = CONVENTIONAL_LOCATIONS.find((l) => l.stage === stage);
1602
1774
  if (!loc) return void 0;
1603
- const locationAbs = join3(projectRoot, loc.path);
1604
- const abs = join3(projectRoot, relPath);
1775
+ const locationAbs = join4(projectRoot, loc.path);
1776
+ const abs = join4(projectRoot, relPath);
1605
1777
  if (loc.kind === "file") {
1606
1778
  if (toPosix(relPath) !== toPosix(loc.path)) return void 0;
1607
1779
  } else if (!isWithin(projectRoot, locationAbs, abs)) {
@@ -1609,7 +1781,7 @@ function readArtifactById(projectRoot, id) {
1609
1781
  }
1610
1782
  if (loc.kind === "directory") {
1611
1783
  const rel = relative(locationAbs, abs);
1612
- if (rel.includes(sep2) || rel.startsWith("..") || !rel.toLowerCase().endsWith(".md")) {
1784
+ if (rel.includes(sep3) || rel.startsWith("..") || !rel.toLowerCase().endsWith(".md")) {
1613
1785
  return void 0;
1614
1786
  }
1615
1787
  }
@@ -1627,7 +1799,7 @@ function readArtifactById(projectRoot, id) {
1627
1799
  return { error: err instanceof Error ? err.message : String(err) };
1628
1800
  }
1629
1801
  try {
1630
- content = readFileSync2(abs, "utf8");
1802
+ content = readFileSync3(abs, "utf8");
1631
1803
  } catch (err) {
1632
1804
  return { error: err instanceof Error ? err.message : String(err) };
1633
1805
  }
@@ -1939,8 +2111,42 @@ var plugin = definePlugin({
1939
2111
  const message = error instanceof Error ? error.message : String(error);
1940
2112
  ctx.logger.error(`Compound Engineering skill install failed: ${message}`);
1941
2113
  }
2114
+ try {
2115
+ const { targetRoot, results } = installBundledCeAgents();
2116
+ const installed = results.filter((r) => r.outcome === "installed").length;
2117
+ const errored = results.filter((r) => r.outcome === "error");
2118
+ if (errored.length > 0) {
2119
+ ctx.logger.warn(
2120
+ `Compound Engineering: ${errored.length} agent def(s) failed to install: ${errored.map((e) => `${e.agentId} (${e.reason})`).join(", ")}`
2121
+ );
2122
+ }
2123
+ ctx.logger.info(
2124
+ `Compound Engineering agent personas ready \u2014 installed=${installed} target=${targetRoot}`
2125
+ );
2126
+ ctx.emitEvent("compound-engineering:agents-installed", { targetRoot, results });
2127
+ } catch (error) {
2128
+ const message = error instanceof Error ? error.message : String(error);
2129
+ ctx.logger.error(`Compound Engineering agent install failed: ${message}`);
2130
+ }
2131
+ recoverStaleSessionsForContext(ctx, { reason: "load", force: true, emitEvent: true });
1942
2132
  }
1943
2133
  },
2134
+ // Expose the plugin-local ce-* persona-definition directory to executor /
2135
+ // workflow-step sessions via FUSION_CE_AGENTS_DIR. The CE skills read a persona
2136
+ // def from here and pass its body to fn_spawn_agent's systemPromptOverride —
2137
+ // the lightweight subagent path (no plugin agent-contribution channel exists).
2138
+ // Defs are installed in onLoad.
2139
+ executorRuntimeEnv: () => ({
2140
+ env: {
2141
+ FUSION_CE_AGENTS_DIR: resolveDefaultAgentsInstallTargetRoot(),
2142
+ // Step sessions run with cwd=projectRoot, so a CE skill can't reach its
2143
+ // bundled scripts via a relative `scripts/...` path. Expose the installed
2144
+ // skills root so skill docs can invoke scripts by absolute path
2145
+ // (`"$FUSION_CE_SKILLS_DIR/<skill-id>/scripts/<name>"`).
2146
+ FUSION_CE_SKILLS_DIR: resolveDefaultInstallTargetRoot()
2147
+ },
2148
+ description: "compound-engineering ce-* persona definitions and bundled skills directories"
2149
+ }),
1944
2150
  routes: [...createSessionRoutes(), ...createArtifactRoutes()],
1945
2151
  dashboardViews: [
1946
2152
  {
@@ -1973,12 +2179,16 @@ export {
1973
2179
  getReconcileIntervalMinutes,
1974
2180
  getReconcileOnHooks,
1975
2181
  getStage,
2182
+ installBundledCeAgents,
1976
2183
  installBundledCeSkills,
2184
+ isPluginLocalAgentsPath,
1977
2185
  isPluginLocalPath,
1978
2186
  listStages,
1979
2187
  reconcileCePipelines,
1980
2188
  registerStage,
2189
+ resolveBundledAgentsRoot,
1981
2190
  resolveBundledSkillsRoot,
2191
+ resolveDefaultAgentsInstallTargetRoot,
1982
2192
  resolveDefaultInstallTargetRoot,
1983
2193
  settingsSchema
1984
2194
  };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusion-plugin-examples/compound-engineering",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusion-plugin-examples/cursor-runtime",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusion-plugin-examples/dependency-graph",
3
- "version": "0.1.34",
3
+ "version": "0.1.36",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusion-plugin-examples/droid-runtime",
3
- "version": "0.1.29",
3
+ "version": "0.1.31",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusion-plugin-examples/hermes-runtime",
3
- "version": "0.2.53",
3
+ "version": "0.2.55",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusion-plugin-examples/openclaw-runtime",
3
- "version": "0.2.53",
3
+ "version": "0.2.55",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusion-plugin-examples/paperclip-runtime",
3
- "version": "0.2.53",
3
+ "version": "0.2.55",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusion-plugin-examples/reports",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "type": "module",
5
5
  "description": "Reports plugin for Fusion",
6
6
  "private": true,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusion-plugin-examples/roadmap",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusion-plugin-examples/whatsapp-chat",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "type": "module",
5
5
  "description": "WhatsApp Web (Baileys) chat bridge for Fusion agents",
6
6
  "keywords": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runfusion/fusion",
3
- "version": "0.41.0",
3
+ "version": "0.43.0",
4
4
  "license": "MIT",
5
5
  "description": "Fusion CLI: HTTP API server, daemon, dashboard launcher, and task tooling for the Fusion AI coding agent.",
6
6
  "homepage": "https://github.com/Runfusion/Fusion#readme",
@@ -57,17 +57,9 @@
57
57
  "ws": "^8.18.0"
58
58
  },
59
59
  "peerDependencies": {
60
- "@earendil-works/pi-ai": "*",
61
- "@earendil-works/pi-coding-agent": "*",
62
60
  "typebox": "*"
63
61
  },
64
62
  "peerDependenciesMeta": {
65
- "@earendil-works/pi-ai": {
66
- "optional": true
67
- },
68
- "@earendil-works/pi-coding-agent": {
69
- "optional": true
70
- },
71
63
  "typebox": {
72
64
  "optional": true
73
65
  }
@@ -108,6 +100,7 @@
108
100
  "typecheck": "tsc --noEmit",
109
101
  "test": "vitest run --silent=passed-only --reporter=dot",
110
102
  "test:ci-shape": "vitest run src/__tests__/ci-workflow.test.ts --silent=passed-only --reporter=dot",
103
+ "test:docs-index": "vitest run src/__tests__/docs-readme-index.test.ts --silent=passed-only --reporter=dot",
111
104
  "test:slow-cli": "cross-env FUSION_TEST_SLOW_CLI=1 vitest run src/commands/__tests__/agent-export.test.ts --silent=passed-only --reporter=dot",
112
105
  "test:extension-integration": "cross-env FUSION_TEST_EXTENSION_INTEGRATION=1 vitest run src/__tests__/extension-integration.test.ts --silent=passed-only --reporter=dot",
113
106
  "test:build-exe": "cross-env FUSION_TEST_BUILD_EXE=1 vitest run --config vitest.build-exe.config.ts --silent=passed-only --reporter=dot",
@@ -104,15 +104,15 @@ Request a refinement of a completed or in-review task. Creates a new follow-up t
104
104
 
105
105
  ### fn_task_archive
106
106
 
107
- Archive a done task (move from done → archived). Archived tasks are preserved for historical reference but moved out of the main board view.
107
+ Archive a task from any live column (move to archived). Archived tasks are preserved for historical reference but moved out of the main board view.
108
108
 
109
109
  | Parameter | Type | Required | Description |
110
110
  |-----------|------|----------|-------------|
111
- | `id` | string | ✓ | Task ID to archive (e.g. FN-001). Must be in 'done' column. |
111
+ | `id` | string | ✓ | Task ID to archive from any live column (e.g. FN-001). |
112
112
 
113
113
  ### fn_task_unarchive
114
114
 
115
- Unarchive an archived task (move from archived → done). Restores the task to the done column.
115
+ Unarchive an archived task (move from archived → its restore column). Restores to the pre-archive column when available, with active execution columns downgraded to todo.
116
116
 
117
117
  | Parameter | Type | Required | Description |
118
118
  |-----------|------|----------|-------------|
@@ -22,8 +22,8 @@ All skill/extension tool invocations in this catalog use the public `fn_*` names
22
22
  | `fn_task_retry` | Retry a failed task — clears the error state. Non-review failures move to todo; in-review execution failures move to todo preserving progress; in-review merge failures stay in-place for auto-merge retry. |
23
23
  | `fn_task_duplicate` | Duplicate an existing task, creating a fresh copy in planning. Copies the title and description but resets all execution state. The AI planning agent will replan the new task. |
24
24
  | `fn_task_refine` | Request a refinement of a completed or in-review task. Creates a new follow-up task in planning that references the original task as a dependency. Use this when a done or in-review task needs additional work, improvements, or follow-up changes. |
25
- | `fn_task_archive` | Archive a done task (move from done → archived). Archived tasks are preserved for historical reference but moved out of the main board view. |
26
- | `fn_task_unarchive` | Unarchive an archived task (move from archived → done). Restores the task to the done column. |
25
+ | `fn_task_archive` | Archive a task from any live column (move to archived). Archived tasks are preserved for historical reference but moved out of the main board view. |
26
+ | `fn_task_unarchive` | Unarchive an archived task (move from archived → its restore column). Restores to the pre-archive column when available, with active execution columns downgraded to todo. |
27
27
  | `fn_task_delete` | Soft-delete a task from active Fusion board views. The task row and artifacts are preserved; optional allowResurrection marks the ID for intentional recreation. |
28
28
  | `fn_task_import_github` | Import GitHub issues as Fusion tasks. Fetches open issues from a repository and creates tasks in the planning column. Each task includes the issue title and body with a link to the source issue. |
29
29
  | `fn_task_import_github_issue` | Import a specific GitHub issue as a Fusion task. Fetches the issue by number and creates a single task in the planning column with the issue title and body. |