@yeaft/webchat-agent 1.0.103 → 1.0.104

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.103",
3
+ "version": "1.0.104",
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",
@@ -232,12 +232,50 @@ function buildRawRequestBase(value) {
232
232
  }
233
233
 
234
234
  function buildRawMessagesDelta(previousMessages, nextMessages) {
235
- if (!Array.isArray(previousMessages) || !Array.isArray(nextMessages)) return null;
236
- const prefix = messagesPrefixLength(previousMessages, nextMessages);
237
- if (prefix === previousMessages.length && prefix <= nextMessages.length) {
235
+ if (!Array.isArray(nextMessages)) return null;
236
+ const priorMessages = Array.isArray(previousMessages) ? previousMessages : [];
237
+ const prefix = messagesPrefixLength(priorMessages, nextMessages);
238
+ if (prefix === priorMessages.length && prefix <= nextMessages.length) {
238
239
  return { messagesFrom: prefix, messagesAppend: boundRawValue(nextMessages.slice(prefix), 'raw_request_messages_append_budget') };
239
240
  }
240
- return { messages: boundRawValue(nextMessages, 'raw_request_messages_budget') };
241
+ return { messagesFrom: 0, messagesAppend: boundRawValue(nextMessages, 'raw_request_messages_append_budget') };
242
+ }
243
+
244
+ function rawRequestMessageKey(body) {
245
+ if (!isPlainObject(body)) return null;
246
+ if (Array.isArray(body.messages)) return 'messages';
247
+ if (Array.isArray(body.input)) return 'input';
248
+ return null;
249
+ }
250
+
251
+ function buildInitialRawRequestDelta(value) {
252
+ if (value == null) return null;
253
+ if (typeof value === 'string') return { replacement: rawRequestSentinel('raw_request_string_replaced', value) };
254
+ if (!isPlainObject(value)) return { replacement: boundRawValue(value) };
255
+ const delta = { set: {}, body: {} };
256
+ for (const [key, item] of Object.entries(value)) {
257
+ if (key === 'body') continue;
258
+ delta.set[key] = boundRawValue(item, `raw_request_${key}_budget`);
259
+ }
260
+ const body = isPlainObject(value.body) ? value.body : null;
261
+ if (body) {
262
+ for (const [key, bodyValue] of Object.entries(body)) {
263
+ if (key === 'messages' || key === 'input') continue;
264
+ delta.body[key] = boundRawValue(bodyValue, `raw_request_body_${key}_budget`);
265
+ }
266
+ const messageKey = rawRequestMessageKey(body);
267
+ if (messageKey) {
268
+ delta.body.messagesKey = messageKey;
269
+ Object.assign(delta.body, buildRawMessagesDelta([], body[messageKey]) || {});
270
+ }
271
+ } else if (Object.prototype.hasOwnProperty.call(value, 'body')) {
272
+ delta.set.body = rawRequestSentinel('raw_request_body_replaced');
273
+ }
274
+ if (Object.keys(delta.set).length === 0) delete delta.set;
275
+ if (Object.keys(delta.body).length === 0) delete delta.body;
276
+ if (!delta.set && !delta.body) return null;
277
+ if (jsonByteLength(delta) > MAX_RAW_REQUEST_BYTES) return { replacement: rawRequestSentinel('raw_request_delta_budget') };
278
+ return delta;
241
279
  }
242
280
 
243
281
  function rawComparableRequest(value) {
@@ -250,7 +288,7 @@ function rawComparableRequest(value) {
250
288
 
251
289
  function buildRawRequestDelta(previous, next) {
252
290
  if (next == null) return previous == null ? null : { replacement: null };
253
- if (previous == null) return { base: buildRawRequestBase(next) };
291
+ if (previous == null) return buildInitialRawRequestDelta(next);
254
292
  const comparablePrevious = rawComparableRequest(previous);
255
293
  const comparableNext = rawComparableRequest(next);
256
294
  if (typeof comparablePrevious === 'string' || typeof comparableNext === 'string') {
@@ -273,13 +311,15 @@ function buildRawRequestDelta(previous, next) {
273
311
  const nextBody = isPlainObject(comparableNext.body) ? comparableNext.body : null;
274
312
  if (prevBody && nextBody) {
275
313
  for (const key of Object.keys(nextBody)) {
276
- if (key === 'messages') continue;
314
+ if (key === 'messages' || key === 'input') continue;
277
315
  if (!stableEqual(prevBody[key], nextBody[key])) delta.body[key] = boundRawValue(nextBody[key], `raw_request_body_${key}_budget`);
278
316
  }
279
317
  for (const key of Object.keys(prevBody)) {
280
- if (key !== 'messages' && !Object.prototype.hasOwnProperty.call(nextBody, key)) delta.body[key] = null;
318
+ if (key !== 'messages' && key !== 'input' && !Object.prototype.hasOwnProperty.call(nextBody, key)) delta.body[key] = null;
281
319
  }
282
- const msgDelta = buildRawMessagesDelta(prevBody.messages, nextBody.messages);
320
+ const prevMessages = Array.isArray(prevBody.messages) ? prevBody.messages : prevBody.input;
321
+ const nextMessages = Array.isArray(nextBody.messages) ? nextBody.messages : nextBody.input;
322
+ const msgDelta = buildRawMessagesDelta(prevMessages, nextMessages);
283
323
  if (msgDelta) Object.assign(delta.body, msgDelta);
284
324
  } else if (!stableEqual(previous.body, next.body)) {
285
325
  delta.set.body = rawRequestSentinel('raw_request_body_replaced');
@@ -294,7 +334,7 @@ function buildRawRequestDelta(previous, next) {
294
334
  return delta;
295
335
  }
296
336
 
297
- function applyRawRequestDelta(previous, delta) {
337
+ export function applyRawRequestDelta(previous, delta) {
298
338
  if (!delta) return previous ?? null;
299
339
  if (Object.prototype.hasOwnProperty.call(delta, 'base')) return cloneJsonValue(delta.base) ?? delta.base ?? null;
300
340
  if (Object.prototype.hasOwnProperty.call(delta, 'replacement')) return cloneJsonValue(delta.replacement) ?? delta.replacement ?? null;
@@ -305,14 +345,18 @@ function applyRawRequestDelta(previous, delta) {
305
345
  if (isPlainObject(delta.body)) {
306
346
  const body = isPlainObject(next.body) ? { ...next.body } : {};
307
347
  for (const [key, value] of Object.entries(delta.body)) {
308
- if (key === 'messagesFrom' || key === 'messagesAppend' || key === 'messages') continue;
348
+ if (key === 'messagesFrom' || key === 'messagesAppend' || key === 'messages' || key === 'messagesKey') continue;
309
349
  body[key] = cloneJsonValue(value) ?? value;
310
350
  }
351
+ const messageKey = delta.body.messagesKey === 'messages' || delta.body.messagesKey === 'input'
352
+ ? delta.body.messagesKey
353
+ : (Array.isArray(body.messages) || Object.prototype.hasOwnProperty.call(delta.body, 'messages') ? 'messages' : 'input');
311
354
  if (Array.isArray(delta.body.messages)) {
312
- body.messages = cloneJsonValue(delta.body.messages) || [];
355
+ body[messageKey] = cloneJsonValue(delta.body.messages) || [];
313
356
  } else if (Array.isArray(delta.body.messagesAppend)) {
314
- const from = Number.isFinite(Number(delta.body.messagesFrom)) ? Number(delta.body.messagesFrom) : (Array.isArray(body.messages) ? body.messages.length : 0);
315
- body.messages = (Array.isArray(body.messages) ? body.messages.slice(0, from) : []).concat(cloneJsonValue(delta.body.messagesAppend) || []);
357
+ const existing = Array.isArray(body[messageKey]) ? body[messageKey] : [];
358
+ const from = Number.isFinite(Number(delta.body.messagesFrom)) ? Number(delta.body.messagesFrom) : existing.length;
359
+ body[messageKey] = existing.slice(0, from).concat(cloneJsonValue(delta.body.messagesAppend) || []);
316
360
  }
317
361
  next.body = body;
318
362
  }
@@ -339,11 +383,14 @@ function buildRequestSnapshot(info = {}) {
339
383
 
340
384
  function buildRequestDelta(previous, next) {
341
385
  if (!previous) {
342
- return {
386
+ const delta = {
343
387
  base: true,
344
388
  systemPrompt: next.systemPrompt || '',
345
389
  messages: Array.isArray(next.messages) ? next.messages : [],
346
390
  };
391
+ const rawRequestDelta = buildRawRequestDelta(null, next.rawRequest);
392
+ if (rawRequestDelta) delta.rawRequestDelta = rawRequestDelta;
393
+ return delta;
347
394
  }
348
395
  const delta = {};
349
396
  if ((next.systemPrompt || '') !== (previous.systemPrompt || '')) {
@@ -372,11 +419,15 @@ function applyRequestDelta(previous, delta = {}) {
372
419
  rawRequest: base.rawRequest ?? null,
373
420
  };
374
421
  if (delta.base) {
375
- return {
422
+ const nextBase = {
376
423
  systemPrompt: delta.systemPrompt || '',
377
424
  messages: Array.isArray(delta.messages) ? delta.messages : [],
378
- rawRequest: base.rawRequest ?? null,
425
+ rawRequest: null,
379
426
  };
427
+ if (Object.prototype.hasOwnProperty.call(delta, 'rawRequestDelta')) {
428
+ nextBase.rawRequest = applyRawRequestDelta(null, delta.rawRequestDelta);
429
+ }
430
+ return nextBase;
380
431
  }
381
432
  if (typeof delta.systemPrompt === 'string') next.systemPrompt = delta.systemPrompt;
382
433
  if (Array.isArray(delta.messages)) {
@@ -389,6 +440,13 @@ function applyRequestDelta(previous, delta = {}) {
389
440
  return next;
390
441
  }
391
442
 
443
+ export function reconstructDebugRawRequest(baseRawRequest, requestDelta) {
444
+ if (!requestDelta || !Object.prototype.hasOwnProperty.call(requestDelta, 'rawRequestDelta')) {
445
+ return baseRawRequest ?? null;
446
+ }
447
+ return applyRawRequestDelta(baseRawRequest ?? null, requestDelta.rawRequestDelta);
448
+ }
449
+
392
450
  function sessionRequestsDir(rootDir, sessionId) {
393
451
  if (sessionId) return join(rootDir, 'sessions', safeDirComponent(sessionId), 'debug', 'requests');
394
452
  return join(rootDir, 'debug', 'requests');
@@ -462,7 +520,7 @@ function expandTrace(trace) {
462
520
  ttfbMs: loop.ttfbMs || null,
463
521
  stopReason: loop.stopReason || null,
464
522
  at: loop.at || null,
465
- rawRequest: snapshot.rawRequest ?? null,
523
+ rawRequest: null,
466
524
  rawResponse: loop.rawResponse ?? null,
467
525
  requestDelta: loop.requestDelta || {},
468
526
  requestBase: trace.baseRequest || null,
@@ -503,7 +561,7 @@ function traceToLegacyRows(trace) {
503
561
  tool_calls_json: JSON.stringify(loop.toolCalls || []),
504
562
  usage_json: JSON.stringify(u),
505
563
  ttfb_ms: loop.ttfbMs || null,
506
- raw_request: typeof snapshot.rawRequest === 'string' ? snapshot.rawRequest : JSON.stringify(snapshot.rawRequest ?? null),
564
+ raw_request: null,
507
565
  raw_response: typeof loop.rawResponse === 'string' ? loop.rawResponse : JSON.stringify(loop.rawResponse ?? null),
508
566
  user_prompt: trace.userPrompt || '',
509
567
  };
@@ -674,8 +732,11 @@ export class DebugTrace {
674
732
  const snapshot = buildRequestSnapshot(info);
675
733
  const previousSnapshot = trace._lastSnapshot || this.#reconstructLastSnapshot(trace);
676
734
  if (!trace.baseRequest) {
677
- const rawRequestBaseDelta = buildRawRequestDelta(null, snapshot.rawRequest);
678
- trace.baseRequest = { ...snapshot, rawRequest: applyRawRequestDelta(null, rawRequestBaseDelta) };
735
+ trace.baseRequest = {
736
+ systemPrompt: snapshot.systemPrompt,
737
+ messages: Array.isArray(snapshot.messages) ? cloneJsonValue(snapshot.messages) : [],
738
+ rawRequest: null,
739
+ };
679
740
  }
680
741
  const loopIndex = (trace.loops || []).findIndex(l => l.turnRowId === turnId);
681
742
  const loop = {
@@ -717,11 +717,33 @@ export function broadcastLanguageChange(language) {
717
717
 
718
718
  /** Query timeout in ms — abort if LLM doesn't respond within this window */
719
719
  const QUERY_TIMEOUT_MS = 120_000;
720
+ const HIGH_REASONING_QUERY_TIMEOUT_MS = 300_000;
721
+
722
+ function isHighReasoningEffort(effort) {
723
+ const value = typeof effort === 'string' ? effort.trim().toLowerCase() : '';
724
+ return value === 'high' || value === 'xhigh' || value === 'max';
725
+ }
726
+
727
+ function queryTimeoutMsForSessionConfig(config = null) {
728
+ return isHighReasoningEffort(config?.modelEffort) ? HIGH_REASONING_QUERY_TIMEOUT_MS : QUERY_TIMEOUT_MS;
729
+ }
730
+
731
+ function queryTimeoutMsForSession(sessionId = null) {
732
+ if (!sessionId || !session) return queryTimeoutMsForSessionConfig(session?.config);
733
+ try {
734
+ return queryTimeoutMsForSessionConfig(resolveSessionConfig(session.config, loadSessionConfig(liveConfigRoot(), sessionId)));
735
+ } catch {
736
+ return queryTimeoutMsForSessionConfig(session?.config);
737
+ }
738
+ }
720
739
 
721
740
  /**
722
741
  * Secondary watchdog grace period (ms).
723
742
  *
724
- * After {@link QUERY_TIMEOUT_MS} of silence the per-VP `vpAbort` is fired.
743
+ * After the active query timeout of silence the per-VP `vpAbort` is fired.
744
+ * Normal turns use {@link QUERY_TIMEOUT_MS}; high-reasoning session turns use
745
+ * {@link HIGH_REASONING_QUERY_TIMEOUT_MS} because large tool-result arcs can
746
+ * legitimately spend longer before the provider emits the first SSE event.
725
747
  * That's enough on its own when adapters / tools cooperate with the
726
748
  * AbortSignal — the engine throws `AbortError`, runVpTurn's catch emits
727
749
  * `result{stopped:true}`, the driver `finally` emits `vp_typing_end`, and
@@ -741,7 +763,7 @@ const QUERY_TIMEOUT_MS = 120_000;
741
763
  *
742
764
  * The driver loop wraps `await runVpTurn(...)` in a Promise.race against
743
765
  * this grace-window timer. If runVpTurn doesn't return within
744
- * QUERY_TIMEOUT_MS + ESCALATE_AFTER_ABORT_MS, the driver forces its
766
+ * active query timeout + ESCALATE_AFTER_ABORT_MS, the driver forces its
745
767
  * `finally` block (vp_typing_end + group_message), emits a synthetic
746
768
  * `result{stopped:true}` so the frontend leaves its in-flight state,
747
769
  * and moves on. The hung tool promise leaks (JS lacks cooperative
@@ -4059,7 +4081,8 @@ function startSessionLoadInBackground({ sessionId = null, sessionMeta = null, pe
4059
4081
  */
4060
4082
  async function runVpTurnWithEscalation(args) {
4061
4083
  const { sessionId, vpId, turnId, threadId, thread } = args;
4062
- const deadlineMs = QUERY_TIMEOUT_MS + ESCALATE_AFTER_ABORT_MS;
4084
+ const queryTimeoutMs = queryTimeoutMsForSession(sessionId);
4085
+ const deadlineMs = queryTimeoutMs + ESCALATE_AFTER_ABORT_MS;
4063
4086
  await raceWithEscalation(runVpTurn(args), {
4064
4087
  deadlineMs,
4065
4088
  onEscalate: () => {
@@ -4213,14 +4236,15 @@ async function runVpTurn({ prompt, promptParts = null, sessionId, vpId, threadId
4213
4236
  }
4214
4237
 
4215
4238
  let queryTimer = null;
4239
+ const queryTimeoutMs = queryTimeoutMsForSession(sessionId);
4216
4240
  const resetQueryTimer = () => {
4217
4241
  if (queryTimer) clearTimeout(queryTimer);
4218
4242
  queryTimer = setTimeout(() => {
4219
4243
  if (!vpAbort.signal.aborted) {
4220
- console.error(`[Yeaft] query timeout after ${QUERY_TIMEOUT_MS / 1000}s of silence — aborting VP ${vpId}`);
4244
+ console.error(`[Yeaft] query timeout after ${queryTimeoutMs / 1000}s of silence — aborting VP ${vpId}`);
4221
4245
  try { vpAbort.abort(); } catch { /* best-effort */ }
4222
4246
  }
4223
- }, QUERY_TIMEOUT_MS);
4247
+ }, queryTimeoutMs);
4224
4248
  };
4225
4249
  resetQueryTimer();
4226
4250
 
@@ -6229,6 +6253,8 @@ export const __testHooks = {
6229
6253
  projectRuntimeCount() {
6230
6254
  return projectRuntimes.size;
6231
6255
  },
6256
+ queryTimeoutMsForSessionConfig,
6257
+ queryTimeoutMsForSession,
6232
6258
  seedQueuedVpTurn({ sessionId = 'session-test', vpId = 'vp-test', threadId = 'main', turnId = 'turn-test' } = {}) {
6233
6259
  const key = threadKey(sessionId, vpId, threadId);
6234
6260
  const inbox = vpInboxes.get(key) || [];