@runfusion/fusion 0.14.3 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/bin.js +210 -71
  2. package/dist/client/assets/{AgentDetailView-BBCnqhqI.js → AgentDetailView-B1zViykq.js} +1 -1
  3. package/dist/client/assets/{AgentsView-BY-Yq-Te.js → AgentsView-Bl9JH5C8.js} +3 -3
  4. package/dist/client/assets/{ChatView-DkoJNxFW.js → ChatView-liNErE53.js} +1 -1
  5. package/dist/client/assets/{DevServerView-qvs6pp6c.js → DevServerView-CV_PpbnZ.js} +1 -1
  6. package/dist/client/assets/{DirectoryPicker-BkAIXNrP.js → DirectoryPicker-DPfkGnj5.js} +1 -1
  7. package/dist/client/assets/{DocumentsView-BcaUGgaL.js → DocumentsView-CESb6RI7.js} +1 -1
  8. package/dist/client/assets/{InsightsView-Dz9Ivclw.js → InsightsView-BKhvyEyQ.js} +1 -1
  9. package/dist/client/assets/{MemoryView-BsweARBT.js → MemoryView-DB-l2miV.js} +1 -1
  10. package/dist/client/assets/{NodesView-bAU-v4bJ.js → NodesView-DgTXO8mm.js} +1 -1
  11. package/dist/client/assets/{PiExtensionsManager-C_U2g7y3.js → PiExtensionsManager-C4fTzemh.js} +1 -1
  12. package/dist/client/assets/{PluginManager-pIDsTk5v.js → PluginManager-C2-dExUL.js} +1 -1
  13. package/dist/client/assets/{ResearchView-D4Eib_uR.js → ResearchView-CkVwRDVA.js} +1 -1
  14. package/dist/client/assets/{RoadmapsView-BaGwsUGS.js → RoadmapsView-Cu85_XrQ.js} +1 -1
  15. package/dist/client/assets/{SettingsModal-BiZVi3cI.js → SettingsModal-BGnSAeqa.js} +1 -1
  16. package/dist/client/assets/{SettingsModal-CRyg643t.js → SettingsModal-C0DokcId.js} +3 -3
  17. package/dist/client/assets/{SetupWizardModal-BcIGBBpA.js → SetupWizardModal-C_d9clJp.js} +1 -1
  18. package/dist/client/assets/{SkillMultiselect-DPARHJeQ.js → SkillMultiselect-DwGWYZi6.js} +1 -1
  19. package/dist/client/assets/{SkillsView-Da_d_HPu.js → SkillsView-C096TB7i.js} +1 -1
  20. package/dist/client/assets/{TodoView-5rAeqYtV.js → TodoView-CUiAt2mR.js} +1 -1
  21. package/dist/client/assets/{folder-open-CgjcFqww.js → folder-open-CKivQd8c.js} +1 -1
  22. package/dist/client/assets/index-B4StE1qN.js +662 -0
  23. package/dist/client/assets/index-DYJk0WDc.css +1 -0
  24. package/dist/client/assets/{list-checks-C9YWtF7h.js → list-checks-B3oufblU.js} +1 -1
  25. package/dist/client/assets/{star-4nUh67-U.js → star-damu_EYz.js} +1 -1
  26. package/dist/client/assets/{upload-CEt5-Bnq.js → upload-uH6CHlEw.js} +1 -1
  27. package/dist/client/assets/{users-4I0JDmgO.js → users-CUySbfji.js} +1 -1
  28. package/dist/client/index.html +2 -2
  29. package/dist/client/version.json +1 -1
  30. package/dist/extension.js +204 -69
  31. package/dist/pi-claude-cli/package.json +1 -1
  32. package/package.json +1 -1
  33. package/dist/client/assets/index-D1gTSlYB.css +0 -1
  34. package/dist/client/assets/index-DoQ5ALYY.js +0 -662
@@ -1,4 +1,4 @@
1
- import{c}from"./index-DoQ5ALYY.js";/**
1
+ import{c}from"./index-B4StE1qN.js";/**
2
2
  * @license lucide-react v1.7.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as a}from"./index-DoQ5ALYY.js";/**
1
+ import{c as a}from"./index-B4StE1qN.js";/**
2
2
  * @license lucide-react v1.7.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as a}from"./index-DoQ5ALYY.js";/**
1
+ import{c as a}from"./index-B4StE1qN.js";/**
2
2
  * @license lucide-react v1.7.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as e}from"./index-DoQ5ALYY.js";/**
1
+ import{c as e}from"./index-B4StE1qN.js";/**
2
2
  * @license lucide-react v1.7.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -92,11 +92,11 @@
92
92
  }
93
93
  })();
94
94
  </script>
95
- <script type="module" crossorigin src="/assets/index-DoQ5ALYY.js"></script>
95
+ <script type="module" crossorigin src="/assets/index-B4StE1qN.js"></script>
96
96
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-K0fH_qHe.js">
97
97
  <link rel="modulepreload" crossorigin href="/assets/vendor-xterm-DzcZoU0P.js">
98
98
  <link rel="stylesheet" crossorigin href="/assets/vendor-xterm-LZoznX6r.css">
99
- <link rel="stylesheet" crossorigin href="/assets/index-D1gTSlYB.css">
99
+ <link rel="stylesheet" crossorigin href="/assets/index-DYJk0WDc.css">
100
100
  </head>
101
101
  <body>
102
102
  <div id="root"></div>
@@ -1 +1 @@
1
- {"version":"monyc3c5-4ad5ec00"}
1
+ {"version":"monyzkz7-8f97cfdc"}
package/dist/extension.js CHANGED
@@ -240,7 +240,8 @@ var init_settings_schema = __esm({
240
240
  maxPostReviewFixes: 1,
241
241
  maxSpawnedAgentsPerParent: 5,
242
242
  maxSpawnedAgentsGlobal: 20,
243
- maintenanceIntervalMs: 9e5,
243
+ // Run maintenance (including WAL checkpointing) every 5 minutes by default.
244
+ maintenanceIntervalMs: 3e5,
244
245
  autoArchiveDoneTasksEnabled: true,
245
246
  autoArchiveDoneAfterMs: 48 * 60 * 60 * 1e3,
246
247
  archiveAgentLogMode: "compact",
@@ -2651,6 +2652,7 @@ var init_sqlite_adapter = __esm({
2651
2652
  // ../core/src/db.ts
2652
2653
  import { isAbsolute, join as join2 } from "node:path";
2653
2654
  import { mkdirSync, existsSync } from "node:fs";
2655
+ import { spawnSync } from "node:child_process";
2654
2656
  function toJson(value) {
2655
2657
  if (value === void 0 || value === null) return "[]";
2656
2658
  if (Array.isArray(value) && value.length === 0) return "[]";
@@ -3282,11 +3284,14 @@ CREATE INDEX IF NOT EXISTS idxTodoItemsSortOrder ON todo_items(listId, sortOrder
3282
3284
  Database = class {
3283
3285
  db;
3284
3286
  dbPath;
3287
+ inMemory;
3288
+ corruptionDetected = false;
3285
3289
  /** Tracks transaction nesting depth for savepoint-based nested transactions. */
3286
3290
  transactionDepth = 0;
3287
3291
  _fts5Available;
3288
3292
  constructor(fusionDir, options) {
3289
3293
  const inMemory = options?.inMemory === true;
3294
+ this.inMemory = inMemory;
3290
3295
  this.dbPath = inMemory ? ":memory:" : join2(fusionDir, "fusion.db");
3291
3296
  if (!inMemory && !isAbsolute(fusionDir)) {
3292
3297
  throw new Error(`[fusion] Database constructor requires an absolute fusionDir path, got: ${fusionDir}`);
@@ -3308,8 +3313,13 @@ This means a caller passed a .fusion directory where a project root was expected
3308
3313
  }
3309
3314
  if (!inMemory) {
3310
3315
  this.db.exec("PRAGMA journal_mode = WAL");
3316
+ this.db.exec("PRAGMA busy_timeout = 5000");
3317
+ this.db.exec("PRAGMA synchronous = NORMAL");
3318
+ this.db.exec("PRAGMA wal_autocheckpoint = 100");
3319
+ this.db.exec("PRAGMA journal_size_limit = 4194304");
3320
+ } else {
3321
+ this.db.exec("PRAGMA busy_timeout = 5000");
3311
3322
  }
3312
- this.db.exec("PRAGMA busy_timeout = 5000");
3313
3323
  this.db.exec("PRAGMA foreign_keys = ON");
3314
3324
  this._fts5Available = probeFts5(this.db);
3315
3325
  }
@@ -3390,6 +3400,35 @@ This means a caller passed a .fusion directory where a project root was expected
3390
3400
  return false;
3391
3401
  }
3392
3402
  }
3403
+ integrityCheck() {
3404
+ if (this.inMemory) {
3405
+ return { ok: true };
3406
+ }
3407
+ const rows = this.db.prepare("PRAGMA integrity_check(100)").all();
3408
+ const errors = rows.map((row) => row.integrity_check).filter((value) => typeof value === "string" && value !== "ok");
3409
+ if (errors.length > 0) {
3410
+ return { ok: false, errors };
3411
+ }
3412
+ return { ok: true };
3413
+ }
3414
+ recoverDatabase(outputPath) {
3415
+ if (this.inMemory) {
3416
+ return false;
3417
+ }
3418
+ const recoveredSql = spawnSync("sqlite3", ["-cmd", ".recover main", this.dbPath], {
3419
+ encoding: "utf-8",
3420
+ maxBuffer: 50 * 1024 * 1024
3421
+ });
3422
+ if (recoveredSql.status !== 0 || !recoveredSql.stdout) {
3423
+ return false;
3424
+ }
3425
+ const rebuilt = spawnSync("sqlite3", [outputPath], {
3426
+ input: recoveredSql.stdout,
3427
+ encoding: "utf-8",
3428
+ maxBuffer: 50 * 1024 * 1024
3429
+ });
3430
+ return rebuilt.status === 0;
3431
+ }
3393
3432
  /**
3394
3433
  * Initialize the database: create tables if they don't exist
3395
3434
  * and seed meta values.
@@ -3408,6 +3447,11 @@ This means a caller passed a .fusion directory where a project root was expected
3408
3447
  this.db.exec(
3409
3448
  `INSERT OR IGNORE INTO config (id, nextId, nextWorkflowStepId, settings, workflowSteps, updatedAt) VALUES (1, 1, 1, '${JSON.stringify(DEFAULT_PROJECT_SETTINGS)}', '[]', '${configNow}')`
3410
3449
  );
3450
+ const integrity = this.integrityCheck();
3451
+ if (!integrity.ok) {
3452
+ this.corruptionDetected = true;
3453
+ console.warn("[fusion:db] Database integrity check FAILED \u2014 corruption detected");
3454
+ }
3411
3455
  }
3412
3456
  /**
3413
3457
  * Run incremental schema migrations based on the stored schema version.
@@ -19077,7 +19121,7 @@ __export(migration_exports, {
19077
19121
  MigrationCoordinator: () => MigrationCoordinator,
19078
19122
  ProjectRequiredError: () => ProjectRequiredError
19079
19123
  });
19080
- import { existsSync as existsSync9, readFileSync as readFileSync2 } from "node:fs";
19124
+ import { existsSync as existsSync9, readFileSync as readFileSync2, realpathSync as realpathSync2 } from "node:fs";
19081
19125
  import { homedir as homedir2, tmpdir } from "node:os";
19082
19126
  import { isAbsolute as isAbsolute3, join as join12, resolve as resolve5, basename as basename3, dirname as dirname4 } from "node:path";
19083
19127
  function getHomeDir2() {
@@ -19178,12 +19222,30 @@ var init_migration = __esm({
19178
19222
  const projects = [];
19179
19223
  const visited = /* @__PURE__ */ new Set();
19180
19224
  let current = resolve5(startDir);
19181
- const home = getHomeDir2();
19225
+ const home = resolve5(getHomeDir2());
19182
19226
  const root = dirname4(current) === current ? current : "/";
19183
19227
  const systemTmp = resolve5(tmpdir());
19184
- while (current !== home && current !== root && current !== systemTmp) {
19228
+ const normalizePath2 = (path2) => {
19229
+ try {
19230
+ return realpathSync2(path2);
19231
+ } catch {
19232
+ return resolve5(path2);
19233
+ }
19234
+ };
19235
+ const normalizedHome = normalizePath2(home);
19236
+ const normalizedSystemTmp = normalizePath2(systemTmp);
19237
+ const normalizedStartDir = normalizePath2(current);
19238
+ while (true) {
19185
19239
  if (visited.has(current)) break;
19186
19240
  visited.add(current);
19241
+ const normalizedCurrent = normalizePath2(current);
19242
+ const isRootBoundary = current === root;
19243
+ const isHomeBoundary = normalizedCurrent === normalizedHome;
19244
+ const isTmpBoundary = normalizedCurrent === normalizedSystemTmp;
19245
+ const isStopBoundary = isRootBoundary || isHomeBoundary || isTmpBoundary;
19246
+ if (isRootBoundary || isTmpBoundary || isHomeBoundary && normalizedCurrent !== normalizedStartDir) {
19247
+ break;
19248
+ }
19187
19249
  if (this.hasFusionProject(current)) {
19188
19250
  const name = await this.generateProjectName(current);
19189
19251
  projects.push({
@@ -19193,6 +19255,9 @@ var init_migration = __esm({
19193
19255
  });
19194
19256
  break;
19195
19257
  }
19258
+ if (isStopBoundary) {
19259
+ break;
19260
+ }
19196
19261
  const parent = dirname4(current);
19197
19262
  if (parent === current) break;
19198
19263
  current = parent;
@@ -34501,6 +34566,39 @@ ${task.description}
34501
34566
  this.db.bumpLastModified();
34502
34567
  this.emit("agent:log", entry);
34503
34568
  }
34569
+ async appendAgentLogBatch(entries) {
34570
+ if (entries.length === 0) {
34571
+ return;
34572
+ }
34573
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
34574
+ const stmt = this.db.prepare(`
34575
+ INSERT INTO agentLogEntries (taskId, timestamp, text, type, detail, agent)
34576
+ VALUES (?, ?, ?, ?, ?, ?)
34577
+ `);
34578
+ this.db.transaction(() => {
34579
+ for (const entry of entries) {
34580
+ stmt.run(
34581
+ entry.taskId,
34582
+ timestamp,
34583
+ entry.text,
34584
+ entry.type,
34585
+ entry.detail ?? null,
34586
+ entry.agent ?? null
34587
+ );
34588
+ }
34589
+ });
34590
+ this.db.bumpLastModified();
34591
+ for (const entry of entries) {
34592
+ this.emit("agent:log", {
34593
+ timestamp,
34594
+ taskId: entry.taskId,
34595
+ text: entry.text,
34596
+ type: entry.type,
34597
+ ...entry.detail !== void 0 && { detail: entry.detail },
34598
+ ...entry.agent !== void 0 && { agent: entry.agent }
34599
+ });
34600
+ }
34601
+ }
34504
34602
  mapAgentLogRow(row) {
34505
34603
  return {
34506
34604
  timestamp: row.timestamp,
@@ -37490,6 +37588,20 @@ var init_plugin_loader = __esm({
37490
37588
  }
37491
37589
  return slots;
37492
37590
  }
37591
+ /**
37592
+ * Get all top-level dashboard view definitions from loaded plugins.
37593
+ */
37594
+ getPluginDashboardViews() {
37595
+ const views = [];
37596
+ for (const [pluginId, plugin4] of this.plugins) {
37597
+ if (plugin4.dashboardViews) {
37598
+ for (const view of plugin4.dashboardViews) {
37599
+ views.push({ pluginId, view });
37600
+ }
37601
+ }
37602
+ }
37603
+ return views;
37604
+ }
37493
37605
  /**
37494
37606
  * Get all runtime registrations from loaded plugins.
37495
37607
  * Returns plugin ownership metadata along with the runtime registration.
@@ -51228,18 +51340,21 @@ function summarizeToolArgs(name, args) {
51228
51340
  }
51229
51341
  return void 0;
51230
51342
  }
51231
- var FLUSH_SIZE_BYTES, FLUSH_INTERVAL_MS, AgentLogger;
51343
+ var FLUSH_SIZE_BYTES, FLUSH_INTERVAL_MS, ENTRY_BATCH_SIZE, AgentLogger;
51232
51344
  var init_agent_logger = __esm({
51233
51345
  "../engine/src/agent-logger.ts"() {
51234
51346
  "use strict";
51235
51347
  init_logger2();
51236
51348
  FLUSH_SIZE_BYTES = 1024;
51237
51349
  FLUSH_INTERVAL_MS = 500;
51350
+ ENTRY_BATCH_SIZE = 50;
51238
51351
  AgentLogger = class {
51239
51352
  textBuffer = "";
51240
51353
  thinkingBuffer = "";
51241
51354
  flushTimer = null;
51242
51355
  thinkingFlushTimer = null;
51356
+ entryFlushTimer = null;
51357
+ pendingEntries = [];
51243
51358
  flushSizeBytes;
51244
51359
  flushIntervalMs;
51245
51360
  store;
@@ -51344,8 +51459,13 @@ var init_agent_logger = __esm({
51344
51459
  clearTimeout(this.thinkingFlushTimer);
51345
51460
  this.thinkingFlushTimer = null;
51346
51461
  }
51462
+ if (this.entryFlushTimer) {
51463
+ clearTimeout(this.entryFlushTimer);
51464
+ this.entryFlushTimer = null;
51465
+ }
51347
51466
  await this.flushTextBuffer();
51348
51467
  await this.flushThinkingBuffer();
51468
+ await this.flushPendingEntries();
51349
51469
  }
51350
51470
  // ── Internal helpers ───────────────────────────────────────────────
51351
51471
  /**
@@ -51354,7 +51474,7 @@ var init_agent_logger = __esm({
51354
51474
  * When only `appendLogCb` is set (no store/taskId), only the callback is used.
51355
51475
  * @param storeWarnMsg - Warning message prefix used when the task-store write fails.
51356
51476
  */
51357
- writeEntry(text, type, detail, storeWarnMsg) {
51477
+ writeEntry(text, type, detail, _storeWarnMsg, immediate = false) {
51358
51478
  const entry = {
51359
51479
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
51360
51480
  taskId: this.taskId,
@@ -51363,72 +51483,38 @@ var init_agent_logger = __esm({
51363
51483
  ...detail !== void 0 && { detail },
51364
51484
  ...this.agent !== void 0 && { agent: this.agent }
51365
51485
  };
51366
- if (this.store && this.taskId) {
51367
- this.store.appendAgentLog(this.taskId, text, type, detail, this.agent).catch((err) => {
51368
- this.log.warn(`${storeWarnMsg}: ${err instanceof Error ? err.message : String(err)}`);
51369
- });
51486
+ this.pendingEntries.push(entry);
51487
+ if (immediate || type !== "text" && type !== "thinking") {
51488
+ if (this.entryFlushTimer) {
51489
+ clearTimeout(this.entryFlushTimer);
51490
+ this.entryFlushTimer = null;
51491
+ }
51492
+ void this.flushPendingEntries();
51493
+ return;
51370
51494
  }
51371
- if (this.appendLogCb) {
51372
- this.appendLogCb(entry).catch((err) => {
51373
- this.log.warn(`appendLog callback failed for entry (${type}): ${err instanceof Error ? err.message : String(err)}`);
51374
- });
51495
+ if (this.pendingEntries.length >= ENTRY_BATCH_SIZE) {
51496
+ if (this.entryFlushTimer) {
51497
+ clearTimeout(this.entryFlushTimer);
51498
+ this.entryFlushTimer = null;
51499
+ }
51500
+ void this.flushPendingEntries();
51501
+ return;
51375
51502
  }
51503
+ this.scheduleEntryFlush();
51376
51504
  }
51377
51505
  flushTextBuffer() {
51378
51506
  if (this.textBuffer.length === 0) return Promise.resolve();
51379
51507
  const chunk = this.textBuffer;
51380
51508
  this.textBuffer = "";
51381
- const entry = {
51382
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
51383
- taskId: this.taskId,
51384
- text: chunk,
51385
- type: "text",
51386
- ...this.agent !== void 0 && { agent: this.agent }
51387
- };
51388
- const promises = [];
51389
- if (this.store && this.taskId) {
51390
- promises.push(
51391
- this.store.appendAgentLog(this.taskId, chunk, "text", void 0, this.agent).catch((err) => {
51392
- this.log.warn(`Failed to flush text buffer for ${this.taskId}: ${err instanceof Error ? err.message : String(err)}`);
51393
- })
51394
- );
51395
- }
51396
- if (this.appendLogCb) {
51397
- promises.push(
51398
- this.appendLogCb(entry).catch((err) => {
51399
- this.log.warn(`appendLog callback failed for text flush: ${err instanceof Error ? err.message : String(err)}`);
51400
- })
51401
- );
51402
- }
51403
- return Promise.all(promises).then(() => void 0);
51509
+ this.writeEntry(chunk, "text", void 0, `Failed to flush text buffer for ${this.taskId}`, true);
51510
+ return this.flushPendingEntries();
51404
51511
  }
51405
51512
  flushThinkingBuffer() {
51406
51513
  if (this.thinkingBuffer.length === 0) return Promise.resolve();
51407
51514
  const chunk = this.thinkingBuffer;
51408
51515
  this.thinkingBuffer = "";
51409
- const entry = {
51410
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
51411
- taskId: this.taskId,
51412
- text: chunk,
51413
- type: "thinking",
51414
- ...this.agent !== void 0 && { agent: this.agent }
51415
- };
51416
- const promises = [];
51417
- if (this.store && this.taskId) {
51418
- promises.push(
51419
- this.store.appendAgentLog(this.taskId, chunk, "thinking", void 0, this.agent).catch((err) => {
51420
- this.log.warn(`Failed to flush thinking buffer for ${this.taskId}: ${err instanceof Error ? err.message : String(err)}`);
51421
- })
51422
- );
51423
- }
51424
- if (this.appendLogCb) {
51425
- promises.push(
51426
- this.appendLogCb(entry).catch((err) => {
51427
- this.log.warn(`appendLog callback failed for thinking flush: ${err instanceof Error ? err.message : String(err)}`);
51428
- })
51429
- );
51430
- }
51431
- return Promise.all(promises).then(() => void 0);
51516
+ this.writeEntry(chunk, "thinking", void 0, `Failed to flush thinking buffer for ${this.taskId}`, true);
51517
+ return this.flushPendingEntries();
51432
51518
  }
51433
51519
  scheduleFlush() {
51434
51520
  if (this.flushTimer) return;
@@ -51444,6 +51530,52 @@ var init_agent_logger = __esm({
51444
51530
  this.flushThinkingBuffer();
51445
51531
  }, this.flushIntervalMs);
51446
51532
  }
51533
+ scheduleEntryFlush() {
51534
+ if (this.entryFlushTimer) return;
51535
+ this.entryFlushTimer = setTimeout(() => {
51536
+ this.entryFlushTimer = null;
51537
+ void this.flushPendingEntries();
51538
+ }, this.flushIntervalMs);
51539
+ }
51540
+ async flushPendingEntries() {
51541
+ if (this.pendingEntries.length === 0) {
51542
+ return;
51543
+ }
51544
+ const entries = this.pendingEntries;
51545
+ this.pendingEntries = [];
51546
+ if (this.store && this.taskId) {
51547
+ if (typeof this.store.appendAgentLogBatch === "function") {
51548
+ await this.store.appendAgentLogBatch(
51549
+ entries.map((entry) => ({
51550
+ taskId: entry.taskId,
51551
+ text: entry.text,
51552
+ type: entry.type,
51553
+ detail: entry.detail,
51554
+ agent: entry.agent
51555
+ }))
51556
+ ).catch((err) => {
51557
+ this.log.warn(`Failed to flush agent log batch for ${this.taskId}: ${err instanceof Error ? err.message : String(err)}`);
51558
+ });
51559
+ } else {
51560
+ await Promise.all(
51561
+ entries.map(
51562
+ (entry) => this.store.appendAgentLog(entry.taskId, entry.text, entry.type, entry.detail, entry.agent).catch((err) => {
51563
+ this.log.warn(`Failed to flush agent log entry for ${this.taskId}: ${err instanceof Error ? err.message : String(err)}`);
51564
+ })
51565
+ )
51566
+ );
51567
+ }
51568
+ }
51569
+ if (this.appendLogCb) {
51570
+ await Promise.all(
51571
+ entries.map(
51572
+ (entry) => this.appendLogCb(entry).catch((err) => {
51573
+ this.log.warn(`appendLog callback failed for entry (${entry.type}): ${err instanceof Error ? err.message : String(err)}`);
51574
+ })
51575
+ )
51576
+ );
51577
+ }
51578
+ }
51447
51579
  };
51448
51580
  }
51449
51581
  });
@@ -86200,7 +86332,7 @@ var init_src3 = __esm({
86200
86332
  });
86201
86333
 
86202
86334
  // ../../plugins/fusion-plugin-hermes-runtime/dist/cli-spawn.js
86203
- import { spawn as spawn6, spawnSync } from "node:child_process";
86335
+ import { spawn as spawn6, spawnSync as spawnSync2 } from "node:child_process";
86204
86336
  import os2 from "node:os";
86205
86337
  import path, { sep as PATH_SEP } from "node:path";
86206
86338
  function resolveBinaryForSpawn(binary) {
@@ -86213,7 +86345,7 @@ function resolveBinaryForSpawn(binary) {
86213
86345
  if (cached)
86214
86346
  return cached;
86215
86347
  try {
86216
- const result = spawnSync("where", [binary], { encoding: "utf-8" });
86348
+ const result = spawnSync2("where", [binary], { encoding: "utf-8" });
86217
86349
  if (result.status === 0) {
86218
86350
  const first = (result.stdout ?? "").trim().split(/\r?\n/)[0];
86219
86351
  if (first?.length) {
@@ -99082,7 +99214,8 @@ async function clearDefaultProject(globalDir) {
99082
99214
  await globalStore.updateSettings(rest);
99083
99215
  }
99084
99216
  async function detectProjectFromCwd(cwd, central) {
99085
- let currentDir = resolve17(cwd);
99217
+ const startDir = resolve17(cwd);
99218
+ let currentDir = startDir;
99086
99219
  while (true) {
99087
99220
  const kbPath = resolve17(currentDir, ".fusion", "fusion.db");
99088
99221
  if (isValidSqliteDatabaseFile(kbPath)) {
@@ -99090,11 +99223,13 @@ async function detectProjectFromCwd(cwd, central) {
99090
99223
  if (project) {
99091
99224
  return project;
99092
99225
  }
99093
- return {
99094
- id: "",
99095
- name: basename9(currentDir) || "current-project",
99096
- path: currentDir
99097
- };
99226
+ if (currentDir === startDir) {
99227
+ return {
99228
+ id: "",
99229
+ name: basename9(currentDir) || "current-project",
99230
+ path: currentDir
99231
+ };
99232
+ }
99098
99233
  }
99099
99234
  const parentDir = dirname12(currentDir);
99100
99235
  if (parentDir === currentDir) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusion/pi-claude-cli",
3
- "version": "0.14.3",
3
+ "version": "0.15.0",
4
4
  "description": "Fusion vendored fork: pi coding-agent extension that routes LLM calls through the Claude Code CLI. Forked from rchern/pi-claude-cli (MIT). See UPSTREAM.md.",
5
5
  "license": "MIT",
6
6
  "private": true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runfusion/fusion",
3
- "version": "0.14.3",
3
+ "version": "0.15.0",
4
4
  "license": "MIT",
5
5
  "description": "Fusion CLI: HTTP API server, daemon, dashboard launcher, and task tooling for the Fusion AI coding agent.",
6
6
  "homepage": "https://github.com/Runfusion/Fusion#readme",