@yeaft/webchat-agent 1.0.232 → 1.0.233

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.232",
3
+ "version": "1.0.233",
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",
@@ -1,3 +1,5 @@
1
+ import { createHash } from 'node:crypto';
2
+
1
3
  const MAX_DEBUG_STRING_BYTES = 64 * 1024;
2
4
  export const MAX_ACTION_REQUEST_DETAIL_BYTES = 256 * 1024;
3
5
  const MAX_ACTION_REQUEST_INPUT_BYTES = MAX_ACTION_REQUEST_DETAIL_BYTES;
@@ -33,6 +35,17 @@ function truncateUtf8(value, maxBytes = MAX_DEBUG_STRING_BYTES) {
33
35
  return `${bytes.subarray(0, end).toString('utf8')}${marker}`;
34
36
  }
35
37
 
38
+ export function boundedDebugIdentity(value, maxBytes) {
39
+ const text = String(value || '');
40
+ if (!text || byteLength(text) <= maxBytes) return text;
41
+ const digest = createHash('sha256').update(text, 'utf8').digest('hex');
42
+ const suffix = `~${digest}`;
43
+ const bytes = Buffer.from(text, 'utf8');
44
+ let end = Math.max(0, maxBytes - byteLength(suffix));
45
+ while (end > 0 && (bytes[end] & 0xc0) === 0x80) end -= 1;
46
+ return `${bytes.subarray(0, end).toString('utf8')}${suffix}`;
47
+ }
48
+
36
49
  function normalizeSensitiveName(name) {
37
50
  return String(name || '').toLowerCase().replace(/[^a-z0-9]/g, '');
38
51
  }
@@ -260,6 +273,76 @@ function boundedDebugInputBytes(value, maxBytes) {
260
273
  return bytes > maxBytes ? maxBytes + 1 : bytes;
261
274
  }
262
275
 
276
+ const MAX_ACTION_REQUEST_SUMMARY_ID_BYTES = 256;
277
+ const MAX_ACTION_REQUEST_SUMMARY_NAME_BYTES = 512;
278
+ const MAX_ACTION_REQUEST_SUMMARY_MODEL_BYTES = 1_024;
279
+ const MAX_ACTION_REQUEST_SUMMARY_STOP_REASON_BYTES = 256;
280
+
281
+ function summaryString(value, maxBytes, fallback = null) {
282
+ if (typeof value !== 'string' || !value) return fallback;
283
+ return truncateUtf8(value, maxBytes);
284
+ }
285
+
286
+ function summaryUsage(usage) {
287
+ if (!usage || typeof usage !== 'object') return null;
288
+ return {
289
+ inputTokens: Math.max(0, Number(usage.inputTokens) || 0),
290
+ outputTokens: Math.max(0, Number(usage.outputTokens) || 0),
291
+ cacheReadTokens: Math.max(0, Number(usage.cacheReadTokens) || 0),
292
+ cacheWriteTokens: Math.max(0, Number(usage.cacheWriteTokens) || 0),
293
+ totalInputTokens: Math.max(0, Number(usage.totalInputTokens) || 0),
294
+ totalTokens: Math.max(0, Number(usage.totalTokens) || 0),
295
+ };
296
+ }
297
+
298
+ function minimalActionRequestLoopSummary(loop) {
299
+ return {
300
+ loopInstanceId: null,
301
+ loopNumber: Number(loop?.loopNumber) || 0,
302
+ model: null,
303
+ response: '',
304
+ usage: null,
305
+ latencyMs: 0,
306
+ ttfbMs: null,
307
+ stopReason: null,
308
+ at: 0,
309
+ toolCalls: [],
310
+ detailTruncated: true,
311
+ };
312
+ }
313
+
314
+ function actionRequestLoopSummary(loop) {
315
+ const toolCalls = Array.isArray(loop?.toolCalls) ? loop.toolCalls : [];
316
+ return {
317
+ loopInstanceId: boundedDebugIdentity(loop?.loopInstanceId, MAX_ACTION_REQUEST_SUMMARY_ID_BYTES) || null,
318
+ loopNumber: Number(loop?.loopNumber) || 0,
319
+ model: summaryString(loop?.model, MAX_ACTION_REQUEST_SUMMARY_MODEL_BYTES),
320
+ response: typeof loop?.response === 'string' ? truncateUtf8(loop.response, 8 * 1024) : '',
321
+ usage: summaryUsage(loop?.usage),
322
+ latencyMs: Math.max(0, Number(loop?.latencyMs) || 0),
323
+ ttfbMs: loop?.ttfbMs == null ? null : Math.max(0, Number(loop.ttfbMs) || 0),
324
+ stopReason: summaryString(loop?.stopReason, MAX_ACTION_REQUEST_SUMMARY_STOP_REASON_BYTES),
325
+ at: Math.max(0, Number(loop?.at) || 0),
326
+ toolCalls: toolCalls.slice(0, MAX_DEBUG_ARRAY_ITEMS).map(call => ({
327
+ id: boundedDebugIdentity(call?.id, MAX_ACTION_REQUEST_SUMMARY_ID_BYTES) || null,
328
+ name: summaryString(call?.name, MAX_ACTION_REQUEST_SUMMARY_NAME_BYTES, '?'),
329
+ input: null,
330
+ })),
331
+ detailTruncated: true,
332
+ };
333
+ }
334
+
335
+ function actionRequestToolSummary(tool) {
336
+ return {
337
+ loopNumber: Number(tool?.loopNumber) || 0,
338
+ callId: boundedDebugIdentity(tool?.callId, MAX_ACTION_REQUEST_SUMMARY_ID_BYTES) || null,
339
+ name: summaryString(tool?.name, MAX_ACTION_REQUEST_SUMMARY_NAME_BYTES, '?'),
340
+ durationMs: Math.max(0, Number(tool?.durationMs) || 0),
341
+ isError: tool?.isError === true,
342
+ toolOutput: null,
343
+ };
344
+ }
345
+
263
346
  export function limitActionRequestDebugInput(loopValues, toolValues) {
264
347
  const sourceLoops = Array.isArray(loopValues) ? loopValues : [];
265
348
  const sourceTools = Array.isArray(toolValues) ? toolValues : [];
@@ -277,11 +360,25 @@ export function limitActionRequestDebugInput(loopValues, toolValues) {
277
360
  let remainingBytes = MAX_ACTION_REQUEST_INPUT_BYTES;
278
361
  const loops = [];
279
362
  const tools = [];
363
+ let summarizedLoopCount = 0;
280
364
  for (let index = candidates.length - 1; index >= 0; index -= 1) {
281
- const loop = candidates[index];
282
- const loopTools = toolsByLoop.get(Number(loop?.loopNumber) || 0) || [];
283
- const inputBytes = boundedDebugInputBytes({ loop, tools: loopTools }, remainingBytes);
284
- if (inputBytes > remainingBytes) continue;
365
+ const sourceLoop = candidates[index];
366
+ const sourceLoopTools = toolsByLoop.get(Number(sourceLoop?.loopNumber) || 0) || [];
367
+ let loop = sourceLoop;
368
+ let loopTools = sourceLoopTools;
369
+ let inputBytes = boundedDebugInputBytes({ loop, tools: loopTools }, remainingBytes);
370
+ if (inputBytes > remainingBytes) {
371
+ loop = actionRequestLoopSummary(sourceLoop);
372
+ loopTools = sourceLoopTools.map(actionRequestToolSummary);
373
+ inputBytes = boundedDebugInputBytes({ loop, tools: loopTools }, remainingBytes);
374
+ if (inputBytes > remainingBytes && loops.length === 0) {
375
+ loop = minimalActionRequestLoopSummary(sourceLoop);
376
+ loopTools = [];
377
+ inputBytes = boundedDebugInputBytes({ loop, tools: loopTools }, remainingBytes);
378
+ }
379
+ if (inputBytes > remainingBytes) continue;
380
+ summarizedLoopCount += 1;
381
+ }
285
382
  loops.unshift(loop);
286
383
  tools.unshift(...loopTools);
287
384
  remainingBytes -= inputBytes;
@@ -290,6 +387,7 @@ export function limitActionRequestDebugInput(loopValues, toolValues) {
290
387
  loops,
291
388
  tools,
292
389
  omittedLoopCount: sourceLoops.length - loops.length,
390
+ summarizedLoopCount,
293
391
  };
294
392
  }
295
393
 
@@ -482,12 +580,25 @@ export function sanitizeDebugValue(value, parent = null, key = '', seen = new We
482
580
  }
483
581
  }
484
582
 
583
+ function finalizeActionRequestDetail(detail) {
584
+ if (!detail?.request) return detail;
585
+ const loops = Array.isArray(detail.request.loops) ? detail.request.loops : [];
586
+ detail.request.summarizedLoopCount = loops.filter(loop => loop?.detailTruncated === true).length;
587
+ detail.request.omittedLoopCount = Math.max(0, Number(detail.request.omittedLoopCount) || 0);
588
+ detail.request.truncated = detail.request.truncated === true
589
+ || detail.request.summarizedLoopCount > 0
590
+ || detail.request.omittedLoopCount > 0;
591
+ return detail;
592
+ }
593
+
485
594
  export function enforceActionRequestDetailBudget(detail, omittedLoopCount = 0) {
486
595
  if (omittedLoopCount > 0 && detail?.request) {
487
596
  detail.request.truncated = true;
488
597
  detail.request.omittedLoopCount = omittedLoopCount;
489
598
  }
490
- if (!detail?.request || jsonByteLength(detail) <= MAX_ACTION_REQUEST_DETAIL_BYTES) return detail;
599
+ if (!detail?.request || jsonByteLength(detail) <= MAX_ACTION_REQUEST_DETAIL_BYTES) {
600
+ return finalizeActionRequestDetail(detail);
601
+ }
491
602
  detail.request.truncated = true;
492
603
  const loops = Array.isArray(detail.request.loops) ? detail.request.loops : [];
493
604
  const stages = [
@@ -504,17 +615,26 @@ export function enforceActionRequestDetailBudget(detail, omittedLoopCount = 0) {
504
615
  loop => { loop.response = ''; },
505
616
  ];
506
617
  for (const stage of stages) {
507
- for (const loop of loops) stage(loop);
508
- if (jsonByteLength(detail) <= MAX_ACTION_REQUEST_DETAIL_BYTES) return detail;
618
+ for (const loop of loops) {
619
+ stage(loop);
620
+ loop.detailTruncated = true;
621
+ }
622
+ if (jsonByteLength(detail) <= MAX_ACTION_REQUEST_DETAIL_BYTES) {
623
+ return finalizeActionRequestDetail(detail);
624
+ }
509
625
  }
510
626
  while (loops.length > 1 && jsonByteLength(detail) > MAX_ACTION_REQUEST_DETAIL_BYTES) {
511
627
  loops.shift();
512
628
  detail.request.omittedLoopCount = (detail.request.omittedLoopCount || 0) + 1;
513
629
  }
514
- if (jsonByteLength(detail) <= MAX_ACTION_REQUEST_DETAIL_BYTES) return detail;
630
+ if (jsonByteLength(detail) <= MAX_ACTION_REQUEST_DETAIL_BYTES) {
631
+ return finalizeActionRequestDetail(detail);
632
+ }
515
633
  detail.request.omittedLoopCount = (detail.request.omittedLoopCount || 0) + loops.length;
516
634
  detail.request.loops = [];
517
- if (jsonByteLength(detail) <= MAX_ACTION_REQUEST_DETAIL_BYTES) return detail;
635
+ if (jsonByteLength(detail) <= MAX_ACTION_REQUEST_DETAIL_BYTES) {
636
+ return finalizeActionRequestDetail(detail);
637
+ }
518
638
  const request = detail.request;
519
639
  const metadata = value => truncateUtf8(value, 4 * 1024);
520
640
  return {
@@ -536,6 +656,7 @@ export function enforceActionRequestDetailBudget(detail, omittedLoopCount = 0) {
536
656
  loops: [],
537
657
  truncated: true,
538
658
  omittedLoopCount: detail.request.omittedLoopCount,
659
+ summarizedLoopCount: 0,
539
660
  },
540
661
  };
541
662
  }
@@ -1,5 +1,6 @@
1
1
  import { reconstructDebugRawRequest } from '../debug-trace.js';
2
2
  import {
3
+ boundedDebugIdentity,
3
4
  enforceActionRequestDetailBudget,
4
5
  limitActionRequestDebugInput,
5
6
  sanitizeDebugValue,
@@ -13,6 +14,11 @@ const MAX_ACTION_MESSAGE_CHARS = 16_000;
13
14
  const MAX_ACTION_DIAGNOSTIC_CHARS = 8_000;
14
15
  const MAX_ACTION_MESSAGES = 20;
15
16
  const MAX_ACTION_REQUEST_TOOL_CALLS = 128;
17
+ const MAX_ACTION_REQUEST_ID_BYTES = 256;
18
+ const MAX_ACTION_REQUEST_NAME_BYTES = 512;
19
+ const MAX_ACTION_REQUEST_MODEL_BYTES = 1_024;
20
+ const MAX_ACTION_REQUEST_STOP_REASON_BYTES = 256;
21
+ const MAX_ACTION_REQUEST_METADATA_BYTES = 4 * 1_024;
16
22
  const MAX_HISTORICAL_BRIEF_CHARS = 256;
17
23
  const MAX_CURRENT_BRIEF_BYTES = 8 * 1024;
18
24
  export const MAX_WORK_ITEM_BROWSER_DTO_BYTES = 512 * 1024;
@@ -29,6 +35,10 @@ function truncateUtf8(value, maxBytes) {
29
35
  return bytes.subarray(0, end).toString('utf8');
30
36
  }
31
37
 
38
+ function exceedsUtf8Bytes(value, maxBytes) {
39
+ return Buffer.byteLength(String(value || ''), 'utf8') > maxBytes;
40
+ }
41
+
32
42
  function currentAction(detail) {
33
43
  if (!detail?.currentActionId || !Array.isArray(detail.actions)) return null;
34
44
  return detail.actions.find(action => action.id === detail.currentActionId) || null;
@@ -1041,47 +1051,71 @@ export function projectActionRequestDetail(action, run, history, runs = [run]) {
1041
1051
  `${count(tool.loopNumber)}:${tool.callId}`,
1042
1052
  tool,
1043
1053
  ]));
1044
- const loops = limited.loops.map(loop => ({
1045
- id: loop.loopInstanceId || `${turn.turnId}:${loop.loopNumber}`,
1046
- loopNumber: count(loop.loopNumber),
1047
- model: loop.model || run.modelSnapshot?.id || null,
1048
- systemPrompt: sanitizeDebugValue(typeof loop.systemPrompt === 'string' ? loop.systemPrompt : ''),
1049
- messages: sanitizeDebugValue(Array.isArray(loop.messages) ? loop.messages : []),
1050
- response: sanitizeDebugValue(typeof loop.response === 'string' ? loop.response : ''),
1051
- usage: projectDebugUsage(loop.usage),
1052
- latencyMs: count(loop.latencyMs),
1053
- ttfbMs: loop.ttfbMs == null ? null : count(loop.ttfbMs),
1054
- stopReason: loop.stopReason || null,
1055
- at: count(loop.at),
1056
- tools: (Array.isArray(loop.toolCalls) ? loop.toolCalls : [])
1057
- .slice(0, MAX_ACTION_REQUEST_TOOL_CALLS)
1058
- .map(call => {
1059
- const result = toolsByCall.get(`${count(loop.loopNumber)}:${call.id}`);
1060
- return {
1061
- id: call.id || null,
1062
- name: call.name || result?.name || '?',
1063
- input: sanitizeDebugValue(call.input),
1064
- output: sanitizeDebugValue(result?.toolOutput ?? null),
1065
- durationMs: count(result?.durationMs),
1066
- isError: result?.isError === true,
1067
- };
1068
- }),
1069
- rawRequest: sanitizeDebugValue(reconstructDebugRawRequest(
1070
- loop.rawRequestBase ?? loop.requestBase?.rawRequest ?? null,
1071
- loop.requestDelta || null,
1072
- )),
1073
- rawResponse: sanitizeDebugValue(loop.rawResponse ?? null),
1074
- }));
1054
+ const requestModel = run.modelSnapshot?.id || limited.loops[0]?.model || null;
1055
+ const metadataTruncated = exceedsUtf8Bytes(action.id, MAX_ACTION_REQUEST_METADATA_BYTES)
1056
+ || exceedsUtf8Bytes(turn.turnId, MAX_ACTION_REQUEST_METADATA_BYTES)
1057
+ || exceedsUtf8Bytes(run.id, MAX_ACTION_REQUEST_METADATA_BYTES)
1058
+ || exceedsUtf8Bytes(run.status || 'running', MAX_ACTION_REQUEST_METADATA_BYTES)
1059
+ || exceedsUtf8Bytes(requestModel, MAX_ACTION_REQUEST_MODEL_BYTES)
1060
+ || exceedsUtf8Bytes(run.vpSnapshot?.id, MAX_ACTION_REQUEST_METADATA_BYTES)
1061
+ || exceedsUtf8Bytes(run.vpSnapshot?.name || run.vpSnapshot?.id, MAX_ACTION_REQUEST_METADATA_BYTES);
1062
+ const loops = limited.loops.map(loop => {
1063
+ const sourceId = loop.loopInstanceId || `${turn.turnId}:${loop.loopNumber}`;
1064
+ const sourceModel = loop.model || run.modelSnapshot?.id || '';
1065
+ const sourceCalls = Array.isArray(loop.toolCalls) ? loop.toolCalls : [];
1066
+ let detailTruncated = loop.detailTruncated === true
1067
+ || exceedsUtf8Bytes(sourceId, MAX_ACTION_REQUEST_ID_BYTES)
1068
+ || exceedsUtf8Bytes(sourceModel, MAX_ACTION_REQUEST_MODEL_BYTES)
1069
+ || exceedsUtf8Bytes(loop.stopReason, MAX_ACTION_REQUEST_STOP_REASON_BYTES)
1070
+ || sourceCalls.length > MAX_ACTION_REQUEST_TOOL_CALLS;
1071
+ const projectedTools = sourceCalls.slice(0, MAX_ACTION_REQUEST_TOOL_CALLS).map((call, index) => {
1072
+ const result = toolsByCall.get(`${count(loop.loopNumber)}:${call.id}`);
1073
+ const sourceCallId = call.id || `${sourceId}:tool:${index}`;
1074
+ const sourceName = call.name || result?.name || '?';
1075
+ if (exceedsUtf8Bytes(sourceCallId, MAX_ACTION_REQUEST_ID_BYTES)
1076
+ || exceedsUtf8Bytes(sourceName, MAX_ACTION_REQUEST_NAME_BYTES)) {
1077
+ detailTruncated = true;
1078
+ }
1079
+ return {
1080
+ id: boundedDebugIdentity(sourceCallId, MAX_ACTION_REQUEST_ID_BYTES),
1081
+ name: truncateUtf8(sourceName, MAX_ACTION_REQUEST_NAME_BYTES) || '?',
1082
+ input: sanitizeDebugValue(call.input),
1083
+ output: sanitizeDebugValue(result?.toolOutput ?? null),
1084
+ durationMs: count(result?.durationMs),
1085
+ isError: result?.isError === true,
1086
+ };
1087
+ });
1088
+ return {
1089
+ id: boundedDebugIdentity(sourceId, MAX_ACTION_REQUEST_ID_BYTES) || null,
1090
+ loopNumber: count(loop.loopNumber),
1091
+ model: truncateUtf8(sourceModel, MAX_ACTION_REQUEST_MODEL_BYTES) || null,
1092
+ systemPrompt: sanitizeDebugValue(typeof loop.systemPrompt === 'string' ? loop.systemPrompt : ''),
1093
+ messages: sanitizeDebugValue(Array.isArray(loop.messages) ? loop.messages : []),
1094
+ response: sanitizeDebugValue(typeof loop.response === 'string' ? loop.response : ''),
1095
+ usage: projectDebugUsage(loop.usage),
1096
+ latencyMs: count(loop.latencyMs),
1097
+ ttfbMs: loop.ttfbMs == null ? null : count(loop.ttfbMs),
1098
+ stopReason: truncateUtf8(loop.stopReason, MAX_ACTION_REQUEST_STOP_REASON_BYTES) || null,
1099
+ at: count(loop.at),
1100
+ detailTruncated,
1101
+ tools: projectedTools,
1102
+ rawRequest: sanitizeDebugValue(reconstructDebugRawRequest(
1103
+ loop.rawRequestBase ?? loop.requestBase?.rawRequest ?? null,
1104
+ loop.requestDelta || null,
1105
+ )),
1106
+ rawResponse: sanitizeDebugValue(loop.rawResponse ?? null),
1107
+ };
1108
+ });
1075
1109
  return enforceActionRequestDetailBudget({
1076
- actionId: action.id,
1110
+ actionId: boundedDebugIdentity(action.id, MAX_ACTION_REQUEST_METADATA_BYTES),
1077
1111
  request: {
1078
- id: turn.turnId,
1079
- runId: run.id,
1080
- status: run.status || 'running',
1081
- model: run.modelSnapshot?.id || loops[0]?.model || null,
1112
+ id: boundedDebugIdentity(turn.turnId, MAX_ACTION_REQUEST_METADATA_BYTES),
1113
+ runId: boundedDebugIdentity(run.id, MAX_ACTION_REQUEST_METADATA_BYTES),
1114
+ status: truncateUtf8(run.status || 'running', MAX_ACTION_REQUEST_METADATA_BYTES),
1115
+ model: truncateUtf8(requestModel, MAX_ACTION_REQUEST_MODEL_BYTES) || null,
1082
1116
  vp: run.vpSnapshot ? {
1083
- id: run.vpSnapshot.id || null,
1084
- name: run.vpSnapshot.name || run.vpSnapshot.id || null,
1117
+ id: boundedDebugIdentity(run.vpSnapshot.id, MAX_ACTION_REQUEST_METADATA_BYTES) || null,
1118
+ name: truncateUtf8(run.vpSnapshot.name || run.vpSnapshot.id, MAX_ACTION_REQUEST_METADATA_BYTES) || null,
1085
1119
  } : null,
1086
1120
  openedAt: count(turn.openedAt || run.startedAt),
1087
1121
  closedAt: count(turn.closedAt || run.endedAt),
@@ -1089,6 +1123,9 @@ export function projectActionRequestDetail(action, run, history, runs = [run]) {
1089
1123
  totalMs: count(turn.totalMs),
1090
1124
  totalTokens: count(turn.totalTokens),
1091
1125
  loops,
1126
+ truncated: metadataTruncated || limited.omittedLoopCount > 0 || limited.summarizedLoopCount > 0,
1127
+ omittedLoopCount: limited.omittedLoopCount,
1128
+ summarizedLoopCount: limited.summarizedLoopCount,
1092
1129
  },
1093
1130
  }, limited.omittedLoopCount);
1094
1131
  }