@yeaft/webchat-agent 1.0.348 → 1.0.349

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.
Binary file
@@ -16,6 +16,6 @@
16
16
  </head>
17
17
  <body>
18
18
  <div id="app"></div>
19
- <script type="module" src="app.bundle.js?v=3b304fd7"></script>
19
+ <script type="module" src="app.bundle.js?v=9e91448e"></script>
20
20
  </body>
21
21
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "1.0.348",
3
+ "version": "1.0.349",
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",
@@ -430,6 +430,7 @@ function serializeMessage(msg) {
430
430
  // Defaults to 'main' for legacy messages (see migrate-messages-threadid.js).
431
431
  fm.push(`threadId: ${msg.threadId || 'main'}`);
432
432
  if (msg.turnId) fm.push(`turnId: ${msg.turnId}`);
433
+ if (msg.executionOrigin === 'route_forward') fm.push('executionOrigin: route_forward');
433
434
  if (msg.imageAssetAnchor) fm.push('imageAssetAnchor: true');
434
435
  // task-313: when a thread is merged into another, the messages keep
435
436
  // their original thread id in `sourceThreadId` so the UI can still
@@ -576,6 +577,9 @@ export function parseMessage(raw) {
576
577
  case 'tokens_est': msg.tokens_est = parseInt(value, 10); break;
577
578
  case 'threadId': msg.threadId = value; break;
578
579
  case 'turnId': msg.turnId = value; break;
580
+ case 'executionOrigin':
581
+ if (value === 'route_forward') msg.executionOrigin = value;
582
+ break;
579
583
  case 'imageAssetAnchor': msg.imageAssetAnchor = value === 'true'; break;
580
584
  case 'sourceThreadId': msg.sourceThreadId = value; break;
581
585
  case 'sessionId': msg.sessionId = value; break;
package/yeaft/engine.js CHANGED
@@ -1519,7 +1519,7 @@ export class Engine {
1519
1519
  return Boolean(this.#conversationStore) && !this.#config._readOnly;
1520
1520
  }
1521
1521
 
1522
- #conversationRecord(message, { sessionId, turnId, model, incomplete = false, stopReason = null } = {}) {
1522
+ #conversationRecord(message, { sessionId, turnId, model, incomplete = false, stopReason = null, executionOrigin = null } = {}) {
1523
1523
  const record = {
1524
1524
  role: message.role,
1525
1525
  content: typeof message.content === 'string'
@@ -1545,6 +1545,9 @@ export class Engine {
1545
1545
  record.foldedMessageIds = [...message.foldedMessageIds];
1546
1546
  }
1547
1547
  if (turnId && (message.role === 'assistant' || message.role === 'tool')) record.turnId = turnId;
1548
+ if (executionOrigin === 'route_forward' && (message.role === 'assistant' || message.role === 'tool')) {
1549
+ record.executionOrigin = executionOrigin;
1550
+ }
1548
1551
  if (this.#vpId && (message.role === 'assistant' || message.role === 'tool')) record.speakerVpId = this.#vpId;
1549
1552
  if (incomplete) record.incomplete = true;
1550
1553
  if (stopReason) record.stopReason = stopReason;
@@ -2117,6 +2120,9 @@ export class Engine {
2117
2120
  const runtimeThreadId = (typeof threadId === 'string' && threadId.trim())
2118
2121
  ? threadId.trim()
2119
2122
  : MAIN_THREAD_ID;
2123
+ const executionOrigin = inboundEnvelope?.msg?.meta?.injectedBy === 'route_forward'
2124
+ ? 'route_forward'
2125
+ : null;
2120
2126
  const queryTurnId = randomUUID();
2121
2127
  const queryStartedAt = Date.now();
2122
2128
  const userQuestionPreview = String(prompt || '').slice(0, 200);
@@ -2615,6 +2621,7 @@ export class Engine {
2615
2621
  model: currentModel,
2616
2622
  incomplete: true,
2617
2623
  stopReason: reason,
2624
+ executionOrigin,
2618
2625
  });
2619
2626
  };
2620
2627
  const toolCalls = [];
@@ -3363,6 +3370,7 @@ export class Engine {
3363
3370
  sessionId: runtimeSessionId,
3364
3371
  turnId: vpTurnId || queryTurnId,
3365
3372
  model: currentModel,
3373
+ executionOrigin,
3366
3374
  });
3367
3375
  if (persistedAssistantMessage) {
3368
3376
  assistantMsg._persistedMessageId = persistedAssistantMessage.id;
@@ -3687,6 +3695,7 @@ export class Engine {
3687
3695
  count: pairs.length,
3688
3696
  originalUserMsg: prompt,
3689
3697
  originatingTurnId: queryTurnId,
3698
+ executionOrigin,
3690
3699
  ready: false,
3691
3700
  result: null,
3692
3701
  error: null,
@@ -3973,6 +3982,7 @@ export class Engine {
3973
3982
  sessionId: runtimeSessionId,
3974
3983
  turnId: vpTurnId || queryTurnId,
3975
3984
  model: currentModel,
3985
+ executionOrigin,
3976
3986
  });
3977
3987
  if (persistedToolMessage) {
3978
3988
  toolMessage._persistedMessageId = persistedToolMessage.id;
@@ -4110,7 +4120,7 @@ export class Engine {
4110
4120
  batchStart,
4111
4121
  batchEnd,
4112
4122
  reflectionMessage,
4113
- { sessionId: runtimeSessionId, model: currentModel },
4123
+ { sessionId: runtimeSessionId, model: currentModel, executionOrigin },
4114
4124
  );
4115
4125
  if (durableRowsInRange && !persistedReflection) {
4116
4126
  throw new Error('T1 reflection could not publish its durable range replacement');
@@ -4303,7 +4313,10 @@ export class Engine {
4303
4313
  startIdx,
4304
4314
  endIdx,
4305
4315
  reflectionMessage,
4306
- context,
4316
+ {
4317
+ ...context,
4318
+ executionOrigin: info.executionOrigin === 'route_forward' ? 'route_forward' : null,
4319
+ },
4307
4320
  );
4308
4321
  if (durableRowsInRange && !persistedReflection) continue;
4309
4322
  // Mutate in place so caller's reference stays valid.
@@ -86,7 +86,8 @@ export function extractPriorPlan(messages, vpId) {
86
86
 
87
87
  /**
88
88
  * Return a copy of the messages array with engine-private metadata stripped
89
- * from every message. The serialisers (anthropic/openai-responses) read this;
89
+ * from every message. This includes durable UI-only execution provenance.
90
+ * The serialisers (anthropic/openai-responses) read this;
90
91
  * these fields are NEVER part of the wire payload. Cheap because we only
91
92
  * shallow-clone the messages that actually have private fields.
92
93
  *
@@ -99,9 +100,17 @@ export function stripMetaForWire(messages) {
99
100
  const out = messages.map(m => {
100
101
  if (m && typeof m === 'object'
101
102
  && ('_meta' in m || '_runtimeTurnId' in m || '_partialTurn' in m
102
- || '_persistedMessageId' in m || 'userAuthored' in m)) {
103
+ || '_persistedMessageId' in m || 'userAuthored' in m || 'executionOrigin' in m)) {
103
104
  mutated = true;
104
- const { _meta, _runtimeTurnId, _partialTurn, _persistedMessageId, userAuthored, ...rest } = m;
105
+ const {
106
+ _meta,
107
+ _runtimeTurnId,
108
+ _partialTurn,
109
+ _persistedMessageId,
110
+ userAuthored,
111
+ executionOrigin,
112
+ ...rest
113
+ } = m;
105
114
  return rest;
106
115
  }
107
116
  return m;