@yeaft/webchat-agent 1.0.19 → 1.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "description": "Remote worker agent for Yeaft Web Code Agent — connects the native Yeaft engine, CLI providers, and workbench tools",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -21,7 +21,6 @@ import {
21
21
  statSync,
22
22
  writeFileSync,
23
23
  } from 'fs';
24
- import { rename as renameAsync, writeFile as writeFileAsync } from 'fs/promises';
25
24
  import { basename, dirname, extname, join } from 'path';
26
25
  import { randomUUID } from 'crypto';
27
26
 
@@ -33,7 +32,8 @@ const MAX_TEXT_BYTES = 1024 * 1024;
33
32
  const MAX_TOOL_INPUT = 10 * 1024;
34
33
  const MAX_INLINE_VALUE_BYTES = 1024 * 1024;
35
34
  const MAX_RAW_REQUEST_BYTES = 2 * 1024 * 1024;
36
- const TRACE_FLUSH_DELAY_MS = 150;
35
+ const TRACE_FLUSH_INTERVAL_MS = 5_000;
36
+ const TRACE_FLUSH_DIRTY_LOOPS = 10;
37
37
 
38
38
  function isPlainObject(value) {
39
39
  return value && typeof value === 'object' && !Array.isArray(value);
@@ -66,13 +66,6 @@ function atomicWriteJson(filePath, value) {
66
66
  renameSync(tmp, filePath);
67
67
  }
68
68
 
69
- async function atomicWriteJsonAsync(filePath, value) {
70
- ensureDir(dirname(filePath));
71
- const tmp = `${filePath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
72
- await writeFileAsync(tmp, JSON.stringify(value), 'utf8');
73
- await renameAsync(tmp, filePath);
74
- }
75
-
76
69
  function readJson(filePath) {
77
70
  try {
78
71
  return JSON.parse(readFileSync(filePath, 'utf8'));
@@ -541,10 +534,10 @@ export class DebugTrace {
541
534
  #turnIndex = new Map();
542
535
  /** @type {Map<string, object>} */
543
536
  #requestCache = new Map();
544
- /** @type {Map<string, object>} */
537
+ /** @type {Map<string, { trace: object, dirtyLoops: number, firstDirtyAt: number }>} */
545
538
  #pendingWrites = new Map();
546
- /** @type {Map<string, NodeJS.Timeout>} */
547
- #flushTimers = new Map();
539
+ /** @type {NodeJS.Timeout|null} */
540
+ #flushTimer = null;
548
541
  /** @type {number} */
549
542
  #sequence = 0;
550
543
 
@@ -626,7 +619,7 @@ export class DebugTrace {
626
619
  trace.updatedAt = loop.at;
627
620
  trace.active = info.stopReason ? !['end_turn', 'error', 'aborted'].includes(String(info.stopReason)) : false;
628
621
  trace._lastSnapshot = snapshot;
629
- if (!trace.active) this.#scheduleSave(trace);
622
+ this.#markDirty(trace, { dirtyLoops: 1, force: !trace.active });
630
623
  }
631
624
 
632
625
  logTool(turnId, { toolName, toolCallId = null, toolInput = null, toolOutput = null, durationMs = null, isError = false } = {}) {
@@ -649,7 +642,7 @@ export class DebugTrace {
649
642
  createdAt: Date.now(),
650
643
  });
651
644
  trace.updatedAt = Date.now();
652
- if (!trace.active) this.#scheduleSave(trace);
645
+ this.#markDirty(trace, { dirtyLoops: 0, force: !trace.active });
653
646
  return id;
654
647
  }
655
648
 
@@ -888,39 +881,44 @@ export class DebugTrace {
888
881
  return toWrite;
889
882
  }
890
883
 
891
- #scheduleSave(trace) {
884
+ #markDirty(trace, { dirtyLoops = 0, force = false } = {}) {
892
885
  if (!trace?.requestKey) return;
893
886
  const key = this.#traceWriteKey(trace);
894
- this.#pendingWrites.set(key, trace);
887
+ const existing = this.#pendingWrites.get(key);
888
+ const now = Date.now();
889
+ const item = existing || { trace, dirtyLoops: 0, firstDirtyAt: now };
890
+ item.trace = trace;
891
+ item.dirtyLoops += Math.max(0, Number(dirtyLoops) || 0);
892
+ this.#pendingWrites.set(key, item);
895
893
  this.#requestCache.set(trace.requestKey, trace);
896
- if (this.#flushTimers.has(key)) return;
897
- const timer = setTimeout(() => this.#flushOneAsync(key), TRACE_FLUSH_DELAY_MS);
898
- this.#flushTimers.set(key, timer);
899
- }
900
894
 
901
- async #flushOneAsync(key) {
902
- const timer = this.#flushTimers.get(key);
903
- if (timer) clearTimeout(timer);
904
- this.#flushTimers.delete(key);
905
- const trace = this.#pendingWrites.get(key);
906
- if (!trace) return;
907
- this.#pendingWrites.delete(key);
908
- try {
909
- await atomicWriteJsonAsync(this.#traceFile(trace), this.#serializableTrace(trace));
910
- this.#pruneSession(trace.sessionId || null, REQUEST_RETENTION);
911
- } catch (err) {
912
- console.warn('[Yeaft] debug trace write failed:', err?.message || err);
895
+ if (force || item.dirtyLoops >= TRACE_FLUSH_DIRTY_LOOPS) {
896
+ this.#flushPendingSync();
897
+ return;
913
898
  }
899
+ this.#scheduleFlushTimer(now);
900
+ }
901
+
902
+ #scheduleFlushTimer(now = Date.now()) {
903
+ if (this.#flushTimer || this.#pendingWrites.size === 0) return;
904
+ const oldestDirtyAt = Math.min(...Array.from(this.#pendingWrites.values()).map(item => item.firstDirtyAt || now));
905
+ const dueIn = Math.max(0, TRACE_FLUSH_INTERVAL_MS - (now - oldestDirtyAt));
906
+ this.#flushTimer = setTimeout(() => {
907
+ this.#flushTimer = null;
908
+ this.#flushPendingSync();
909
+ }, dueIn);
910
+ if (typeof this.#flushTimer.unref === 'function') this.#flushTimer.unref();
914
911
  }
915
912
 
916
913
  #flushPendingSync() {
917
- const entries = Array.from(this.#pendingWrites.entries());
914
+ if (this.#flushTimer) {
915
+ clearTimeout(this.#flushTimer);
916
+ this.#flushTimer = null;
917
+ }
918
+ const entries = Array.from(this.#pendingWrites.values());
918
919
  if (entries.length === 0) return;
919
- for (const [key, trace] of entries) {
920
- const timer = this.#flushTimers.get(key);
921
- if (timer) clearTimeout(timer);
922
- this.#flushTimers.delete(key);
923
- this.#pendingWrites.delete(key);
920
+ this.#pendingWrites.clear();
921
+ for (const { trace } of entries) {
924
922
  try {
925
923
  atomicWriteJson(this.#traceFile(trace), this.#serializableTrace(trace));
926
924
  } catch (err) {