@yeaft/webchat-agent 0.1.970 → 0.1.972

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/yeaft/web-bridge.js +101 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.1.970",
3
+ "version": "0.1.972",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -150,7 +150,9 @@ const turnAbortCtrls = new Map();
150
150
 
151
151
  /**
152
152
  * Per-turn runtime ownership. Targeted thread aborts use this instead of
153
- * blindly aborting every turn controller in the process.
153
+ * blindly aborting every turn controller in the process. Queued VP turns are
154
+ * registered here before their AbortController exists so the VP-list Stop
155
+ * button can remove them from the inbox instead of becoming a no-op.
154
156
  * @type {Map<string, { sessionId: string, vpId: string, threadId: string, key: string }>}
155
157
  */
156
158
  const turnAbortMeta = new Map();
@@ -1062,6 +1064,7 @@ async function routeEnvelopeToVpThread(sessionId, vpId, envelope) {
1062
1064
  vpInboxes.set(key, inbox);
1063
1065
  }
1064
1066
  inbox.push({ envelope, turnId, thread });
1067
+ turnAbortMeta.set(turnId, { sessionId, vpId, threadId: thread.threadId, key });
1065
1068
 
1066
1069
  try {
1067
1070
  sendSessionEvent({
@@ -3354,6 +3357,63 @@ const _persistedUserMsgIds = new Set();
3354
3357
  *
3355
3358
  * @param {string[]} aborted — output array, mutated in place
3356
3359
  */
3360
+ function removeQueuedVpTurn(turnId) {
3361
+ const meta = turnAbortMeta.get(turnId);
3362
+ const keys = meta?.key ? [meta.key] : Array.from(vpInboxes.keys());
3363
+ let removed = false;
3364
+ for (const key of keys) {
3365
+ const inbox = vpInboxes.get(key);
3366
+ if (!Array.isArray(inbox) || inbox.length === 0) continue;
3367
+ const before = inbox.length;
3368
+ const kept = inbox.filter((entry) => entry?.turnId !== turnId);
3369
+ if (kept.length === before) continue;
3370
+ removed = true;
3371
+ inbox.length = 0;
3372
+ inbox.push(...kept);
3373
+ }
3374
+ return removed;
3375
+ }
3376
+
3377
+ function emitQueuedTurnAbort(meta, turnId) {
3378
+ if (!meta?.vpId) return;
3379
+ const sessionId = meta.sessionId || null;
3380
+ const vpId = meta.vpId;
3381
+ const threadId = meta.threadId || 'main';
3382
+ try {
3383
+ getVpStatusBroker().transition({
3384
+ sessionId,
3385
+ vpId,
3386
+ threadId,
3387
+ state: 'idle',
3388
+ turnId,
3389
+ messageCount: 0,
3390
+ });
3391
+ } catch (err) {
3392
+ console.warn('[Yeaft] queued VP abort status transition failed:', err?.message || err);
3393
+ }
3394
+ try {
3395
+ sendSessionEvent({
3396
+ type: 'vp_typing_end',
3397
+ sessionId,
3398
+ vpId,
3399
+ threadId,
3400
+ turnId,
3401
+ ts: Date.now(),
3402
+ }, { sessionId, vpId, threadId, turnId });
3403
+ } catch { /* never crash WS pipeline */ }
3404
+ try {
3405
+ sendSessionEvent({
3406
+ type: 'vp_turn_end',
3407
+ sessionId,
3408
+ vpId,
3409
+ threadId,
3410
+ turnId,
3411
+ reason: 'aborted',
3412
+ ts: Date.now(),
3413
+ }, { sessionId, vpId, threadId, turnId });
3414
+ } catch { /* never crash WS pipeline */ }
3415
+ }
3416
+
3357
3417
  function abortAllVpRuntime(aborted) {
3358
3418
  for (const [key, ctrl] of vpAborts) {
3359
3419
  try {
@@ -3450,17 +3510,21 @@ export function handleYeaftAbortTurn(msg = {}) {
3450
3510
  sendSessionEvent({ type: 'yeaft_turn_aborted', turnId: null, success: false });
3451
3511
  return;
3452
3512
  }
3513
+
3514
+ const meta = turnAbortMeta.get(turnId);
3453
3515
  const ctrl = turnAbortCtrls.get(turnId);
3516
+ let success = false;
3517
+
3454
3518
  if (ctrl && !ctrl.signal.aborted) {
3455
- try { ctrl.abort(); } catch { /* best-effort */ }
3456
- turnAbortCtrls.delete(turnId);
3457
- turnAbortMeta.delete(turnId);
3458
- sendSessionEvent({ type: 'yeaft_turn_aborted', turnId, success: true });
3459
- } else {
3460
- turnAbortCtrls.delete(turnId);
3461
- turnAbortMeta.delete(turnId);
3462
- sendSessionEvent({ type: 'yeaft_turn_aborted', turnId, success: false });
3519
+ try { ctrl.abort(); success = true; } catch { /* best-effort */ }
3520
+ } else if (removeQueuedVpTurn(turnId)) {
3521
+ success = true;
3522
+ emitQueuedTurnAbort(meta, turnId);
3463
3523
  }
3524
+
3525
+ turnAbortCtrls.delete(turnId);
3526
+ turnAbortMeta.delete(turnId);
3527
+ sendSessionEvent({ type: 'yeaft_turn_aborted', turnId, success });
3464
3528
  }
3465
3529
 
3466
3530
  /**
@@ -4460,3 +4524,31 @@ export async function handleYeaftMcpReload(msg = {}) {
4460
4524
  });
4461
4525
  broadcastMcpUpdated({ reason: 'reload', name: targetName, failures });
4462
4526
  }
4527
+
4528
+ export const __testHooks = {
4529
+ resetAbortState() {
4530
+ turnAbortCtrls.clear();
4531
+ turnAbortMeta.clear();
4532
+ vpAborts.clear();
4533
+ vpInboxes.clear();
4534
+ },
4535
+ seedQueuedVpTurn({ sessionId = 'session-test', vpId = 'vp-test', threadId = 'main', turnId = 'turn-test' } = {}) {
4536
+ const key = threadKey(sessionId, vpId, threadId);
4537
+ const inbox = vpInboxes.get(key) || [];
4538
+ inbox.push({ envelope: { msg: { id: `${turnId}-msg` } }, turnId, thread: { threadId, title: '', messageIds: [] } });
4539
+ vpInboxes.set(key, inbox);
4540
+ turnAbortMeta.set(turnId, { sessionId, vpId, threadId, key });
4541
+ return { key, turnId };
4542
+ },
4543
+ seedRunningVpTurn({ sessionId = 'session-test', vpId = 'vp-test', threadId = 'main', turnId = 'turn-test' } = {}) {
4544
+ const key = threadKey(sessionId, vpId, threadId);
4545
+ const ctrl = new AbortController();
4546
+ vpAborts.set(key, ctrl);
4547
+ turnAbortCtrls.set(turnId, ctrl);
4548
+ turnAbortMeta.set(turnId, { sessionId, vpId, threadId, key });
4549
+ return { key, turnId, ctrl };
4550
+ },
4551
+ queuedTurnIds() {
4552
+ return Array.from(vpInboxes.values()).flatMap((inbox) => Array.isArray(inbox) ? inbox.map((entry) => entry.turnId) : []);
4553
+ },
4554
+ };