@yeaft/webchat-agent 0.1.988 → 0.1.990

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.988",
3
+ "version": "0.1.990",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -1104,6 +1104,7 @@ export class ConversationStore {
1104
1104
  const all = [...cold, ...hot].sort(compareMessagesBySeq);
1105
1105
  const after = all.filter((m) => {
1106
1106
  if (!m || m.sessionId !== sessionId) return false;
1107
+ if (m._reflection || m.internal || m.systemOnly || m.systemOnlyMessage) return false;
1107
1108
  const seq = parseSeqFromId(m.id);
1108
1109
  return Number.isFinite(seq) && seq > cutoff;
1109
1110
  });
package/yeaft/engine.js CHANGED
@@ -1827,6 +1827,7 @@ export class Engine {
1827
1827
  loopNumber: turnNumber,
1828
1828
  threadId,
1829
1829
  preview: String(item.preview || '').slice(0, 200),
1830
+ internal: Boolean(item.internal),
1830
1831
  };
1831
1832
  }
1832
1833
  }
@@ -2233,6 +2234,7 @@ export class Engine {
2233
2234
  loopNumber: turnNumber,
2234
2235
  threadId,
2235
2236
  preview: String(item.preview || '').slice(0, 200),
2237
+ internal: Boolean(item.internal),
2236
2238
  };
2237
2239
  }
2238
2240
  yield { type: 'turn_end', turnNumber, stopReason: 'user_append_continue', threadId };
@@ -143,6 +143,11 @@ export function createRouter(deps = {}) {
143
143
  role: 'assistant', // VP-authored — persists as assistant turn
144
144
  text: injectText,
145
145
  taskId: args.taskId ?? null,
146
+ // route_forward is already visible as the source VP's tool action.
147
+ // Persist the synthetic handoff for audit/dispatch, but keep it out
148
+ // of UI replay and future visible history so it doesn't render as a
149
+ // second assistant/user block after the target VP answers.
150
+ internal: true,
146
151
  meta: {
147
152
  synthetic: true,
148
153
  injectedBy: 'route_forward',
@@ -1032,15 +1032,23 @@ async function routeEnvelopeToVpThread(sessionId, vpId, envelope) {
1032
1032
 
1033
1033
  if (related) {
1034
1034
  const content = promptParts || prompt;
1035
- thread.pendingQueries.push({ content, preview: prompt, originalText: text, originalParts: Array.isArray(envelope?._promptParts) ? envelope._promptParts : null });
1035
+ const isForwardAppend = envelope?.msg?.meta?.injectedBy === 'route_forward';
1036
+ thread.pendingQueries.push({
1037
+ content,
1038
+ preview: prompt,
1039
+ originalText: text,
1040
+ originalParts: Array.isArray(envelope?._promptParts) ? envelope._promptParts : null,
1041
+ internal: isForwardAppend,
1042
+ });
1036
1043
  persistInboundMessageOnceByMsgId({
1037
1044
  msgId: envelope?.msg?.id,
1038
1045
  text,
1039
1046
  sessionId,
1040
1047
  threadId: visibleInboundThreadId(envelope, thread.threadId),
1041
- role: envelope?.msg?.meta?.injectedBy === 'route_forward' ? 'assistant' : 'user',
1048
+ role: isForwardAppend ? 'assistant' : 'user',
1042
1049
  speakerVpId: envelope?.msg?.meta?.senderVpId || envelope?.msg?.from || null,
1043
1050
  attachments: Array.isArray(envelope?.msg?.meta?.attachments) ? envelope.msg.meta.attachments : [],
1051
+ internal: isForwardAppend,
1044
1052
  });
1045
1053
  thread.updatedAt = Date.now();
1046
1054
  try {
@@ -1135,6 +1143,7 @@ function ensureDriverRunning(sessionId, vpId, threadId = 'main') {
1135
1143
  role: isForward ? 'assistant' : 'user',
1136
1144
  speakerVpId: senderVpId,
1137
1145
  attachments: Array.isArray(meta.attachments) ? meta.attachments : [],
1146
+ internal: isForward,
1138
1147
  });
1139
1148
  }
1140
1149
  } catch { /* never crash WS pipeline */ }
@@ -2254,7 +2263,7 @@ function handleEngineEvent(event, hctx) {
2254
2263
  break;
2255
2264
 
2256
2265
  case 'user_append':
2257
- if (hctx && Array.isArray(hctx.appendedUserPrompts) && event.preview) {
2266
+ if (hctx && Array.isArray(hctx.appendedUserPrompts) && event.preview && !event.internal) {
2258
2267
  hctx.appendedUserPrompts.push(String(event.preview));
2259
2268
  }
2260
2269
  sendSessionEvent({
@@ -3063,7 +3072,13 @@ async function runVpTurn({ prompt, promptParts = null, sessionId, vpId, threadId
3063
3072
  }
3064
3073
 
3065
3074
  // Turn completed — atomically append this VP's output to shared history.
3066
- appendTurnToSessionHistory(sessionId, threadId, vpId, [prompt, ...appendedUserPrompts], assistantTextParts, toolCallsAccum, toolResultsAccum, thinkingBlocksAccum);
3075
+ // route_forward handoff text is an internal trigger, already visible as
3076
+ // the source VP's tool action. Do not append it as a visible prompt for
3077
+ // the target VP turn; otherwise UI replay can show a trailing handoff
3078
+ // block after the target response.
3079
+ const inboundIsRouteForward = inboundEnvelope?.msg?.meta?.injectedBy === 'route_forward';
3080
+ const visiblePrompts = inboundIsRouteForward ? appendedUserPrompts : [prompt, ...appendedUserPrompts];
3081
+ appendTurnToSessionHistory(sessionId, threadId, vpId, visiblePrompts, assistantTextParts, toolCallsAccum, toolResultsAccum, thinkingBlocksAccum);
3067
3082
 
3068
3083
  sendSessionOutputFrame({
3069
3084
  type: 'assistant',
@@ -3268,11 +3283,11 @@ function appendTurnToSessionHistory(sessionId, threadId, vpId, prompts, assistan
3268
3283
  * refresh replay can render chips without leaking image source data into
3269
3284
  * the message body.
3270
3285
  *
3271
- * @param {{ msgId:string, text:string, sessionId:string, role?:string, speakerVpId?:string|null, attachments?:Array<object> }} args
3286
+ * @param {{ msgId:string, text:string, sessionId:string, role?:string, speakerVpId?:string|null, attachments?:Array<object>, internal?:boolean }} args
3272
3287
  * @returns {boolean} true if this call wrote the row, false if a prior
3273
3288
  * call already wrote it (dedup hit).
3274
3289
  */
3275
- function persistInboundMessageOnceByMsgId({ msgId, text, sessionId, threadId = 'main', role, speakerVpId, attachments }) {
3290
+ function persistInboundMessageOnceByMsgId({ msgId, text, sessionId, threadId = 'main', role, speakerVpId, attachments, internal = false }) {
3276
3291
  if (!session?.conversationStore) return false;
3277
3292
  // No msgId means no dedup key — caller is responsible for guarding.
3278
3293
  // Both call sites already do (`if (envMsgId && text)` and
@@ -3323,6 +3338,7 @@ function persistInboundMessageOnceByMsgId({ msgId, text, sessionId, threadId = '
3323
3338
  if (persistRole === 'assistant' && speakerVpId && typeof speakerVpId === 'string') {
3324
3339
  record.speakerVpId = speakerVpId;
3325
3340
  }
3341
+ if (internal) record.internal = true;
3326
3342
  if (persistRole === 'user' && Array.isArray(attachments) && attachments.length > 0) {
3327
3343
  record.attachments = attachments;
3328
3344
  }