@yeaft/webchat-agent 1.0.259 → 1.0.261

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.259",
3
+ "version": "1.0.261",
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",
@@ -326,6 +326,17 @@ function parseAskUserResult(toolCall, toolResult) {
326
326
  };
327
327
  }
328
328
 
329
+ function projectedResponseKind(row) {
330
+ if (row?.responseKind === 'progress' || row?.responseKind === 'result') {
331
+ return row.responseKind;
332
+ }
333
+ if (row?.incomplete === true || row?.stopReason === 'aborted' || row?.stopReason === 'error') {
334
+ return 'progress';
335
+ }
336
+ if (row?.stopReason === 'end_turn') return 'result';
337
+ return null;
338
+ }
339
+
329
340
  export function projectVisibleSessionMessages(messages) {
330
341
  const rows = Array.isArray(messages) ? messages : [];
331
342
  const toolResults = new Map();
@@ -342,7 +353,10 @@ export function projectVisibleSessionMessages(messages) {
342
353
  if (row.role !== 'assistant' || !Array.isArray(row.toolCalls) || row.toolCalls.length === 0) {
343
354
  if (row.role === 'assistant' && !row.content && !row.attachments && !row.images
344
355
  && !row.todos && !row.toolSummaryCount && !row.askUserResults) continue;
345
- visible.push(row);
356
+ const responseKind = row.role === 'assistant' ? projectedResponseKind(row) : null;
357
+ visible.push(responseKind && row.responseKind !== responseKind
358
+ ? { ...row, responseKind }
359
+ : row);
346
360
  continue;
347
361
  }
348
362
 
@@ -355,7 +369,11 @@ export function projectVisibleSessionMessages(messages) {
355
369
  if (result) askUserResults.push(result);
356
370
  else omittedToolCount += 1;
357
371
  }
358
- const { toolCalls, ...rest } = row;
372
+ const { toolCalls, ...rowWithoutToolCalls } = row;
373
+ const responseKind = projectedResponseKind(rowWithoutToolCalls);
374
+ const rest = responseKind && rowWithoutToolCalls.responseKind !== responseKind
375
+ ? { ...rowWithoutToolCalls, responseKind }
376
+ : rowWithoutToolCalls;
359
377
  const todos = latestTodoWriteSnapshot(toolCalls);
360
378
  const projected = {
361
379
  ...rest,
@@ -413,6 +431,7 @@ function serializeMessage(msg) {
413
431
  if (typeof msg.userAuthored === 'boolean') fm.push(`userAuthored: ${msg.userAuthored}`);
414
432
  if (msg.incomplete) fm.push('incomplete: true');
415
433
  if (msg.stopReason) fm.push(`stopReason: ${msg.stopReason}`);
434
+ if (msg.responseKind === 'progress' || msg.responseKind === 'result') fm.push(`responseKind: ${msg.responseKind}`);
416
435
  // Session attribution: when a VP authors an assistant turn (either
417
436
  // its own reply or a route_forward injection from another VP), stamp
418
437
  // the speaker so the UI can render the message on the correct VP track.
@@ -551,6 +570,9 @@ export function parseMessage(raw) {
551
570
  case 'userAuthored': msg.userAuthored = value === 'true'; break;
552
571
  case 'incomplete': msg.incomplete = value === 'true'; break;
553
572
  case 'stopReason': msg.stopReason = value; break;
573
+ case 'responseKind':
574
+ if (value === 'progress' || value === 'result') msg.responseKind = value;
575
+ break;
554
576
  case 'speakerVpId': msg.speakerVpId = value; break;
555
577
  case 'quoteB64':
556
578
  try {
package/yeaft/engine.js CHANGED
@@ -1403,6 +1403,9 @@ export class Engine {
1403
1403
  if (message._reflection) record._reflection = true;
1404
1404
  if (message.role === 'user') record.userAuthored = message.userAuthored === true;
1405
1405
  if (message.internal === true) record.internal = true;
1406
+ if (message.responseKind === 'progress' || message.responseKind === 'result') {
1407
+ record.responseKind = message.responseKind;
1408
+ }
1406
1409
  if (Array.isArray(message.foldedMessageIds) && message.foldedMessageIds.length > 0) {
1407
1410
  record.foldedMessageIds = [...message.foldedMessageIds];
1408
1411
  }
@@ -2292,6 +2295,7 @@ export class Engine {
2292
2295
  let fullResponseText = '';
2293
2296
  let displayImageAnchorMessage = null;
2294
2297
  let lastPersistedAssistantMessage = null;
2298
+ let lastPersistedAssistantTextMessage = null;
2295
2299
  let currentModel = this.#config.model;
2296
2300
  let cumulativeInputTokens = 0;
2297
2301
  let cumulativeOutputTokens = 0;
@@ -2368,7 +2372,7 @@ export class Engine {
2368
2372
  const persistIncompleteAssistantOnce = (reason) => {
2369
2373
  if (incompleteAssistantPersisted || !responseText) return null;
2370
2374
  incompleteAssistantPersisted = true;
2371
- return this.#persistConversationMessage({ role: 'assistant', content: responseText }, {
2375
+ return this.#persistConversationMessage({ role: 'assistant', content: responseText, responseKind: 'progress' }, {
2372
2376
  sessionId: runtimeSessionId,
2373
2377
  turnId: vpTurnId || queryTurnId,
2374
2378
  model: currentModel,
@@ -2935,7 +2939,7 @@ export class Engine {
2935
2939
  // yielding any post-stream diagnostics. A consumer may stop iterating at
2936
2940
  // any yield; persistence therefore cannot wait for turn_end or even the
2937
2941
  // debug `loop` event below.
2938
- const assistantMsg = { role: 'assistant', content: responseText };
2942
+ const assistantMsg = { role: 'assistant', content: responseText, responseKind: 'progress' };
2939
2943
  if (toolCalls.length > 0) {
2940
2944
  assistantMsg.toolCalls = toolCalls.map(tc => ({
2941
2945
  id: tc.id,
@@ -2987,6 +2991,9 @@ export class Engine {
2987
2991
  if (assistantMsg.imageAssetAnchor) displayImageAnchorMessage = persistedAssistantMessage;
2988
2992
  lastPersistedAssistantMessage = persistedAssistantMessage;
2989
2993
  }
2994
+ if (responseText.trim() && persistedAssistantMessage) {
2995
+ lastPersistedAssistantTextMessage = persistedAssistantMessage;
2996
+ }
2990
2997
  if (previousImageAnchorMessage && displayImageAnchorMessage === null && !persistedAssistantMessage) {
2991
2998
  const restored = this.#conversationStore.update(previousImageAnchorMessage, { imageAssetAnchor: true });
2992
2999
  displayImageAnchorMessage = restored || null;
@@ -3197,6 +3204,15 @@ export class Engine {
3197
3204
  if (pendingSubAgentNotifs.length > 0) {
3198
3205
  acknowledgePendingNotifications(notifScope, pendingSubAgentNotifs.map(n => n.id));
3199
3206
  }
3207
+ if (stopReason === 'end_turn'
3208
+ && lastPersistedAssistantTextMessage
3209
+ && typeof this.#conversationStore?.update === 'function') {
3210
+ const resultMessage = this.#conversationStore.update(lastPersistedAssistantTextMessage, {
3211
+ responseKind: 'result',
3212
+ stopReason,
3213
+ });
3214
+ if (resultMessage) lastPersistedAssistantTextMessage = resultMessage;
3215
+ }
3200
3216
  yield { type: 'turn_end', turnNumber, stopReason, threadId, terminal: true };
3201
3217
 
3202
3218
  // Message durability is handled incrementally before this terminal
@@ -1189,6 +1189,7 @@ function projectPersistedToHistoryEntry(m, { includeReflections = false } = {})
1189
1189
  entry.threadId = m.threadId || m.turnId || 'main';
1190
1190
  if (m.turnId) entry.turnId = m.turnId;
1191
1191
  if (m.imageAssetAnchor) entry.imageAssetAnchor = true;
1192
+ if (m.responseKind === 'progress' || m.responseKind === 'result') entry.responseKind = m.responseKind;
1192
1193
  if (m.sessionId) entry.sessionId = m.sessionId;
1193
1194
  if (m.clientMessageId) entry.clientMessageId = m.clientMessageId;
1194
1195
  if (m.speakerVpId) entry.speakerVpId = m.speakerVpId;
@@ -1308,6 +1309,7 @@ function projectVisibleHistoryChunkMessages(messages = []) {
1308
1309
  ...(m.quote ? { quote: m.quote } : {}),
1309
1310
  ...(Array.isArray(m.images) && m.images.length > 0 ? { images: m.images } : {}),
1310
1311
  ...(m.speakerVpId ? { speakerVpId: m.speakerVpId } : {}),
1312
+ ...(m.responseKind === 'progress' || m.responseKind === 'result' ? { responseKind: m.responseKind } : {}),
1311
1313
  ...(Array.isArray(m.todos) ? { todos: m.todos } : {}),
1312
1314
  ...(Array.isArray(m.askUserResults) && m.askUserResults.length > 0 ? { askUserResults: m.askUserResults } : {}),
1313
1315
  ...(Number.isFinite(m.toolSummaryCount) && m.toolSummaryCount > 0
@@ -3785,6 +3787,12 @@ function handleEngineEvent(event, hctx) {
3785
3787
  }
3786
3788
  break;
3787
3789
  }
3790
+ if (event.stopReason === 'error') {
3791
+ if (typeof hctx.markEngineTerminal === 'function') {
3792
+ hctx.markEngineTerminal('error', hctx.lastEngineErrorDetail || event.detail || null);
3793
+ }
3794
+ break;
3795
+ }
3788
3796
  // route_forward is different: the tool has handed control to another
3789
3797
  // VP and Engine.query will not stream more text for this VP. Settle the
3790
3798
  // current VP immediately so the roster row does not sit on "thinking"
@@ -4038,6 +4046,7 @@ function handleEngineEvent(event, hctx) {
4038
4046
 
4039
4047
  case 'error': {
4040
4048
  const errMsg = event.error?.message || 'Unknown error';
4049
+ hctx.lastEngineErrorDetail = { message: errMsg };
4041
4050
  sendSessionEvent({
4042
4051
  type: 'error',
4043
4052
  message: errMsg,
@@ -5141,6 +5150,17 @@ async function runVpTurn({ prompt, promptParts = null, sessionId, vpId, threadId
5141
5150
  finishAbortedTurn(engineTerminalDetail);
5142
5151
  return;
5143
5152
  }
5153
+ if (engineTerminalReason === 'error') {
5154
+ turnEndReason = 'errored';
5155
+ turnEndDetail = engineTerminalDetail;
5156
+ flushStreamTextBatch(handlerCtx, envelope, { resetImmediate: true });
5157
+ sendSessionOutputFrame({
5158
+ type: 'result',
5159
+ result_text: '',
5160
+ is_error: true,
5161
+ }, envelope);
5162
+ return;
5163
+ }
5144
5164
 
5145
5165
  flushStreamTextBatch(handlerCtx, envelope, { resetImmediate: true });
5146
5166