@yeaft/webchat-agent 0.1.780 → 0.1.781

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 (2) hide show
  1. package/package.json +1 -1
  2. package/unify/session.js +45 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.1.780",
3
+ "version": "0.1.781",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/unify/session.js CHANGED
@@ -50,7 +50,7 @@ import { openSegmentIndex } from './memory/index-db.js';
50
50
  import { syncAll as syncSegmentIndex } from './memory/segment-sync.js';
51
51
  import { openAmsRegistry } from './memory/ams-registry.js';
52
52
  import { join } from 'path';
53
- import { existsSync as existsSyncSafe, readFileSync as readFileSyncSafe } from 'fs';
53
+ import { existsSync as existsSyncSafe, readFileSync as readFileSyncSafe, mkdirSync as mkdirSyncSafe } from 'fs';
54
54
 
55
55
  /**
56
56
  * @typedef {Object} SessionOptions
@@ -80,6 +80,33 @@ import { existsSync as existsSyncSafe, readFileSync as readFileSyncSafe } from '
80
80
  * @property {() => Promise<void>} shutdown — Graceful shutdown
81
81
  */
82
82
 
83
+ /**
84
+ * Eagerly create `<yeaftDir>/stats/` and surface failures as a warn.
85
+ *
86
+ * Returns the resolved path either way — the caller can still pass it
87
+ * to `ToolUsageStats`, which keeps an in-memory counter path even when
88
+ * the disk is read-only.
89
+ *
90
+ * NOTE: this knowledge ("stats lives under `stats/`") belongs inside
91
+ * `ToolUsageStats.init()`. Once that exists, delete this helper and
92
+ * the call site collapses to `await toolStats.init(yeaftDir)`.
93
+ *
94
+ * @param {string} yeaftDir
95
+ * @returns {string} statsDir
96
+ */
97
+ function prepareToolStatsDir(yeaftDir) {
98
+ const statsDir = join(yeaftDir, 'stats');
99
+ try {
100
+ mkdirSyncSafe(statsDir, { recursive: true });
101
+ } catch (err) {
102
+ console.warn(
103
+ `[Unify] Could not create stats dir ${statsDir}: ${err?.message || err}. ` +
104
+ `Tool-usage counters will live in memory only.`
105
+ );
106
+ }
107
+ return statsDir;
108
+ }
109
+
83
110
  /**
84
111
  * Load (or initialize) a Yeaft session.
85
112
  *
@@ -320,8 +347,24 @@ export async function loadSession(options = {}) {
320
347
  // Tool-call usage statistics: persisted to <yeaftDir>/stats/tool-usage.json.
321
348
  // Loaded synchronously at boot so the first turn already sees prior counts.
322
349
  // Threaded into the engine so it can `record` each tool_exec event.
350
+ //
351
+ // 2026-05-16: eagerly create the `stats/` directory at boot. The
352
+ // ToolUsageStats writer does `fsp.mkdir(..., {recursive:true})` lazily
353
+ // inside `#doFlush()` and swallows any mkdir error, which meant an
354
+ // unwritable parent (perm denied, ENOSPC) was silently invisible
355
+ // until the user filed a support ticket. Doing it here surfaces the
356
+ // failure as a console.warn while still leaving the in-memory
357
+ // counter path functional — the engine keeps recording even if the
358
+ // disk is read-only.
359
+ //
360
+ // FOLLOW-UP: this leaks `ToolUsageStats`'s storage layout (its
361
+ // directory name) into the session orchestrator. The right home is
362
+ // a `ToolUsageStats.init()` that owns the mkdir + the warn + a
363
+ // `writesDisabled` flag. Tracking as future work; for now the helper
364
+ // below visually quarantines the leak so the migration is one delete.
365
+ const statsDir = prepareToolStatsDir(yeaftDir);
323
366
  const toolStats = new ToolUsageStats({
324
- path: join(yeaftDir, 'stats', 'tool-usage.json'),
367
+ path: join(statsDir, 'tool-usage.json'),
325
368
  });
326
369
  toolStats.loadSync();
327
370
  const engine = new Engine({