@yeaft/webchat-agent 0.1.1058 → 0.1.1060

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": "0.1.1058",
3
+ "version": "0.1.1060",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -474,12 +474,36 @@ export class DebugTrace {
474
474
  };
475
475
  // Group rows by (turnId, threadId, sessionId, vpId) → frontend Turn
476
476
  // record. Each row is also surfaced as a Loop.
477
+ const duplicateLoopTraceIds = new Set();
478
+ const seenLoopKeys = new Set();
479
+ for (const r of rows) {
480
+ const traceId = r.trace_id || r.id;
481
+ const key = `${traceId}#${r.turn_number || 0}`;
482
+ if (seenLoopKeys.has(key)) duplicateLoopTraceIds.add(traceId);
483
+ else seenLoopKeys.add(key);
484
+ }
485
+ // Legacy rows used one Engine-instance trace_id for many user requests.
486
+ // That creates duplicate Loop 1/2/... rows under the same trace. Split
487
+ // only those corrupted traces by SQLite row id; healthy rows keep trace_id
488
+ // so multi-loop requests still hydrate as one turn. Keep this as one
489
+ // function so loop hydration and tool attachment use the same identity.
490
+ const turnKeyForRow = (r) => {
491
+ const baseTurnId = r.trace_id || r.id;
492
+ return duplicateLoopTraceIds.has(baseTurnId) ? (r.id || baseTurnId) : baseTurnId;
493
+ };
494
+ const loopInstanceIdForRow = (r) => {
495
+ const baseTurnId = r.trace_id || r.id;
496
+ return duplicateLoopTraceIds.has(baseTurnId) ? (r.id || `${baseTurnId}#${r.turn_number || 0}`) : null;
497
+ };
477
498
  const turnsById = new Map();
478
499
  const loops = rows.map((r) => {
479
500
  const parsedMessages = parseJsonSafe(r.messages_json) || [];
480
501
  const parsedUsage = parseJsonSafe(r.usage_json);
502
+ const hydratedTurnId = turnKeyForRow(r);
503
+ const loopInstanceId = loopInstanceIdForRow(r);
481
504
  const loop = {
482
- turnId: r.trace_id, // panel groups loops by trace_id-as-turnId
505
+ turnId: hydratedTurnId,
506
+ ...(loopInstanceId ? { loopInstanceId } : {}),
483
507
  loopNumber: r.turn_number || 0,
484
508
  model: r.model || null,
485
509
  systemPrompt: r.system_prompt || '',
@@ -496,9 +520,9 @@ export class DebugTrace {
496
520
  vpId: r.vp_id || null,
497
521
  threadId: r.thread_id || null,
498
522
  };
499
- if (!turnsById.has(r.trace_id)) {
500
- turnsById.set(r.trace_id, {
501
- turnId: r.trace_id,
523
+ if (!turnsById.has(hydratedTurnId)) {
524
+ turnsById.set(hydratedTurnId, {
525
+ turnId: hydratedTurnId,
502
526
  // C2 fix: read the explicit `user_prompt` column persisted at
503
527
  // startTurn time. Deriving from messages_json is unsafe — each
504
528
  // tool-loop iteration overwrites messages_json with the
@@ -518,7 +542,7 @@ export class DebugTrace {
518
542
  tools: [],
519
543
  });
520
544
  }
521
- const t = turnsById.get(r.trace_id);
545
+ const t = turnsById.get(hydratedTurnId);
522
546
  t.loopCount += 1;
523
547
  // Aggregate per-loop latency / tokens so the Turn header shows the
524
548
  // same totals the live `turn_close` event would have stamped.
@@ -530,11 +554,11 @@ export class DebugTrace {
530
554
  // Attach tools to their parent Turn so the panel can render per-tool
531
555
  // timing without scanning the loop bodies.
532
556
  for (const tool of tools) {
533
- // Find which loop row this tool belongs to that row's trace_id
534
- // identifies the Turn.
557
+ // Find which loop row this tool belongs to; use the same hydrated
558
+ // identity as the loop/turn records so split legacy rows keep tools.
535
559
  const owner = rows.find(r => r.id === tool.turn_id);
536
560
  if (!owner) continue;
537
- const t = turnsById.get(owner.trace_id);
561
+ const t = turnsById.get(turnKeyForRow(owner));
538
562
  if (!t) continue;
539
563
  t.tools.push({
540
564
  loopNumber: owner.turn_number || 0,
package/yeaft/engine.js CHANGED
@@ -1863,7 +1863,11 @@ export class Engine {
1863
1863
  }
1864
1864
 
1865
1865
  const turnId = this.#trace.startTurn({
1866
- traceId: this.#traceId,
1866
+ // Persist the wire-level query turn id, not the Engine-instance trace id.
1867
+ // Debug history groups SQLite loop rows by traceId; using #traceId here
1868
+ // merged separate user requests into one debug turn, so every request's
1869
+ // first LLM call showed up as another stale "Loop 1" under the same row.
1870
+ traceId: queryTurnId,
1867
1871
  turnNumber,
1868
1872
  // fix-vp-multi-thread (bug 4): stamp routing context so the
1869
1873
  // debug-trace SQL row carries enough info to be filtered by