@runfusion/fusion 0.13.0 → 0.14.1

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 (72) hide show
  1. package/README.md +13 -0
  2. package/dist/bin.js +1332 -528
  3. package/dist/client/assets/AgentDetailView-B3KAsP2O.js +18 -0
  4. package/dist/client/assets/{AgentsView-Dvf_xUkx.js → AgentsView-DoXb_amw.js} +4 -4
  5. package/dist/client/assets/ChatView-BJ2c7wvd.js +1 -0
  6. package/dist/client/assets/{DevServerView-C2qTJch7.js → DevServerView-DbgM4tlT.js} +1 -1
  7. package/dist/client/assets/{DirectoryPicker-DRfhg9zz.js → DirectoryPicker-DfmtfMiu.js} +1 -1
  8. package/dist/client/assets/{DocumentsView-j8ic1xUw.js → DocumentsView-_-Efkx_W.js} +1 -1
  9. package/dist/client/assets/{InsightsView-CpAz3o0i.js → InsightsView-DUjcfW53.js} +1 -1
  10. package/dist/client/assets/{MemoryView-BcQsi_JK.js → MemoryView-DxMPBb0q.js} +1 -1
  11. package/dist/client/assets/{NodesView-Bo_Yhr4N.js → NodesView-BEBTI15s.js} +1 -1
  12. package/dist/client/assets/PiExtensionsManager-BpMYhHH_.js +11 -0
  13. package/dist/client/assets/PluginManager-CPv7yQd3.js +1 -0
  14. package/dist/client/assets/PluginManager-DA_T0GHn.css +1 -0
  15. package/dist/client/assets/{ResearchView-CLyyqAWE.js → ResearchView-BrFvdyXT.js} +1 -1
  16. package/dist/client/assets/{RoadmapsView-tG7IdOoc.js → RoadmapsView-BDjLrtcj.js} +1 -1
  17. package/dist/client/assets/SettingsModal-Cd-QGB0C.js +31 -0
  18. package/dist/client/assets/{SettingsModal-CXUGeZ0_.js → SettingsModal-CxDxiTRy.js} +1 -1
  19. package/dist/client/assets/SettingsModal-D_AFkDJa.css +1 -0
  20. package/dist/client/assets/{SetupWizardModal-BMJL6eNR.js → SetupWizardModal-DFUA4X3z.js} +1 -1
  21. package/dist/client/assets/{SkillMultiselect-ILMft-Kz.js → SkillMultiselect-BUWe5ujb.js} +1 -1
  22. package/dist/client/assets/{SkillsView-x4_YwBz6.js → SkillsView-RAkqGX3y.js} +1 -1
  23. package/dist/client/assets/TodoView-Ceb0wrg1.js +6 -0
  24. package/dist/client/assets/TodoView-SeO9o7km.css +1 -0
  25. package/dist/client/assets/{folder-open-DDdJt8aE.js → folder-open-DcM-Vd6r.js} +1 -1
  26. package/dist/client/assets/index-C1prPuSl.css +1 -0
  27. package/dist/client/assets/index-DH3aprf6.js +661 -0
  28. package/dist/client/assets/{list-checks-DFxQ9biT.js → list-checks-ByGHVQpZ.js} +1 -1
  29. package/dist/client/assets/{star-BKs1bgJN.js → star-DlEYI8GL.js} +1 -1
  30. package/dist/client/assets/{upload-Bb5Pidne.js → upload-DKshabz-.js} +1 -1
  31. package/dist/client/assets/{users-BImNn91Q.js → users-X6tYPPBV.js} +1 -1
  32. package/dist/client/index.html +2 -2
  33. package/dist/client/sw.js +6 -0
  34. package/dist/client/version.json +1 -1
  35. package/dist/droid-cli/index.ts +127 -0
  36. package/dist/droid-cli/package.json +37 -0
  37. package/dist/droid-cli/src/__tests__/control-handler.test.ts +164 -0
  38. package/dist/droid-cli/src/__tests__/event-bridge.test.ts +1318 -0
  39. package/dist/droid-cli/src/__tests__/mcp-config.test.ts +310 -0
  40. package/dist/droid-cli/src/__tests__/process-manager.test.ts +818 -0
  41. package/dist/droid-cli/src/__tests__/prompt-builder.test.ts +1206 -0
  42. package/dist/droid-cli/src/__tests__/provider.test.ts +1894 -0
  43. package/dist/droid-cli/src/__tests__/setup-test-isolation.test.ts +32 -0
  44. package/dist/droid-cli/src/__tests__/setup-test-isolation.ts +14 -0
  45. package/dist/droid-cli/src/__tests__/stream-parser.test.ts +188 -0
  46. package/dist/droid-cli/src/__tests__/thinking-config.test.ts +141 -0
  47. package/dist/droid-cli/src/__tests__/tool-mapping.test.ts +253 -0
  48. package/dist/droid-cli/src/control-handler.ts +82 -0
  49. package/dist/droid-cli/src/event-bridge.ts +397 -0
  50. package/dist/droid-cli/src/mcp-config.ts +144 -0
  51. package/dist/droid-cli/src/mcp-schema-server.cjs +49 -0
  52. package/dist/droid-cli/src/process-manager.ts +358 -0
  53. package/dist/droid-cli/src/prompt-builder.ts +629 -0
  54. package/dist/droid-cli/src/provider.ts +447 -0
  55. package/dist/droid-cli/src/stream-parser.ts +37 -0
  56. package/dist/droid-cli/src/thinking-config.ts +83 -0
  57. package/dist/droid-cli/src/tool-mapping.ts +147 -0
  58. package/dist/droid-cli/src/types.ts +87 -0
  59. package/dist/extension.js +555 -125
  60. package/dist/pi-claude-cli/package.json +1 -1
  61. package/package.json +2 -1
  62. package/dist/client/assets/AgentDetailView-B7j297GT.js +0 -18
  63. package/dist/client/assets/ChatView-BgUt38ty.js +0 -1
  64. package/dist/client/assets/PiExtensionsManager-DHt2zFg8.js +0 -11
  65. package/dist/client/assets/PluginManager-BQhBHWrB.js +0 -1
  66. package/dist/client/assets/PluginManager-jyNkJZSz.css +0 -1
  67. package/dist/client/assets/SettingsModal-9HS8MnmW.css +0 -1
  68. package/dist/client/assets/SettingsModal-UziTDnLh.js +0 -31
  69. package/dist/client/assets/TodoView-BBYcMbXE.js +0 -6
  70. package/dist/client/assets/TodoView-C1g65hJo.css +0 -1
  71. package/dist/client/assets/index-B15xwijw.css +0 -1
  72. package/dist/client/assets/index-DmSs2FGE.js +0 -661
package/dist/bin.js CHANGED
@@ -1009,11 +1009,40 @@ function hasAgentIdentity(agent) {
1009
1009
  if (!agent) return false;
1010
1010
  return !!(agent.soul?.trim() || agent.instructionsText?.trim() || agent.instructionsPath?.trim() || agent.memory?.trim());
1011
1011
  }
1012
- function getDefaultHeartbeatProcedurePath(agentId) {
1012
+ function slugifyAgentAssetSegment(value) {
1013
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
1014
+ }
1015
+ function getSafeAgentAssetIdSegment(agentId) {
1016
+ const slug = slugifyAgentAssetSegment(agentId);
1017
+ return slug || "agent";
1018
+ }
1019
+ function getCanonicalAgentAssetDirectoryName(agentName, agentId) {
1020
+ if (!agentId || typeof agentId !== "string") {
1021
+ throw new Error("getCanonicalAgentAssetDirectoryName requires a non-empty agentId");
1022
+ }
1023
+ const safeId = getSafeAgentAssetIdSegment(agentId);
1024
+ const nameSlug = slugifyAgentAssetSegment(agentName ?? "");
1025
+ const prefix = nameSlug || safeId;
1026
+ return `${prefix}-${safeId}`;
1027
+ }
1028
+ function getLegacyAgentAssetDirectoryName(agentId) {
1029
+ if (!agentId || typeof agentId !== "string") {
1030
+ throw new Error("getLegacyAgentAssetDirectoryName requires a non-empty agentId");
1031
+ }
1032
+ return agentId;
1033
+ }
1034
+ function getCanonicalAgentInstructionsBundleDirName(agentName, agentId) {
1035
+ return `${getCanonicalAgentAssetDirectoryName(agentName, agentId)}-instructions`;
1036
+ }
1037
+ function getLegacyAgentInstructionsBundleDirName(agentId) {
1038
+ return `${getLegacyAgentAssetDirectoryName(agentId)}-instructions`;
1039
+ }
1040
+ function getDefaultHeartbeatProcedurePath(agentId, agentName) {
1013
1041
  if (!agentId || typeof agentId !== "string") {
1014
1042
  throw new Error("getDefaultHeartbeatProcedurePath requires a non-empty agentId");
1015
1043
  }
1016
- return `.fusion/agents/${agentId}/HEARTBEAT.md`;
1044
+ const directory = agentName ? getCanonicalAgentAssetDirectoryName(agentName, agentId) : getLegacyAgentAssetDirectoryName(agentId);
1045
+ return `.fusion/agents/${directory}/HEARTBEAT.md`;
1017
1046
  }
1018
1047
  function agentToConfigSnapshot(agent) {
1019
1048
  return {
@@ -2707,7 +2736,7 @@ var init_db = __esm({
2707
2736
  "use strict";
2708
2737
  init_sqlite_adapter();
2709
2738
  init_types();
2710
- SCHEMA_VERSION = 57;
2739
+ SCHEMA_VERSION = 58;
2711
2740
  SCHEMA_SQL = `
2712
2741
  -- Tasks table with JSON columns for nested data
2713
2742
  CREATE TABLE IF NOT EXISTS tasks (
@@ -4408,6 +4437,27 @@ CREATE INDEX IF NOT EXISTS idxTodoItemsSortOrder ON todo_items(listId, sortOrder
4408
4437
  }
4409
4438
  });
4410
4439
  }
4440
+ if (version < 58) {
4441
+ this.applyMigration(58, () => {
4442
+ const newCommand = "npx runfusion.ai backup --create";
4443
+ if (this.hasTable("automations") && this.hasColumn("automations", "command")) {
4444
+ this.db.prepare(
4445
+ `UPDATE automations
4446
+ SET command = ?, updatedAt = ?
4447
+ WHERE name = 'Database Backup'
4448
+ AND (command LIKE 'fn backup%' OR command LIKE 'kb backup%' OR command LIKE 'fusion backup%')`
4449
+ ).run(newCommand, (/* @__PURE__ */ new Date()).toISOString());
4450
+ }
4451
+ if (this.hasTable("routines") && this.hasColumn("routines", "command")) {
4452
+ this.db.prepare(
4453
+ `UPDATE routines
4454
+ SET command = ?, updatedAt = ?
4455
+ WHERE name = 'Database Backup'
4456
+ AND (command LIKE 'fn backup%' OR command LIKE 'kb backup%' OR command LIKE 'fusion backup%')`
4457
+ ).run(newCommand, (/* @__PURE__ */ new Date()).toISOString());
4458
+ }
4459
+ });
4460
+ }
4411
4461
  }
4412
4462
  /**
4413
4463
  * Run a single migration step inside a transaction and bump the version.
@@ -4671,7 +4721,7 @@ var init_agent_store = __esm({
4671
4721
  if (agent.heartbeatProcedurePath !== DEFAULT_HEARTBEAT_PROCEDURE_PATH) {
4672
4722
  continue;
4673
4723
  }
4674
- const newRelPath = getDefaultHeartbeatProcedurePath(agent.id);
4724
+ const newRelPath = await this.resolveCompatibleHeartbeatProcedurePath(agent);
4675
4725
  const newAbsPath = join3(this.rootDir, "..", newRelPath);
4676
4726
  if (legacyContent !== null) {
4677
4727
  try {
@@ -4832,7 +4882,7 @@ var init_agent_store = __esm({
4832
4882
  const metadata = input.metadata ?? {};
4833
4883
  const runtimeConfig = resolveCreationRuntimeConfig(input.runtimeConfig, metadata);
4834
4884
  const ephemeral = isEphemeralAgent({ metadata, name: input.name, role: input.role, reportsTo: input.reportsTo });
4835
- const resolvedHeartbeatProcedurePath = input.heartbeatProcedurePath ?? (ephemeral ? void 0 : getDefaultHeartbeatProcedurePath(agentId));
4885
+ const resolvedHeartbeatProcedurePath = input.heartbeatProcedurePath ?? (ephemeral ? void 0 : getDefaultHeartbeatProcedurePath(agentId, input.name));
4836
4886
  const agent = {
4837
4887
  id: agentId,
4838
4888
  name: input.name.trim(),
@@ -5069,14 +5119,16 @@ var init_agent_store = __esm({
5069
5119
  * Does not create the directory.
5070
5120
  */
5071
5121
  getInstructionsDir(agentId) {
5072
- return this.getBundleDir(agentId);
5122
+ const agent = this.readAgent(agentId);
5123
+ const agentName = agent?.name ?? "";
5124
+ return join3(this.agentsDir, getCanonicalAgentInstructionsBundleDirName(agentName, agentId));
5073
5125
  }
5074
5126
  /**
5075
5127
  * List markdown files in an agent's managed instructions bundle.
5076
5128
  * Returns [] when the bundle directory does not exist.
5077
5129
  */
5078
5130
  async listBundleFiles(agentId) {
5079
- const bundleDir = this.getBundleDir(agentId);
5131
+ const bundleDir = await this.resolveCompatibleBundleDir(agentId, false);
5080
5132
  try {
5081
5133
  const entries = await readdir(bundleDir, { withFileTypes: true });
5082
5134
  return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
@@ -5092,7 +5144,8 @@ var init_agent_store = __esm({
5092
5144
  */
5093
5145
  async readBundleFile(agentId, filePath) {
5094
5146
  this.validateBundleFilePath(filePath);
5095
- const resolvedPath = join3(this.getBundleDir(agentId), filePath);
5147
+ const bundleDir = await this.resolveCompatibleBundleDir(agentId, false);
5148
+ const resolvedPath = join3(bundleDir, filePath);
5096
5149
  return readFile(resolvedPath, "utf-8");
5097
5150
  }
5098
5151
  /**
@@ -5101,7 +5154,7 @@ var init_agent_store = __esm({
5101
5154
  async writeBundleFile(agentId, filePath, content) {
5102
5155
  return this.withLock(agentId, async () => {
5103
5156
  this.validateBundleFilePath(filePath);
5104
- const bundleDir = this.getBundleDir(agentId);
5157
+ const bundleDir = await this.resolveCompatibleBundleDir(agentId, true);
5105
5158
  await mkdir(bundleDir, { recursive: true });
5106
5159
  const existingFiles = await this.listBundleFiles(agentId);
5107
5160
  const isOverwrite = existingFiles.includes(filePath);
@@ -5120,7 +5173,8 @@ var init_agent_store = __esm({
5120
5173
  async deleteBundleFile(agentId, filePath) {
5121
5174
  return this.withLock(agentId, async () => {
5122
5175
  this.validateBundleFilePath(filePath);
5123
- await unlink(join3(this.getBundleDir(agentId), filePath));
5176
+ const bundleDir = await this.resolveCompatibleBundleDir(agentId, false);
5177
+ await unlink(join3(bundleDir, filePath));
5124
5178
  });
5125
5179
  }
5126
5180
  /**
@@ -5142,7 +5196,7 @@ var init_agent_store = __esm({
5142
5196
  };
5143
5197
  const updated = await this.updateAgent(agentId, { bundleConfig: normalizedConfig });
5144
5198
  if (normalizedConfig.mode === "managed") {
5145
- await mkdir(this.getBundleDir(agentId), { recursive: true });
5199
+ await mkdir(await this.resolveCompatibleBundleDir(agentId, true), { recursive: true });
5146
5200
  }
5147
5201
  return updated;
5148
5202
  }
@@ -5165,7 +5219,7 @@ var init_agent_store = __esm({
5165
5219
  bundleConfig: { mode: "managed", entryFile, files: [] }
5166
5220
  });
5167
5221
  }
5168
- await mkdir(this.getBundleDir(agentId), { recursive: true });
5222
+ await mkdir(await this.resolveCompatibleBundleDir(agentId, true), { recursive: true });
5169
5223
  const files = [];
5170
5224
  if (hasInstructionsText) {
5171
5225
  await this.writeBundleFile(agentId, entryFile, agent.instructionsText ?? "");
@@ -6126,8 +6180,87 @@ var init_agent_store = __esm({
6126
6180
  }
6127
6181
  return null;
6128
6182
  }
6129
- getBundleDir(agentId) {
6130
- return join3(this.agentsDir, `${agentId}-instructions`);
6183
+ getCanonicalBundleDir(agent) {
6184
+ return join3(this.agentsDir, getCanonicalAgentInstructionsBundleDirName(agent.name, agent.id));
6185
+ }
6186
+ getLegacyBundleDir(agentId) {
6187
+ return join3(this.agentsDir, getLegacyAgentInstructionsBundleDirName(agentId));
6188
+ }
6189
+ async resolveCompatibleBundleDir(agentId, createIfMissing) {
6190
+ const agent = this.readAgent(agentId);
6191
+ if (!agent) {
6192
+ throw new Error(`Agent ${agentId} not found`);
6193
+ }
6194
+ const canonicalDir = this.getCanonicalBundleDir(agent);
6195
+ if (await this.pathExists(canonicalDir)) {
6196
+ return canonicalDir;
6197
+ }
6198
+ const compatibleDir = await this.findExistingDisplayNameBundleDir(agent);
6199
+ if (compatibleDir) {
6200
+ return compatibleDir;
6201
+ }
6202
+ const legacyDir = this.getLegacyBundleDir(agent.id);
6203
+ if (await this.pathExists(legacyDir)) {
6204
+ return legacyDir;
6205
+ }
6206
+ return createIfMissing ? canonicalDir : canonicalDir;
6207
+ }
6208
+ async findExistingDisplayNameBundleDir(agent) {
6209
+ const safeId = getSafeAgentAssetIdSegment(agent.id);
6210
+ try {
6211
+ const entries = await readdir(this.agentsDir, { withFileTypes: true });
6212
+ const candidates = entries.filter((entry) => entry.isDirectory() && entry.name.endsWith("-instructions")).map((entry) => entry.name).filter((name) => {
6213
+ const base = name.slice(0, -"-instructions".length);
6214
+ return base.endsWith(`-${safeId}`);
6215
+ }).sort((a, b) => a.localeCompare(b));
6216
+ if (candidates.length === 0) {
6217
+ return null;
6218
+ }
6219
+ const canonicalName = getCanonicalAgentInstructionsBundleDirName(agent.name, agent.id);
6220
+ const selected = candidates.find((candidate) => candidate === canonicalName) ?? candidates[0];
6221
+ return join3(this.agentsDir, selected);
6222
+ } catch (err) {
6223
+ if (err.code === "ENOENT") {
6224
+ return null;
6225
+ }
6226
+ throw err;
6227
+ }
6228
+ }
6229
+ async resolveCompatibleHeartbeatProcedurePath(agent) {
6230
+ const canonicalPath = getDefaultHeartbeatProcedurePath(agent.id, agent.name);
6231
+ const canonicalAbs = join3(this.rootDir, "..", canonicalPath);
6232
+ if (await this.pathExists(canonicalAbs)) {
6233
+ return canonicalPath;
6234
+ }
6235
+ const safeId = getSafeAgentAssetIdSegment(agent.id);
6236
+ try {
6237
+ const entries = await readdir(this.agentsDir, { withFileTypes: true });
6238
+ const compatibleDir = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).find((name) => name.endsWith(`-${safeId}`));
6239
+ if (compatibleDir) {
6240
+ const candidatePath = `.fusion/agents/${compatibleDir}/HEARTBEAT.md`;
6241
+ if (await this.pathExists(join3(this.rootDir, "..", candidatePath))) {
6242
+ return candidatePath;
6243
+ }
6244
+ }
6245
+ } catch (err) {
6246
+ if (err.code !== "ENOENT") {
6247
+ throw err;
6248
+ }
6249
+ }
6250
+ const legacyPath = `.fusion/agents/${getLegacyAgentAssetDirectoryName(agent.id)}/HEARTBEAT.md`;
6251
+ const legacyAbs = join3(this.rootDir, "..", legacyPath);
6252
+ if (await this.pathExists(legacyAbs)) {
6253
+ return legacyPath;
6254
+ }
6255
+ return canonicalPath;
6256
+ }
6257
+ async pathExists(path5) {
6258
+ try {
6259
+ await access(path5, fsConstants.F_OK);
6260
+ return true;
6261
+ } catch {
6262
+ return false;
6263
+ }
6131
6264
  }
6132
6265
  validateBundleFilePath(filePath) {
6133
6266
  if (typeof filePath !== "string") {
@@ -7013,9 +7146,9 @@ var init_global_settings = __esm({
7013
7146
  * Serialize operations via promise chain to prevent lost-update races.
7014
7147
  */
7015
7148
  withLock(fn) {
7016
- let resolve40;
7149
+ let resolve42;
7017
7150
  const next = new Promise((r) => {
7018
- resolve40 = r;
7151
+ resolve42 = r;
7019
7152
  });
7020
7153
  const prev = this.lock;
7021
7154
  this.lock = next;
@@ -7023,7 +7156,7 @@ var init_global_settings = __esm({
7023
7156
  try {
7024
7157
  return await fn();
7025
7158
  } finally {
7026
- resolve40();
7159
+ resolve42();
7027
7160
  }
7028
7161
  });
7029
7162
  }
@@ -10240,7 +10373,7 @@ function validatePluginManifest(manifest) {
10240
10373
  const m = manifest;
10241
10374
  if (!m.id || typeof m.id !== "string" || m.id.trim() === "") {
10242
10375
  errors.push("id is required and must be a non-empty string");
10243
- } else if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(m.id)) {
10376
+ } else if (!SLUG_PATTERN.test(m.id)) {
10244
10377
  errors.push("id must be a valid slug (lowercase, alphanumeric, hyphens only, cannot start or end with hyphen)");
10245
10378
  }
10246
10379
  if (!m.name || typeof m.name !== "string" || m.name.trim() === "") {
@@ -10293,7 +10426,7 @@ function validatePluginManifest(manifest) {
10293
10426
  const runtime = m.runtime;
10294
10427
  if (!runtime.runtimeId || typeof runtime.runtimeId !== "string" || runtime.runtimeId.trim() === "") {
10295
10428
  errors.push("runtime.runtimeId is required and must be a non-empty string");
10296
- } else if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(runtime.runtimeId)) {
10429
+ } else if (!SLUG_PATTERN.test(runtime.runtimeId)) {
10297
10430
  errors.push("runtime.runtimeId must be a valid slug (lowercase, alphanumeric, hyphens only, cannot start or end with hyphen)");
10298
10431
  }
10299
10432
  if (!runtime.name || typeof runtime.name !== "string" || runtime.name.trim() === "") {
@@ -10308,14 +10441,93 @@ function validatePluginManifest(manifest) {
10308
10441
  }
10309
10442
  }
10310
10443
  }
10444
+ if (m.skills !== void 0) {
10445
+ if (!Array.isArray(m.skills)) {
10446
+ errors.push("skills must be an array");
10447
+ } else {
10448
+ for (const [index2, skill] of m.skills.entries()) {
10449
+ if (!skill || typeof skill !== "object") {
10450
+ errors.push(`skills[${index2}] must be an object`);
10451
+ continue;
10452
+ }
10453
+ const skillMeta = skill;
10454
+ if (!skillMeta.skillId || typeof skillMeta.skillId !== "string" || skillMeta.skillId.trim() === "") {
10455
+ errors.push(`skills[${index2}].skillId is required and must be a non-empty string`);
10456
+ } else if (!SLUG_PATTERN.test(skillMeta.skillId)) {
10457
+ errors.push(`skills[${index2}].skillId must be a valid slug (lowercase, alphanumeric, hyphens only, cannot start or end with hyphen)`);
10458
+ }
10459
+ if (!skillMeta.name || typeof skillMeta.name !== "string" || skillMeta.name.trim() === "") {
10460
+ errors.push(`skills[${index2}].name is required and must be a non-empty string`);
10461
+ }
10462
+ }
10463
+ }
10464
+ }
10465
+ if (m.workflowSteps !== void 0) {
10466
+ if (!Array.isArray(m.workflowSteps)) {
10467
+ errors.push("workflowSteps must be an array");
10468
+ } else {
10469
+ for (const [index2, step] of m.workflowSteps.entries()) {
10470
+ if (!step || typeof step !== "object") {
10471
+ errors.push(`workflowSteps[${index2}] must be an object`);
10472
+ continue;
10473
+ }
10474
+ const stepMeta = step;
10475
+ if (!stepMeta.stepId || typeof stepMeta.stepId !== "string" || stepMeta.stepId.trim() === "") {
10476
+ errors.push(`workflowSteps[${index2}].stepId is required and must be a non-empty string`);
10477
+ } else if (!SLUG_PATTERN.test(stepMeta.stepId)) {
10478
+ errors.push(`workflowSteps[${index2}].stepId must be a valid slug (lowercase, alphanumeric, hyphens only, cannot start or end with hyphen)`);
10479
+ }
10480
+ if (!stepMeta.name || typeof stepMeta.name !== "string" || stepMeta.name.trim() === "") {
10481
+ errors.push(`workflowSteps[${index2}].name is required and must be a non-empty string`);
10482
+ }
10483
+ if (stepMeta.mode !== void 0 && (typeof stepMeta.mode !== "string" || !["prompt", "script"].includes(stepMeta.mode))) {
10484
+ errors.push(`workflowSteps[${index2}].mode must be one of: prompt, script`);
10485
+ }
10486
+ }
10487
+ }
10488
+ }
10489
+ if (m.promptSurfaces !== void 0) {
10490
+ if (!Array.isArray(m.promptSurfaces)) {
10491
+ errors.push("promptSurfaces must be an array");
10492
+ } else {
10493
+ for (const [index2, surface] of m.promptSurfaces.entries()) {
10494
+ if (typeof surface !== "string" || !PROMPT_CONTRIBUTION_SURFACES.includes(surface)) {
10495
+ errors.push(`promptSurfaces[${index2}] must be one of: ${PROMPT_CONTRIBUTION_SURFACES.join(", ")}`);
10496
+ }
10497
+ }
10498
+ }
10499
+ }
10500
+ if (m.setup !== void 0) {
10501
+ if (typeof m.setup !== "object" || m.setup === null) {
10502
+ errors.push("setup must be an object");
10503
+ } else {
10504
+ const setup = m.setup;
10505
+ if (!setup.binaryName || typeof setup.binaryName !== "string" || setup.binaryName.trim() === "") {
10506
+ errors.push("setup.binaryName is required and must be a non-empty string");
10507
+ }
10508
+ if (!setup.description || typeof setup.description !== "string" || setup.description.trim() === "") {
10509
+ errors.push("setup.description is required and must be a non-empty string");
10510
+ }
10511
+ if (setup.channel !== void 0 && (typeof setup.channel !== "string" || !SETUP_CHANNELS.includes(setup.channel))) {
10512
+ errors.push(`setup.channel must be one of: ${SETUP_CHANNELS.join(", ")}`);
10513
+ }
10514
+ if (setup.defaultTimeoutMs !== void 0 && (typeof setup.defaultTimeoutMs !== "number" || !Number.isFinite(setup.defaultTimeoutMs) || setup.defaultTimeoutMs <= 0)) {
10515
+ errors.push("setup.defaultTimeoutMs must be a positive finite number");
10516
+ }
10517
+ }
10518
+ }
10311
10519
  return {
10312
10520
  valid: errors.length === 0,
10313
10521
  errors
10314
10522
  };
10315
10523
  }
10524
+ var SLUG_PATTERN, PROMPT_CONTRIBUTION_SURFACES, SETUP_CHANNELS;
10316
10525
  var init_plugin_types = __esm({
10317
10526
  "../core/src/plugin-types.ts"() {
10318
10527
  "use strict";
10528
+ SLUG_PATTERN = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
10529
+ PROMPT_CONTRIBUTION_SURFACES = ["executor-system", "executor-task", "triage", "reviewer", "heartbeat"];
10530
+ SETUP_CHANNELS = ["stable", "beta", "nightly"];
10319
10531
  }
10320
10532
  });
10321
10533
 
@@ -28483,8 +28695,8 @@ var require_CronFileParser = __commonJS({
28483
28695
  * @throws If file cannot be read
28484
28696
  */
28485
28697
  static parseFileSync(filePath) {
28486
- const { readFileSync: readFileSync24 } = __require("fs");
28487
- const data = readFileSync24(filePath, "utf8");
28698
+ const { readFileSync: readFileSync25 } = __require("fs");
28699
+ const data = readFileSync25(filePath, "utf8");
28488
28700
  return _CronFileParser.#parseContent(data);
28489
28701
  }
28490
28702
  /**
@@ -28701,9 +28913,9 @@ var init_automation_store = __esm({
28701
28913
  */
28702
28914
  withScheduleLock(id, fn) {
28703
28915
  const prev = this.scheduleLocks.get(id) ?? Promise.resolve();
28704
- let resolve40;
28916
+ let resolve42;
28705
28917
  const next = new Promise((r) => {
28706
- resolve40 = r;
28918
+ resolve42 = r;
28707
28919
  });
28708
28920
  this.scheduleLocks.set(id, next);
28709
28921
  return prev.then(async () => {
@@ -28713,7 +28925,7 @@ var init_automation_store = __esm({
28713
28925
  if (this.scheduleLocks.get(id) === next) {
28714
28926
  this.scheduleLocks.delete(id);
28715
28927
  }
28716
- resolve40();
28928
+ resolve42();
28717
28929
  }
28718
28930
  });
28719
28931
  }
@@ -30502,7 +30714,7 @@ var init_project_memory = __esm({
30502
30714
  // ../core/src/run-command.ts
30503
30715
  import { spawn } from "node:child_process";
30504
30716
  function runCommandAsync(command, options = {}) {
30505
- return new Promise((resolve40) => {
30717
+ return new Promise((resolve42) => {
30506
30718
  const maxBuffer = options.maxBuffer ?? DEFAULT_MAX_BUFFER;
30507
30719
  let stdout = "";
30508
30720
  let stderr = "";
@@ -30561,7 +30773,7 @@ function runCommandAsync(command, options = {}) {
30561
30773
  clearTimeout(forceKillTimer);
30562
30774
  forceKillTimer = null;
30563
30775
  }
30564
- resolve40({
30776
+ resolve42({
30565
30777
  stdout,
30566
30778
  stderr,
30567
30779
  exitCode: null,
@@ -30579,7 +30791,7 @@ function runCommandAsync(command, options = {}) {
30579
30791
  }
30580
30792
  signalProcessGroup("SIGTERM");
30581
30793
  scheduleForceKill(NORMAL_CLEANUP_FORCE_KILL_DELAY_MS);
30582
- resolve40({
30794
+ resolve42({
30583
30795
  stdout,
30584
30796
  stderr,
30585
30797
  exitCode: code,
@@ -31794,9 +32006,9 @@ ${outcome}`;
31794
32006
  * lost-update races on the nextId counter.
31795
32007
  */
31796
32008
  withConfigLock(fn) {
31797
- let resolve40;
32009
+ let resolve42;
31798
32010
  const next = new Promise((r) => {
31799
- resolve40 = r;
32011
+ resolve42 = r;
31800
32012
  });
31801
32013
  const prev = this.configLock;
31802
32014
  this.configLock = next;
@@ -31804,7 +32016,7 @@ ${outcome}`;
31804
32016
  try {
31805
32017
  return await fn();
31806
32018
  } finally {
31807
- resolve40();
32019
+ resolve42();
31808
32020
  }
31809
32021
  });
31810
32022
  }
@@ -31814,9 +32026,9 @@ ${outcome}`;
31814
32026
  */
31815
32027
  withTaskLock(id, fn) {
31816
32028
  const prev = this.taskLocks.get(id) ?? Promise.resolve();
31817
- let resolve40;
32029
+ let resolve42;
31818
32030
  const next = new Promise((r) => {
31819
- resolve40 = r;
32031
+ resolve42 = r;
31820
32032
  });
31821
32033
  this.taskLocks.set(id, next);
31822
32034
  return prev.then(async () => {
@@ -31826,7 +32038,7 @@ ${outcome}`;
31826
32038
  if (this.taskLocks.get(id) === next) {
31827
32039
  this.taskLocks.delete(id);
31828
32040
  }
31829
- resolve40();
32041
+ resolve42();
31830
32042
  }
31831
32043
  });
31832
32044
  }
@@ -34094,7 +34306,7 @@ ${task.description}
34094
34306
  }
34095
34307
  }
34096
34308
  }
34097
- await new Promise((resolve40) => setImmediate(resolve40));
34309
+ await new Promise((resolve42) => setImmediate(resolve42));
34098
34310
  const selectClause = this.getTaskSelectClause(true);
34099
34311
  const changedRows = this.lastPollTime ? this.db.prepare(`SELECT ${selectClause} FROM tasks WHERE updatedAt > ? OR columnMovedAt > ?`).all(this.lastPollTime, this.lastPollTime) : this.db.prepare(`SELECT ${selectClause} FROM tasks`).all();
34100
34312
  this.lastPollTime = (/* @__PURE__ */ new Date()).toISOString();
@@ -34114,7 +34326,7 @@ ${task.description}
34114
34326
  this.emit("task:updated", task);
34115
34327
  }
34116
34328
  if (i > 0 && i % 50 === 0) {
34117
- await new Promise((resolve40) => setImmediate(resolve40));
34329
+ await new Promise((resolve42) => setImmediate(resolve42));
34118
34330
  }
34119
34331
  }
34120
34332
  const elapsed = Date.now() - startTime;
@@ -35859,6 +36071,20 @@ function reconcileClaudeCliPaths(paths, vendoredPath) {
35859
36071
  }
35860
36072
  return filtered;
35861
36073
  }
36074
+ function isExternalDroidCliPath(p, vendoredPath) {
36075
+ if (vendoredPath && p === vendoredPath) return false;
36076
+ return /(^|[/\\])droid-cli([/\\]|$)/i.test(p);
36077
+ }
36078
+ function reconcileDroidCliPaths(paths, vendoredPath) {
36079
+ if (!vendoredPath) {
36080
+ return [...paths];
36081
+ }
36082
+ const filtered = paths.filter((p) => !isExternalDroidCliPath(p, vendoredPath));
36083
+ if (!filtered.includes(vendoredPath)) {
36084
+ return [vendoredPath, ...filtered];
36085
+ }
36086
+ return filtered;
36087
+ }
35862
36088
  function getDisplayPathWithinRoot(root, targetPath) {
35863
36089
  const usesWindowsPaths = /^[A-Za-z]:[\\/]/.test(root) || /^[A-Za-z]:[\\/]/.test(targetPath) || root.includes("\\") || targetPath.includes("\\");
35864
36090
  const pathApi = usesWindowsPaths ? win32 : { relative: relative2, isAbsolute: isAbsolute5, sep: sep4 };
@@ -35970,7 +36196,7 @@ function runGh(args, cwd) {
35970
36196
  }
35971
36197
  function runGhAsync(args, cwdOrOptions) {
35972
36198
  const { cwd, signal: externalSignal, timeoutMs = DEFAULT_GH_TIMEOUT_MS } = normalizeRunGhOptions(cwdOrOptions);
35973
- return new Promise((resolve40, reject2) => {
36199
+ return new Promise((resolve42, reject2) => {
35974
36200
  if (externalSignal?.aborted) {
35975
36201
  reject2(makeGhError(`gh command aborted: ${describeAbortReason(externalSignal.reason)}`, "ABORT_ERR"));
35976
36202
  return;
@@ -36021,7 +36247,7 @@ function runGhAsync(args, cwdOrOptions) {
36021
36247
  ghError.stderr = stderr ?? "";
36022
36248
  reject2(ghError);
36023
36249
  } else {
36024
- resolve40(stdout ?? "");
36250
+ resolve42(stdout ?? "");
36025
36251
  }
36026
36252
  }
36027
36253
  );
@@ -36116,6 +36342,85 @@ var init_gh_cli = __esm({
36116
36342
  }
36117
36343
  });
36118
36344
 
36345
+ // ../core/src/fn-binary.ts
36346
+ import { spawn as spawn2 } from "node:child_process";
36347
+ import { platform as platform2 } from "node:os";
36348
+ function runProbe(command, args, timeoutMs) {
36349
+ return new Promise((resolve42) => {
36350
+ let stdout = "";
36351
+ let stderr = "";
36352
+ const child = spawn2(command, args, { stdio: ["ignore", "pipe", "pipe"], shell: false });
36353
+ const timer = setTimeout(() => {
36354
+ try {
36355
+ child.kill("SIGKILL");
36356
+ } catch {
36357
+ }
36358
+ }, timeoutMs);
36359
+ child.stdout?.on("data", (chunk) => {
36360
+ stdout += chunk.toString("utf8");
36361
+ });
36362
+ child.stderr?.on("data", (chunk) => {
36363
+ stderr += chunk.toString("utf8");
36364
+ });
36365
+ child.on("error", (err) => {
36366
+ clearTimeout(timer);
36367
+ resolve42({ exitCode: null, stdout, stderr: stderr || err.message });
36368
+ });
36369
+ child.on("close", (exitCode) => {
36370
+ clearTimeout(timer);
36371
+ resolve42({ exitCode, stdout, stderr });
36372
+ });
36373
+ });
36374
+ }
36375
+ async function whichBinary(name) {
36376
+ const isWindows = platform2() === "win32";
36377
+ const lookup = isWindows ? "where" : "which";
36378
+ const result = await runProbe(lookup, [name], 5e3);
36379
+ if (result.exitCode !== 0) return void 0;
36380
+ const firstLine = result.stdout.split(/\r?\n/).map((s) => s.trim()).find(Boolean);
36381
+ return firstLine || void 0;
36382
+ }
36383
+ async function probeVersion(binary) {
36384
+ const result = await runProbe(binary, ["--version"], 1e4);
36385
+ if (result.exitCode !== 0) return void 0;
36386
+ const text = (result.stdout || result.stderr).trim();
36387
+ if (!text) return void 0;
36388
+ const match = text.match(/\d+\.\d+\.\d+(?:-[\w.]+)?/);
36389
+ return match ? match[0] : text.split(/\s+/)[0];
36390
+ }
36391
+ async function detectFnBinary() {
36392
+ for (const candidate of CANDIDATES) {
36393
+ try {
36394
+ const resolvedPath = await whichBinary(candidate);
36395
+ if (!resolvedPath) continue;
36396
+ const version = await probeVersion(candidate);
36397
+ return {
36398
+ installed: true,
36399
+ binary: candidate,
36400
+ path: resolvedPath,
36401
+ version,
36402
+ invocation: candidate
36403
+ };
36404
+ } catch {
36405
+ }
36406
+ }
36407
+ return {
36408
+ installed: false,
36409
+ invocation: FN_NPX_INVOCATION
36410
+ };
36411
+ }
36412
+ var FN_NPM_PACKAGE, FN_INSTALL_CURL, FN_INSTALL_NPM, FN_NPX_INVOCATION, CANDIDATES;
36413
+ var init_fn_binary = __esm({
36414
+ "../core/src/fn-binary.ts"() {
36415
+ "use strict";
36416
+ FN_NPM_PACKAGE = "runfusion.ai";
36417
+ FN_INSTALL_CURL = "curl -fsSL https://runfusion.ai/install.sh | sh";
36418
+ FN_INSTALL_NPM = `npm install -g ${FN_NPM_PACKAGE}`;
36419
+ FN_NPX_INVOCATION = `npx -y ${FN_NPM_PACKAGE}`;
36420
+ CANDIDATES = ["fn", "fusion"];
36421
+ }
36422
+ });
36423
+
36119
36424
  // ../core/src/settings-validation.ts
36120
36425
  function validateUnavailableNodePolicy(value) {
36121
36426
  if (value === void 0) {
@@ -36309,9 +36614,9 @@ var init_routine_store = __esm({
36309
36614
  */
36310
36615
  withRoutineLock(id, fn) {
36311
36616
  const prev = this.routineLocks.get(id) ?? Promise.resolve();
36312
- let resolve40;
36617
+ let resolve42;
36313
36618
  const next = new Promise((r) => {
36314
- resolve40 = r;
36619
+ resolve42 = r;
36315
36620
  });
36316
36621
  this.routineLocks.set(id, next);
36317
36622
  return prev.then(async () => {
@@ -36321,7 +36626,7 @@ var init_routine_store = __esm({
36321
36626
  if (this.routineLocks.get(id) === next) {
36322
36627
  this.routineLocks.delete(id);
36323
36628
  }
36324
- resolve40();
36629
+ resolve42();
36325
36630
  }
36326
36631
  });
36327
36632
  }
@@ -36920,13 +37225,13 @@ var init_plugin_loader = __esm({
36920
37225
  * Execute a promise with a timeout.
36921
37226
  */
36922
37227
  withTimeout(promise, ms, timeoutMessage) {
36923
- return new Promise((resolve40, reject2) => {
37228
+ return new Promise((resolve42, reject2) => {
36924
37229
  const timer = setTimeout(() => {
36925
37230
  reject2(new Error(timeoutMessage));
36926
37231
  }, ms);
36927
37232
  promise.then((result) => {
36928
37233
  clearTimeout(timer);
36929
- resolve40(result);
37234
+ resolve42(result);
36930
37235
  }).catch((err) => {
36931
37236
  clearTimeout(timer);
36932
37237
  reject2(err);
@@ -40491,7 +40796,7 @@ var require_get_stream = __commonJS({
40491
40796
  };
40492
40797
  const { maxBuffer } = options;
40493
40798
  let stream;
40494
- await new Promise((resolve40, reject2) => {
40799
+ await new Promise((resolve42, reject2) => {
40495
40800
  const rejectPromise = (error) => {
40496
40801
  if (error && stream.getBufferedLength() <= BufferConstants.MAX_LENGTH) {
40497
40802
  error.bufferedData = stream.getBufferedValue();
@@ -40503,7 +40808,7 @@ var require_get_stream = __commonJS({
40503
40808
  rejectPromise(error);
40504
40809
  return;
40505
40810
  }
40506
- resolve40();
40811
+ resolve42();
40507
40812
  });
40508
40813
  stream.on("data", () => {
40509
40814
  if (stream.getBufferedLength() > maxBuffer) {
@@ -41797,7 +42102,7 @@ var require_extract_zip = __commonJS({
41797
42102
  debug("opening", this.zipPath, "with opts", this.opts);
41798
42103
  this.zipfile = await openZip(this.zipPath, { lazyEntries: true });
41799
42104
  this.canceled = false;
41800
- return new Promise((resolve40, reject2) => {
42105
+ return new Promise((resolve42, reject2) => {
41801
42106
  this.zipfile.on("error", (err) => {
41802
42107
  this.canceled = true;
41803
42108
  reject2(err);
@@ -41806,7 +42111,7 @@ var require_extract_zip = __commonJS({
41806
42111
  this.zipfile.on("close", () => {
41807
42112
  if (!this.canceled) {
41808
42113
  debug("zip extraction complete");
41809
- resolve40();
42114
+ resolve42();
41810
42115
  }
41811
42116
  });
41812
42117
  this.zipfile.on("entry", async (entry) => {
@@ -50403,6 +50708,10 @@ __export(src_exports, {
50403
50708
  EXECUTION_MODES: () => EXECUTION_MODES,
50404
50709
  FEATURE_LOOP_STATES: () => FEATURE_LOOP_STATES,
50405
50710
  FEATURE_STATUSES: () => FEATURE_STATUSES,
50711
+ FN_INSTALL_CURL: () => FN_INSTALL_CURL,
50712
+ FN_INSTALL_NPM: () => FN_INSTALL_NPM,
50713
+ FN_NPM_PACKAGE: () => FN_NPM_PACKAGE,
50714
+ FN_NPX_INVOCATION: () => FN_NPX_INVOCATION,
50406
50715
  FileMemoryBackend: () => FileMemoryBackend,
50407
50716
  FirstRunDetector: () => FirstRunDetector,
50408
50717
  GLOBAL_SETTINGS_KEYS: () => GLOBAL_SETTINGS_KEYS,
@@ -50514,6 +50823,7 @@ __export(src_exports, {
50514
50823
  createInsightExtractionAutomation: () => createInsightExtractionAutomation,
50515
50824
  createMemoryDreamsAutomation: () => createMemoryDreamsAutomation,
50516
50825
  dailyMemoryPath: () => dailyMemoryPath,
50826
+ detectFnBinary: () => detectFnBinary,
50517
50827
  detectLegacyData: () => detectLegacyData,
50518
50828
  diffConfigSnapshots: () => diffConfigSnapshots,
50519
50829
  discoverPiExtensions: () => discoverPiExtensions,
@@ -50635,6 +50945,7 @@ __export(src_exports, {
50635
50945
  readProjectMemoryWithBackend: () => readProjectMemoryWithBackend,
50636
50946
  readWorkingMemory: () => readWorkingMemory,
50637
50947
  reconcileClaudeCliPaths: () => reconcileClaudeCliPaths,
50948
+ reconcileDroidCliPaths: () => reconcileDroidCliPaths,
50638
50949
  refreshQmdProjectMemoryIndex: () => refreshQmdProjectMemoryIndex,
50639
50950
  registerMemoryBackend: () => registerMemoryBackend,
50640
50951
  renderMemoryAuditMarkdown: () => renderMemoryAuditMarkdown,
@@ -50724,6 +51035,7 @@ var init_src = __esm({
50724
51035
  init_automation();
50725
51036
  init_automation_store();
50726
51037
  init_run_command();
51038
+ init_fn_binary();
50727
51039
  init_node_override_guard();
50728
51040
  init_settings_validation();
50729
51041
  init_routine();
@@ -51885,12 +52197,12 @@ var init_concurrency = __esm({
51885
52197
  this._active++;
51886
52198
  return Promise.resolve();
51887
52199
  }
51888
- return new Promise((resolve40) => {
52200
+ return new Promise((resolve42) => {
51889
52201
  this._waiters.push({
51890
52202
  priority,
51891
52203
  resolve: () => {
51892
52204
  this._active++;
51893
- resolve40();
52205
+ resolve42();
51894
52206
  }
51895
52207
  });
51896
52208
  });
@@ -53454,8 +53766,23 @@ function getPackageManagerAgentDir() {
53454
53766
  }
53455
53767
  function resolveVendoredClaudeCliEntry() {
53456
53768
  try {
53457
- const require_2 = createRequire2(import.meta.url);
53458
- const pkgJsonPath = require_2.resolve("@fusion/pi-claude-cli/package.json");
53769
+ const require_3 = createRequire2(import.meta.url);
53770
+ const pkgJsonPath = require_3.resolve("@fusion/pi-claude-cli/package.json");
53771
+ const pkgJson = JSON.parse(readFileSync9(pkgJsonPath, "utf-8"));
53772
+ const extensions = pkgJson.pi?.extensions;
53773
+ if (!Array.isArray(extensions) || extensions.length === 0) return null;
53774
+ const entry = extensions[0];
53775
+ if (typeof entry !== "string" || entry.length === 0) return null;
53776
+ const path5 = resolve11(dirname8(pkgJsonPath), entry);
53777
+ return existsSync20(path5) ? path5 : null;
53778
+ } catch {
53779
+ return null;
53780
+ }
53781
+ }
53782
+ function resolveVendoredDroidCliEntry() {
53783
+ try {
53784
+ const require_3 = createRequire2(import.meta.url);
53785
+ const pkgJsonPath = require_3.resolve("@fusion/droid-cli/package.json");
53459
53786
  const pkgJson = JSON.parse(readFileSync9(pkgJsonPath, "utf-8"));
53460
53787
  const extensions = pkgJson.pi?.extensions;
53461
53788
  if (!Array.isArray(extensions) || extensions.length === 0) return null;
@@ -53482,8 +53809,13 @@ async function registerExtensionProviders(cwd, modelRegistry) {
53482
53809
  [...getEnabledPiExtensionPaths(cwd), ...packageExtensionPaths],
53483
53810
  vendoredClaudeCli
53484
53811
  );
53485
- const extensionsResult = await discoverAndLoadExtensions(
53812
+ const vendoredDroidCli = resolveVendoredDroidCliEntry();
53813
+ const doubleReconciledPaths = reconcileDroidCliPaths(
53486
53814
  reconciledPaths,
53815
+ vendoredDroidCli
53816
+ );
53817
+ const extensionsResult = await discoverAndLoadExtensions(
53818
+ doubleReconciledPaths,
53487
53819
  cwd,
53488
53820
  join25(resolvePiExtensionProjectRoot(cwd), ".fusion", "disabled-auto-extension-discovery")
53489
53821
  );
@@ -53727,11 +54059,8 @@ async function createFnAgent2(options) {
53727
54059
  const createSessionWithModel = async (modelOverride) => {
53728
54060
  const customToolList = [
53729
54061
  ...wrappedTools,
53730
- ...isReadonly ? [] : options.customTools ?? []
54062
+ ...options.customTools ?? []
53731
54063
  ];
53732
- if (isReadonly && (options.customTools?.length ?? 0) > 0) {
53733
- piLog.log(`readonly session \u2014 customTools (${options.customTools.length}) skipped`);
53734
- }
53735
54064
  if (options.beforeSpawnSession) {
53736
54065
  await options.beforeSpawnSession();
53737
54066
  }
@@ -54422,8 +54751,8 @@ var init_page_fetch_provider = __esm({
54422
54751
 
54423
54752
  // ../engine/src/research/providers/web-search-provider.ts
54424
54753
  async function sleep(ms, signal) {
54425
- await new Promise((resolve40, reject2) => {
54426
- const timer = setTimeout(resolve40, ms);
54754
+ await new Promise((resolve42, reject2) => {
54755
+ const timer = setTimeout(resolve42, ms);
54427
54756
  const onAbort = () => {
54428
54757
  clearTimeout(timer);
54429
54758
  reject2(new ResearchProviderError({ providerType: "web-search", code: "abort", message: "Search aborted" }));
@@ -55140,14 +55469,25 @@ async function getAgentMemoryWindow(rootDir, agentMemory, path5, startLine = 1,
55140
55469
  backend: "agent-memory"
55141
55470
  };
55142
55471
  }
55143
- function createTaskCreateTool(store, provenance) {
55472
+ async function createAgentTask(store, input, options) {
55473
+ const settings = typeof store.getSettings === "function" ? await store.getSettings() : {};
55474
+ const rootDir = options?.rootDir;
55475
+ return store.createTask(input, {
55476
+ settings: { autoSummarizeTitles: settings.autoSummarizeTitles === true },
55477
+ onSummarize: rootDir ? async (description) => {
55478
+ const resolved = resolveTitleSummarizerSettingsModel(settings);
55479
+ return summarizeTitle(description, rootDir, resolved.provider, resolved.modelId);
55480
+ } : void 0
55481
+ });
55482
+ }
55483
+ function createTaskCreateTool(store, provenance, options) {
55144
55484
  return {
55145
55485
  name: "fn_task_create",
55146
55486
  label: "Create Task",
55147
55487
  description: "Create a new task for out-of-scope work discovered during execution. The task goes into triage where it will be specified by the AI. Optionally set dependencies (e.g., the new task depends on the current one, or the current task should wait for the new one).",
55148
55488
  parameters: taskCreateParams,
55149
55489
  execute: async (_id, params) => {
55150
- const task = await store.createTask({
55490
+ const task = await createAgentTask(store, {
55151
55491
  description: params.description,
55152
55492
  dependencies: params.dependencies,
55153
55493
  column: "triage",
@@ -55156,7 +55496,7 @@ function createTaskCreateTool(store, provenance) {
55156
55496
  sourceAgentId: provenance.sourceAgentId,
55157
55497
  sourceRunId: provenance.sourceRunId
55158
55498
  } : void 0
55159
- });
55499
+ }, options);
55160
55500
  const deps = task.dependencies.length ? ` (depends on: ${task.dependencies.join(", ")})` : "";
55161
55501
  return {
55162
55502
  content: [{
@@ -55509,7 +55849,7 @@ ${lines.join("\n\n")}` }],
55509
55849
  }
55510
55850
  };
55511
55851
  }
55512
- function createDelegateTaskTool(agentStore, taskStore) {
55852
+ function createDelegateTaskTool(agentStore, taskStore, options) {
55513
55853
  return {
55514
55854
  name: "fn_delegate_task",
55515
55855
  label: "Delegate Task",
@@ -55529,13 +55869,13 @@ function createDelegateTaskTool(agentStore, taskStore) {
55529
55869
  details: {}
55530
55870
  };
55531
55871
  }
55532
- const task = await taskStore.createTask({
55872
+ const task = await createAgentTask(taskStore, {
55533
55873
  description: params.description,
55534
55874
  dependencies: params.dependencies,
55535
55875
  column: "todo",
55536
55876
  assignedAgentId: params.agent_id,
55537
55877
  source: { sourceType: "api" }
55538
- });
55878
+ }, options);
55539
55879
  const deps = task.dependencies.length ? ` (depends on: ${task.dependencies.join(", ")})` : "";
55540
55880
  return {
55541
55881
  content: [{
@@ -55710,9 +56050,9 @@ function createResearchTools(options) {
55710
56050
  const maxWaitMs = Math.max(1e3, Math.min(params.max_wait_ms ?? 9e4, resolved.limits.maxDurationMs));
55711
56051
  const completed = await Promise.race([
55712
56052
  runPromise,
55713
- new Promise((resolve40) => setTimeout(() => {
56053
+ new Promise((resolve42) => setTimeout(() => {
55714
56054
  const latest = options.store.getResearchStore().getRun(runId);
55715
- resolve40(latest ?? {
56055
+ resolve42(latest ?? {
55716
56056
  id: runId,
55717
56057
  query: params.query,
55718
56058
  status: "running",
@@ -57286,20 +57626,20 @@ async function withRateLimitRetry(fn, options = {}) {
57286
57626
  throw lastError ?? new Error("withRateLimitRetry: unexpected state");
57287
57627
  }
57288
57628
  function sleep2(ms, signal) {
57289
- return new Promise((resolve40, reject2) => {
57629
+ return new Promise((resolve42, reject2) => {
57290
57630
  if (signal?.aborted) {
57291
57631
  reject2(signal.reason ?? new Error("Aborted"));
57292
57632
  return;
57293
57633
  }
57294
- const timer = setTimeout(resolve40, ms);
57634
+ const timer = setTimeout(resolve42, ms);
57295
57635
  if (signal) {
57296
57636
  const onAbort = () => {
57297
57637
  clearTimeout(timer);
57298
57638
  reject2(signal.reason ?? new Error("Aborted"));
57299
57639
  };
57300
57640
  signal.addEventListener("abort", onAbort, { once: true });
57301
- const origResolve = resolve40;
57302
- resolve40 = () => {
57641
+ const origResolve = resolve42;
57642
+ resolve42 = () => {
57303
57643
  signal.removeEventListener("abort", onAbort);
57304
57644
  origResolve();
57305
57645
  };
@@ -58362,7 +58702,7 @@ Write the PROMPT.md directly using the write tool, then call \`fn_review_spec()\
58362
58702
  // Agent delegation tools — discover and delegate work to other agents.
58363
58703
  ...this.options.agentStore ? [
58364
58704
  createListAgentsTool(this.options.agentStore),
58365
- createDelegateTaskTool(this.options.agentStore, this.store)
58705
+ createDelegateTaskTool(this.options.agentStore, this.store, { rootDir: this.rootDir })
58366
58706
  ] : [],
58367
58707
  this.createReviewSpecTool(
58368
58708
  task.id,
@@ -58901,7 +59241,7 @@ Remove or replace these ids and call fn_task_create again.`
58901
59241
  planLog.warn(`${options.parentTaskId}: failed to load parent task for fn_task_create inheritance: ${msg}`);
58902
59242
  parentTask = void 0;
58903
59243
  }
58904
- const newTask = await store.createTask({
59244
+ const newTask = await createAgentTask(store, {
58905
59245
  title: params.title,
58906
59246
  description: params.description,
58907
59247
  dependencies: validDeps,
@@ -58915,7 +59255,7 @@ Remove or replace these ids and call fn_task_create again.`
58915
59255
  sourceType: "agent_heartbeat",
58916
59256
  sourceParentTaskId: options.parentTaskId
58917
59257
  }
58918
- });
59258
+ }, { rootDir: this.rootDir });
58919
59259
  options.createdSubtasksRef.current.push(newTask.id);
58920
59260
  return {
58921
59261
  content: [
@@ -59343,13 +59683,13 @@ var init_run_audit = __esm({
59343
59683
  });
59344
59684
 
59345
59685
  // ../engine/src/merger.ts
59346
- import { execSync, exec as exec2, spawn as spawn2 } from "node:child_process";
59686
+ import { execSync, exec as exec2, spawn as spawn3 } from "node:child_process";
59347
59687
  import { promisify as promisify3 } from "node:util";
59348
59688
  import { existsSync as existsSync22 } from "node:fs";
59349
59689
  import { join as join29 } from "node:path";
59350
59690
  import { Type as Type3 } from "typebox";
59351
59691
  async function execWithProcessGroup(command, options) {
59352
- return new Promise((resolve40, reject2) => {
59692
+ return new Promise((resolve42, reject2) => {
59353
59693
  if (options.signal?.aborted) {
59354
59694
  reject2(Object.assign(
59355
59695
  new Error(`Command aborted before start: ${command}`),
@@ -59358,7 +59698,7 @@ async function execWithProcessGroup(command, options) {
59358
59698
  return;
59359
59699
  }
59360
59700
  const useProcessGroup = process.platform !== "win32";
59361
- const child = spawn2(command, {
59701
+ const child = spawn3(command, {
59362
59702
  cwd: options.cwd,
59363
59703
  shell: true,
59364
59704
  detached: useProcessGroup,
@@ -59442,7 +59782,7 @@ async function execWithProcessGroup(command, options) {
59442
59782
  return;
59443
59783
  }
59444
59784
  if (code === 0) {
59445
- resolve40({ stdout, stderr, bufferOverflow: stdoutOverflow || stderrOverflow });
59785
+ resolve42({ stdout, stderr, bufferOverflow: stdoutOverflow || stderrOverflow });
59446
59786
  return;
59447
59787
  }
59448
59788
  reject2(Object.assign(
@@ -63866,7 +64206,7 @@ function resolveExecutorModelPair(taskModelProvider, taskModelId, settings) {
63866
64206
  return { provider: void 0, modelId: void 0 };
63867
64207
  }
63868
64208
  function sleep3(ms) {
63869
- return new Promise((resolve40) => setTimeout(resolve40, ms));
64209
+ return new Promise((resolve42) => setTimeout(resolve42, ms));
63870
64210
  }
63871
64211
  var execAsync4, stepExecLog, MAX_STEP_RETRIES, RETRY_DELAYS_MS, NOOP_TASK_STORE, StepSessionExecutor;
63872
64212
  var init_step_session_executor = __esm({
@@ -64094,10 +64434,10 @@ var init_step_session_executor = __esm({
64094
64434
  ] : [];
64095
64435
  const memoryTools = createMemoryTools(this.options.rootDir, settings);
64096
64436
  const taskLogTool = this.options.store ? [createTaskLogTool(this.options.store, taskDetail.id)] : [];
64097
- const taskCreateTool = this.options.store ? [createTaskCreateTool(this.options.store)] : [];
64437
+ const taskCreateTool = this.options.store ? [createTaskCreateTool(this.options.store, void 0, { rootDir: this.options.rootDir })] : [];
64098
64438
  const delegationTools = this.options.agentStore ? [
64099
64439
  createListAgentsTool(this.options.agentStore),
64100
- createDelegateTaskTool(this.options.agentStore, this.options.store)
64440
+ createDelegateTaskTool(this.options.agentStore, this.options.store, { rootDir: this.options.rootDir })
64101
64441
  ] : [];
64102
64442
  const messagingTools = this.options.messageStore && taskDetail.assignedAgentId ? [
64103
64443
  createSendMessageTool(this.options.messageStore, taskDetail.assignedAgentId),
@@ -64508,7 +64848,7 @@ var init_task_completion = __esm({
64508
64848
  });
64509
64849
 
64510
64850
  // ../engine/src/run-verification-tool.ts
64511
- import { spawn as spawn3 } from "node:child_process";
64851
+ import { spawn as spawn4 } from "node:child_process";
64512
64852
  import { existsSync as existsSync26 } from "node:fs";
64513
64853
  import { isAbsolute as isAbsolute10, join as join34 } from "node:path";
64514
64854
  import { Type as Type4 } from "@mariozechner/pi-ai";
@@ -64540,8 +64880,8 @@ async function runVerificationCommand2(opts) {
64540
64880
  const warnings = [];
64541
64881
  const stdoutBuf = { head: "", tail: "", totalBytes: 0 };
64542
64882
  const stderrBuf = { head: "", tail: "", totalBytes: 0 };
64543
- return new Promise((resolve40) => {
64544
- const child = spawn3(command, {
64883
+ return new Promise((resolve42) => {
64884
+ const child = spawn4(command, {
64545
64885
  cwd,
64546
64886
  stdio: ["ignore", "pipe", "pipe"],
64547
64887
  env: { ...process.env },
@@ -64619,7 +64959,7 @@ async function runVerificationCommand2(opts) {
64619
64959
  `[fn_run_verification] command failed (exit=${exitCode}, signal=${signal ?? "none"}): ${command}`
64620
64960
  );
64621
64961
  }
64622
- resolve40({
64962
+ resolve42({
64623
64963
  success,
64624
64964
  exitCode,
64625
64965
  durationMs,
@@ -64639,7 +64979,7 @@ async function runVerificationCommand2(opts) {
64639
64979
  clearTimeout(hardTimer);
64640
64980
  const durationMs = Date.now() - startMs;
64641
64981
  warnings.push(`Spawn error: ${err.message}`);
64642
- resolve40({
64982
+ resolve42({
64643
64983
  success: false,
64644
64984
  exitCode: null,
64645
64985
  durationMs,
@@ -66829,7 +67169,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
66829
67169
  // Agent delegation tools — discover and delegate work to other agents.
66830
67170
  ...this.options.agentStore ? [
66831
67171
  createListAgentsTool(this.options.agentStore),
66832
- createDelegateTaskTool(this.options.agentStore, this.store)
67172
+ createDelegateTaskTool(this.options.agentStore, this.store, { rootDir: this.rootDir })
66833
67173
  ] : [],
66834
67174
  // Messaging tools — allows executor agents to send and receive messages.
66835
67175
  ...this.options.messageStore && assignedAgentId ? [
@@ -67523,7 +67863,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
67523
67863
  return createTaskLogTool(this.store, taskId);
67524
67864
  }
67525
67865
  createTaskCreateTool() {
67526
- return createTaskCreateTool(this.store, { sourceType: "api" });
67866
+ return createTaskCreateTool(this.store, { sourceType: "api" }, { rootDir: this.rootDir });
67527
67867
  }
67528
67868
  createTaskDocumentWriteTool(taskId) {
67529
67869
  return createTaskDocumentWriteTool(this.store, taskId);
@@ -68617,7 +68957,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
68617
68957
  );
68618
68958
  }
68619
68959
  const delay2 = this.WORKTREE_RETRY_DELAYS[attempt] || 1e3;
68620
- await new Promise((resolve40) => setTimeout(resolve40, delay2));
68960
+ await new Promise((resolve42) => setTimeout(resolve42, delay2));
68621
68961
  }
68622
68962
  }
68623
68963
  throw new Error("Unexpected exit from worktree creation retry loop");
@@ -72458,7 +72798,7 @@ function isTickableState(state) {
72458
72798
  function isHeartbeatManaged(agent) {
72459
72799
  return !isEphemeralAgent(agent);
72460
72800
  }
72461
- var HEARTBEAT_SYSTEM_PROMPT, HEARTBEAT_NO_TASK_SYSTEM_PROMPT, HEARTBEAT_PROCEDURE, heartbeatDoneParams, HeartbeatMonitor, OVERDUE_FIRE_JITTER_MS, HeartbeatTriggerScheduler;
72801
+ var HEARTBEAT_SYSTEM_PROMPT, HEARTBEAT_NO_TASK_SYSTEM_PROMPT, HEARTBEAT_PROCEDURE, HEARTBEAT_NO_TASK_PROCEDURE, heartbeatDoneParams, HeartbeatMonitor, OVERDUE_FIRE_JITTER_MS, HeartbeatTriggerScheduler;
72462
72802
  var init_agent_heartbeat = __esm({
72463
72803
  "../engine/src/agent-heartbeat.ts"() {
72464
72804
  "use strict";
@@ -72648,6 +72988,32 @@ When sending messages:
72648
72988
  Critical: a heartbeat without observable progress (a log, a document write, a
72649
72989
  status change, a comment, a delegation, or an explicit "no-op with reason") is
72650
72990
  a bug. Do not loop on the same plan across heartbeats without recording why.`;
72991
+ HEARTBEAT_NO_TASK_PROCEDURE = `## Heartbeat Procedure (run every tick, in order)
72992
+
72993
+ 1. **Identity & context** \u2014 review the **Identity Snapshot** at the top of
72994
+ this prompt. Confirm your role, soul, instructions, and memory match what
72995
+ you expect, and surface any anomalies in your first text output before
72996
+ doing anything else. (If fn_identity is available in your runtime you may
72997
+ also call it for full structured detail; the snapshot above is the
72998
+ authoritative source.)
72999
+ 2. **Inbox** \u2014 when fn_read_messages is available, call it. Process any pending
73000
+ messages first; reply with reply_to_message_id when answering.
73001
+ 3. **Wake delta** \u2014 read the Wake Delta block above. The wake reason is the
73002
+ highest-priority change for this heartbeat. If you were woken by a comment
73003
+ or a message, acknowledge it before doing anything else.
73004
+ 4. **Ambient review** \u2014 since you have no assigned task, review board/project
73005
+ signals and recent memory context before acting.
73006
+ 5. **Pick the next concrete action** \u2014 exactly ONE useful action this heartbeat:
73007
+ create a focused task, delegate work, send/reply to a message, or append
73008
+ durable memory.
73009
+ 6. **Persist progress** \u2014 use available ambient tools only:
73010
+ fn_task_create, fn_delegate_task, fn_send_message, fn_memory_append.
73011
+ 7. **Exit** \u2014 call fn_heartbeat_done with a one-line summary of what changed
73012
+ this tick. If you took no action, say so and explain why.
73013
+
73014
+ Critical: a heartbeat without observable progress (a created task, delegation,
73015
+ message reply, memory append, or explicit "no-op with reason") is a bug. Do
73016
+ not loop on the same plan across heartbeats without recording why.`;
72651
73017
  heartbeatDoneParams = Type6.Object({
72652
73018
  summary: Type6.Optional(Type6.String({ description: "Summary of what was accomplished this heartbeat" }))
72653
73019
  });
@@ -72687,13 +73053,6 @@ a bug. Do not loop on the same plan across heartbeats without recording why.`;
72687
73053
  this.rootDir = options.rootDir;
72688
73054
  this.messageStore = options.messageStore;
72689
73055
  this.pluginRunner = options.pluginRunner;
72690
- this.onRecovered = options.onRecovered;
72691
- this.onTerminated = options.onTerminated;
72692
- this.onRunStarted = options.onRunStarted;
72693
- this.onRunCompleted = options.onRunCompleted;
72694
- this.taskStore = options.taskStore;
72695
- this.rootDir = options.rootDir;
72696
- this.messageStore = options.messageStore;
72697
73056
  }
72698
73057
  /**
72699
73058
  * Start the heartbeat monitoring loop.
@@ -73392,9 +73751,9 @@ a bug. Do not loop on the same plan across heartbeats without recording why.`;
73392
73751
  sourceType: "agent_heartbeat",
73393
73752
  sourceAgentId: agentId,
73394
73753
  sourceRunId: runContext?.runId
73395
- }));
73754
+ }, { rootDir: this.rootDir }));
73396
73755
  heartbeatTools.push(createListAgentsTool(this.store));
73397
- heartbeatTools.push(createDelegateTaskTool(this.store, taskStore));
73756
+ heartbeatTools.push(createDelegateTaskTool(this.store, taskStore, { rootDir: this.rootDir }));
73398
73757
  if (this.messageStore) {
73399
73758
  heartbeatTools.push(createSendMessageTool(this.messageStore, agentId));
73400
73759
  heartbeatTools.push(createReadMessagesTool(this.messageStore, agentId));
@@ -73417,22 +73776,27 @@ a bug. Do not loop on the same plan across heartbeats without recording why.`;
73417
73776
  heartbeatLog.warn(`Failed to configure heartbeat memory tools for ${agentId}: ${message}`);
73418
73777
  }
73419
73778
  const skillContext = buildSessionSkillContextSync2(agent, "heartbeat", rootDir);
73420
- let systemPrompt = isNoTaskRun ? HEARTBEAT_NO_TASK_SYSTEM_PROMPT : HEARTBEAT_SYSTEM_PROMPT;
73421
- const baseHeartbeatSystemPrompt = systemPrompt;
73779
+ const baseHeartbeatSystemPrompt = isNoTaskRun ? HEARTBEAT_NO_TASK_SYSTEM_PROMPT : HEARTBEAT_SYSTEM_PROMPT;
73422
73780
  let resolvedInstructionsForIdentity = "";
73423
73781
  try {
73424
- const agentInstructions = await resolveAgentInstructionsWithRatings(agent, rootDir, this.store);
73425
- resolvedInstructionsForIdentity = agentInstructions;
73426
- const memoryInstructions = memorySettings?.memoryEnabled === false ? "" : buildExecutionMemoryInstructions(rootDir, memorySettings);
73427
- systemPrompt = buildSystemPromptWithInstructions(
73428
- baseHeartbeatSystemPrompt,
73429
- [agentInstructions, memoryInstructions].filter((part) => part.trim()).join("\n\n")
73430
- );
73782
+ resolvedInstructionsForIdentity = await resolveAgentInstructionsWithRatings(agent, rootDir, this.store);
73431
73783
  } catch (instructionError) {
73432
- systemPrompt = baseHeartbeatSystemPrompt;
73433
73784
  const message = instructionError instanceof Error ? instructionError.message : String(instructionError);
73434
- heartbeatLog.warn(`Failed to enrich heartbeat system prompt for ${agentId}: ${message}`);
73785
+ heartbeatLog.warn(`Failed to resolve agent instructions for heartbeat ${agentId}: ${message}`);
73786
+ }
73787
+ let memoryInstructions = "";
73788
+ if (memorySettings?.memoryEnabled !== false) {
73789
+ try {
73790
+ memoryInstructions = buildExecutionMemoryInstructions(rootDir, memorySettings);
73791
+ } catch (memoryInstructionErr) {
73792
+ const message = memoryInstructionErr instanceof Error ? memoryInstructionErr.message : String(memoryInstructionErr);
73793
+ heartbeatLog.warn(`Failed to resolve project memory instructions for heartbeat ${agentId}: ${message}`);
73794
+ }
73435
73795
  }
73796
+ const systemPrompt = buildSystemPromptWithInstructions(
73797
+ baseHeartbeatSystemPrompt,
73798
+ [resolvedInstructionsForIdentity, memoryInstructions].filter((part) => part.trim()).join("\n\n")
73799
+ );
73436
73800
  heartbeatTools.push(createIdentityTool({ agent, resolvedInstructions: resolvedInstructionsForIdentity }));
73437
73801
  heartbeatTools.push(heartbeatDoneTool);
73438
73802
  if (isNoTaskRun) {
@@ -73493,7 +73857,7 @@ a bug. Do not loop on the same plan across heartbeats without recording why.`;
73493
73857
  };
73494
73858
  const wakeReason = deriveWakeReason();
73495
73859
  const customProcedure = await resolveAgentHeartbeatProcedure(agent, rootDir);
73496
- const heartbeatProcedureText = customProcedure ?? HEARTBEAT_PROCEDURE;
73860
+ const heartbeatProcedureText = customProcedure ?? (isNoTaskRun ? HEARTBEAT_NO_TASK_PROCEDURE : HEARTBEAT_PROCEDURE);
73497
73861
  if (isNoTaskRun) {
73498
73862
  if (this.messageStore) {
73499
73863
  try {
@@ -73526,6 +73890,8 @@ a bug. Do not loop on the same plan across heartbeats without recording why.`;
73526
73890
  `- pending messages: ${pendingMessages.length}`,
73527
73891
  "",
73528
73892
  "Treat this wake delta as the highest-priority change for this heartbeat.",
73893
+ "This is an autonomous heartbeat run (manual or automatic): re-anchor on",
73894
+ "identity, process wake context, then complete ONE concrete action.",
73529
73895
  "Run the Heartbeat Procedure (below) before doing anything else \u2014 even a",
73530
73896
  "timer-only wake should re-check messages, memory, and project state.",
73531
73897
  "",
@@ -73617,6 +73983,8 @@ a bug. Do not loop on the same plan across heartbeats without recording why.`;
73617
73983
  `- triggering comments: ${effectiveTriggeringCommentIds?.length ?? 0}`,
73618
73984
  "",
73619
73985
  "Treat this wake delta as the highest-priority change for this heartbeat.",
73986
+ "This is an autonomous heartbeat run (manual or automatic): re-anchor on",
73987
+ "identity, process wake context, then complete ONE concrete action.",
73620
73988
  "Before resuming prior task work, run the Heartbeat Procedure (below) and",
73621
73989
  "decide what action this delta requires. Your assigned task is one input",
73622
73990
  "to the procedure \u2014 not the only thing to consider.",
@@ -73776,7 +74144,7 @@ ${taskDetail.prompt}` : "No PROMPT.md available.",
73776
74144
  const baseCreateTool = createTaskCreateTool(taskStore, {
73777
74145
  sourceType: "agent_heartbeat",
73778
74146
  sourceAgentId: agentId
73779
- });
74147
+ }, { rootDir: this.rootDir });
73780
74148
  const trackedCreateTool = {
73781
74149
  ...baseCreateTool,
73782
74150
  execute: async (id, params, signal, onUpdate, ctx) => {
@@ -73803,7 +74171,7 @@ ${taskDetail.prompt}` : "No PROMPT.md available.",
73803
74171
  tools.push(createTaskDocumentWriteTool(taskStore, taskId));
73804
74172
  tools.push(createTaskDocumentReadTool(taskStore, taskId));
73805
74173
  tools.push(createListAgentsTool(this.store));
73806
- tools.push(createDelegateTaskTool(this.store, taskStore));
74174
+ tools.push(createDelegateTaskTool(this.store, taskStore, { rootDir: this.rootDir }));
73807
74175
  if (messageStore) {
73808
74176
  tools.push(createSendMessageTool(messageStore, agentId));
73809
74177
  tools.push(createReadMessagesTool(messageStore, agentId));
@@ -75387,7 +75755,7 @@ var init_shell_utils = __esm({
75387
75755
  // ../engine/src/cron-runner.ts
75388
75756
  import { exec as exec6 } from "node:child_process";
75389
75757
  function execCommand(command, options) {
75390
- return new Promise((resolve40, reject2) => {
75758
+ return new Promise((resolve42, reject2) => {
75391
75759
  exec6(command, options, (error, stdout, stderr) => {
75392
75760
  const stdoutText = typeof stdout === "string" ? stdout : String(stdout ?? "");
75393
75761
  const stderrText = typeof stderr === "string" ? stderr : String(stderr ?? "");
@@ -75398,7 +75766,7 @@ function execCommand(command, options) {
75398
75766
  reject2(errWithOutput);
75399
75767
  return;
75400
75768
  }
75401
- resolve40({ stdout: stdoutText, stderr: stderrText });
75769
+ resolve42({ stdout: stdoutText, stderr: stderrText });
75402
75770
  });
75403
75771
  });
75404
75772
  }
@@ -78749,13 +79117,13 @@ var init_plugin_runner = __esm({
78749
79117
  * Returns the result on success, throws on timeout.
78750
79118
  */
78751
79119
  withTimeout(promise, ms, timeoutMessage) {
78752
- return new Promise((resolve40, reject2) => {
79120
+ return new Promise((resolve42, reject2) => {
78753
79121
  const timer = setTimeout(() => {
78754
79122
  reject2(new Error(timeoutMessage));
78755
79123
  }, ms);
78756
79124
  promise.then((result) => {
78757
79125
  clearTimeout(timer);
78758
- resolve40(result);
79126
+ resolve42(result);
78759
79127
  }).catch((err) => {
78760
79128
  clearTimeout(timer);
78761
79129
  reject2(err);
@@ -79391,7 +79759,7 @@ var init_in_process_runtime = __esm({
79391
79759
  runtimeLog.log(
79392
79760
  `Waiting for ${metrics.inFlightTasks} in-flight tasks to complete...`
79393
79761
  );
79394
- await new Promise((resolve40) => setTimeout(resolve40, 1e3));
79762
+ await new Promise((resolve42) => setTimeout(resolve42, 1e3));
79395
79763
  }
79396
79764
  const finalMetrics = this.getMetrics();
79397
79765
  if (finalMetrics.inFlightTasks > 0) {
@@ -79788,13 +80156,13 @@ var init_ipc_host = __esm({
79788
80156
  }
79789
80157
  const id = generateCorrelationId();
79790
80158
  const message = { type, id, payload };
79791
- return new Promise((resolve40, reject2) => {
80159
+ return new Promise((resolve42, reject2) => {
79792
80160
  const timeout2 = setTimeout(() => {
79793
80161
  this.pendingCommands.delete(id);
79794
80162
  reject2(new Error(`Command ${type} timed out after ${timeoutMs ?? this.commandTimeoutMs}ms`));
79795
80163
  }, timeoutMs ?? this.commandTimeoutMs);
79796
80164
  this.pendingCommands.set(id, {
79797
- resolve: resolve40,
80165
+ resolve: resolve42,
79798
80166
  reject: reject2,
79799
80167
  timeout: timeout2,
79800
80168
  type
@@ -80603,8 +80971,8 @@ var init_remote_node_client = __esm({
80603
80971
  return error instanceof TypeError;
80604
80972
  }
80605
80973
  async sleep(ms) {
80606
- await new Promise((resolve40) => {
80607
- setTimeout(resolve40, ms);
80974
+ await new Promise((resolve42) => {
80975
+ setTimeout(resolve42, ms);
80608
80976
  });
80609
80977
  }
80610
80978
  };
@@ -80868,14 +81236,14 @@ var init_remote_node_runtime = __esm({
80868
81236
  return error instanceof Error ? error : new Error(String(error));
80869
81237
  }
80870
81238
  async sleep(ms, signal) {
80871
- await new Promise((resolve40) => {
81239
+ await new Promise((resolve42) => {
80872
81240
  const timeout2 = setTimeout(() => {
80873
81241
  cleanup();
80874
- resolve40();
81242
+ resolve42();
80875
81243
  }, ms);
80876
81244
  const onAbort = () => {
80877
81245
  cleanup();
80878
- resolve40();
81246
+ resolve42();
80879
81247
  };
80880
81248
  const cleanup = () => {
80881
81249
  clearTimeout(timeout2);
@@ -81505,7 +81873,7 @@ var init_provider_adapters = __esm({
81505
81873
 
81506
81874
  // ../engine/src/remote-access/tunnel-process-manager.ts
81507
81875
  import { EventEmitter as EventEmitter24 } from "node:events";
81508
- import { exec as exec9, execFile as execFile3, spawn as spawn4 } from "node:child_process";
81876
+ import { exec as exec9, execFile as execFile3, spawn as spawn5 } from "node:child_process";
81509
81877
  import { promisify as promisify9 } from "node:util";
81510
81878
  function nowIso() {
81511
81879
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -81596,7 +81964,7 @@ var init_tunnel_process_manager = __esm({
81596
81964
  super();
81597
81965
  this.maxLogEntries = options.maxLogEntries ?? DEFAULT_MAX_LOG_ENTRIES;
81598
81966
  this.defaultStopTimeoutMs = options.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS2;
81599
- this.spawnImpl = options.spawnImpl ?? spawn4;
81967
+ this.spawnImpl = options.spawnImpl ?? spawn5;
81600
81968
  }
81601
81969
  getStatus() {
81602
81970
  return { ...this.status, lastError: this.status.lastError ? { ...this.status.lastError } : null };
@@ -81826,10 +82194,10 @@ var init_tunnel_process_manager = __esm({
81826
82194
  lastError: null
81827
82195
  });
81828
82196
  this.emitLog("info", "manager", `Stopping ${currentHandle.provider} tunnel (pid=${currentHandle.child.pid ?? "n/a"})`);
81829
- this.activeStopPromise = new Promise((resolve40) => {
82197
+ this.activeStopPromise = new Promise((resolve42) => {
81830
82198
  const onClose = () => {
81831
82199
  currentHandle.child.removeListener("close", onClose);
81832
- resolve40();
82200
+ resolve42();
81833
82201
  };
81834
82202
  currentHandle.child.once("close", onClose);
81835
82203
  killManagedProcess(currentHandle.child, "SIGTERM");
@@ -81970,6 +82338,7 @@ var execFileAsync2, MERGE_HANDOFF_GRACE_MS, isRemoteActive, ProjectEngine;
81970
82338
  var init_project_engine = __esm({
81971
82339
  "../engine/src/project-engine.ts"() {
81972
82340
  "use strict";
82341
+ init_src();
81973
82342
  init_in_process_runtime();
81974
82343
  init_pr_monitor();
81975
82344
  init_pr_comment_handler();
@@ -82436,12 +82805,12 @@ ${detail}`
82436
82805
  */
82437
82806
  async onMerge(taskId) {
82438
82807
  if (this.mergeActive.has(taskId)) {
82439
- return new Promise((resolve40, reject2) => {
82440
- this.manualMergeResolvers.set(taskId, { resolve: resolve40, reject: reject2 });
82808
+ return new Promise((resolve42, reject2) => {
82809
+ this.manualMergeResolvers.set(taskId, { resolve: resolve42, reject: reject2 });
82441
82810
  });
82442
82811
  }
82443
- return new Promise((resolve40, reject2) => {
82444
- this.manualMergeResolvers.set(taskId, { resolve: resolve40, reject: reject2 });
82812
+ return new Promise((resolve42, reject2) => {
82813
+ this.manualMergeResolvers.set(taskId, { resolve: resolve42, reject: reject2 });
82445
82814
  this.internalEnqueueMerge(taskId);
82446
82815
  });
82447
82816
  }
@@ -82664,6 +83033,48 @@ ${detail}`
82664
83033
  if (task.status === "failed") return false;
82665
83034
  return (task.mergeRetries ?? 0) < _ProjectEngine.MAX_AUTO_MERGE_RETRIES || this.hasAutoHealableVerificationBufferFailure(task) || this.isRetryCooldownElapsed(task);
82666
83035
  }
83036
+ /**
83037
+ * Remove and return the highest-priority taskId from the merge queue.
83038
+ * Ordering: priority (urgent→low), then createdAt ASC, then id ASC — matching
83039
+ * the triage and scheduler comparators. Manual merges (onMerge resolvers) are
83040
+ * preferred over auto-merges so awaited callers aren't starved by a flood of
83041
+ * higher-priority auto-enqueues. IDs whose tasks can't be loaded fall back to
83042
+ * FIFO order so they still drain.
83043
+ */
83044
+ async pickNextMergeTaskId(store) {
83045
+ if (this.mergeQueue.length === 0) return void 0;
83046
+ if (this.mergeQueue.length === 1) {
83047
+ return this.mergeQueue.shift();
83048
+ }
83049
+ const queueSnapshot = [...this.mergeQueue];
83050
+ const entries = [];
83051
+ for (let i = 0; i < queueSnapshot.length; i++) {
83052
+ const taskId = queueSnapshot[i];
83053
+ const task = await store.getTask(taskId).catch(() => void 0);
83054
+ entries.push({
83055
+ taskId,
83056
+ task,
83057
+ manual: this.manualMergeResolvers.has(taskId),
83058
+ order: i
83059
+ });
83060
+ }
83061
+ if (this.shuttingDown) return void 0;
83062
+ entries.sort((a, b) => {
83063
+ if (a.manual !== b.manual) return a.manual ? -1 : 1;
83064
+ if (a.task && b.task) return compareTasksByPriorityThenAgeAndId(a.task, b.task);
83065
+ if (a.task) return -1;
83066
+ if (b.task) return 1;
83067
+ return a.order - b.order;
83068
+ });
83069
+ for (const entry of entries) {
83070
+ const liveIndex = this.mergeQueue.indexOf(entry.taskId);
83071
+ if (liveIndex !== -1) {
83072
+ this.mergeQueue.splice(liveIndex, 1);
83073
+ return entry.taskId;
83074
+ }
83075
+ }
83076
+ return void 0;
83077
+ }
82667
83078
  internalEnqueueMerge(taskId) {
82668
83079
  if (this.shuttingDown) return;
82669
83080
  if (this.mergeActive.has(taskId)) return;
@@ -82671,6 +83082,23 @@ ${detail}`
82671
83082
  this.mergeQueue.push(taskId);
82672
83083
  void this.drainMergeQueue();
82673
83084
  }
83085
+ /**
83086
+ * Filter a sweep's listTasks() result to merge-eligible tasks, sort by
83087
+ * priority (urgent → low, then createdAt ASC, then id ASC), and enqueue.
83088
+ * Sorting before enqueue matters because each enqueue may immediately
83089
+ * trigger drainMergeQueue's single-item fast path, so the first task
83090
+ * pushed wins. listTasks returns createdAt ASC — without this sort an
83091
+ * older low-priority task would start before a later urgent one.
83092
+ */
83093
+ enqueueEligibleInReviewTasks(tasks) {
83094
+ const eligible = sortTasksByPriorityThenAgeAndId(
83095
+ tasks.filter((t) => !t.paused && this.canMergeTask(t))
83096
+ );
83097
+ for (const t of eligible) {
83098
+ this.internalEnqueueMerge(t.id);
83099
+ }
83100
+ return eligible.length;
83101
+ }
82674
83102
  async drainMergeQueue() {
82675
83103
  if (this.mergeRunning) return;
82676
83104
  this.mergeRunning = true;
@@ -82678,7 +83106,9 @@ ${detail}`
82678
83106
  const store = this.runtime.getTaskStore();
82679
83107
  const cwd = this.config.workingDirectory;
82680
83108
  while (this.mergeQueue.length > 0 && !this.shuttingDown) {
82681
- const taskId = this.mergeQueue.shift();
83109
+ const taskId = await this.pickNextMergeTaskId(store);
83110
+ if (!taskId) break;
83111
+ if (this.shuttingDown) break;
82682
83112
  const manualResolver = this.manualMergeResolvers.get(taskId);
82683
83113
  try {
82684
83114
  if (!manualResolver) {
@@ -83148,12 +83578,9 @@ ${detail}`
83148
83578
  }
83149
83579
  const settings = await store.getSettings();
83150
83580
  if (!settings.autoMerge) return;
83151
- const eligible = tasks.filter((t) => !t.paused && this.canMergeTask(t));
83152
- if (eligible.length > 0) {
83153
- runtimeLog.log(`Auto-merge startup sweep: enqueueing ${eligible.length} task(s)`);
83154
- for (const t of eligible) {
83155
- this.internalEnqueueMerge(t.id);
83156
- }
83581
+ const enqueued = this.enqueueEligibleInReviewTasks(tasks);
83582
+ if (enqueued > 0) {
83583
+ runtimeLog.log(`Auto-merge startup sweep: enqueueing ${enqueued} task(s)`);
83157
83584
  }
83158
83585
  } catch (err) {
83159
83586
  runtimeLog.warn(
@@ -83169,14 +83596,7 @@ ${detail}`
83169
83596
  const settings = await store.getSettings();
83170
83597
  if (!settings.globalPause && !settings.enginePaused && settings.autoMerge) {
83171
83598
  const tasks = await store.listTasks({ column: "in-review" });
83172
- for (const t of tasks) {
83173
- if (t.paused) {
83174
- continue;
83175
- }
83176
- if (this.canMergeTask(t)) {
83177
- this.internalEnqueueMerge(t.id);
83178
- }
83179
- }
83599
+ this.enqueueEligibleInReviewTasks(tasks);
83180
83600
  }
83181
83601
  } catch (err) {
83182
83602
  runtimeLog.warn(
@@ -83232,14 +83652,7 @@ ${detail}`
83232
83652
  if (s.autoMerge) {
83233
83653
  try {
83234
83654
  const tasks = await store.listTasks({ column: "in-review" });
83235
- for (const t of tasks) {
83236
- if (t.paused) {
83237
- continue;
83238
- }
83239
- if (this.canMergeTask(t)) {
83240
- this.internalEnqueueMerge(t.id);
83241
- }
83242
- }
83655
+ this.enqueueEligibleInReviewTasks(tasks);
83243
83656
  } catch (err) {
83244
83657
  runtimeLog.warn(
83245
83658
  `Global unpause: failed to scan in-review tasks for auto-merge: ${err instanceof Error ? err.message : String(err)}`
@@ -83269,14 +83682,7 @@ ${detail}`
83269
83682
  if (s.autoMerge) {
83270
83683
  try {
83271
83684
  const tasks = await store.listTasks({ column: "in-review" });
83272
- for (const t of tasks) {
83273
- if (t.paused) {
83274
- continue;
83275
- }
83276
- if (this.canMergeTask(t)) {
83277
- this.internalEnqueueMerge(t.id);
83278
- }
83279
- }
83685
+ this.enqueueEligibleInReviewTasks(tasks);
83280
83686
  } catch (err) {
83281
83687
  runtimeLog.warn(
83282
83688
  `Engine unpause: failed to scan in-review tasks for auto-merge: ${err instanceof Error ? err.message : String(err)}`
@@ -83869,7 +84275,7 @@ var init_peer_exchange_service = __esm({
83869
84275
  syncIntervalMs;
83870
84276
  interval = null;
83871
84277
  activeSync = null;
83872
- stopped = false;
84278
+ running = false;
83873
84279
  /** Whether settings sync is enabled. Default: false. */
83874
84280
  settingsSyncEnabled;
83875
84281
  /** Minimum interval between settings syncs with the same node in ms. Default: 5 minutes. */
@@ -83911,10 +84317,11 @@ var init_peer_exchange_service = __esm({
83911
84317
  * Begins periodic gossip with all online remote nodes.
83912
84318
  */
83913
84319
  start() {
83914
- if (this.stopped) {
83915
- peerExchangeLog.warn("Cannot start - service has been stopped");
84320
+ if (this.running) {
84321
+ peerExchangeLog.log("Peer exchange service already running");
83916
84322
  return;
83917
84323
  }
84324
+ this.running = true;
83918
84325
  this.centralCore.listNodes().then((nodes) => {
83919
84326
  const onlineRemoteCount = nodes.filter(
83920
84327
  (n) => n.type === "remote" && n.status === "online" && n.url
@@ -83924,6 +84331,7 @@ var init_peer_exchange_service = __esm({
83924
84331
  peerExchangeLog.warn(`Failed to get initial peer count: ${err}`);
83925
84332
  });
83926
84333
  this.interval = setInterval(() => {
84334
+ if (!this.running) return;
83927
84335
  void this.syncWithAllPeers();
83928
84336
  }, this.syncIntervalMs);
83929
84337
  }
@@ -83931,12 +84339,21 @@ var init_peer_exchange_service = __esm({
83931
84339
  * Stop the peer exchange service.
83932
84340
  * Clears the sync interval and prevents further syncs.
83933
84341
  */
83934
- stop() {
84342
+ async stop() {
84343
+ if (!this.running) {
84344
+ return;
84345
+ }
84346
+ this.running = false;
83935
84347
  if (this.interval) {
83936
84348
  clearInterval(this.interval);
83937
84349
  this.interval = null;
83938
84350
  }
83939
- this.stopped = true;
84351
+ if (this.activeSync) {
84352
+ try {
84353
+ await this.activeSync;
84354
+ } catch {
84355
+ }
84356
+ }
83940
84357
  peerExchangeLog.log("Stopped peer exchange service");
83941
84358
  }
83942
84359
  /**
@@ -85495,7 +85912,7 @@ For completion:
85495
85912
  }`;
85496
85913
  SESSION_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
85497
85914
  CLEANUP_INTERVAL_MS2 = 5 * 60 * 1e3;
85498
- MAX_SESSIONS_PER_IP_PER_HOUR = 5;
85915
+ MAX_SESSIONS_PER_IP_PER_HOUR = 1e3;
85499
85916
  RATE_LIMIT_WINDOW_MS2 = 60 * 60 * 1e3;
85500
85917
  GENERATION_TIMEOUT_MS = 12e4;
85501
85918
  sessions = /* @__PURE__ */ new Map();
@@ -88381,7 +88798,7 @@ var init_src3 = __esm({
88381
88798
  });
88382
88799
 
88383
88800
  // ../../plugins/fusion-plugin-hermes-runtime/dist/cli-spawn.js
88384
- import { spawn as spawn5, spawnSync } from "node:child_process";
88801
+ import { spawn as spawn6, spawnSync } from "node:child_process";
88385
88802
  import os2 from "node:os";
88386
88803
  import path, { sep as PATH_SEP } from "node:path";
88387
88804
  function resolveBinaryForSpawn(binary) {
@@ -88443,9 +88860,9 @@ function hermesProfileHome(profileName) {
88443
88860
  async function listHermesProfiles(opts) {
88444
88861
  const binary = resolveBinaryForSpawn(opts?.binaryPath ?? "hermes");
88445
88862
  const timeoutMs = opts?.timeoutMs ?? 5e3;
88446
- return new Promise((resolve40, reject2) => {
88863
+ return new Promise((resolve42, reject2) => {
88447
88864
  let settled = false;
88448
- const child = spawn5(binary, ["profile", "list"], {
88865
+ const child = spawn6(binary, ["profile", "list"], {
88449
88866
  stdio: ["ignore", "pipe", "pipe"],
88450
88867
  env: { ...process.env }
88451
88868
  });
@@ -88486,7 +88903,7 @@ async function listHermesProfiles(opts) {
88486
88903
  ${combined}`));
88487
88904
  return;
88488
88905
  }
88489
- resolve40(parseProfileListOutput(stdout));
88906
+ resolve42(parseProfileListOutput(stdout));
88490
88907
  });
88491
88908
  });
88492
88909
  }
@@ -88563,13 +88980,13 @@ function buildHermesArgs(prompt, settings, resumeSessionId) {
88563
88980
  async function invokeHermesCli(prompt, settings, resumeSessionId, signal) {
88564
88981
  const args = buildHermesArgs(prompt, settings, resumeSessionId);
88565
88982
  const binary = resolveBinaryForSpawn(settings.binaryPath);
88566
- return new Promise((resolve40, reject2) => {
88983
+ return new Promise((resolve42, reject2) => {
88567
88984
  let settled = false;
88568
88985
  const spawnEnv = { ...process.env, PYTHONUNBUFFERED: "1" };
88569
88986
  if (settings.profile) {
88570
88987
  spawnEnv.HERMES_HOME = hermesProfileHome(settings.profile);
88571
88988
  }
88572
- const child = spawn5(binary, args, {
88989
+ const child = spawn6(binary, args, {
88573
88990
  stdio: ["ignore", "pipe", "pipe"],
88574
88991
  env: spawnEnv
88575
88992
  });
@@ -88631,7 +89048,7 @@ ${combined}`));
88631
89048
  return;
88632
89049
  }
88633
89050
  try {
88634
- resolve40(parseHermesOutput(stdout, stderr));
89051
+ resolve42(parseHermesOutput(stdout, stderr));
88635
89052
  } catch (parseErr) {
88636
89053
  reject2(parseErr);
88637
89054
  }
@@ -88724,7 +89141,7 @@ var init_runtime_adapter = __esm({
88724
89141
  });
88725
89142
 
88726
89143
  // ../../plugins/fusion-plugin-hermes-runtime/dist/probe.js
88727
- import { spawn as spawn6 } from "node:child_process";
89144
+ import { spawn as spawn7 } from "node:child_process";
88728
89145
  async function probeHermesBinary(opts) {
88729
89146
  const startedAt = Date.now();
88730
89147
  const binary = typeof opts?.binaryPath === "string" && opts.binaryPath.trim().length > 0 ? opts.binaryPath.trim() : "hermes";
@@ -88735,7 +89152,7 @@ async function probeHermesBinary(opts) {
88735
89152
  resolvePromise({ ...result, probeDurationMs: Date.now() - startedAt });
88736
89153
  };
88737
89154
  let settled = false;
88738
- const child = spawn6(resolvedPath ?? binary, ["--version"], {
89155
+ const child = spawn7(resolvedPath ?? binary, ["--version"], {
88739
89156
  stdio: ["ignore", "pipe", "pipe"]
88740
89157
  });
88741
89158
  const timer = setTimeout(() => {
@@ -88796,7 +89213,7 @@ async function probeHermesBinary(opts) {
88796
89213
  async function tryResolveBinaryPath(binary) {
88797
89214
  return new Promise((resolvePromise) => {
88798
89215
  const which = process.platform === "win32" ? "where" : "which";
88799
- const child = spawn6(which, [binary], { stdio: ["ignore", "pipe", "ignore"] });
89216
+ const child = spawn7(which, [binary], { stdio: ["ignore", "pipe", "ignore"] });
88800
89217
  let out = "";
88801
89218
  child.stdout?.on("data", (chunk) => {
88802
89219
  out += chunk.toString("utf-8");
@@ -88874,7 +89291,7 @@ var init_dist = __esm({
88874
89291
  });
88875
89292
 
88876
89293
  // ../../plugins/fusion-plugin-openclaw-runtime/dist/pi-module.js
88877
- import { spawn as spawn7 } from "node:child_process";
89294
+ import { spawn as spawn8 } from "node:child_process";
88878
89295
  import { randomUUID as randomUUID15 } from "node:crypto";
88879
89296
  function asString(v) {
88880
89297
  return typeof v === "string" && v.trim() !== "" ? v.trim() : void 0;
@@ -88949,9 +89366,9 @@ async function promptCli(session, message, config, callbacks, signal) {
88949
89366
  const args = buildOpenClawArgs(config, session.sessionId, message);
88950
89367
  const cb = { ...session.callbacks, ...callbacks };
88951
89368
  cb.onToolStart?.("openclaw.agent", { sessionId: session.sessionId });
88952
- return new Promise((resolve40, reject2) => {
89369
+ return new Promise((resolve42, reject2) => {
88953
89370
  let settled = false;
88954
- const child = spawn7(config.binaryPath, args, {
89371
+ const child = spawn8(config.binaryPath, args, {
88955
89372
  stdio: ["ignore", "pipe", "pipe"]
88956
89373
  });
88957
89374
  const hardKill = setTimeout(() => {
@@ -89042,7 +89459,7 @@ async function promptCli(session, message, config, callbacks, signal) {
89042
89459
  ...metaError ? { error: metaError } : {},
89043
89460
  ...errorText.length > 0 ? { toolErrors: errorText } : {}
89044
89461
  });
89045
- resolve40();
89462
+ resolve42();
89046
89463
  });
89047
89464
  });
89048
89465
  }
@@ -89102,7 +89519,7 @@ var init_runtime_adapter2 = __esm({
89102
89519
  });
89103
89520
 
89104
89521
  // ../../plugins/fusion-plugin-openclaw-runtime/dist/probe.js
89105
- import { spawn as spawn8 } from "node:child_process";
89522
+ import { spawn as spawn9 } from "node:child_process";
89106
89523
  async function probeOpenClawBinary(opts = {}) {
89107
89524
  const startedAt = Date.now();
89108
89525
  const binary = opts.binaryPath ?? "openclaw";
@@ -89113,7 +89530,7 @@ async function probeOpenClawBinary(opts = {}) {
89113
89530
  resolvePromise({ ...partial, probeDurationMs: Date.now() - startedAt });
89114
89531
  };
89115
89532
  let settled = false;
89116
- const child = spawn8(resolvedPath ?? binary, ["--version"], {
89533
+ const child = spawn9(resolvedPath ?? binary, ["--version"], {
89117
89534
  stdio: ["ignore", "pipe", "pipe"]
89118
89535
  });
89119
89536
  const timer = setTimeout(() => {
@@ -89174,7 +89591,7 @@ async function probeOpenClawBinary(opts = {}) {
89174
89591
  async function tryResolveBinaryPath2(binary) {
89175
89592
  return new Promise((resolvePromise) => {
89176
89593
  const which = process.platform === "win32" ? "where" : "which";
89177
- const child = spawn8(which, [binary], { stdio: ["ignore", "pipe", "ignore"] });
89594
+ const child = spawn9(which, [binary], { stdio: ["ignore", "pipe", "ignore"] });
89178
89595
  let out = "";
89179
89596
  child.stdout?.on("data", (chunk) => {
89180
89597
  out += chunk.toString("utf-8");
@@ -90595,7 +91012,7 @@ function registerTaskWorkflowRoutes(ctx, deps) {
90595
91012
  router.patch("/tasks/:id", async (req, res) => {
90596
91013
  try {
90597
91014
  const { store: scopedStore } = await getProjectContext3(req);
90598
- const { title, description, prompt, dependencies, enabledWorkflowSteps, modelProvider, modelId, validatorModelProvider, validatorModelId, planningModelProvider, planningModelId, thinkingLevel, assigneeUserId, reviewLevel, executionMode, sourceIssue, nodeId } = req.body;
91015
+ const { title, description, prompt, priority, dependencies, enabledWorkflowSteps, modelProvider, modelId, validatorModelProvider, validatorModelId, planningModelProvider, planningModelId, thinkingLevel, assigneeUserId, reviewLevel, executionMode, sourceIssue, nodeId } = req.body;
90599
91016
  const hasBodyField = (field) => Object.prototype.hasOwnProperty.call(req.body, field);
90600
91017
  const validateModelField = (value, name) => {
90601
91018
  if (value === void 0) return void 0;
@@ -90625,6 +91042,9 @@ function registerTaskWorkflowRoutes(ctx, deps) {
90625
91042
  if (executionMode !== void 0 && executionMode !== null && !validExecutionModes.includes(executionMode)) {
90626
91043
  throw new Error(`executionMode must be one of: ${validExecutionModes.join(", ")}`);
90627
91044
  }
91045
+ if (priority !== void 0 && priority !== null && !isTaskPriority(priority)) {
91046
+ throw new Error(`priority must be one of: ${TASK_PRIORITIES.join(", ")}`);
91047
+ }
90628
91048
  if (enabledWorkflowSteps !== void 0) {
90629
91049
  if (!Array.isArray(enabledWorkflowSteps) || !enabledWorkflowSteps.every((id) => typeof id === "string")) {
90630
91050
  throw new Error("enabledWorkflowSteps must be an array of strings");
@@ -90684,6 +91104,7 @@ function registerTaskWorkflowRoutes(ctx, deps) {
90684
91104
  if (title !== void 0) updates.title = title;
90685
91105
  if (description !== void 0) updates.description = description;
90686
91106
  if (prompt !== void 0) updates.prompt = prompt;
91107
+ if (hasBodyField("priority")) updates.priority = priority;
90687
91108
  if (dependencies !== void 0) updates.dependencies = dependencies;
90688
91109
  if (enabledWorkflowSteps !== void 0) updates.enabledWorkflowSteps = enabledWorkflowSteps;
90689
91110
  if (hasBodyField("modelProvider")) updates.modelProvider = validatedModelProvider;
@@ -90714,7 +91135,7 @@ function registerTaskWorkflowRoutes(ctx, deps) {
90714
91135
  if (err instanceof ApiError) {
90715
91136
  throw err;
90716
91137
  }
90717
- const status = (err instanceof Error ? err.message : String(err)).includes("must be a string") || (err instanceof Error ? err.message : String(err)).includes("must be a non-empty string") || (err instanceof Error ? err.message : String(err)).includes("must be a string or null") || (err instanceof Error ? err.message : String(err)).includes("must be an array of strings") || (err instanceof Error ? err.message : String(err)).includes("thinkingLevel must be one of") || (err instanceof Error ? err.message : String(err)).includes("reviewLevel must be an integer") || (err instanceof Error ? err.message : String(err)).includes("executionMode must be one of") || (err instanceof Error ? err.message : String(err)).includes("sourceIssue") ? 400 : 500;
91138
+ const status = (err instanceof Error ? err.message : String(err)).includes("must be a string") || (err instanceof Error ? err.message : String(err)).includes("must be a non-empty string") || (err instanceof Error ? err.message : String(err)).includes("must be a string or null") || (err instanceof Error ? err.message : String(err)).includes("must be an array of strings") || (err instanceof Error ? err.message : String(err)).includes("thinkingLevel must be one of") || (err instanceof Error ? err.message : String(err)).includes("reviewLevel must be an integer") || (err instanceof Error ? err.message : String(err)).includes("executionMode must be one of") || (err instanceof Error ? err.message : String(err)).includes("priority must be one of") || (err instanceof Error ? err.message : String(err)).includes("sourceIssue") ? 400 : 500;
90718
91139
  throw new ApiError(status, err instanceof Error ? err.message : String(err));
90719
91140
  }
90720
91141
  });
@@ -97386,10 +97807,10 @@ var require_browser3 = __commonJS({
97386
97807
  text = canvas;
97387
97808
  canvas = void 0;
97388
97809
  }
97389
- return new Promise(function(resolve40, reject2) {
97810
+ return new Promise(function(resolve42, reject2) {
97390
97811
  try {
97391
97812
  const data = QRCode2.create(text, opts);
97392
- resolve40(renderFunc(data, canvas, opts));
97813
+ resolve42(renderFunc(data, canvas, opts));
97393
97814
  } catch (e) {
97394
97815
  reject2(e);
97395
97816
  }
@@ -97471,11 +97892,11 @@ var require_server = __commonJS({
97471
97892
  }
97472
97893
  function render(renderFunc, text, params) {
97473
97894
  if (!params.cb) {
97474
- return new Promise(function(resolve40, reject2) {
97895
+ return new Promise(function(resolve42, reject2) {
97475
97896
  try {
97476
97897
  const data = QRCode2.create(text, params.opts);
97477
97898
  return renderFunc(data, params.opts, function(err, data2) {
97478
- return err ? reject2(err) : resolve40(data2);
97899
+ return err ? reject2(err) : resolve42(data2);
97479
97900
  });
97480
97901
  } catch (e) {
97481
97902
  reject2(e);
@@ -99123,9 +99544,9 @@ import * as fs2 from "node:fs";
99123
99544
  import { createRequire as createRequire3 } from "node:module";
99124
99545
  import { join as join42, dirname as dirname12 } from "node:path";
99125
99546
  function getNativePrebuildName() {
99126
- const platform3 = process.platform === "darwin" ? "darwin" : process.platform === "linux" ? "linux" : process.platform === "win32" ? "win32" : "unknown";
99547
+ const platform4 = process.platform === "darwin" ? "darwin" : process.platform === "linux" ? "linux" : process.platform === "win32" ? "win32" : "unknown";
99127
99548
  const arch = process.arch === "arm64" ? "arm64" : process.arch === "x64" ? "x64" : "unknown";
99128
- return `${platform3}-${arch}`;
99549
+ return `${platform4}-${arch}`;
99129
99550
  }
99130
99551
  function findInstalledNodePtyNativeDir() {
99131
99552
  try {
@@ -99307,8 +99728,8 @@ var init_terminal_service = __esm({
99307
99728
  * Get the default allowed shells for the current platform
99308
99729
  */
99309
99730
  getAllowedShells() {
99310
- const platform3 = os3.platform();
99311
- return ALLOWED_SHELL_PATHS[platform3] || ALLOWED_SHELL_PATHS.linux;
99731
+ const platform4 = os3.platform();
99732
+ return ALLOWED_SHELL_PATHS[platform4] || ALLOWED_SHELL_PATHS.linux;
99312
99733
  }
99313
99734
  /**
99314
99735
  * Validate that a shell path is allowed
@@ -99322,7 +99743,7 @@ var init_terminal_service = __esm({
99322
99743
  * Detect the best shell for the current platform
99323
99744
  */
99324
99745
  detectShell() {
99325
- const platform3 = os3.platform();
99746
+ const platform4 = os3.platform();
99326
99747
  const allowedShells = this.getAllowedShells();
99327
99748
  const getBasename = (shellPath) => {
99328
99749
  const lastSep = Math.max(shellPath.lastIndexOf("/"), shellPath.lastIndexOf("\\"));
@@ -99339,7 +99760,7 @@ var init_terminal_service = __esm({
99339
99760
  return ["--login"];
99340
99761
  };
99341
99762
  const userShell = process.env.SHELL;
99342
- if (userShell && platform3 !== "win32") {
99763
+ if (userShell && platform4 !== "win32") {
99343
99764
  const normalizedUserShell = this.isWindows ? userShell.toLowerCase() : userShell;
99344
99765
  for (const allowed of allowedShells) {
99345
99766
  const normalizedAllowed = this.isWindows ? allowed.toLowerCase() : allowed;
@@ -99353,7 +99774,7 @@ var init_terminal_service = __esm({
99353
99774
  return { shell, args: getShellArgs(shell) };
99354
99775
  }
99355
99776
  }
99356
- if (platform3 === "win32") {
99777
+ if (platform4 === "win32") {
99357
99778
  return { shell: "cmd.exe", args: [] };
99358
99779
  }
99359
99780
  return { shell: "/bin/sh", args: [] };
@@ -100186,7 +100607,7 @@ var init_register_messaging_scripts = __esm({
100186
100607
 
100187
100608
  // ../dashboard/src/github.ts
100188
100609
  function delay(ms) {
100189
- return new Promise((resolve40) => setTimeout(resolve40, ms));
100610
+ return new Promise((resolve42) => setTimeout(resolve42, ms));
100190
100611
  }
100191
100612
  function normalizeCheckState(state) {
100192
100613
  switch ((state ?? "").toLowerCase()) {
@@ -104580,7 +105001,7 @@ var init_register_git_github = __esm({
104580
105001
  });
104581
105002
 
104582
105003
  // ../dashboard/src/terminal.ts
104583
- import { spawn as spawn9 } from "node:child_process";
105004
+ import { spawn as spawn10 } from "node:child_process";
104584
105005
  import { randomUUID as randomUUID17 } from "node:crypto";
104585
105006
  import { EventEmitter as EventEmitter32 } from "node:events";
104586
105007
  function extractBaseCommand(command) {
@@ -104742,7 +105163,7 @@ var init_terminal = __esm({
104742
105163
  return { sessionId: "", error: validation.error };
104743
105164
  }
104744
105165
  const sessionId = randomUUID17();
104745
- const childProcess = spawn9(command, [], {
105166
+ const childProcess = spawn10(command, [], {
104746
105167
  cwd,
104747
105168
  shell: true,
104748
105169
  stdio: ["pipe", "pipe", "pipe"],
@@ -106460,9 +106881,9 @@ var require_readdir_glob = __commonJS({
106460
106881
  var fs3 = __require("fs");
106461
106882
  var { EventEmitter: EventEmitter37 } = __require("events");
106462
106883
  var { Minimatch } = require_minimatch();
106463
- var { resolve: resolve40 } = __require("path");
106884
+ var { resolve: resolve42 } = __require("path");
106464
106885
  function readdir12(dir2, strict) {
106465
- return new Promise((resolve41, reject2) => {
106886
+ return new Promise((resolve43, reject2) => {
106466
106887
  fs3.readdir(dir2, { withFileTypes: true }, (err, files) => {
106467
106888
  if (err) {
106468
106889
  switch (err.code) {
@@ -106470,7 +106891,7 @@ var require_readdir_glob = __commonJS({
106470
106891
  if (strict) {
106471
106892
  reject2(err);
106472
106893
  } else {
106473
- resolve41([]);
106894
+ resolve43([]);
106474
106895
  }
106475
106896
  break;
106476
106897
  case "ENOTSUP":
@@ -106480,7 +106901,7 @@ var require_readdir_glob = __commonJS({
106480
106901
  case "ENAMETOOLONG":
106481
106902
  // Filename too long
106482
106903
  case "UNKNOWN":
106483
- resolve41([]);
106904
+ resolve43([]);
106484
106905
  break;
106485
106906
  case "ELOOP":
106486
106907
  // Too many levels of symbolic links
@@ -106489,30 +106910,30 @@ var require_readdir_glob = __commonJS({
106489
106910
  break;
106490
106911
  }
106491
106912
  } else {
106492
- resolve41(files);
106913
+ resolve43(files);
106493
106914
  }
106494
106915
  });
106495
106916
  });
106496
106917
  }
106497
106918
  function stat12(file, followSymlinks) {
106498
- return new Promise((resolve41, reject2) => {
106919
+ return new Promise((resolve43, reject2) => {
106499
106920
  const statFunc = followSymlinks ? fs3.stat : fs3.lstat;
106500
106921
  statFunc(file, (err, stats) => {
106501
106922
  if (err) {
106502
106923
  switch (err.code) {
106503
106924
  case "ENOENT":
106504
106925
  if (followSymlinks) {
106505
- resolve41(stat12(file, false));
106926
+ resolve43(stat12(file, false));
106506
106927
  } else {
106507
- resolve41(null);
106928
+ resolve43(null);
106508
106929
  }
106509
106930
  break;
106510
106931
  default:
106511
- resolve41(null);
106932
+ resolve43(null);
106512
106933
  break;
106513
106934
  }
106514
106935
  } else {
106515
- resolve41(stats);
106936
+ resolve43(stats);
106516
106937
  }
106517
106938
  });
106518
106939
  });
@@ -106602,7 +107023,7 @@ var require_readdir_glob = __commonJS({
106602
107023
  (skip) => new Minimatch(skip, { dot: true })
106603
107024
  );
106604
107025
  }
106605
- this.iterator = explore(resolve40(cwd || "."), this.options.follow, this.options.stat, this._shouldSkipDirectory.bind(this));
107026
+ this.iterator = explore(resolve42(cwd || "."), this.options.follow, this.options.stat, this._shouldSkipDirectory.bind(this));
106606
107027
  this.paused = false;
106607
107028
  this.inactive = false;
106608
107029
  this.aborted = false;
@@ -106856,10 +107277,10 @@ function awaitify(asyncFn, arity) {
106856
107277
  if (typeof args[arity - 1] === "function") {
106857
107278
  return asyncFn.apply(this, args);
106858
107279
  }
106859
- return new Promise((resolve40, reject2) => {
107280
+ return new Promise((resolve42, reject2) => {
106860
107281
  args[arity - 1] = (err, ...cbArgs) => {
106861
107282
  if (err) return reject2(err);
106862
- resolve40(cbArgs.length > 1 ? cbArgs : cbArgs[0]);
107283
+ resolve42(cbArgs.length > 1 ? cbArgs : cbArgs[0]);
106863
107284
  };
106864
107285
  asyncFn.apply(this, args);
106865
107286
  });
@@ -107041,13 +107462,13 @@ function mapSeries(coll, iteratee, callback) {
107041
107462
  return _asyncMap(eachOfSeries$1, coll, iteratee, callback);
107042
107463
  }
107043
107464
  function promiseCallback() {
107044
- let resolve40, reject2;
107465
+ let resolve42, reject2;
107045
107466
  function callback(err, ...args) {
107046
107467
  if (err) return reject2(err);
107047
- resolve40(args.length > 1 ? args : args[0]);
107468
+ resolve42(args.length > 1 ? args : args[0]);
107048
107469
  }
107049
107470
  callback[PROMISE_SYMBOL] = new Promise((res, rej) => {
107050
- resolve40 = res, reject2 = rej;
107471
+ resolve42 = res, reject2 = rej;
107051
107472
  });
107052
107473
  return callback;
107053
107474
  }
@@ -107320,8 +107741,8 @@ function queue$1(worker, concurrency, payload) {
107320
107741
  });
107321
107742
  }
107322
107743
  if (rejectOnError || !callback) {
107323
- return new Promise((resolve40, reject2) => {
107324
- res = resolve40;
107744
+ return new Promise((resolve42, reject2) => {
107745
+ res = resolve42;
107325
107746
  rej = reject2;
107326
107747
  });
107327
107748
  }
@@ -107360,10 +107781,10 @@ function queue$1(worker, concurrency, payload) {
107360
107781
  }
107361
107782
  const eventMethod = (name) => (handler) => {
107362
107783
  if (!handler) {
107363
- return new Promise((resolve40, reject2) => {
107784
+ return new Promise((resolve42, reject2) => {
107364
107785
  once3(name, (err, data) => {
107365
107786
  if (err) return reject2(err);
107366
- resolve40(data);
107787
+ resolve42(data);
107367
107788
  });
107368
107789
  });
107369
107790
  }
@@ -108539,7 +108960,7 @@ var require_polyfills = __commonJS({
108539
108960
  var constants2 = __require("constants");
108540
108961
  var origCwd = process.cwd;
108541
108962
  var cwd = null;
108542
- var platform3 = process.env.GRACEFUL_FS_PLATFORM || process.platform;
108963
+ var platform4 = process.env.GRACEFUL_FS_PLATFORM || process.platform;
108543
108964
  process.cwd = function() {
108544
108965
  if (!cwd)
108545
108966
  cwd = origCwd.call(process);
@@ -108598,7 +109019,7 @@ var require_polyfills = __commonJS({
108598
109019
  fs3.lchownSync = function() {
108599
109020
  };
108600
109021
  }
108601
- if (platform3 === "win32") {
109022
+ if (platform4 === "win32") {
108602
109023
  fs3.rename = typeof fs3.rename !== "function" ? fs3.rename : (function(fs$rename) {
108603
109024
  function rename6(from, to, cb) {
108604
109025
  var start = Date.now();
@@ -113369,25 +113790,25 @@ var require_util2 = __commonJS({
113369
113790
  };
113370
113791
  },
113371
113792
  createDeferredPromise: function() {
113372
- let resolve40;
113793
+ let resolve42;
113373
113794
  let reject2;
113374
113795
  const promise = new Promise((res, rej) => {
113375
- resolve40 = res;
113796
+ resolve42 = res;
113376
113797
  reject2 = rej;
113377
113798
  });
113378
113799
  return {
113379
113800
  promise,
113380
- resolve: resolve40,
113801
+ resolve: resolve42,
113381
113802
  reject: reject2
113382
113803
  };
113383
113804
  },
113384
113805
  promisify(fn) {
113385
- return new Promise((resolve40, reject2) => {
113806
+ return new Promise((resolve42, reject2) => {
113386
113807
  fn((err, ...args) => {
113387
113808
  if (err) {
113388
113809
  return reject2(err);
113389
113810
  }
113390
- return resolve40(...args);
113811
+ return resolve42(...args);
113391
113812
  });
113392
113813
  });
113393
113814
  },
@@ -114179,7 +114600,7 @@ var require_end_of_stream2 = __commonJS({
114179
114600
  validateBoolean3(opts.cleanup, "cleanup");
114180
114601
  autoCleanup = opts.cleanup;
114181
114602
  }
114182
- return new Promise2((resolve40, reject2) => {
114603
+ return new Promise2((resolve42, reject2) => {
114183
114604
  const cleanup = eos(stream, opts, (err) => {
114184
114605
  if (autoCleanup) {
114185
114606
  cleanup();
@@ -114187,7 +114608,7 @@ var require_end_of_stream2 = __commonJS({
114187
114608
  if (err) {
114188
114609
  reject2(err);
114189
114610
  } else {
114190
- resolve40();
114611
+ resolve42();
114191
114612
  }
114192
114613
  });
114193
114614
  });
@@ -115354,7 +115775,7 @@ var require_readable2 = __commonJS({
115354
115775
  error = this.readableEnded ? null : new AbortError();
115355
115776
  this.destroy(error);
115356
115777
  }
115357
- return new Promise2((resolve40, reject2) => eos(this, (err) => err && err !== error ? reject2(err) : resolve40(null)));
115778
+ return new Promise2((resolve42, reject2) => eos(this, (err) => err && err !== error ? reject2(err) : resolve42(null)));
115358
115779
  };
115359
115780
  Readable2.prototype.push = function(chunk, encoding) {
115360
115781
  return readableAddChunk(this, chunk, encoding, false);
@@ -115898,12 +116319,12 @@ var require_readable2 = __commonJS({
115898
116319
  }
115899
116320
  async function* createAsyncIterator(stream, options) {
115900
116321
  let callback = nop;
115901
- function next(resolve40) {
116322
+ function next(resolve42) {
115902
116323
  if (this === stream) {
115903
116324
  callback();
115904
116325
  callback = nop;
115905
116326
  } else {
115906
- callback = resolve40;
116327
+ callback = resolve42;
115907
116328
  }
115908
116329
  }
115909
116330
  stream.on("readable", next);
@@ -116956,7 +117377,7 @@ var require_duplexify = __commonJS({
116956
117377
  );
116957
117378
  };
116958
117379
  function fromAsyncGen(fn) {
116959
- let { promise, resolve: resolve40 } = createDeferredPromise();
117380
+ let { promise, resolve: resolve42 } = createDeferredPromise();
116960
117381
  const ac = new AbortController2();
116961
117382
  const signal = ac.signal;
116962
117383
  const value = fn(
@@ -116971,7 +117392,7 @@ var require_duplexify = __commonJS({
116971
117392
  throw new AbortError(void 0, {
116972
117393
  cause: signal.reason
116973
117394
  });
116974
- ({ promise, resolve: resolve40 } = createDeferredPromise());
117395
+ ({ promise, resolve: resolve42 } = createDeferredPromise());
116975
117396
  yield chunk;
116976
117397
  }
116977
117398
  })(),
@@ -116982,8 +117403,8 @@ var require_duplexify = __commonJS({
116982
117403
  return {
116983
117404
  value,
116984
117405
  write(chunk, encoding, cb) {
116985
- const _resolve = resolve40;
116986
- resolve40 = null;
117406
+ const _resolve = resolve42;
117407
+ resolve42 = null;
116987
117408
  _resolve({
116988
117409
  chunk,
116989
117410
  done: false,
@@ -116991,8 +117412,8 @@ var require_duplexify = __commonJS({
116991
117412
  });
116992
117413
  },
116993
117414
  final(cb) {
116994
- const _resolve = resolve40;
116995
- resolve40 = null;
117415
+ const _resolve = resolve42;
117416
+ resolve42 = null;
116996
117417
  _resolve({
116997
117418
  done: true,
116998
117419
  cb
@@ -117444,7 +117865,7 @@ var require_pipeline = __commonJS({
117444
117865
  callback();
117445
117866
  }
117446
117867
  };
117447
- const wait = () => new Promise2((resolve40, reject2) => {
117868
+ const wait = () => new Promise2((resolve42, reject2) => {
117448
117869
  if (error) {
117449
117870
  reject2(error);
117450
117871
  } else {
@@ -117452,7 +117873,7 @@ var require_pipeline = __commonJS({
117452
117873
  if (error) {
117453
117874
  reject2(error);
117454
117875
  } else {
117455
- resolve40();
117876
+ resolve42();
117456
117877
  }
117457
117878
  };
117458
117879
  }
@@ -118096,8 +118517,8 @@ var require_operators = __commonJS({
118096
118517
  next = null;
118097
118518
  }
118098
118519
  if (!done && (queue2.length >= highWaterMark2 || cnt >= concurrency)) {
118099
- await new Promise2((resolve40) => {
118100
- resume = resolve40;
118520
+ await new Promise2((resolve42) => {
118521
+ resume = resolve42;
118101
118522
  });
118102
118523
  }
118103
118524
  }
@@ -118131,8 +118552,8 @@ var require_operators = __commonJS({
118131
118552
  queue2.shift();
118132
118553
  maybeResume();
118133
118554
  }
118134
- await new Promise2((resolve40) => {
118135
- next = resolve40;
118555
+ await new Promise2((resolve42) => {
118556
+ next = resolve42;
118136
118557
  });
118137
118558
  }
118138
118559
  } finally {
@@ -118390,7 +118811,7 @@ var require_promises = __commonJS({
118390
118811
  var { finished } = require_end_of_stream2();
118391
118812
  require_stream2();
118392
118813
  function pipeline(...streams) {
118393
- return new Promise2((resolve40, reject2) => {
118814
+ return new Promise2((resolve42, reject2) => {
118394
118815
  let signal;
118395
118816
  let end;
118396
118817
  const lastArg = streams[streams.length - 1];
@@ -118405,7 +118826,7 @@ var require_promises = __commonJS({
118405
118826
  if (err) {
118406
118827
  reject2(err);
118407
118828
  } else {
118408
- resolve40(value);
118829
+ resolve42(value);
118409
118830
  }
118410
118831
  },
118411
118832
  {
@@ -123178,10 +123599,10 @@ var require_commonjs3 = __commonJS({
123178
123599
  * Return a void Promise that resolves once the stream ends.
123179
123600
  */
123180
123601
  async promise() {
123181
- return new Promise((resolve40, reject2) => {
123602
+ return new Promise((resolve42, reject2) => {
123182
123603
  this.on(DESTROYED, () => reject2(new Error("stream destroyed")));
123183
123604
  this.on("error", (er) => reject2(er));
123184
- this.on("end", () => resolve40());
123605
+ this.on("end", () => resolve42());
123185
123606
  });
123186
123607
  }
123187
123608
  /**
@@ -123205,7 +123626,7 @@ var require_commonjs3 = __commonJS({
123205
123626
  return Promise.resolve({ done: false, value: res });
123206
123627
  if (this[EOF])
123207
123628
  return stop();
123208
- let resolve40;
123629
+ let resolve42;
123209
123630
  let reject2;
123210
123631
  const onerr = (er) => {
123211
123632
  this.off("data", ondata);
@@ -123219,19 +123640,19 @@ var require_commonjs3 = __commonJS({
123219
123640
  this.off("end", onend);
123220
123641
  this.off(DESTROYED, ondestroy);
123221
123642
  this.pause();
123222
- resolve40({ value, done: !!this[EOF] });
123643
+ resolve42({ value, done: !!this[EOF] });
123223
123644
  };
123224
123645
  const onend = () => {
123225
123646
  this.off("error", onerr);
123226
123647
  this.off("data", ondata);
123227
123648
  this.off(DESTROYED, ondestroy);
123228
123649
  stop();
123229
- resolve40({ done: true, value: void 0 });
123650
+ resolve42({ done: true, value: void 0 });
123230
123651
  };
123231
123652
  const ondestroy = () => onerr(new Error("stream destroyed"));
123232
123653
  return new Promise((res2, rej) => {
123233
123654
  reject2 = rej;
123234
- resolve40 = res2;
123655
+ resolve42 = res2;
123235
123656
  this.once(DESTROYED, ondestroy);
123236
123657
  this.once("error", onerr);
123237
123658
  this.once("end", onend);
@@ -124247,9 +124668,9 @@ var require_commonjs4 = __commonJS({
124247
124668
  if (this.#asyncReaddirInFlight) {
124248
124669
  await this.#asyncReaddirInFlight;
124249
124670
  } else {
124250
- let resolve40 = () => {
124671
+ let resolve42 = () => {
124251
124672
  };
124252
- this.#asyncReaddirInFlight = new Promise((res) => resolve40 = res);
124673
+ this.#asyncReaddirInFlight = new Promise((res) => resolve42 = res);
124253
124674
  try {
124254
124675
  for (const e of await this.#fs.promises.readdir(fullpath, {
124255
124676
  withFileTypes: true
@@ -124262,7 +124683,7 @@ var require_commonjs4 = __commonJS({
124262
124683
  children.provisional = 0;
124263
124684
  }
124264
124685
  this.#asyncReaddirInFlight = void 0;
124265
- resolve40();
124686
+ resolve42();
124266
124687
  }
124267
124688
  return children.slice(0, children.provisional);
124268
124689
  }
@@ -125129,7 +125550,7 @@ var require_pattern = __commonJS({
125129
125550
  #isUNC;
125130
125551
  #isAbsolute;
125131
125552
  #followGlobstar = true;
125132
- constructor(patternList, globList, index2, platform3) {
125553
+ constructor(patternList, globList, index2, platform4) {
125133
125554
  if (!isPatternList(patternList)) {
125134
125555
  throw new TypeError("empty pattern list");
125135
125556
  }
@@ -125146,7 +125567,7 @@ var require_pattern = __commonJS({
125146
125567
  this.#patternList = patternList;
125147
125568
  this.#globList = globList;
125148
125569
  this.#index = index2;
125149
- this.#platform = platform3;
125570
+ this.#platform = platform4;
125150
125571
  if (this.#index === 0) {
125151
125572
  if (this.isUNC()) {
125152
125573
  const [p0, p1, p2, p3, ...prest] = this.#patternList;
@@ -125298,12 +125719,12 @@ var require_ignore = __commonJS({
125298
125719
  absoluteChildren;
125299
125720
  platform;
125300
125721
  mmopts;
125301
- constructor(ignored, { nobrace, nocase, noext, noglobstar, platform: platform3 = defaultPlatform }) {
125722
+ constructor(ignored, { nobrace, nocase, noext, noglobstar, platform: platform4 = defaultPlatform }) {
125302
125723
  this.relative = [];
125303
125724
  this.absolute = [];
125304
125725
  this.relativeChildren = [];
125305
125726
  this.absoluteChildren = [];
125306
- this.platform = platform3;
125727
+ this.platform = platform4;
125307
125728
  this.mmopts = {
125308
125729
  dot: true,
125309
125730
  nobrace,
@@ -125311,7 +125732,7 @@ var require_ignore = __commonJS({
125311
125732
  noext,
125312
125733
  noglobstar,
125313
125734
  optimizationLevel: 2,
125314
- platform: platform3,
125735
+ platform: platform4,
125315
125736
  nocomment: true,
125316
125737
  nonegate: true
125317
125738
  };
@@ -127035,11 +127456,11 @@ var require_core = __commonJS({
127035
127456
  this._finalize();
127036
127457
  }
127037
127458
  var self2 = this;
127038
- return new Promise(function(resolve40, reject2) {
127459
+ return new Promise(function(resolve42, reject2) {
127039
127460
  var errored;
127040
127461
  self2._module.on("end", function() {
127041
127462
  if (!errored) {
127042
- resolve40();
127463
+ resolve42();
127043
127464
  }
127044
127465
  });
127045
127466
  self2._module.on("error", function(err) {
@@ -127533,8 +127954,8 @@ var require_zip_archive_entry = __commonJS({
127533
127954
  }
127534
127955
  this.name = name;
127535
127956
  };
127536
- ZipArchiveEntry.prototype.setPlatform = function(platform3) {
127537
- this.platform = platform3;
127957
+ ZipArchiveEntry.prototype.setPlatform = function(platform4) {
127958
+ this.platform = platform4;
127538
127959
  };
127539
127960
  ZipArchiveEntry.prototype.setSize = function(size) {
127540
127961
  if (size < 0) {
@@ -129479,8 +129900,8 @@ var require_streamx = __commonJS({
129479
129900
  return this;
129480
129901
  },
129481
129902
  next() {
129482
- return new Promise(function(resolve40, reject2) {
129483
- promiseResolve = resolve40;
129903
+ return new Promise(function(resolve42, reject2) {
129904
+ promiseResolve = resolve42;
129484
129905
  promiseReject = reject2;
129485
129906
  const data = stream.read();
129486
129907
  if (data !== null) ondata(data);
@@ -129510,11 +129931,11 @@ var require_streamx = __commonJS({
129510
129931
  }
129511
129932
  function destroy(err) {
129512
129933
  stream.destroy(err);
129513
- return new Promise((resolve40, reject2) => {
129514
- if (stream._duplexState & DESTROYED) return resolve40({ value: void 0, done: true });
129934
+ return new Promise((resolve42, reject2) => {
129935
+ if (stream._duplexState & DESTROYED) return resolve42({ value: void 0, done: true });
129515
129936
  stream.once("close", function() {
129516
129937
  if (err) reject2(err);
129517
- else resolve40({ value: void 0, done: true });
129938
+ else resolve42({ value: void 0, done: true });
129518
129939
  });
129519
129940
  });
129520
129941
  }
@@ -129558,8 +129979,8 @@ var require_streamx = __commonJS({
129558
129979
  const writes = pending + (ws._duplexState & WRITE_WRITING ? 1 : 0);
129559
129980
  if (writes === 0) return Promise.resolve(true);
129560
129981
  if (state.drains === null) state.drains = [];
129561
- return new Promise((resolve40) => {
129562
- state.drains.push({ writes, resolve: resolve40 });
129982
+ return new Promise((resolve42) => {
129983
+ state.drains.push({ writes, resolve: resolve42 });
129563
129984
  });
129564
129985
  }
129565
129986
  write(data) {
@@ -129664,10 +130085,10 @@ var require_streamx = __commonJS({
129664
130085
  cb(null);
129665
130086
  }
129666
130087
  function pipelinePromise(...streams) {
129667
- return new Promise((resolve40, reject2) => {
130088
+ return new Promise((resolve42, reject2) => {
129668
130089
  return pipeline(...streams, (err) => {
129669
130090
  if (err) return reject2(err);
129670
- resolve40();
130091
+ resolve42();
129671
130092
  });
129672
130093
  });
129673
130094
  }
@@ -130324,16 +130745,16 @@ var require_extract = __commonJS({
130324
130745
  entryCallback = null;
130325
130746
  cb(err);
130326
130747
  }
130327
- function onnext(resolve40, reject2) {
130748
+ function onnext(resolve42, reject2) {
130328
130749
  if (error) {
130329
130750
  return reject2(error);
130330
130751
  }
130331
130752
  if (entryStream) {
130332
- resolve40({ value: entryStream, done: false });
130753
+ resolve42({ value: entryStream, done: false });
130333
130754
  entryStream = null;
130334
130755
  return;
130335
130756
  }
130336
- promiseResolve = resolve40;
130757
+ promiseResolve = resolve42;
130337
130758
  promiseReject = reject2;
130338
130759
  consumeCallback(null);
130339
130760
  if (extract._finished && promiseResolve) {
@@ -130361,11 +130782,11 @@ var require_extract = __commonJS({
130361
130782
  function destroy(err) {
130362
130783
  extract.destroy(err);
130363
130784
  consumeCallback(err);
130364
- return new Promise((resolve40, reject2) => {
130365
- if (extract.destroyed) return resolve40({ value: void 0, done: true });
130785
+ return new Promise((resolve42, reject2) => {
130786
+ if (extract.destroyed) return resolve42({ value: void 0, done: true });
130366
130787
  extract.once("close", function() {
130367
130788
  if (err) reject2(err);
130368
- else resolve40({ value: void 0, done: true });
130789
+ else resolve42({ value: void 0, done: true });
130369
130790
  });
130370
130791
  });
130371
130792
  }
@@ -133702,6 +134123,20 @@ var init_register_settings_sync_inbound_routes = __esm({
133702
134123
  });
133703
134124
 
133704
134125
  // ../dashboard/src/routes/register-agent-core-routes.ts
134126
+ function isCompatibleDefaultHeartbeatPath(path5, agent) {
134127
+ const trimmed = path5?.trim();
134128
+ if (!trimmed) {
134129
+ return false;
134130
+ }
134131
+ if (trimmed === getDefaultHeartbeatProcedurePath(agent.id, agent.name)) {
134132
+ return true;
134133
+ }
134134
+ if (trimmed === getDefaultHeartbeatProcedurePath(agent.id)) {
134135
+ return true;
134136
+ }
134137
+ const safeId = agent.id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "agent";
134138
+ return new RegExp(`^\\.fusion/agents/[^/]+-${safeId}/HEARTBEAT\\.md$`).test(trimmed);
134139
+ }
133705
134140
  function registerAgentCoreListCreateRoutes(ctx, deps) {
133706
134141
  const { router, getProjectContext: getProjectContext3, rethrowAsApiError: rethrowAsApiError8 } = ctx;
133707
134142
  const { sanitizeAgentTaskLinks, validateAgentInstructionsPayload } = deps;
@@ -133831,7 +134266,7 @@ function registerAgentCoreListCreateRoutes(ctx, deps) {
133831
134266
  bundleConfig: bundleConfig ?? void 0,
133832
134267
  heartbeatProcedurePath: heartbeatProcedurePath ?? void 0
133833
134268
  });
133834
- const expectedDefaultPath = getDefaultHeartbeatProcedurePath(agent.id);
134269
+ const expectedDefaultPath = getDefaultHeartbeatProcedurePath(agent.id, agent.name);
133835
134270
  if (agent.heartbeatProcedurePath === expectedDefaultPath) {
133836
134271
  try {
133837
134272
  await ensureDefaultHeartbeatProcedureFile(scopedStore.getRootDir(), expectedDefaultPath, HEARTBEAT_PROCEDURE);
@@ -134089,7 +134524,7 @@ function registerAgentCoreRoutes(ctx, deps) {
134089
134524
  if (!existing) {
134090
134525
  throw notFound(`agent ${req.params.id} not found`);
134091
134526
  }
134092
- const targetPath = getDefaultHeartbeatProcedurePath(req.params.id);
134527
+ const targetPath = isCompatibleDefaultHeartbeatPath(existing.heartbeatProcedurePath, existing) ? existing.heartbeatProcedurePath : getDefaultHeartbeatProcedurePath(existing.id, existing.name);
134093
134528
  const filePath = await ensureDefaultHeartbeatProcedureFile(
134094
134529
  scopedStore.getRootDir(),
134095
134530
  targetPath,
@@ -137349,9 +137784,9 @@ function registerProxyRoutes(router, deps) {
137349
137784
  if (req.rawBody && req.rawBody.length > 0) {
137350
137785
  body = req.rawBody;
137351
137786
  } else {
137352
- await new Promise((resolve40, reject2) => {
137787
+ await new Promise((resolve42, reject2) => {
137353
137788
  req.on("data", (chunk) => chunks.push(chunk));
137354
- req.on("end", resolve40);
137789
+ req.on("end", resolve42);
137355
137790
  req.on("error", reject2);
137356
137791
  });
137357
137792
  if (chunks.length > 0) {
@@ -137482,6 +137917,7 @@ var init_register_model_routes = __esm({
137482
137917
  let defaultProvider;
137483
137918
  let defaultModelId;
137484
137919
  let useClaudeCli = false;
137920
+ let useDroidCli = false;
137485
137921
  if (store) {
137486
137922
  try {
137487
137923
  const globalStore = store.getGlobalSettingsStore();
@@ -137491,6 +137927,7 @@ var init_register_model_routes = __esm({
137491
137927
  defaultProvider = globalSettings.defaultProvider;
137492
137928
  defaultModelId = globalSettings.defaultModelId;
137493
137929
  useClaudeCli = globalSettings.useClaudeCli === true;
137930
+ useDroidCli = globalSettings.useDroidCli === true;
137494
137931
  } catch {
137495
137932
  }
137496
137933
  }
@@ -137516,6 +137953,9 @@ var init_register_model_routes = __esm({
137516
137953
  if (!useClaudeCli) {
137517
137954
  models = models.filter((m) => m.provider !== "pi-claude-cli");
137518
137955
  }
137956
+ if (!useDroidCli) {
137957
+ models = models.filter((m) => m.provider !== "droid-cli");
137958
+ }
137519
137959
  res.json({
137520
137960
  models,
137521
137961
  favoriteProviders,
@@ -137763,13 +138203,13 @@ function getHomeDir5() {
137763
138203
  return process.env.HOME || process.env.USERPROFILE || os4.homedir();
137764
138204
  }
137765
138205
  function execFileAsync5(file, args, options) {
137766
- return new Promise((resolve40, reject2) => {
138206
+ return new Promise((resolve42, reject2) => {
137767
138207
  child_process.execFile(file, args, options, (error, stdout, stderr) => {
137768
138208
  if (error) {
137769
138209
  reject2(error);
137770
138210
  return;
137771
138211
  }
137772
- resolve40({ stdout: String(stdout), stderr: String(stderr) });
138212
+ resolve42({ stdout: String(stdout), stderr: String(stderr) });
137773
138213
  });
137774
138214
  });
137775
138215
  }
@@ -137828,7 +138268,7 @@ function formatDuration(ms) {
137828
138268
  return remHours > 0 ? `${days}d ${remHours}h` : `${days}d`;
137829
138269
  }
137830
138270
  function httpsRequest(url, options) {
137831
- return new Promise((resolve40, reject2) => {
138271
+ return new Promise((resolve42, reject2) => {
137832
138272
  const parsed = new URL(url);
137833
138273
  const req = https.request(
137834
138274
  {
@@ -137848,7 +138288,7 @@ function httpsRequest(url, options) {
137848
138288
  if (typeof v === "string") hdrs[k.toLowerCase()] = v;
137849
138289
  else if (Array.isArray(v)) hdrs[k.toLowerCase()] = v.join(", ");
137850
138290
  }
137851
- resolve40({
138291
+ resolve42({
137852
138292
  status: res.statusCode || 0,
137853
138293
  headers: hdrs,
137854
138294
  body: Buffer.concat(chunks).toString("utf-8")
@@ -138095,7 +138535,7 @@ async function fetchClaudeUsageViaCli() {
138095
138535
  env: { ...process.env, TERM: "xterm-256color" }
138096
138536
  };
138097
138537
  if (isWindows) ptyOptions.useConpty = false;
138098
- const output = await new Promise((resolve40, reject2) => {
138538
+ const output = await new Promise((resolve42, reject2) => {
138099
138539
  let buf = "";
138100
138540
  let settled = false;
138101
138541
  let sentCommand = false;
@@ -138111,7 +138551,7 @@ async function fetchClaudeUsageViaCli() {
138111
138551
  }
138112
138552
  const clean = _stripClaudeAnsi(buf);
138113
138553
  if (clean.includes("Current session") || clean.includes("% left") || clean.includes("% used")) {
138114
- resolve40(buf);
138554
+ resolve42(buf);
138115
138555
  } else {
138116
138556
  reject2(new Error("Claude CLI timed out after 60s \u2014 got output but no usage data. Try running `claude /usage` manually."));
138117
138557
  }
@@ -138162,7 +138602,7 @@ async function fetchClaudeUsageViaCli() {
138162
138602
  ptyProcess.kill();
138163
138603
  } catch {
138164
138604
  }
138165
- resolve40(buf);
138605
+ resolve42(buf);
138166
138606
  }
138167
138607
  }, 2e3);
138168
138608
  }
@@ -138173,7 +138613,7 @@ async function fetchClaudeUsageViaCli() {
138173
138613
  if (settled) return;
138174
138614
  settled = true;
138175
138615
  clearTimeout(timeout2);
138176
- resolve40(buf);
138616
+ resolve42(buf);
138177
138617
  });
138178
138618
  });
138179
138619
  const cleanOutput = _stripClaudeAnsi(output);
@@ -138841,9 +139281,9 @@ async function fetchGitHubCopilotUsage() {
138841
139281
  return usage;
138842
139282
  }
138843
139283
  function withTimeout(providerPromise, providerName, timeoutMs = PROVIDER_FETCH_TIMEOUT_MS) {
138844
- return new Promise((resolve40) => {
139284
+ return new Promise((resolve42) => {
138845
139285
  const timer = setTimeout(() => {
138846
- resolve40({
139286
+ resolve42({
138847
139287
  name: providerName,
138848
139288
  icon: "\u23F1\uFE0F",
138849
139289
  status: "error",
@@ -138853,10 +139293,10 @@ function withTimeout(providerPromise, providerName, timeoutMs = PROVIDER_FETCH_T
138853
139293
  }, timeoutMs);
138854
139294
  providerPromise.then((result) => {
138855
139295
  clearTimeout(timer);
138856
- resolve40(result);
139296
+ resolve42(result);
138857
139297
  }).catch((err) => {
138858
139298
  clearTimeout(timer);
138859
- resolve40({
139299
+ resolve42({
138860
139300
  name: providerName,
138861
139301
  icon: "\u23F1\uFE0F",
138862
139302
  status: "error",
@@ -138911,7 +139351,7 @@ var init_usage = __esm({
138911
139351
  ANTHROPIC_OAUTH_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
138912
139352
  ANTHROPIC_OAUTH_BETA = "oauth-2025-04-20";
138913
139353
  CLAUDE_USAGE_USER_AGENT = "claude-code-fusion-dashboard";
138914
- _sleep = (ms) => new Promise((resolve40) => setTimeout(resolve40, ms));
139354
+ _sleep = (ms) => new Promise((resolve42) => setTimeout(resolve42, ms));
138915
139355
  sleepFn = _sleep;
138916
139356
  PROVIDER_FETCH_TIMEOUT_MS = 1e4;
138917
139357
  CLAUDE_FETCH_TIMEOUT_MS = 75e3;
@@ -138943,7 +139383,7 @@ var init_register_usage_routes = __esm({
138943
139383
  });
138944
139384
 
138945
139385
  // ../dashboard/src/claude-cli-probe.ts
138946
- import { spawn as spawn10 } from "node:child_process";
139386
+ import { spawn as spawn11 } from "node:child_process";
138947
139387
  async function probeClaudeCli(options = {}) {
138948
139388
  const startedAt = Date.now();
138949
139389
  const timeoutMs = options.timeoutMs ?? PROBE_TIMEOUT_MS;
@@ -138953,7 +139393,7 @@ async function probeClaudeCli(options = {}) {
138953
139393
  resolvePromise({ ...result, probeDurationMs: Date.now() - startedAt });
138954
139394
  };
138955
139395
  let settled = false;
138956
- const child = spawn10(binaryPath ?? "claude", ["--version"], {
139396
+ const child = spawn11(binaryPath ?? "claude", ["--version"], {
138957
139397
  stdio: ["ignore", "pipe", "pipe"]
138958
139398
  });
138959
139399
  const timer = setTimeout(() => {
@@ -139011,7 +139451,7 @@ async function probeClaudeCli(options = {}) {
139011
139451
  async function tryResolveBinaryPath3(binary) {
139012
139452
  return new Promise((resolvePromise) => {
139013
139453
  const which = process.platform === "win32" ? "where" : "which";
139014
- const child = spawn10(which, [binary], { stdio: ["ignore", "pipe", "ignore"] });
139454
+ const child = spawn11(which, [binary], { stdio: ["ignore", "pipe", "ignore"] });
139015
139455
  let out = "";
139016
139456
  child.stdout?.on("data", (chunk) => {
139017
139457
  out += chunk.toString("utf-8");
@@ -139036,7 +139476,7 @@ var init_claude_cli_probe = __esm({
139036
139476
  });
139037
139477
 
139038
139478
  // ../dashboard/src/droid-cli-probe.ts
139039
- import { spawn as spawn11 } from "node:child_process";
139479
+ import { spawn as spawn12 } from "node:child_process";
139040
139480
  async function probeDroidCli(options = {}) {
139041
139481
  const startedAt = Date.now();
139042
139482
  const timeoutMs = options.timeoutMs ?? PROBE_TIMEOUT_MS2;
@@ -139046,7 +139486,7 @@ async function probeDroidCli(options = {}) {
139046
139486
  resolvePromise({ ...result, probeDurationMs: Date.now() - startedAt });
139047
139487
  };
139048
139488
  let settled = false;
139049
- const child = spawn11(binaryPath ?? "droid", ["--version"], {
139489
+ const child = spawn12(binaryPath ?? "droid", ["--version"], {
139050
139490
  stdio: ["ignore", "pipe", "pipe"]
139051
139491
  });
139052
139492
  const timer = setTimeout(() => {
@@ -139104,7 +139544,7 @@ async function probeDroidCli(options = {}) {
139104
139544
  async function tryResolveBinaryPath4(binary) {
139105
139545
  return new Promise((resolvePromise) => {
139106
139546
  const which = process.platform === "win32" ? "where" : "which";
139107
- const child = spawn11(which, [binary], { stdio: ["ignore", "pipe", "ignore"] });
139547
+ const child = spawn12(which, [binary], { stdio: ["ignore", "pipe", "ignore"] });
139108
139548
  let out = "";
139109
139549
  child.stdout?.on("data", (chunk) => {
139110
139550
  out += chunk.toString("utf-8");
@@ -139479,8 +139919,8 @@ var init_register_auth_routes = __esm({
139479
139919
  loginInProgress.set(provider, abortController);
139480
139920
  let authResolve;
139481
139921
  let authReject;
139482
- const authUrlPromise = new Promise((resolve40, reject2) => {
139483
- authResolve = resolve40;
139922
+ const authUrlPromise = new Promise((resolve42, reject2) => {
139923
+ authResolve = resolve42;
139484
139924
  authReject = reject2;
139485
139925
  });
139486
139926
  const loginPromise = storage.login(provider, {
@@ -139948,7 +140388,7 @@ function stripAnsi2(str) {
139948
140388
  return str.replace(/\x1B\[[0-9;]*[mGKJHFABCDSTsu]/g, "");
139949
140389
  }
139950
140390
  async function mintAgentApiKeyViaCli(opts) {
139951
- const { spawn: spawn17 } = await import("node:child_process");
140391
+ const { spawn: spawn19 } = await import("node:child_process");
139952
140392
  const bin = opts.cliBinaryPath ?? "paperclipai";
139953
140393
  const args = [
139954
140394
  "agent",
@@ -139969,10 +140409,10 @@ async function mintAgentApiKeyViaCli(opts) {
139969
140409
  args.push("--data-dir", opts.dataDir);
139970
140410
  }
139971
140411
  const timeoutMs = opts.cliTimeoutMs ?? 3e4;
139972
- return new Promise((resolve40, reject2) => {
140412
+ return new Promise((resolve42, reject2) => {
139973
140413
  let child;
139974
140414
  try {
139975
- child = spawn17(bin, args, { stdio: ["ignore", "pipe", "pipe"] });
140415
+ child = spawn19(bin, args, { stdio: ["ignore", "pipe", "pipe"] });
139976
140416
  } catch (err) {
139977
140417
  const code = err.code;
139978
140418
  if (code === "ENOENT") {
@@ -140043,7 +140483,7 @@ async function mintAgentApiKeyViaCli(opts) {
140043
140483
  const apiBase = (typeof r.apiBase === "string" ? r.apiBase : void 0) ?? (typeof r.api_base === "string" ? r.api_base : void 0);
140044
140484
  const agentId = (typeof r.agentId === "string" ? r.agentId : void 0) ?? (typeof r.id === "string" ? r.id : void 0);
140045
140485
  const companyId = typeof r.companyId === "string" ? r.companyId : void 0;
140046
- resolve40({ apiKey, apiBase, agentId, companyId, raw: parsed });
140486
+ resolve42({ apiKey, apiBase, agentId, companyId, raw: parsed });
140047
140487
  });
140048
140488
  });
140049
140489
  }
@@ -140055,7 +140495,7 @@ function remapSpawnError(err, bin) {
140055
140495
  return err instanceof Error ? err : new Error(String(err));
140056
140496
  }
140057
140497
  async function spawnPaperclipCliJson(args, opts) {
140058
- const { spawn: spawn17 } = await import("node:child_process");
140498
+ const { spawn: spawn19 } = await import("node:child_process");
140059
140499
  const bin = opts.cliBinaryPath ?? "paperclipai";
140060
140500
  const fullArgs = [...args, "--json"];
140061
140501
  if (opts.cliConfigPath) {
@@ -140063,10 +140503,10 @@ async function spawnPaperclipCliJson(args, opts) {
140063
140503
  }
140064
140504
  const timeoutMs = opts.cliTimeoutMs ?? 15e3;
140065
140505
  const label = ["paperclipai", ...args].join(" ");
140066
- return new Promise((resolve40, reject2) => {
140506
+ return new Promise((resolve42, reject2) => {
140067
140507
  let child;
140068
140508
  try {
140069
- child = spawn17(bin, fullArgs, { stdio: ["ignore", "pipe", "pipe"] });
140509
+ child = spawn19(bin, fullArgs, { stdio: ["ignore", "pipe", "pipe"] });
140070
140510
  } catch (err) {
140071
140511
  reject2(remapSpawnError(err, bin));
140072
140512
  return;
@@ -140109,7 +140549,7 @@ async function spawnPaperclipCliJson(args, opts) {
140109
140549
  return;
140110
140550
  }
140111
140551
  try {
140112
- resolve40(JSON.parse(cleaned));
140552
+ resolve42(JSON.parse(cleaned));
140113
140553
  } catch {
140114
140554
  reject2(new Error(`${label} returned non-JSON output: ${cleaned.slice(0, 200)}`));
140115
140555
  }
@@ -140238,7 +140678,7 @@ var init_paperclip_client = __esm({
140238
140678
  // ../../plugins/fusion-plugin-paperclip-runtime/dist/runtime-adapter.js
140239
140679
  import { randomUUID as randomUUID20 } from "node:crypto";
140240
140680
  function sleep4(ms) {
140241
- return new Promise((resolve40) => setTimeout(resolve40, ms));
140681
+ return new Promise((resolve42) => setTimeout(resolve42, ms));
140242
140682
  }
140243
140683
  function asString2(value) {
140244
140684
  return typeof value === "string" ? value : void 0;
@@ -140879,8 +141319,184 @@ var init_register_runtime_provider_routes = __esm({
140879
141319
  }
140880
141320
  });
140881
141321
 
141322
+ // ../dashboard/src/cli-package-version.ts
141323
+ import { existsSync as existsSync32, readFileSync as readFileSync10 } from "node:fs";
141324
+ import { dirname as dirname15, resolve as resolve23 } from "node:path";
141325
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
141326
+ function readCliPackageVersion(pkgPath) {
141327
+ if (!existsSync32(pkgPath)) {
141328
+ return null;
141329
+ }
141330
+ try {
141331
+ const parsed = JSON.parse(readFileSync10(pkgPath, "utf-8"));
141332
+ if (parsed.name === CLI_PACKAGE_NAME && typeof parsed.version === "string" && parsed.version.length > 0) {
141333
+ return {
141334
+ packageJsonPath: pkgPath,
141335
+ version: parsed.version
141336
+ };
141337
+ }
141338
+ } catch {
141339
+ }
141340
+ return null;
141341
+ }
141342
+ function resolveCliPackageVersionInfo(startDir) {
141343
+ let currentDir = startDir;
141344
+ for (let i = 0; i < 8; i += 1) {
141345
+ const versionInfo = readCliPackageVersion(resolve23(currentDir, "package.json"));
141346
+ if (versionInfo) {
141347
+ return versionInfo;
141348
+ }
141349
+ const parentDir = resolve23(currentDir, "..");
141350
+ if (parentDir === currentDir) {
141351
+ break;
141352
+ }
141353
+ currentDir = parentDir;
141354
+ }
141355
+ currentDir = startDir;
141356
+ for (let i = 0; i < 8; i += 1) {
141357
+ const versionInfo = readCliPackageVersion(resolve23(currentDir, "..", "cli", "package.json"));
141358
+ if (versionInfo) {
141359
+ return versionInfo;
141360
+ }
141361
+ const parentDir = resolve23(currentDir, "..");
141362
+ if (parentDir === currentDir) {
141363
+ break;
141364
+ }
141365
+ currentDir = parentDir;
141366
+ }
141367
+ return null;
141368
+ }
141369
+ function getCliPackageVersion(importMetaUrl = import.meta.url) {
141370
+ const startDir = dirname15(fileURLToPath4(importMetaUrl));
141371
+ return resolveCliPackageVersionInfo(startDir)?.version ?? process.env.npm_package_version ?? "0.0.0";
141372
+ }
141373
+ var CLI_PACKAGE_NAME;
141374
+ var init_cli_package_version = __esm({
141375
+ "../dashboard/src/cli-package-version.ts"() {
141376
+ "use strict";
141377
+ CLI_PACKAGE_NAME = "@runfusion/fusion";
141378
+ }
141379
+ });
141380
+
141381
+ // ../dashboard/src/routes/register-fn-binary-routes.ts
141382
+ import { spawn as spawn13 } from "node:child_process";
141383
+ function buildStatusPayload(binary, expectedVersion) {
141384
+ let state = "missing";
141385
+ if (binary.installed) {
141386
+ state = binary.version && binary.version !== expectedVersion ? "version-mismatch" : "installed";
141387
+ }
141388
+ return {
141389
+ binary,
141390
+ expectedVersion,
141391
+ state,
141392
+ install: {
141393
+ npm: FN_INSTALL_NPM,
141394
+ curl: FN_INSTALL_CURL,
141395
+ package: FN_NPM_PACKAGE
141396
+ }
141397
+ };
141398
+ }
141399
+ function runNpmInstall() {
141400
+ const startedAt = Date.now();
141401
+ const command = FN_INSTALL_NPM;
141402
+ return new Promise((resolve42) => {
141403
+ let stdout = "";
141404
+ let stderr = "";
141405
+ let timedOut = false;
141406
+ const child = spawn13("npm", ["install", "-g", FN_NPM_PACKAGE], {
141407
+ stdio: ["ignore", "pipe", "pipe"],
141408
+ shell: false
141409
+ });
141410
+ const timer = setTimeout(() => {
141411
+ timedOut = true;
141412
+ try {
141413
+ child.kill("SIGKILL");
141414
+ } catch {
141415
+ }
141416
+ }, INSTALL_TIMEOUT_MS);
141417
+ const append = (target, chunk) => {
141418
+ const text = chunk.toString("utf8");
141419
+ if (target === "stdout") {
141420
+ if (stdout.length < MAX_OUTPUT_BYTES2) {
141421
+ stdout += text.slice(0, MAX_OUTPUT_BYTES2 - stdout.length);
141422
+ }
141423
+ } else {
141424
+ if (stderr.length < MAX_OUTPUT_BYTES2) {
141425
+ stderr += text.slice(0, MAX_OUTPUT_BYTES2 - stderr.length);
141426
+ }
141427
+ }
141428
+ };
141429
+ child.stdout?.on("data", (c) => append("stdout", c));
141430
+ child.stderr?.on("data", (c) => append("stderr", c));
141431
+ child.on("error", (err) => {
141432
+ clearTimeout(timer);
141433
+ resolve42({
141434
+ success: false,
141435
+ exitCode: null,
141436
+ stdout,
141437
+ stderr: stderr || err.message,
141438
+ command,
141439
+ durationMs: Date.now() - startedAt
141440
+ });
141441
+ });
141442
+ child.on("close", (exitCode) => {
141443
+ clearTimeout(timer);
141444
+ const combined = `${stdout}
141445
+ ${stderr}`;
141446
+ const eaccesHit = /EACCES|permission denied|Operation not permitted/i.test(combined);
141447
+ const success = exitCode === 0 && !timedOut;
141448
+ resolve42({
141449
+ success,
141450
+ exitCode,
141451
+ stdout,
141452
+ stderr: timedOut ? `${stderr}
141453
+ [install timed out after ${INSTALL_TIMEOUT_MS / 1e3}s]` : stderr,
141454
+ command,
141455
+ durationMs: Date.now() - startedAt,
141456
+ permissionsHint: !success && eaccesHit ? "npm reported a permissions error. On macOS/Linux this usually means npm's global prefix needs `sudo` or a fix to your npm prefix (https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally)." : void 0
141457
+ });
141458
+ });
141459
+ });
141460
+ }
141461
+ var INSTALL_TIMEOUT_MS, MAX_OUTPUT_BYTES2, registerFnBinaryRoutes;
141462
+ var init_register_fn_binary_routes = __esm({
141463
+ "../dashboard/src/routes/register-fn-binary-routes.ts"() {
141464
+ "use strict";
141465
+ init_src();
141466
+ init_api_error();
141467
+ init_cli_package_version();
141468
+ INSTALL_TIMEOUT_MS = 18e4;
141469
+ MAX_OUTPUT_BYTES2 = 64 * 1024;
141470
+ registerFnBinaryRoutes = (ctx) => {
141471
+ const { router, rethrowAsApiError: rethrowAsApiError8 } = ctx;
141472
+ router.get("/system/fn-binary/status", async (_req, res) => {
141473
+ try {
141474
+ const binary = await detectFnBinary();
141475
+ const expectedVersion = getCliPackageVersion();
141476
+ res.json(buildStatusPayload(binary, expectedVersion));
141477
+ } catch (err) {
141478
+ if (err instanceof ApiError) throw err;
141479
+ rethrowAsApiError8(err);
141480
+ }
141481
+ });
141482
+ router.post("/system/fn-binary/install", async (_req, res) => {
141483
+ try {
141484
+ const installResult = await runNpmInstall();
141485
+ const binary = await detectFnBinary();
141486
+ const expectedVersion = getCliPackageVersion();
141487
+ const status = buildStatusPayload(binary, expectedVersion);
141488
+ res.json({ ...status, installResult });
141489
+ } catch (err) {
141490
+ if (err instanceof ApiError) throw err;
141491
+ rethrowAsApiError8(err);
141492
+ }
141493
+ });
141494
+ };
141495
+ }
141496
+ });
141497
+
140882
141498
  // ../dashboard/src/update-check.ts
140883
- import { readFileSync as readFileSync10 } from "node:fs";
141499
+ import { readFileSync as readFileSync11 } from "node:fs";
140884
141500
  import { mkdir as mkdir17, rm as rm5, writeFile as writeFile15 } from "node:fs/promises";
140885
141501
  import { join as join47 } from "node:path";
140886
141502
  function ttlForFrequency(frequency) {
@@ -140920,7 +141536,7 @@ function isValidResult(value) {
140920
141536
  }
140921
141537
  function readCachedUpdateCheck(fusionDir) {
140922
141538
  try {
140923
- const raw = readFileSync10(getCachePath(fusionDir), "utf-8");
141539
+ const raw = readFileSync11(getCachePath(fusionDir), "utf-8");
140924
141540
  const parsed = JSON.parse(raw);
140925
141541
  return isValidResult(parsed) ? parsed : null;
140926
141542
  } catch {
@@ -140987,65 +141603,6 @@ var init_update_check = __esm({
140987
141603
  }
140988
141604
  });
140989
141605
 
140990
- // ../dashboard/src/cli-package-version.ts
140991
- import { existsSync as existsSync32, readFileSync as readFileSync11 } from "node:fs";
140992
- import { dirname as dirname15, resolve as resolve23 } from "node:path";
140993
- import { fileURLToPath as fileURLToPath4 } from "node:url";
140994
- function readCliPackageVersion(pkgPath) {
140995
- if (!existsSync32(pkgPath)) {
140996
- return null;
140997
- }
140998
- try {
140999
- const parsed = JSON.parse(readFileSync11(pkgPath, "utf-8"));
141000
- if (parsed.name === CLI_PACKAGE_NAME && typeof parsed.version === "string" && parsed.version.length > 0) {
141001
- return {
141002
- packageJsonPath: pkgPath,
141003
- version: parsed.version
141004
- };
141005
- }
141006
- } catch {
141007
- }
141008
- return null;
141009
- }
141010
- function resolveCliPackageVersionInfo(startDir) {
141011
- let currentDir = startDir;
141012
- for (let i = 0; i < 8; i += 1) {
141013
- const versionInfo = readCliPackageVersion(resolve23(currentDir, "package.json"));
141014
- if (versionInfo) {
141015
- return versionInfo;
141016
- }
141017
- const parentDir = resolve23(currentDir, "..");
141018
- if (parentDir === currentDir) {
141019
- break;
141020
- }
141021
- currentDir = parentDir;
141022
- }
141023
- currentDir = startDir;
141024
- for (let i = 0; i < 8; i += 1) {
141025
- const versionInfo = readCliPackageVersion(resolve23(currentDir, "..", "cli", "package.json"));
141026
- if (versionInfo) {
141027
- return versionInfo;
141028
- }
141029
- const parentDir = resolve23(currentDir, "..");
141030
- if (parentDir === currentDir) {
141031
- break;
141032
- }
141033
- currentDir = parentDir;
141034
- }
141035
- return null;
141036
- }
141037
- function getCliPackageVersion(importMetaUrl = import.meta.url) {
141038
- const startDir = dirname15(fileURLToPath4(importMetaUrl));
141039
- return resolveCliPackageVersionInfo(startDir)?.version ?? process.env.npm_package_version ?? "0.0.0";
141040
- }
141041
- var CLI_PACKAGE_NAME;
141042
- var init_cli_package_version = __esm({
141043
- "../dashboard/src/cli-package-version.ts"() {
141044
- "use strict";
141045
- CLI_PACKAGE_NAME = "@runfusion/fusion";
141046
- }
141047
- });
141048
-
141049
141606
  // ../dashboard/src/routes/register-update-check-routes.ts
141050
141607
  var registerUpdateCheckRoutes;
141051
141608
  var init_register_update_check_routes = __esm({
@@ -145824,7 +146381,7 @@ function detectPortFromLogLine(line) {
145824
146381
  return detectViteLine(cleanLine) ?? detectNextLine(cleanLine) ?? detectStorybookLine(cleanLine) ?? detectAngularLine(cleanLine) ?? detectGenericUrl(cleanLine) ?? detectGenericPortLine(cleanLine);
145825
146382
  }
145826
146383
  function probePort(host, port, timeoutMs) {
145827
- return new Promise((resolve40) => {
146384
+ return new Promise((resolve42) => {
145828
146385
  let settled = false;
145829
146386
  const socket = createConnection({ host, port });
145830
146387
  const settle = (isOpen) => {
@@ -145838,7 +146395,7 @@ function probePort(host, port, timeoutMs) {
145838
146395
  } else {
145839
146396
  socket.destroy();
145840
146397
  }
145841
- resolve40(isOpen);
146398
+ resolve42(isOpen);
145842
146399
  };
145843
146400
  socket.setTimeout(timeoutMs);
145844
146401
  socket.once("connect", () => settle(true));
@@ -145879,7 +146436,7 @@ var init_dev_server_port_detect = __esm({
145879
146436
 
145880
146437
  // ../dashboard/src/dev-server-process.ts
145881
146438
  import { EventEmitter as EventEmitter34 } from "node:events";
145882
- import { spawn as spawn12 } from "node:child_process";
146439
+ import { spawn as spawn14 } from "node:child_process";
145883
146440
  function killManagedProcess2(child, signal) {
145884
146441
  if (typeof child.pid !== "number") {
145885
146442
  return;
@@ -145958,15 +146515,15 @@ var init_dev_server_process = __esm({
145958
146515
  detectedUrl: void 0,
145959
146516
  detectedPort: void 0
145960
146517
  });
145961
- const child = spawn12(safeCommand, [], {
146518
+ const child = spawn14(safeCommand, [], {
145962
146519
  cwd: safeCwd,
145963
146520
  detached: process.platform !== "win32",
145964
146521
  shell: true,
145965
146522
  stdio: ["pipe", "pipe", "pipe"]
145966
146523
  });
145967
146524
  this.childProcess = child;
145968
- this.closePromise = new Promise((resolve40) => {
145969
- this.resolveClosePromise = resolve40;
146525
+ this.closePromise = new Promise((resolve42) => {
146526
+ this.resolveClosePromise = resolve42;
145970
146527
  });
145971
146528
  const runningState = await this.store.updateState({
145972
146529
  pid: child.pid,
@@ -147592,6 +148149,7 @@ function createApiRoutes(store, options) {
147592
148149
  registerCustomProviderRoutes(routeContext);
147593
148150
  registerAuthRoutes(routeContext);
147594
148151
  registerRuntimeProviderRoutes(routeContext);
148152
+ registerFnBinaryRoutes(routeContext);
147595
148153
  router.post("/ai/refine-text", async (req, res) => {
147596
148154
  try {
147597
148155
  const { text, type } = req.body;
@@ -149182,15 +149740,15 @@ Description: ${step.description}`
149182
149740
  return;
149183
149741
  }
149184
149742
  }
149185
- const { resolve: resolve40, dirname: dirname29, join: join70 } = await import("node:path");
149743
+ const { resolve: resolve42, dirname: dirname30, join: join70 } = await import("node:path");
149186
149744
  const { readdir: readdir12, stat: stat12 } = await import("node:fs/promises");
149187
149745
  const rawPath = req.query.path || process.env.HOME || process.env.USERPROFILE || "/";
149188
149746
  const showHidden = req.query.showHidden === "true";
149189
- const resolvedPath = resolve40(rawPath);
149747
+ const resolvedPath = resolve42(rawPath);
149190
149748
  if (rawPath.includes("..")) {
149191
149749
  throw badRequest("Path must not contain '..' traversal");
149192
149750
  }
149193
- if (resolvedPath !== resolve40(resolvedPath)) {
149751
+ if (resolvedPath !== resolve42(resolvedPath)) {
149194
149752
  throw badRequest("Path must be absolute");
149195
149753
  }
149196
149754
  let pathStat;
@@ -149217,7 +149775,7 @@ Description: ${step.description}`
149217
149775
  entries.push({ name: entry.name, path: entryPath, hasChildren });
149218
149776
  }
149219
149777
  entries.sort((a, b) => a.name.localeCompare(b.name));
149220
- const parentPath = dirname29(resolvedPath) === resolvedPath ? null : dirname29(resolvedPath);
149778
+ const parentPath = dirname30(resolvedPath) === resolvedPath ? null : dirname30(resolvedPath);
149221
149779
  res.json({ currentPath: resolvedPath, parentPath, entries });
149222
149780
  } catch (err) {
149223
149781
  if (err instanceof ApiError) {
@@ -149727,6 +150285,7 @@ var init_routes = __esm({
149727
150285
  init_register_usage_routes();
149728
150286
  init_register_auth_routes();
149729
150287
  init_register_runtime_provider_routes();
150288
+ init_register_fn_binary_routes();
149730
150289
  init_register_update_check_routes();
149731
150290
  init_register_integrated_routers();
149732
150291
  init_resolve_diff_base();
@@ -156484,7 +157043,7 @@ var init_task_lifecycle = __esm({
156484
157043
  // src/commands/port-prompt.ts
156485
157044
  import { createInterface } from "node:readline";
156486
157045
  function promptForPort(defaultPort = 4040, input = process.stdin) {
156487
- return new Promise((resolve40, reject2) => {
157046
+ return new Promise((resolve42, reject2) => {
156488
157047
  const rl = createInterface({
156489
157048
  input,
156490
157049
  output: process.stdout
@@ -156501,7 +157060,7 @@ function promptForPort(defaultPort = 4040, input = process.stdin) {
156501
157060
  if (trimmed === "") {
156502
157061
  process.removeListener("SIGINT", sigintHandler);
156503
157062
  rl.close();
156504
- resolve40(defaultPort);
157063
+ resolve42(defaultPort);
156505
157064
  return;
156506
157065
  }
156507
157066
  const port = parseInt(trimmed, 10);
@@ -156517,7 +157076,7 @@ function promptForPort(defaultPort = 4040, input = process.stdin) {
156517
157076
  }
156518
157077
  process.removeListener("SIGINT", sigintHandler);
156519
157078
  rl.close();
156520
- resolve40(port);
157079
+ resolve42(port);
156521
157080
  });
156522
157081
  };
156523
157082
  ask();
@@ -157268,13 +157827,109 @@ var init_claude_cli_extension = __esm({
157268
157827
  }
157269
157828
  });
157270
157829
 
157830
+ // src/commands/droid-cli-extension.ts
157831
+ import { existsSync as existsSync40, readFileSync as readFileSync18 } from "node:fs";
157832
+ import { createRequire as createRequire5 } from "node:module";
157833
+ import { dirname as dirname24, resolve as resolve30 } from "node:path";
157834
+ import { fileURLToPath as fileURLToPath8 } from "node:url";
157835
+ function resolveDroidCliExtensionFromModuleUrl(moduleUrl) {
157836
+ let pkgJsonPath;
157837
+ const here = dirname24(fileURLToPath8(moduleUrl));
157838
+ for (const rel of ["droid-cli", "../droid-cli", "../../droid-cli"]) {
157839
+ const candidate = resolve30(here, rel, "package.json");
157840
+ if (existsSync40(candidate)) {
157841
+ pkgJsonPath = candidate;
157842
+ break;
157843
+ }
157844
+ }
157845
+ if (!pkgJsonPath) {
157846
+ try {
157847
+ pkgJsonPath = require_2.resolve("@fusion/droid-cli/package.json");
157848
+ } catch {
157849
+ return { status: "not-installed" };
157850
+ }
157851
+ }
157852
+ let pkgJson;
157853
+ try {
157854
+ pkgJson = JSON.parse(readFileSync18(pkgJsonPath, "utf-8"));
157855
+ } catch (err) {
157856
+ return {
157857
+ status: "error",
157858
+ reason: `Failed to read @fusion/droid-cli package.json: ${err instanceof Error ? err.message : String(err)}`
157859
+ };
157860
+ }
157861
+ const extensions = pkgJson.pi?.extensions;
157862
+ if (!Array.isArray(extensions) || extensions.length === 0) {
157863
+ return {
157864
+ status: "missing-entry",
157865
+ reason: "@fusion/droid-cli package.json has no pi.extensions array"
157866
+ };
157867
+ }
157868
+ const rawEntry = extensions[0];
157869
+ if (typeof rawEntry !== "string" || rawEntry.length === 0) {
157870
+ return {
157871
+ status: "missing-entry",
157872
+ reason: "@fusion/droid-cli pi.extensions[0] is not a valid path string"
157873
+ };
157874
+ }
157875
+ const entryPath = resolve30(dirname24(pkgJsonPath), rawEntry);
157876
+ if (!existsSync40(entryPath)) {
157877
+ return {
157878
+ status: "missing-entry",
157879
+ reason: `@fusion/droid-cli extension file not found at ${entryPath}`
157880
+ };
157881
+ }
157882
+ return {
157883
+ status: "ok",
157884
+ path: entryPath,
157885
+ packageVersion: pkgJson.version ?? "unknown"
157886
+ };
157887
+ }
157888
+ function resolveDroidCliExtension() {
157889
+ return resolveDroidCliExtensionFromModuleUrl(import.meta.url);
157890
+ }
157891
+ function resolveDroidCliExtensionPaths(globalSettings) {
157892
+ const enabled = globalSettings?.useDroidCli === true;
157893
+ if (!enabled) {
157894
+ return { paths: [], resolution: null };
157895
+ }
157896
+ const resolution = resolveDroidCliExtension();
157897
+ switch (resolution.status) {
157898
+ case "ok":
157899
+ return { paths: [resolution.path], resolution };
157900
+ case "not-installed":
157901
+ return {
157902
+ paths: [],
157903
+ resolution,
157904
+ warning: "useDroidCli is on but @fusion/droid-cli is not installed in node_modules. Run `pnpm install`."
157905
+ };
157906
+ case "missing-entry":
157907
+ case "error":
157908
+ return { paths: [], resolution, warning: resolution.reason };
157909
+ }
157910
+ }
157911
+ function setCachedDroidCliResolution(resolution) {
157912
+ cachedResolution2 = resolution;
157913
+ }
157914
+ function getCachedDroidCliResolution() {
157915
+ return cachedResolution2;
157916
+ }
157917
+ var require_2, cachedResolution2;
157918
+ var init_droid_cli_extension = __esm({
157919
+ "src/commands/droid-cli-extension.ts"() {
157920
+ "use strict";
157921
+ require_2 = createRequire5(import.meta.url);
157922
+ cachedResolution2 = null;
157923
+ }
157924
+ });
157925
+
157271
157926
  // src/update-cache.ts
157272
- import { readFileSync as readFileSync18 } from "node:fs";
157927
+ import { readFileSync as readFileSync19 } from "node:fs";
157273
157928
  import { join as join56 } from "node:path";
157274
157929
  function getCachedUpdateStatus(currentVersion) {
157275
157930
  try {
157276
157931
  const cachePath = join56(resolveGlobalDir(), "update-check.json");
157277
- const raw = readFileSync18(cachePath, "utf-8");
157932
+ const raw = readFileSync19(cachePath, "utf-8");
157278
157933
  const parsed = JSON.parse(raw);
157279
157934
  if (parsed.updateAvailable === true && typeof parsed.latestVersion === "string" && parsed.latestVersion.length > 0 && typeof parsed.currentVersion === "string" && parsed.currentVersion.length > 0) {
157280
157935
  if (typeof currentVersion === "string" && currentVersion.length > 0 && parsed.currentVersion !== currentVersion) {
@@ -157305,17 +157960,17 @@ var init_update_cache = __esm({
157305
157960
  });
157306
157961
 
157307
157962
  // src/commands/self-extension.ts
157308
- import { existsSync as existsSync40, readFileSync as readFileSync19 } from "node:fs";
157309
- import { dirname as dirname24, resolve as resolve30 } from "node:path";
157310
- import { fileURLToPath as fileURLToPath8 } from "node:url";
157963
+ import { existsSync as existsSync41, readFileSync as readFileSync20 } from "node:fs";
157964
+ import { dirname as dirname25, resolve as resolve31 } from "node:path";
157965
+ import { fileURLToPath as fileURLToPath9 } from "node:url";
157311
157966
  function resolveSelfExtension() {
157312
- const here = dirname24(fileURLToPath8(import.meta.url));
157967
+ const here = dirname25(fileURLToPath9(import.meta.url));
157313
157968
  let pkgDir;
157314
157969
  let cur = here;
157315
157970
  for (let i = 0; i < 5; i++) {
157316
- if (existsSync40(resolve30(cur, "package.json"))) {
157971
+ if (existsSync41(resolve31(cur, "package.json"))) {
157317
157972
  try {
157318
- const parsed = JSON.parse(readFileSync19(resolve30(cur, "package.json"), "utf-8"));
157973
+ const parsed = JSON.parse(readFileSync20(resolve31(cur, "package.json"), "utf-8"));
157319
157974
  if (parsed.name === "@runfusion/fusion") {
157320
157975
  pkgDir = cur;
157321
157976
  break;
@@ -157323,7 +157978,7 @@ function resolveSelfExtension() {
157323
157978
  } catch {
157324
157979
  }
157325
157980
  }
157326
- const parent2 = resolve30(cur, "..");
157981
+ const parent2 = resolve31(cur, "..");
157327
157982
  if (parent2 === cur) break;
157328
157983
  cur = parent2;
157329
157984
  }
@@ -157332,12 +157987,12 @@ function resolveSelfExtension() {
157332
157987
  }
157333
157988
  let pkgJson;
157334
157989
  try {
157335
- pkgJson = JSON.parse(readFileSync19(resolve30(pkgDir, "package.json"), "utf-8"));
157990
+ pkgJson = JSON.parse(readFileSync20(resolve31(pkgDir, "package.json"), "utf-8"));
157336
157991
  } catch (err) {
157337
157992
  return { status: "missing", reason: `Failed to read @runfusion/fusion package.json: ${err instanceof Error ? err.message : String(err)}` };
157338
157993
  }
157339
- const srcEntry = resolve30(pkgDir, "src", "extension.ts");
157340
- if (existsSync40(srcEntry)) {
157994
+ const srcEntry = resolve31(pkgDir, "src", "extension.ts");
157995
+ if (existsSync41(srcEntry)) {
157341
157996
  return { status: "ok", path: srcEntry, packageVersion: pkgJson.version ?? "unknown" };
157342
157997
  }
157343
157998
  const extensions = pkgJson.pi?.extensions;
@@ -157348,8 +158003,8 @@ function resolveSelfExtension() {
157348
158003
  if (typeof rawEntry !== "string" || rawEntry.length === 0) {
157349
158004
  return { status: "missing", reason: "@runfusion/fusion pi.extensions[0] is not a valid path string" };
157350
158005
  }
157351
- const entryPath = resolve30(pkgDir, rawEntry);
157352
- if (!existsSync40(entryPath)) {
158006
+ const entryPath = resolve31(pkgDir, rawEntry);
158007
+ if (!existsSync41(entryPath)) {
157353
158008
  return { status: "missing", reason: `@runfusion/fusion extension file not found at ${entryPath}` };
157354
158009
  }
157355
158010
  return { status: "ok", path: entryPath, packageVersion: pkgJson.version ?? "unknown" };
@@ -157561,7 +158216,7 @@ var init_use_projects = __esm({
157561
158216
  });
157562
158217
 
157563
158218
  // src/commands/dashboard-tui/utils.ts
157564
- import { spawn as spawn13 } from "node:child_process";
158219
+ import { spawn as spawn15 } from "node:child_process";
157565
158220
  function isTTYAvailable() {
157566
158221
  return Boolean(process.stdout.isTTY && process.stdin.isTTY);
157567
158222
  }
@@ -157572,14 +158227,14 @@ async function copyToClipboard(text) {
157572
158227
  { cmd: "xsel", args: ["--clipboard", "--input"] }
157573
158228
  ];
157574
158229
  for (const { cmd, args } of candidates) {
157575
- const ok = await new Promise((resolve40) => {
158230
+ const ok = await new Promise((resolve42) => {
157576
158231
  try {
157577
- const child = spawn13(cmd, args, { stdio: ["pipe", "ignore", "ignore"] });
157578
- child.once("error", () => resolve40(false));
157579
- child.once("close", (code) => resolve40(code === 0));
158232
+ const child = spawn15(cmd, args, { stdio: ["pipe", "ignore", "ignore"] });
158233
+ child.once("error", () => resolve42(false));
158234
+ child.once("close", (code) => resolve42(code === 0));
157580
158235
  child.stdin.end(text);
157581
158236
  } catch {
157582
- resolve40(false);
158237
+ resolve42(false);
157583
158238
  }
157584
158239
  });
157585
158240
  if (ok) return true;
@@ -157601,7 +158256,7 @@ import { useState as useState2, useSyncExternalStore, useCallback as useCallback
157601
158256
  import { Box, Text, useInput, useApp, useStdout } from "ink";
157602
158257
  import Spinner from "ink-spinner";
157603
158258
  import TextInput from "ink-text-input";
157604
- import { spawn as spawn14 } from "node:child_process";
158259
+ import { spawn as spawn16 } from "node:child_process";
157605
158260
  import { appendFileSync } from "node:fs";
157606
158261
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
157607
158262
  function tuiDebug(tag, data) {
@@ -157627,7 +158282,7 @@ function openInBrowser(url) {
157627
158282
  args = [url];
157628
158283
  }
157629
158284
  try {
157630
- const child = spawn14(cmd, args, { detached: true, stdio: "ignore" });
158285
+ const child = spawn16(cmd, args, { detached: true, stdio: "ignore" });
157631
158286
  child.unref();
157632
158287
  } catch {
157633
158288
  }
@@ -161640,8 +162295,8 @@ async function resolveCachedStartupUpdateStatus(importMetaUrl) {
161640
162295
  try {
161641
162296
  const updateCheckEnabled = await Promise.race([
161642
162297
  isUpdateCheckEnabled(),
161643
- new Promise((resolve40) => {
161644
- setTimeout(() => resolve40(false), 3e3);
162298
+ new Promise((resolve42) => {
162299
+ setTimeout(() => resolve42(false), 3e3);
161645
162300
  })
161646
162301
  ]);
161647
162302
  if (!updateCheckEnabled) {
@@ -162361,6 +163016,23 @@ async function runDashboard(port, opts = {}) {
162361
163016
  return [];
162362
163017
  }
162363
163018
  })();
163019
+ const droidCliPaths = await (async () => {
163020
+ try {
163021
+ const globalSettings = await store.getGlobalSettingsStore().getSettings();
163022
+ const result = resolveDroidCliExtensionPaths(globalSettings);
163023
+ setCachedDroidCliResolution(result.resolution);
163024
+ if (result.warning) {
163025
+ console.warn(`[extensions] droid-cli: ${result.warning}`);
163026
+ }
163027
+ return result.paths;
163028
+ } catch (err) {
163029
+ console.warn(
163030
+ `[extensions] Unable to evaluate useDroidCli setting: ${err instanceof Error ? err.message : String(err)}`
163031
+ );
163032
+ setCachedDroidCliResolution(null);
163033
+ return [];
163034
+ }
163035
+ })();
162364
163036
  const selfExtension = resolveSelfExtension();
162365
163037
  const selfExtensionPaths = selfExtension.status === "ok" ? [selfExtension.path] : [];
162366
163038
  if (selfExtension.status !== "ok") {
@@ -162372,7 +163044,8 @@ async function runDashboard(port, opts = {}) {
162372
163044
  ...selfExtensionPaths,
162373
163045
  ...getEnabledPiExtensionPaths(cwd),
162374
163046
  ...packageExtensionPaths,
162375
- ...claudeCliPaths
163047
+ ...claudeCliPaths,
163048
+ ...droidCliPaths
162376
163049
  ],
162377
163050
  cwd,
162378
163051
  join57(cwd, ".fusion", "disabled-auto-extension-discovery")
@@ -162569,6 +163242,17 @@ async function runDashboard(port, opts = {}) {
162569
163242
  }
162570
163243
  return { status: r.status, reason: r.reason };
162571
163244
  },
163245
+ getDroidCliExtensionStatus: () => {
163246
+ const r = getCachedDroidCliResolution();
163247
+ if (!r) return null;
163248
+ if (r.status === "ok") {
163249
+ return { status: "ok", path: r.path, packageVersion: r.packageVersion };
163250
+ }
163251
+ if (r.status === "not-installed") {
163252
+ return { status: "not-installed" };
163253
+ }
163254
+ return { status: r.status, reason: r.reason };
163255
+ },
162572
163256
  onUseClaudeCliToggled: (_prev, next) => {
162573
163257
  if (!next) return;
162574
163258
  void (async () => {
@@ -162586,6 +163270,11 @@ async function runDashboard(port, opts = {}) {
162586
163270
  }
162587
163271
  })();
162588
163272
  },
163273
+ onUseDroidCliToggled: (_prev, next) => {
163274
+ if (next) {
163275
+ logSink.log("Droid CLI enabled \u2014 restart required for full effect", "extensions");
163276
+ }
163277
+ },
162589
163278
  skillsAdapter,
162590
163279
  https: loadTlsCredentialsFromEnv(),
162591
163280
  daemon: dashboardAuthToken ? { token: dashboardAuthToken } : void 0,
@@ -162752,6 +163441,17 @@ async function runDashboard(port, opts = {}) {
162752
163441
  }
162753
163442
  return { status: r.status, reason: r.reason };
162754
163443
  },
163444
+ getDroidCliExtensionStatus: () => {
163445
+ const r = getCachedDroidCliResolution();
163446
+ if (!r) return null;
163447
+ if (r.status === "ok") {
163448
+ return { status: "ok", path: r.path, packageVersion: r.packageVersion };
163449
+ }
163450
+ if (r.status === "not-installed") {
163451
+ return { status: "not-installed" };
163452
+ }
163453
+ return { status: r.status, reason: r.reason };
163454
+ },
162755
163455
  onUseClaudeCliToggled: (_prev, next) => {
162756
163456
  if (!next) return;
162757
163457
  void (async () => {
@@ -162769,6 +163469,11 @@ async function runDashboard(port, opts = {}) {
162769
163469
  }
162770
163470
  })();
162771
163471
  },
163472
+ onUseDroidCliToggled: (_prev, next) => {
163473
+ if (next) {
163474
+ logSink.log("Droid CLI enabled \u2014 restart required for full effect", "extensions");
163475
+ }
163476
+ },
162772
163477
  skillsAdapter,
162773
163478
  https: loadTlsCredentialsFromEnv(),
162774
163479
  daemon: dashboardAuthToken ? { token: dashboardAuthToken } : void 0,
@@ -163345,6 +164050,7 @@ var init_dashboard = __esm({
163345
164050
  init_project_context();
163346
164051
  init_claude_skills_runner();
163347
164052
  init_claude_cli_extension();
164053
+ init_droid_cli_extension();
163348
164054
  init_update_cache();
163349
164055
  init_self_extension();
163350
164056
  init_custom_provider_registry();
@@ -164122,6 +164828,23 @@ async function runServe(port, opts = {}) {
164122
164828
  return [];
164123
164829
  }
164124
164830
  })();
164831
+ const droidCliPaths = await (async () => {
164832
+ try {
164833
+ const globalSettings = await store.getGlobalSettingsStore().getSettings();
164834
+ const result = resolveDroidCliExtensionPaths(globalSettings);
164835
+ setCachedDroidCliResolution(result.resolution);
164836
+ if (result.warning) {
164837
+ console.warn(`[extensions] droid-cli: ${result.warning}`);
164838
+ }
164839
+ return result.paths;
164840
+ } catch (err) {
164841
+ console.warn(
164842
+ `[extensions] Unable to evaluate useDroidCli setting: ${err instanceof Error ? err.message : String(err)}`
164843
+ );
164844
+ setCachedDroidCliResolution(null);
164845
+ return [];
164846
+ }
164847
+ })();
164125
164848
  const selfExtension = resolveSelfExtension();
164126
164849
  const selfExtensionPaths = selfExtension.status === "ok" ? [selfExtension.path] : [];
164127
164850
  if (selfExtension.status !== "ok") {
@@ -164133,7 +164856,8 @@ async function runServe(port, opts = {}) {
164133
164856
  ...selfExtensionPaths,
164134
164857
  ...getEnabledPiExtensionPaths(cwd),
164135
164858
  ...packageExtensionPaths,
164136
- ...claudeCliPaths
164859
+ ...claudeCliPaths,
164860
+ ...droidCliPaths
164137
164861
  ],
164138
164862
  cwd,
164139
164863
  join58(cwd, ".fusion", "disabled-auto-extension-discovery")
@@ -164292,6 +165016,17 @@ async function runServe(port, opts = {}) {
164292
165016
  }
164293
165017
  return { status: r.status, reason: r.reason };
164294
165018
  },
165019
+ getDroidCliExtensionStatus: () => {
165020
+ const r = getCachedDroidCliResolution();
165021
+ if (!r) return null;
165022
+ if (r.status === "ok") {
165023
+ return { status: "ok", path: r.path, packageVersion: r.packageVersion };
165024
+ }
165025
+ if (r.status === "not-installed") {
165026
+ return { status: "not-installed" };
165027
+ }
165028
+ return { status: r.status, reason: r.reason };
165029
+ },
164295
165030
  onUseClaudeCliToggled: (_prev, next) => {
164296
165031
  if (!next) return;
164297
165032
  void (async () => {
@@ -164308,14 +165043,19 @@ async function runServe(port, opts = {}) {
164308
165043
  }
164309
165044
  })();
164310
165045
  },
165046
+ onUseDroidCliToggled: (_prev, next) => {
165047
+ if (next) {
165048
+ console.log("[extensions] Droid CLI enabled \u2014 restart required for full effect");
165049
+ }
165050
+ },
164311
165051
  headless: true,
164312
165052
  skillsAdapter,
164313
165053
  daemon: daemonToken ? { token: daemonToken } : void 0,
164314
165054
  https: loadTlsCredentialsFromEnv()
164315
165055
  });
164316
165056
  const server = app.listen(selectedPort, selectedHost);
164317
- await new Promise((resolve40, reject2) => {
164318
- server.once("listening", resolve40);
165057
+ await new Promise((resolve42, reject2) => {
165058
+ server.once("listening", resolve42);
164319
165059
  server.once("error", reject2);
164320
165060
  });
164321
165061
  const actualPort = server.address().port;
@@ -164459,6 +165199,7 @@ var init_serve = __esm({
164459
165199
  init_project_context();
164460
165200
  init_claude_skills_runner();
164461
165201
  init_claude_cli_extension();
165202
+ init_droid_cli_extension();
164462
165203
  init_self_extension();
164463
165204
  init_custom_provider_registry();
164464
165205
  DIAGNOSTIC_INTERVAL_MS2 = 30 * 60 * 1e3;
@@ -164730,6 +165471,23 @@ async function runDaemon(opts = {}) {
164730
165471
  return [];
164731
165472
  }
164732
165473
  })();
165474
+ const droidCliPaths = await (async () => {
165475
+ try {
165476
+ const globalSettings = await store.getGlobalSettingsStore().getSettings();
165477
+ const result = resolveDroidCliExtensionPaths(globalSettings);
165478
+ setCachedDroidCliResolution(result.resolution);
165479
+ if (result.warning) {
165480
+ console.warn(`[extensions] droid-cli: ${result.warning}`);
165481
+ }
165482
+ return result.paths;
165483
+ } catch (err) {
165484
+ console.warn(
165485
+ `[extensions] Unable to evaluate useDroidCli setting: ${err instanceof Error ? err.message : String(err)}`
165486
+ );
165487
+ setCachedDroidCliResolution(null);
165488
+ return [];
165489
+ }
165490
+ })();
164733
165491
  const selfExtension = resolveSelfExtension();
164734
165492
  const selfExtensionPaths = selfExtension.status === "ok" ? [selfExtension.path] : [];
164735
165493
  if (selfExtension.status !== "ok") {
@@ -164741,7 +165499,7 @@ async function runDaemon(opts = {}) {
164741
165499
  claudeCliPaths[0] ?? null
164742
165500
  );
164743
165501
  const extensionsResult = await discoverAndLoadExtensions4(
164744
- reconciledExtensionPaths,
165502
+ [...reconciledExtensionPaths, ...droidCliPaths],
164745
165503
  cwd,
164746
165504
  join59(cwd, ".fusion", "disabled-auto-extension-discovery")
164747
165505
  );
@@ -164812,6 +165570,17 @@ async function runDaemon(opts = {}) {
164812
165570
  }
164813
165571
  return { status: r.status, reason: r.reason };
164814
165572
  },
165573
+ getDroidCliExtensionStatus: () => {
165574
+ const r = getCachedDroidCliResolution();
165575
+ if (!r) return null;
165576
+ if (r.status === "ok") {
165577
+ return { status: "ok", path: r.path, packageVersion: r.packageVersion };
165578
+ }
165579
+ if (r.status === "not-installed") {
165580
+ return { status: "not-installed" };
165581
+ }
165582
+ return { status: r.status, reason: r.reason };
165583
+ },
164815
165584
  onUseClaudeCliToggled: (_prev, next) => {
164816
165585
  if (!next) return;
164817
165586
  void (async () => {
@@ -164828,14 +165597,19 @@ async function runDaemon(opts = {}) {
164828
165597
  }
164829
165598
  })();
164830
165599
  },
165600
+ onUseDroidCliToggled: (_prev, next) => {
165601
+ if (next) {
165602
+ console.log("[extensions] Droid CLI enabled \u2014 restart required for full effect");
165603
+ }
165604
+ },
164831
165605
  headless: true,
164832
165606
  daemon: { token: daemonToken },
164833
165607
  skillsAdapter,
164834
165608
  https: loadTlsCredentialsFromEnv()
164835
165609
  });
164836
165610
  const server = app.listen(selectedPort, selectedHost);
164837
- await new Promise((resolve40, reject2) => {
164838
- server.once("listening", resolve40);
165611
+ await new Promise((resolve42, reject2) => {
165612
+ server.once("listening", resolve42);
164839
165613
  server.once("error", reject2);
164840
165614
  });
164841
165615
  const actualPort = server.address().port;
@@ -164929,6 +165703,7 @@ var init_daemon = __esm({
164929
165703
  init_provider_settings();
164930
165704
  init_claude_skills_runner();
164931
165705
  init_claude_cli_extension();
165706
+ init_droid_cli_extension();
164932
165707
  init_self_extension();
164933
165708
  init_provider_auth();
164934
165709
  init_auth_paths2();
@@ -164944,13 +165719,13 @@ var desktop_exports = {};
164944
165719
  __export(desktop_exports, {
164945
165720
  runDesktop: () => runDesktop
164946
165721
  });
164947
- import { spawn as spawn15 } from "node:child_process";
165722
+ import { spawn as spawn17 } from "node:child_process";
164948
165723
  import { once as once2 } from "node:events";
164949
165724
  import { join as join60 } from "node:path";
164950
- import { createRequire as createRequire5 } from "node:module";
165725
+ import { createRequire as createRequire6 } from "node:module";
164951
165726
  function runCommand(command, args, cwd) {
164952
- return new Promise((resolve40, reject2) => {
164953
- const child = spawn15(command, args, {
165727
+ return new Promise((resolve42, reject2) => {
165728
+ const child = spawn17(command, args, {
164954
165729
  cwd,
164955
165730
  stdio: "inherit",
164956
165731
  env: process.env
@@ -164958,7 +165733,7 @@ function runCommand(command, args, cwd) {
164958
165733
  child.on("error", (error) => reject2(error));
164959
165734
  child.on("exit", (code) => {
164960
165735
  if (code === 0) {
164961
- resolve40();
165736
+ resolve42();
164962
165737
  return;
164963
165738
  }
164964
165739
  reject2(new Error(`${command} ${args.join(" ")} exited with code ${code ?? "unknown"}`));
@@ -165001,8 +165776,8 @@ async function startDashboardRuntime(rootDir, paused) {
165001
165776
  };
165002
165777
  }
165003
165778
  async function closeDashboardRuntime(runtime) {
165004
- await new Promise((resolve40) => {
165005
- runtime.server.close(() => resolve40());
165779
+ await new Promise((resolve42) => {
165780
+ runtime.server.close(() => resolve42());
165006
165781
  });
165007
165782
  runtime.store.close();
165008
165783
  }
@@ -165035,7 +165810,7 @@ async function runDesktop(options = {}) {
165035
165810
  electronEnv.FUSION_DASHBOARD_URL = process.env.FUSION_DASHBOARD_URL ?? "http://localhost:5173";
165036
165811
  electronEnv.NODE_ENV = "development";
165037
165812
  }
165038
- const electronProcess = spawn15(electronBinary, electronArgs, {
165813
+ const electronProcess = spawn17(electronBinary, electronArgs, {
165039
165814
  cwd: rootDir,
165040
165815
  stdio: "inherit",
165041
165816
  env: electronEnv
@@ -165074,7 +165849,7 @@ var init_desktop = __esm({
165074
165849
  "use strict";
165075
165850
  init_src();
165076
165851
  init_src4();
165077
- require3 = createRequire5(import.meta.url);
165852
+ require3 = createRequire6(import.meta.url);
165078
165853
  }
165079
165854
  });
165080
165855
 
@@ -165110,7 +165885,7 @@ __export(task_exports, {
165110
165885
  runTaskUpdate: () => runTaskUpdate
165111
165886
  });
165112
165887
  import { createInterface as createInterface3 } from "node:readline/promises";
165113
- import { watchFile, unwatchFile, statSync as statSync6, existsSync as existsSync41, readFileSync as readFileSync20 } from "node:fs";
165888
+ import { watchFile, unwatchFile, statSync as statSync6, existsSync as existsSync42, readFileSync as readFileSync21 } from "node:fs";
165114
165889
  import { basename as basename17, join as join61 } from "node:path";
165115
165890
  function getGitHubIssueUrl(sourceMetadata) {
165116
165891
  if (!sourceMetadata || typeof sourceMetadata !== "object") return void 0;
@@ -165275,9 +166050,9 @@ async function runTaskCreate(descriptionArg, attachFiles, depends, projectName,
165275
166050
  console.log(` Path: .fusion/tasks/${task.id}/`);
165276
166051
  if (attachFiles && attachFiles.length > 0) {
165277
166052
  const { readFile: readFile24 } = await import("node:fs/promises");
165278
- const { basename: basename22, extname: extname3, resolve: resolve40 } = await import("node:path");
166053
+ const { basename: basename22, extname: extname3, resolve: resolve42 } = await import("node:path");
165279
166054
  for (const filePath of attachFiles) {
165280
- const resolvedPath = resolve40(filePath);
166055
+ const resolvedPath = resolve42(filePath);
165281
166056
  const filename = basename22(resolvedPath);
165282
166057
  const ext = extname3(filename).toLowerCase();
165283
166058
  const mimeType = MIME_TYPES[ext];
@@ -165412,7 +166187,7 @@ async function runTaskLogs(id, options = {}, projectName) {
165412
166187
  if (options.follow) {
165413
166188
  const projectPath = projectContext?.projectPath ?? process.cwd();
165414
166189
  const logPath = join61(projectPath, ".fusion", "tasks", id, "agent.log");
165415
- if (!existsSync41(logPath)) {
166190
+ if (!existsSync42(logPath)) {
165416
166191
  console.log(`
165417
166192
  Waiting for log file to be created...`);
165418
166193
  }
@@ -165441,7 +166216,7 @@ async function runTaskLogs(id, options = {}, projectName) {
165441
166216
  lastPosition = 0;
165442
166217
  }
165443
166218
  if (stats.size > lastPosition) {
165444
- const content = readFileSync20(logPath, "utf-8");
166219
+ const content = readFileSync21(logPath, "utf-8");
165445
166220
  const lines = content.slice(lastPosition).split("\n");
165446
166221
  for (const line of lines) {
165447
166222
  if (!line.trim()) continue;
@@ -165575,8 +166350,8 @@ async function runTaskMerge(id, projectName) {
165575
166350
  async function runTaskAttach(id, filePath, projectName) {
165576
166351
  const { readFile: readFile24 } = await import("node:fs/promises");
165577
166352
  const { basename: basename22, extname: extname3 } = await import("node:path");
165578
- const { resolve: resolve40 } = await import("node:path");
165579
- const resolvedPath = resolve40(filePath);
166353
+ const { resolve: resolve42 } = await import("node:path");
166354
+ const resolvedPath = resolve42(filePath);
165580
166355
  const filename = basename22(resolvedPath);
165581
166356
  const ext = extname3(filename).toLowerCase();
165582
166357
  const mimeType = MIME_TYPES[ext];
@@ -166115,12 +166890,12 @@ async function promptText(question) {
166115
166890
  console.log(" (Enter your response. Type DONE on its own line when finished):\n");
166116
166891
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
166117
166892
  const lines = [];
166118
- return new Promise((resolve40) => {
166893
+ return new Promise((resolve42) => {
166119
166894
  const askLine = () => {
166120
166895
  rl.question(" ").then((line) => {
166121
166896
  if (line.trim() === "DONE") {
166122
166897
  rl.close();
166123
- resolve40(lines.join("\n"));
166898
+ resolve42(lines.join("\n"));
166124
166899
  } else {
166125
166900
  lines.push(line);
166126
166901
  askLine();
@@ -166285,7 +167060,7 @@ async function runTaskPlan(initialPlanArg, yesFlag = false, projectName) {
166285
167060
  } catch (err) {
166286
167061
  clearThinking();
166287
167062
  if (err instanceof RateLimitError2) {
166288
- console.error("\n Rate limit exceeded. Maximum 5 planning sessions per hour.\n");
167063
+ console.error("\n Rate limit exceeded. Maximum 1000 planning sessions per hour.\n");
166289
167064
  process.exit(1);
166290
167065
  }
166291
167066
  console.error(`
@@ -166689,7 +167464,7 @@ __export(settings_export_exports, {
166689
167464
  runSettingsExport: () => runSettingsExport
166690
167465
  });
166691
167466
  import { writeFile as writeFile18 } from "node:fs/promises";
166692
- import { resolve as resolve31, join as join62 } from "node:path";
167467
+ import { resolve as resolve32, join as join62 } from "node:path";
166693
167468
  async function runSettingsExport(options = {}) {
166694
167469
  const scope = options.scope ?? "both";
166695
167470
  const project = options.projectName ? await resolveProject(options.projectName) : void 0;
@@ -166700,7 +167475,7 @@ async function runSettingsExport(options = {}) {
166700
167475
  const exportData = await exportSettings(store, { scope });
166701
167476
  let targetPath;
166702
167477
  if (outputPath) {
166703
- targetPath = resolve31(outputPath);
167478
+ targetPath = resolve32(outputPath);
166704
167479
  } else {
166705
167480
  const filename = generateExportFilename();
166706
167481
  targetPath = join62(process.cwd(), filename);
@@ -166749,8 +167524,8 @@ var settings_import_exports = {};
166749
167524
  __export(settings_import_exports, {
166750
167525
  runSettingsImport: () => runSettingsImport
166751
167526
  });
166752
- import { existsSync as existsSync42 } from "node:fs";
166753
- import { resolve as resolve32 } from "node:path";
167527
+ import { existsSync as existsSync43 } from "node:fs";
167528
+ import { resolve as resolve33 } from "node:path";
166754
167529
  async function runSettingsImport(filePath, options = {}) {
166755
167530
  const scope = options.scope ?? "both";
166756
167531
  const project = options.projectName ? await resolveProject(options.projectName) : void 0;
@@ -166759,8 +167534,8 @@ async function runSettingsImport(filePath, options = {}) {
166759
167534
  const merge = options.merge ?? true;
166760
167535
  const skipConfirm = options.yes ?? false;
166761
167536
  try {
166762
- const resolvedPath = resolve32(filePath);
166763
- if (!existsSync42(resolvedPath)) {
167537
+ const resolvedPath = resolve33(filePath);
167538
+ if (!existsSync43(resolvedPath)) {
166764
167539
  console.error(`Error: File not found: ${filePath}`);
166765
167540
  process.exit(1);
166766
167541
  }
@@ -167233,8 +168008,8 @@ var init_backup2 = __esm({
167233
168008
  });
167234
168009
 
167235
168010
  // src/project-resolver.ts
167236
- import { existsSync as existsSync43, statSync as statSync7 } from "node:fs";
167237
- import { basename as basename18, dirname as dirname25, resolve as resolve33, normalize as normalize5 } from "node:path";
168011
+ import { existsSync as existsSync44, statSync as statSync7 } from "node:fs";
168012
+ import { basename as basename18, dirname as dirname26, resolve as resolve34, normalize as normalize5 } from "node:path";
167238
168013
  import { createInterface as createInterface5 } from "node:readline/promises";
167239
168014
  async function getCentralCore() {
167240
168015
  if (!centralCoreInstance) {
@@ -167251,13 +168026,13 @@ async function getProjectManager() {
167251
168026
  return projectManagerInstance;
167252
168027
  }
167253
168028
  function findKbDir(startPath) {
167254
- let current = resolve33(startPath);
168029
+ let current = resolve34(startPath);
167255
168030
  for (let i = 0; i < 100; i++) {
167256
- const dbPath = resolve33(current, ".fusion", "fusion.db");
168031
+ const dbPath = resolve34(current, ".fusion", "fusion.db");
167257
168032
  if (isValidSqliteDatabaseFile(dbPath)) {
167258
168033
  return current;
167259
168034
  }
167260
- const parent2 = dirname25(current);
168035
+ const parent2 = dirname26(current);
167261
168036
  if (parent2 === current) {
167262
168037
  break;
167263
168038
  }
@@ -167309,7 +168084,7 @@ async function resolveProject2(options = {}) {
167309
168084
  { searchedName: options.project, availableProjects: projects.map((p) => p.name) }
167310
168085
  );
167311
168086
  }
167312
- if (!existsSync43(match.path)) {
168087
+ if (!existsSync44(match.path)) {
167313
168088
  throw new ProjectResolutionError(
167314
168089
  `Project "${match.name}" is registered but the directory no longer exists: ${match.path}
167315
168090
 
@@ -167320,14 +168095,14 @@ Run \`fn project remove ` + match.name + "` to clean up the registry entry.",
167320
168095
  }
167321
168096
  return createResolvedProject(match);
167322
168097
  }
167323
- const cwd = options.cwd ? resolve33(options.cwd) : process.cwd();
168098
+ const cwd = options.cwd ? resolve34(options.cwd) : process.cwd();
167324
168099
  const fusionDir = findKbDir(cwd);
167325
168100
  if (fusionDir) {
167326
168101
  const allProjects2 = await central.listProjects();
167327
168102
  const normalizedKbDir = normalize5(fusionDir);
167328
168103
  const match = allProjects2.find((p) => normalize5(p.path) === normalizedKbDir);
167329
168104
  if (match) {
167330
- if (!existsSync43(match.path)) {
168105
+ if (!existsSync44(match.path)) {
167331
168106
  throw new ProjectResolutionError(
167332
168107
  `Project "${match.name}" is registered but the directory no longer exists: ${match.path}
167333
168108
 
@@ -167392,7 +168167,7 @@ Run \`fn project add ` + fusionDir + "` to register it, or use --project <name>.
167392
168167
  }
167393
168168
  if (allProjects.length === 1) {
167394
168169
  const project = allProjects[0];
167395
- if (!existsSync43(project.path)) {
168170
+ if (!existsSync44(project.path)) {
167396
168171
  throw new ProjectResolutionError(
167397
168172
  `The only registered project "${project.name}" has a missing directory: ${project.path}
167398
168173
 
@@ -167832,8 +168607,8 @@ __export(project_exports, {
167832
168607
  runProjectSetDefault: () => runProjectSetDefault,
167833
168608
  runProjectShow: () => runProjectShow
167834
168609
  });
167835
- import { resolve as resolve34, isAbsolute as isAbsolute19, relative as relative14, basename as basename19 } from "node:path";
167836
- import { existsSync as existsSync44, statSync as statSync8 } from "node:fs";
168610
+ import { resolve as resolve35, isAbsolute as isAbsolute19, relative as relative14, basename as basename19 } from "node:path";
168611
+ import { existsSync as existsSync45, statSync as statSync8 } from "node:fs";
167837
168612
  import { createInterface as createInterface7 } from "node:readline/promises";
167838
168613
  function formatDisplayPath(projectPath) {
167839
168614
  const rel = relative14(process.cwd(), projectPath);
@@ -167961,8 +168736,8 @@ async function runProjectAdd(name, path5, options = {}) {
167961
168736
  const pathInput = await rl.question(` Project path [${defaultPath}]: `);
167962
168737
  projectPath = pathInput.trim() || defaultPath;
167963
168738
  }
167964
- const absolutePath2 = isAbsolute19(projectPath) ? projectPath : resolve34(process.cwd(), projectPath);
167965
- if (!existsSync44(absolutePath2)) {
168739
+ const absolutePath2 = isAbsolute19(projectPath) ? projectPath : resolve35(process.cwd(), projectPath);
168740
+ if (!existsSync45(absolutePath2)) {
167966
168741
  console.error(`
167967
168742
  \u2717 Path does not exist: ${projectPath}`);
167968
168743
  rl.close();
@@ -167974,8 +168749,8 @@ async function runProjectAdd(name, path5, options = {}) {
167974
168749
  rl.close();
167975
168750
  process.exit(1);
167976
168751
  }
167977
- const kbDbPath2 = resolve34(absolutePath2, ".fusion", "fusion.db");
167978
- if (!existsSync44(kbDbPath2) && !options.force) {
168752
+ const kbDbPath2 = resolve35(absolutePath2, ".fusion", "fusion.db");
168753
+ if (!existsSync45(kbDbPath2) && !options.force) {
167979
168754
  console.log(`
167980
168755
  No fn project found at ${formatDisplayPath(absolutePath2)}`);
167981
168756
  const init = await rl.question(" Initialize fn here first? [Y/n] ");
@@ -168006,8 +168781,8 @@ async function runProjectAdd(name, path5, options = {}) {
168006
168781
  console.error(" Name must be 1-64 characters and contain only: a-z, A-Z, 0-9, _, -\n");
168007
168782
  process.exit(1);
168008
168783
  }
168009
- const absolutePath = isAbsolute19(projectPath) ? projectPath : resolve34(process.cwd(), projectPath);
168010
- if (!existsSync44(absolutePath)) {
168784
+ const absolutePath = isAbsolute19(projectPath) ? projectPath : resolve35(process.cwd(), projectPath);
168785
+ if (!existsSync45(absolutePath)) {
168011
168786
  console.error(`
168012
168787
  \u2717 Path does not exist: ${projectPath}
168013
168788
  `);
@@ -168019,8 +168794,8 @@ async function runProjectAdd(name, path5, options = {}) {
168019
168794
  `);
168020
168795
  process.exit(1);
168021
168796
  }
168022
- const kbDbPath = resolve34(absolutePath, ".fusion", "fusion.db");
168023
- if (!existsSync44(kbDbPath) && !options.force) {
168797
+ const kbDbPath = resolve35(absolutePath, ".fusion", "fusion.db");
168798
+ if (!existsSync45(kbDbPath) && !options.force) {
168024
168799
  console.error(`
168025
168800
  \u2717 No fn project found at ${formatDisplayPath(absolutePath)}`);
168026
168801
  console.error(" Run `fn init` first to initialize the project.\n");
@@ -168276,10 +169051,10 @@ var init_project = __esm({
168276
169051
  });
168277
169052
 
168278
169053
  // src/commands/skill-installation.ts
168279
- import { cpSync as cpSync2, existsSync as existsSync45, mkdirSync as mkdirSync8 } from "node:fs";
169054
+ import { cpSync as cpSync2, existsSync as existsSync46, mkdirSync as mkdirSync8 } from "node:fs";
168280
169055
  import { homedir as homedir10 } from "node:os";
168281
- import { dirname as dirname26, join as join63, resolve as resolve35 } from "node:path";
168282
- import { fileURLToPath as fileURLToPath9 } from "node:url";
169056
+ import { dirname as dirname27, join as join63, resolve as resolve36 } from "node:path";
169057
+ import { fileURLToPath as fileURLToPath10 } from "node:url";
168283
169058
  function getSupportedSkillInstallTargets(homeDir = process.env.HOME || process.env.USERPROFILE || homedir10()) {
168284
169059
  return [
168285
169060
  { client: "claude", targetDir: join63(homeDir, ".claude", "skills", FUSION_SKILL_NAME2) },
@@ -168288,9 +169063,9 @@ function getSupportedSkillInstallTargets(homeDir = process.env.HOME || process.e
168288
169063
  ];
168289
169064
  }
168290
169065
  function resolveBundledFusionSkillSource() {
168291
- const here = fileURLToPath9(import.meta.url);
168292
- const source = resolve35(dirname26(here), "..", "..", "skill", FUSION_SKILL_NAME2);
168293
- return existsSync45(source) ? source : null;
169066
+ const here = fileURLToPath10(import.meta.url);
169067
+ const source = resolve36(dirname27(here), "..", "..", "skill", FUSION_SKILL_NAME2);
169068
+ return existsSync46(source) ? source : null;
168294
169069
  }
168295
169070
  function installBundledFusionSkill(options = {}) {
168296
169071
  const sourceDir = options.sourceDir ?? resolveBundledFusionSkillSource();
@@ -168308,7 +169083,7 @@ function installBundledFusionSkill(options = {}) {
168308
169083
  }
168309
169084
  const results = targets.map((target) => {
168310
169085
  try {
168311
- if (existsSync45(target.targetDir)) {
169086
+ if (existsSync46(target.targetDir)) {
168312
169087
  return {
168313
169088
  client: target.client,
168314
169089
  targetDir: target.targetDir,
@@ -168316,7 +169091,7 @@ function installBundledFusionSkill(options = {}) {
168316
169091
  reason: "existing install preserved"
168317
169092
  };
168318
169093
  }
168319
- mkdirSync8(dirname26(target.targetDir), { recursive: true });
169094
+ mkdirSync8(dirname27(target.targetDir), { recursive: true });
168320
169095
  cpSync2(sourceDir, target.targetDir, { recursive: true });
168321
169096
  return {
168322
169097
  client: target.client,
@@ -168347,17 +169122,17 @@ var init_exports = {};
168347
169122
  __export(init_exports, {
168348
169123
  runInit: () => runInit
168349
169124
  });
168350
- import { existsSync as existsSync46, mkdirSync as mkdirSync9, writeFileSync as writeFileSync3, readFileSync as readFileSync21 } from "node:fs";
168351
- import { join as join64, resolve as resolve36, basename as basename20 } from "node:path";
169125
+ import { existsSync as existsSync47, mkdirSync as mkdirSync9, writeFileSync as writeFileSync3, readFileSync as readFileSync22 } from "node:fs";
169126
+ import { join as join64, resolve as resolve37, basename as basename20 } from "node:path";
168352
169127
  import { exec as exec12 } from "node:child_process";
168353
169128
  import { promisify as promisify17 } from "node:util";
168354
169129
  async function runInit(options = {}) {
168355
- const cwd = options.path ? resolve36(options.path) : process.cwd();
169130
+ const cwd = options.path ? resolve37(options.path) : process.cwd();
168356
169131
  const fusionDir = join64(cwd, ".fusion");
168357
169132
  const dbPath = join64(fusionDir, "fusion.db");
168358
- const hasDbPath = existsSync46(dbPath);
169133
+ const hasDbPath = existsSync47(dbPath);
168359
169134
  const hasValidDb = hasDbPath && isValidSqliteDatabaseFile(dbPath);
168360
- if (existsSync46(fusionDir) && hasDbPath && hasValidDb) {
169135
+ if (existsSync47(fusionDir) && hasDbPath && hasValidDb) {
168361
169136
  const central2 = new CentralCore();
168362
169137
  await central2.init();
168363
169138
  const existing = await central2.getProjectByPath(cwd);
@@ -168379,7 +169154,7 @@ async function runInit(options = {}) {
168379
169154
  await central2.close();
168380
169155
  return;
168381
169156
  }
168382
- if (existsSync46(fusionDir) && hasDbPath && !hasValidDb) {
169157
+ if (existsSync47(fusionDir) && hasDbPath && !hasValidDb) {
168383
169158
  throw new Error(
168384
169159
  `Existing database at ${dbPath} is not a valid SQLite database. Restore it from .fusion/backups or move it aside before re-running fn init.`
168385
169160
  );
@@ -168387,7 +169162,7 @@ async function runInit(options = {}) {
168387
169162
  const projectName = options.name ?? await detectProjectName(cwd);
168388
169163
  console.log(`Initializing fn project: "${projectName}"`);
168389
169164
  console.log(` Path: ${cwd}`);
168390
- if (!existsSync46(fusionDir)) {
169165
+ if (!existsSync47(fusionDir)) {
168391
169166
  mkdirSync9(fusionDir, { recursive: true });
168392
169167
  console.log(` \u2713 Created .fusion/ directory`);
168393
169168
  }
@@ -168400,7 +169175,7 @@ async function runInit(options = {}) {
168400
169175
  }
168401
169176
  await addLocalStorageToGitignore(cwd);
168402
169177
  await warnIfQmdMissing();
168403
- if (!existsSync46(dbPath)) {
169178
+ if (!existsSync47(dbPath)) {
168404
169179
  writeFileSync3(dbPath, "");
168405
169180
  console.log(` \u2713 Created fusion.db`);
168406
169181
  }
@@ -168450,7 +169225,7 @@ async function runInit(options = {}) {
168450
169225
  }
168451
169226
  }
168452
169227
  async function detectProjectName(dir2) {
168453
- if (!existsSync46(join64(dir2, ".git"))) {
169228
+ if (!existsSync47(join64(dir2, ".git"))) {
168454
169229
  return basename20(dir2) || "my-project";
168455
169230
  }
168456
169231
  try {
@@ -168472,9 +169247,9 @@ async function detectProjectName(dir2) {
168472
169247
  async function addLocalStorageToGitignore(cwd) {
168473
169248
  const gitignorePath = join64(cwd, ".gitignore");
168474
169249
  let content = "";
168475
- if (existsSync46(gitignorePath)) {
169250
+ if (existsSync47(gitignorePath)) {
168476
169251
  try {
168477
- content = readFileSync21(gitignorePath, "utf-8");
169252
+ content = readFileSync22(gitignorePath, "utf-8");
168478
169253
  } catch {
168479
169254
  }
168480
169255
  }
@@ -168514,7 +169289,7 @@ async function initializeGitRepo(cwd) {
168514
169289
  await ensureGitConfig(cwd, "user.name", "Fusion");
168515
169290
  await ensureGitConfig(cwd, "user.email", "noreply@runfusion.ai");
168516
169291
  const gitkeepPath = join64(cwd, ".gitkeep");
168517
- if (!existsSync46(gitkeepPath)) {
169292
+ if (!existsSync47(gitkeepPath)) {
168518
169293
  writeFileSync3(gitkeepPath, "\n");
168519
169294
  }
168520
169295
  await execAsync11("git add .gitkeep", { cwd, timeout: 1e4 });
@@ -168652,8 +169427,8 @@ var agent_import_exports = {};
168652
169427
  __export(agent_import_exports, {
168653
169428
  runAgentImport: () => runAgentImport
168654
169429
  });
168655
- import { existsSync as existsSync47, mkdirSync as mkdirSync10, readFileSync as readFileSync22, statSync as statSync9, writeFileSync as writeFileSync4 } from "node:fs";
168656
- import { resolve as resolve37 } from "node:path";
169430
+ import { existsSync as existsSync48, mkdirSync as mkdirSync10, readFileSync as readFileSync23, statSync as statSync9, writeFileSync as writeFileSync4 } from "node:fs";
169431
+ import { resolve as resolve38 } from "node:path";
168657
169432
  function slugifyPathSegment(input) {
168658
169433
  if (!input || typeof input !== "string") {
168659
169434
  return "unnamed";
@@ -168702,16 +169477,16 @@ async function importSkillsToProject(projectPath, skills, companySlug, dryRun) {
168702
169477
  errors: []
168703
169478
  };
168704
169479
  const companyDir = slugifyPathSegment(companySlug ?? "unknown-company");
168705
- const baseSkillsDir = resolve37(projectPath, "skills", "imported", companyDir);
169480
+ const baseSkillsDir = resolve38(projectPath, "skills", "imported", companyDir);
168706
169481
  for (const skill of skills) {
168707
169482
  if (!skill.name || typeof skill.name !== "string" || skill.name.trim().length === 0) {
168708
169483
  result.errors.push({ name: "(unnamed)", error: "Skill is missing required 'name' field" });
168709
169484
  continue;
168710
169485
  }
168711
169486
  const skillSlug = slugifyPathSegment(skill.name);
168712
- const skillDir = resolve37(baseSkillsDir, skillSlug);
168713
- const skillPath = resolve37(skillDir, "SKILL.md");
168714
- if (existsSync47(skillPath)) {
169487
+ const skillDir = resolve38(baseSkillsDir, skillSlug);
169488
+ const skillPath = resolve38(skillDir, "SKILL.md");
169489
+ if (existsSync48(skillPath)) {
168715
169490
  result.skipped.push(skill.name);
168716
169491
  continue;
168717
169492
  }
@@ -168783,8 +169558,8 @@ function isArchivePath(path5) {
168783
169558
  async function runAgentImport(source, options) {
168784
169559
  const dryRun = options?.dryRun ?? false;
168785
169560
  const skipExisting = options?.skipExisting ?? false;
168786
- const sourcePath = resolve37(source);
168787
- if (!existsSync47(sourcePath)) {
169561
+ const sourcePath = resolve38(source);
169562
+ if (!existsSync48(sourcePath)) {
168788
169563
  console.error(`Path not found: ${sourcePath}`);
168789
169564
  process.exit(1);
168790
169565
  }
@@ -168830,7 +169605,7 @@ async function runAgentImport(source, options) {
168830
169605
  isPackageImport = true;
168831
169606
  ({ items: importItems, result } = prepareAgentCompaniesImport(pkg, conversionOptions));
168832
169607
  } else if (sourcePath.endsWith(".md")) {
168833
- const content = readFileSync22(sourcePath, "utf-8");
169608
+ const content = readFileSync23(sourcePath, "utf-8");
168834
169609
  const { manifest } = parseSingleAgentManifest(content);
168835
169610
  const pkg = {
168836
169611
  company: void 0,
@@ -168922,7 +169697,7 @@ var agent_export_exports = {};
168922
169697
  __export(agent_export_exports, {
168923
169698
  runAgentExport: () => runAgentExport
168924
169699
  });
168925
- import { resolve as resolve38 } from "node:path";
169700
+ import { resolve as resolve39 } from "node:path";
168926
169701
  async function getProjectPath4(projectName) {
168927
169702
  if (projectName) {
168928
169703
  const context = await resolveProject(projectName);
@@ -168960,7 +169735,7 @@ async function runAgentExport(outputDir, options) {
168960
169735
  console.error("No agents found to export");
168961
169736
  process.exit(1);
168962
169737
  }
168963
- const result = await exportAgentsToDirectory(agents, resolve38(outputDir), {
169738
+ const result = await exportAgentsToDirectory(agents, resolve39(outputDir), {
168964
169739
  companyName: options?.companyName,
168965
169740
  companySlug: options?.companySlug
168966
169741
  });
@@ -169179,7 +169954,7 @@ __export(plugin_exports, {
169179
169954
  runPluginList: () => runPluginList,
169180
169955
  runPluginUninstall: () => runPluginUninstall
169181
169956
  });
169182
- import { existsSync as existsSync48 } from "node:fs";
169957
+ import { existsSync as existsSync49 } from "node:fs";
169183
169958
  import { join as join65 } from "node:path";
169184
169959
  import { readFile as readFile23 } from "node:fs/promises";
169185
169960
  import * as readline from "node:readline";
@@ -169219,7 +169994,7 @@ async function createPluginLoader(pluginStore, projectName) {
169219
169994
  }
169220
169995
  async function loadManifestFromPath(pluginPath) {
169221
169996
  const manifestPath = join65(pluginPath, "manifest.json");
169222
- if (!existsSync48(manifestPath)) {
169997
+ if (!existsSync49(manifestPath)) {
169223
169998
  throw new Error(`Plugin manifest not found at: ${manifestPath}`);
169224
169999
  }
169225
170000
  const content = await readFile23(manifestPath, "utf-8");
@@ -169277,7 +170052,7 @@ async function runPluginInstall(source, options) {
169277
170052
  console.error("Please provide a local path to the plugin directory.");
169278
170053
  process.exit(1);
169279
170054
  }
169280
- if (!existsSync48(source)) {
170055
+ if (!existsSync49(source)) {
169281
170056
  console.error(`Plugin path does not exist: ${source}`);
169282
170057
  process.exit(1);
169283
170058
  }
@@ -169322,14 +170097,14 @@ async function runPluginUninstall(id, options) {
169322
170097
  console.log(` Uninstall "${plugin4.name}"?`);
169323
170098
  console.log(` This will stop and remove the plugin.`);
169324
170099
  console.log();
169325
- const response = await new Promise((resolve40) => {
170100
+ const response = await new Promise((resolve42) => {
169326
170101
  const rl = readline.createInterface({
169327
170102
  input: process.stdin,
169328
170103
  output: process.stdout
169329
170104
  });
169330
170105
  rl.question(" Continue? [y/N] ", (answer) => {
169331
170106
  rl.close();
169332
- resolve40(answer.toLowerCase());
170107
+ resolve42(answer.toLowerCase());
169333
170108
  });
169334
170109
  });
169335
170110
  if (response !== "y" && response !== "yes") {
@@ -169409,7 +170184,7 @@ var plugin_scaffold_exports = {};
169409
170184
  __export(plugin_scaffold_exports, {
169410
170185
  runPluginCreate: () => runPluginCreate
169411
170186
  });
169412
- import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync5, existsSync as existsSync49 } from "node:fs";
170187
+ import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync5, existsSync as existsSync50 } from "node:fs";
169413
170188
  import { join as join66 } from "node:path";
169414
170189
  function toTitleCase(str) {
169415
170190
  return str.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
@@ -169544,7 +170319,7 @@ async function runPluginCreate(name, options) {
169544
170319
  }
169545
170320
  const targetDir = options?.output ?? name;
169546
170321
  const targetPath = join66(process.cwd(), targetDir);
169547
- if (existsSync49(targetPath)) {
170322
+ if (existsSync50(targetPath)) {
169548
170323
  console.error(`Error: Directory '${targetDir}' already exists.`);
169549
170324
  console.error("Please choose a different name or remove the existing directory.");
169550
170325
  process.exit(1);
@@ -169594,7 +170369,7 @@ __export(skills_exports, {
169594
170369
  runSkillsSearch: () => runSkillsSearch,
169595
170370
  searchSkills: () => searchSkills
169596
170371
  });
169597
- import { spawn as spawn16 } from "node:child_process";
170372
+ import { spawn as spawn18 } from "node:child_process";
169598
170373
  async function searchSkills(query, limit = 10) {
169599
170374
  const url = `${SKILLS_API_BASE}/api/search?q=${encodeURIComponent(query)}&limit=${limit}`;
169600
170375
  try {
@@ -169672,14 +170447,14 @@ async function runSkillsInstall(args, options) {
169672
170447
  npxArgs.push("--skill", options.skill);
169673
170448
  }
169674
170449
  npxArgs.push("-y", "-a", "pi");
169675
- const child = spawn16("npx", npxArgs, {
170450
+ const child = spawn18("npx", npxArgs, {
169676
170451
  cwd: process.cwd(),
169677
170452
  stdio: "inherit",
169678
170453
  shell: true
169679
170454
  });
169680
- const exitCode = await new Promise((resolve40, reject2) => {
170455
+ const exitCode = await new Promise((resolve42, reject2) => {
169681
170456
  child.on("exit", (code) => {
169682
- resolve40(code ?? 1);
170457
+ resolve42(code ?? 1);
169683
170458
  });
169684
170459
  child.on("error", (err) => {
169685
170460
  reject2(err);
@@ -169712,7 +170487,7 @@ __export(research_exports, {
169712
170487
  runResearchShow: () => runResearchShow
169713
170488
  });
169714
170489
  import { writeFile as writeFile19 } from "node:fs/promises";
169715
- import { join as join67, resolve as resolve39 } from "node:path";
170490
+ import { join as join67, resolve as resolve40 } from "node:path";
169716
170491
  async function getStore3(projectName) {
169717
170492
  const project = projectName ? await resolveProject(projectName) : void 0;
169718
170493
  const store = new TaskStore(project?.projectPath ?? process.cwd());
@@ -169868,7 +170643,7 @@ async function runResearchExport(options) {
169868
170643
  }
169869
170644
  const content = format === "json" ? JSON.stringify(run, null, 2) : renderMarkdown(run);
169870
170645
  const ext = format === "json" ? "json" : "md";
169871
- const outputPath = options.output ? resolve39(options.output) : join67(process.cwd(), `research-${run.id.toLowerCase()}.${ext}`);
170646
+ const outputPath = options.output ? resolve40(options.output) : join67(process.cwd(), `research-${run.id.toLowerCase()}.${ext}`);
169872
170647
  await writeFile19(outputPath, content, "utf8");
169873
170648
  store.getResearchStore().createExport(run.id, format, content);
169874
170649
  if (options.json) {
@@ -169933,21 +170708,21 @@ __export(native_patch_exports, {
169933
170708
  isTerminalAvailable: () => isTerminalAvailable,
169934
170709
  setupNativeResolution: () => setupNativeResolution
169935
170710
  });
169936
- import { join as join68, basename as basename21, dirname as dirname27 } from "node:path";
169937
- import { existsSync as existsSync50, copyFileSync, mkdirSync as mkdirSync12, symlinkSync as symlinkSync2, rmSync as rmSync5, lstatSync as lstatSync3, readlinkSync as readlinkSync2 } from "node:fs";
170711
+ import { join as join68, basename as basename21, dirname as dirname28 } from "node:path";
170712
+ import { existsSync as existsSync51, copyFileSync, mkdirSync as mkdirSync12, symlinkSync as symlinkSync2, rmSync as rmSync5, lstatSync as lstatSync3, readlinkSync as readlinkSync2 } from "node:fs";
169938
170713
  import { tmpdir as tmpdir4 } from "node:os";
169939
170714
  function findStagedNativeDir2() {
169940
- const platform3 = process.platform === "darwin" ? "darwin" : process.platform === "linux" ? "linux" : process.platform === "win32" ? "win32" : "unknown";
170715
+ const platform4 = process.platform === "darwin" ? "darwin" : process.platform === "linux" ? "linux" : process.platform === "win32" ? "win32" : "unknown";
169941
170716
  const arch = process.arch === "arm64" ? "arm64" : process.arch === "x64" ? "x64" : "unknown";
169942
- const prebuildName = `${platform3}-${arch}`;
169943
- const execDir = dirname27(process.execPath);
170717
+ const prebuildName = `${platform4}-${arch}`;
170718
+ const execDir = dirname28(process.execPath);
169944
170719
  const nextToBinary = join68(execDir, "runtime", prebuildName);
169945
- if (existsSync50(join68(nextToBinary, "pty.node"))) {
170720
+ if (existsSync51(join68(nextToBinary, "pty.node"))) {
169946
170721
  return nextToBinary;
169947
170722
  }
169948
170723
  if (process.env.FUSION_RUNTIME_DIR) {
169949
170724
  const envPath = join68(process.env.FUSION_RUNTIME_DIR, prebuildName);
169950
- if (existsSync50(join68(envPath, "pty.node"))) {
170725
+ if (existsSync51(join68(envPath, "pty.node"))) {
169951
170726
  return envPath;
169952
170727
  }
169953
170728
  }
@@ -169957,11 +170732,11 @@ function cleanupStaleBunfsLinks() {
169957
170732
  if (process.platform === "win32") return;
169958
170733
  const bunfsRoot = "/$bunfs/root";
169959
170734
  try {
169960
- if (existsSync50(bunfsRoot)) {
170735
+ if (existsSync51(bunfsRoot)) {
169961
170736
  const stats = lstatSync3(bunfsRoot);
169962
170737
  if (stats.isSymbolicLink()) {
169963
170738
  const target = readlinkSync2(bunfsRoot);
169964
- if (target.includes("fn-bunfs-") && !existsSync50(target)) {
170739
+ if (target.includes("fn-bunfs-") && !existsSync51(target)) {
169965
170740
  rmSync5(bunfsRoot);
169966
170741
  console.log("[fn-native-patch] Cleaned up stale /$bunfs/root symlink");
169967
170742
  }
@@ -169989,14 +170764,14 @@ function setupNativeResolution() {
169989
170764
  mkdirSync12(platformDir, { recursive: true });
169990
170765
  const ptyNodeDest = join68(platformDir, "pty.node");
169991
170766
  copyFileSync(join68(nativeDir, "pty.node"), ptyNodeDest);
169992
- if (existsSync50(join68(nativeDir, "spawn-helper"))) {
170767
+ if (existsSync51(join68(nativeDir, "spawn-helper"))) {
169993
170768
  copyFileSync(join68(nativeDir, "spawn-helper"), join68(platformDir, "spawn-helper"));
169994
170769
  }
169995
170770
  process.env.FUSION_FAKE_BUNFS_ROOT = tmpRoot;
169996
170771
  if (process.platform !== "win32") {
169997
170772
  const bunfsRoot = "/$bunfs/root";
169998
170773
  try {
169999
- if (existsSync50(bunfsRoot)) {
170774
+ if (existsSync51(bunfsRoot)) {
170000
170775
  const stats = lstatSync3(bunfsRoot);
170001
170776
  if (stats.isSymbolicLink()) {
170002
170777
  rmSync5(bunfsRoot);
@@ -170019,7 +170794,7 @@ function setupNativeResolution() {
170019
170794
  function cleanupNativeResolution() {
170020
170795
  if (bunfsSymlinkPath && process.platform !== "win32") {
170021
170796
  try {
170022
- if (existsSync50(bunfsSymlinkPath)) {
170797
+ if (existsSync51(bunfsSymlinkPath)) {
170023
170798
  const stats = lstatSync3(bunfsSymlinkPath);
170024
170799
  if (stats.isSymbolicLink()) {
170025
170800
  rmSync5(bunfsSymlinkPath);
@@ -170065,11 +170840,12 @@ var init_native_patch = __esm({
170065
170840
  });
170066
170841
 
170067
170842
  // src/bin.ts
170068
- import { existsSync as existsSync51, mkdtempSync as mkdtempSync2, readFileSync as readFileSync23, symlinkSync as symlinkSync3, writeFileSync as writeFileSync6 } from "node:fs";
170069
- import { createRequire as createRequire6 } from "node:module";
170070
- import { join as join69, dirname as dirname28 } from "node:path";
170843
+ import { existsSync as existsSync52, mkdtempSync as mkdtempSync2, readFileSync as readFileSync24, symlinkSync as symlinkSync3, writeFileSync as writeFileSync6 } from "node:fs";
170844
+ import { createRequire as createRequire7 } from "node:module";
170845
+ import { join as join69, dirname as dirname29, resolve as resolve41 } from "node:path";
170071
170846
  import { tmpdir as tmpdir5 } from "node:os";
170072
170847
  import { performance as performance3 } from "node:perf_hooks";
170848
+ import { fileURLToPath as fileURLToPath11 } from "node:url";
170073
170849
  var isBunBinary3 = typeof Bun !== "undefined" && !!Bun.embeddedFiles;
170074
170850
  function configurePiPackage() {
170075
170851
  if (process.env.PI_PACKAGE_DIR) {
@@ -170082,13 +170858,13 @@ function configurePiPackage() {
170082
170858
  type: "module"
170083
170859
  };
170084
170860
  try {
170085
- const require4 = createRequire6(import.meta.url);
170861
+ const require4 = createRequire7(import.meta.url);
170086
170862
  const piPackagePath = require4.resolve("@mariozechner/pi-coding-agent/package.json");
170087
- const piPackageDir = dirname28(piPackagePath);
170088
- packageJson = JSON.parse(readFileSync23(piPackagePath, "utf-8"));
170863
+ const piPackageDir = dirname29(piPackagePath);
170864
+ packageJson = JSON.parse(readFileSync24(piPackagePath, "utf-8"));
170089
170865
  for (const entry of ["dist", "docs", "examples", "README.md", "CHANGELOG.md"]) {
170090
170866
  const source = join69(piPackageDir, entry);
170091
- if (existsSync51(source)) {
170867
+ if (existsSync52(source)) {
170092
170868
  symlinkSync3(source, join69(tmp, entry));
170093
170869
  }
170094
170870
  }
@@ -170107,8 +170883,8 @@ setInterval(() => {
170107
170883
  performance3.clearMarks();
170108
170884
  }, 3e4).unref();
170109
170885
  function loadEnvFile(path5) {
170110
- if (!existsSync51(path5)) return;
170111
- const contents = readFileSync23(path5, "utf-8");
170886
+ if (!existsSync52(path5)) return;
170887
+ const contents = readFileSync24(path5, "utf-8");
170112
170888
  for (const rawLine of contents.split(/\r?\n/)) {
170113
170889
  const line = rawLine.trim();
170114
170890
  if (!line || line.startsWith("#")) continue;
@@ -170419,8 +171195,36 @@ function getFlagValueNumber(args, flag) {
170419
171195
  const parsed = Number(value);
170420
171196
  return Number.isFinite(parsed) ? parsed : void 0;
170421
171197
  }
171198
+ function readOwnCliVersion() {
171199
+ let currentDir;
171200
+ try {
171201
+ currentDir = dirname29(fileURLToPath11(import.meta.url));
171202
+ } catch {
171203
+ return void 0;
171204
+ }
171205
+ for (let i = 0; i < 8; i += 1) {
171206
+ const pkgPath = resolve41(currentDir, "package.json");
171207
+ if (existsSync52(pkgPath)) {
171208
+ try {
171209
+ const parsed = JSON.parse(readFileSync24(pkgPath, "utf-8"));
171210
+ if (parsed.name === "@runfusion/fusion" && typeof parsed.version === "string") {
171211
+ return parsed.version;
171212
+ }
171213
+ } catch {
171214
+ }
171215
+ }
171216
+ const parentDir = resolve41(currentDir, "..");
171217
+ if (parentDir === currentDir) break;
171218
+ currentDir = parentDir;
171219
+ }
171220
+ return void 0;
171221
+ }
170422
171222
  async function main() {
170423
171223
  const { cleanedArgs: args, projectName } = extractGlobalProjectFlag(process.argv.slice(2));
171224
+ if (args.includes("--version") || args.includes("-v")) {
171225
+ console.log(readOwnCliVersion() ?? "unknown");
171226
+ process.exit(0);
171227
+ }
170424
171228
  if (args.includes("--help") || args.includes("-h")) {
170425
171229
  console.log(HELP);
170426
171230
  process.exit(0);