@tea-agent/loop-agent 0.43.0-next.11 → 0.43.0-next.12

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 (76) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/dist/build-stamp.json +3 -3
  3. package/dist/commands/client-recovery.js +20 -55
  4. package/dist/shared/pi-context-pressure/extension.js +26 -16
  5. package/dist/shared/pi-provider-recovery.js +182 -0
  6. package/dist/shared/pi-retry-settings.js +40 -0
  7. package/dist/worker/console/chat/pi-runtime.js +7 -5
  8. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-CTSK0xbU.js → abnfDiagram-N423BO3Z-BEg5712w.js} +1 -1
  9. package/dist/worker/console/static/assets/{arc-_2MfctFP.js → arc-CkQvaBGZ.js} +1 -1
  10. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DsDe2wTg.js → architectureDiagram-T3A2C74G-BrdwFNJ7.js} +1 -1
  11. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-Dtz_779U.js → blockDiagram-VBNYF7ZC-M_LjP2kx.js} +1 -1
  12. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-LaULi-gw.js → c4Diagram-5PPSVZJV-Bwr5Dtjc.js} +1 -1
  13. package/dist/worker/console/static/assets/channel-CKmyKgY-.js +1 -0
  14. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-DuHBqqX8.js → chunk-2GRJ4B5K-CDc44stW.js} +1 -1
  15. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-BZgZs1dn.js → chunk-2Q5K7J3B-DZELwsTt.js} +1 -1
  16. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-tzwmlIO_.js → chunk-5RXB4S5H-D7YI6SWX.js} +1 -1
  17. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CeYrLkSn.js → chunk-5VM5RSS4-Bc46bEyj.js} +1 -1
  18. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-C92cgilm.js → chunk-6Q2QTUOP-Bj4vc-Dp.js} +1 -1
  19. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-XOXpGC0F.js → chunk-GF5L2VYU-DRi-0nDX.js} +1 -1
  20. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-CGHbJyCH.js → chunk-JWPE2WC7-Dr-wFY60.js} +1 -1
  21. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-Bs2-LTzf.js → chunk-KBJHAD2P-CqIHE_D7.js} +1 -1
  22. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-rN7up7E4.js → chunk-RYQCIY6F-C-Dl8wzk.js} +1 -1
  23. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-B_fWjKpR.js → chunk-XXDRQBXY-CV4Mydz3.js} +1 -1
  24. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-D8Wc3jJP.js +1 -0
  25. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-D8Wc3jJP.js +1 -0
  26. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-DE3SdJvB.js → cose-bilkent-JH36ORCC-C3MamSMJ.js} +1 -1
  27. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-B-4yuRAX.js → cynefin-VYW2F7L2-zo_Jy2sf.js} +1 -1
  28. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-TiCPFuO8.js → cynefinDiagram-MW4NZA55-BB2PN1AG.js} +1 -1
  29. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-7crzLMzA.js → dagre-VZM6K2ZE-BimYueoi.js} +1 -1
  30. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Cvr-x6Fd.js → diagram-7IWD3JNH-Di1ObJRS.js} +1 -1
  31. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-BzcRHUjH.js → diagram-B4RE2ZJO-C83l-IJx.js} +1 -1
  32. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-BjkAXilh.js → diagram-LBJQPF4R-B3S3fOpm.js} +1 -1
  33. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-BkzyATK7.js → diagram-Q27KOJAE-urW_Zhe7.js} +1 -1
  34. package/dist/worker/console/static/assets/{diagram-UB23O5K3-rZuTarYP.js → diagram-UB23O5K3-nvIbCwF6.js} +1 -1
  35. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-CAYENaN1.js → ebnfDiagram-BXEA7PRR-DG6O3KpP.js} +1 -1
  36. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DCEam0Uq.js → erDiagram-JOGREHBK-DpXdtdsj.js} +1 -1
  37. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-e_dH4wVO.js → flowDiagram-UKHOOZJN-DvXWHrND.js} +1 -1
  38. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CvZa2zOK.js → ganttDiagram-PKOTCBZU-6F7SbvUS.js} +1 -1
  39. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-CbT5TuKp.js → gitGraphDiagram-DS77QQ5N-D1tZnFec.js} +1 -1
  40. package/dist/worker/console/static/assets/{index-CUPeoE8W.js → index-CWarwRqc.js} +94 -94
  41. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-BWzHQa_Z.js → infoDiagram-6WML65LV-HN4qwJ2M.js} +1 -1
  42. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-BTjlovtU.js → ishikawaDiagram-WSZJBQD7-OJ4qwDRo.js} +1 -1
  43. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DkvQoCc1.js → journeyDiagram-NVQOT4AX-CJZ6giQk.js} +1 -1
  44. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CHSzGi3W.js → kanban-definition-27J2QSJJ-co60m19S.js} +1 -1
  45. package/dist/worker/console/static/assets/{linear-DZMkTCWH.js → linear-D1KeFqZY.js} +1 -1
  46. package/dist/worker/console/static/assets/{mermaid.core-BIMhty7A.js → mermaid.core-i7dDGqov.js} +5 -5
  47. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-Dx-tyMl3.js → mindmap-definition-FAOFIHXS-1SkQxBh1.js} +1 -1
  48. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-C6G34rbY.js → pegDiagram-VL7TDLO6-B10dhI-Y.js} +1 -1
  49. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CHK9XOfm.js → pieDiagram-7S7Q4E2Y-CiUKUo6j.js} +1 -1
  50. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CDARkI0w.js → quadrantDiagram-CIZ2JOQS-C5vH4LXZ.js} +1 -1
  51. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-DiuUe0ZQ.js → railroadDiagram-AXF67PYL-5nlWOCEj.js} +1 -1
  52. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-RE61UR56.js → requirementDiagram-LRYGKXZP-CILd6QS3.js} +1 -1
  53. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-KDNLHMEG.js → sankeyDiagram-W5VNT64P-Dfp18ZO2.js} +1 -1
  54. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-CeVgXnD9.js → sequenceDiagram-SI44F4Z6-B9pkswBi.js} +1 -1
  55. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-CXwGyjjG.js → sizeCapture-X5ZJPWSS-C1-eSgx4.js} +1 -1
  56. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-BjTfozab.js → stateDiagram-OKZ733FA-COJsOoAF.js} +1 -1
  57. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Bpfb0q5s.js +1 -0
  58. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-BaXOB8GT.js → swimlanes-SLNWSIFB-Dv5NXJUM.js} +2 -2
  59. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BEwGGabW.js +8 -0
  60. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-C3r2hqUU.js → timeline-definition-Z64GVDOM-DRdrZ7eR.js} +1 -1
  61. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-MWKgl1S9.js → vennDiagram-T6HMQDX7-0-2TlwCj.js} +1 -1
  62. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-C9iFcZPk.js → wardleyDiagram-T6FBY63Y-3-o3q92P.js} +1 -1
  63. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-DqnM24Jp.js → xychartDiagram-ELKLHX3M-DSgOgdUi.js} +1 -1
  64. package/dist/worker/console/static/index.html +1 -1
  65. package/dist/worker/console/static-src/operator-chat/composer-session-ledger.js +192 -0
  66. package/dist/worker/console/static-src/operator-chat/pending-user-message.js +9 -1
  67. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +78 -35
  68. package/dist/worker/console/static-src/operator-chat/useChatStream.js +145 -70
  69. package/dist/worker/console/static-src/operator-chat/useComposer.js +182 -69
  70. package/docs/architecture/runtime-boundaries.md +3 -3
  71. package/package.json +1 -1
  72. package/dist/worker/console/static/assets/channel-CqS4yBWT.js +0 -1
  73. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DVOxNOq0.js +0 -1
  74. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DVOxNOq0.js +0 -1
  75. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BXoYsyr2.js +0 -1
  76. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DfKliS4e.js +0 -8
@@ -180,7 +180,7 @@ function mergeCompactionMessages(messages, compactions) {
180
180
  export function useChatSessions(params) {
181
181
  const { origin, refs, setSession, thread, composer, browser, setError, setNotice, onCompactionChange, setProcessOpen, setAutoScroll, } = params;
182
182
  const { applyActivatedSession, resetForNewSession, setMessages } = thread;
183
- const { setInput, setPendingImages } = composer;
183
+ const { seedSession, releaseSession } = composer;
184
184
  const { setPreviews, setActivePreviewPath } = browser;
185
185
  const { workspaceId, blocked } = params;
186
186
  const workspaceQuery = workspaceId
@@ -198,6 +198,9 @@ export function useChatSessions(params) {
198
198
  return owner ? `?workspaceId=${encodeURIComponent(owner)}` : "";
199
199
  }, [refs]);
200
200
  const [sessionList, setSessionList] = useState([]);
201
+ const [selectedSession, setSelectedSession] = useState(null);
202
+ const selectedSessionRef = useRef(null);
203
+ selectedSessionRef.current = selectedSession;
201
204
  const sessionListRef = useRef([]);
202
205
  const pendingDeletedIdsRef = useRef(new Set());
203
206
  const pendingCreatedRowsRef = useRef(new Map());
@@ -240,8 +243,6 @@ export function useChatSessions(params) {
240
243
  refs.settledTurnIdsRef.current = new Set();
241
244
  refs.activeTurnIdRef.current = null;
242
245
  resetForNewSession();
243
- setInput("");
244
- setPendingImages([]);
245
246
  setPreviews([]);
246
247
  setActivePreviewPath(null);
247
248
  setProcessOpen(false);
@@ -249,8 +250,6 @@ export function useChatSessions(params) {
249
250
  }, [
250
251
  refs,
251
252
  resetForNewSession,
252
- setInput,
253
- setPendingImages,
254
253
  setPreviews,
255
254
  setActivePreviewPath,
256
255
  setProcessOpen,
@@ -301,7 +300,15 @@ export function useChatSessions(params) {
301
300
  const activeSession = refs.sessionRef.current?.sessionId === sessionId
302
301
  ? refs.sessionRef.current
303
302
  : null;
304
- const activeWorkspaceId = activeSession?.workspaceId ?? workspaceId;
303
+ const selectedForDelete = selectedSessionRef.current?.sessionId === sessionId
304
+ ? selectedSessionRef.current
305
+ : null;
306
+ const deletingIdentity = selectedForDelete ??
307
+ activeSession ?? {
308
+ sessionId,
309
+ ...(removed.workspaceId ? { workspaceId: removed.workspaceId } : {}),
310
+ };
311
+ const activeWorkspaceId = deletingIdentity.workspaceId ?? workspaceId;
305
312
  const deletingRow = {
306
313
  ...removed,
307
314
  optimisticState: "deleting",
@@ -329,7 +336,7 @@ export function useChatSessions(params) {
329
336
  setSessionList(restored);
330
337
  };
331
338
  let deletionSelectionVersion = null;
332
- if (activeSession) {
339
+ if (activeSession || selectedForDelete) {
333
340
  deletionSelectionVersion = ++sessionSelectionVersionRef.current;
334
341
  refs.sessionRef.current = null;
335
342
  resetActiveProjection();
@@ -337,10 +344,10 @@ export function useChatSessions(params) {
337
344
  forgetActiveChatSession(window.localStorage, activeWorkspaceId, sessionId);
338
345
  }
339
346
  const restoreActiveSession = async () => {
340
- if (activeSession &&
347
+ if ((activeSession || selectedForDelete) &&
341
348
  deletionSelectionVersion === sessionSelectionVersionRef.current &&
342
349
  !refs.sessionRef.current) {
343
- await activateSessionRef.current?.(activeSession.sessionId, activeSession.workspaceId);
350
+ await activateSessionRef.current?.(deletingIdentity.sessionId, deletingIdentity.workspaceId);
344
351
  }
345
352
  };
346
353
  try {
@@ -362,6 +369,10 @@ export function useChatSessions(params) {
362
369
  return false;
363
370
  }
364
371
  titleWatcherRef.current?.cancel(sessionId);
372
+ if (deletionSelectionVersion === sessionSelectionVersionRef.current &&
373
+ selectedSessionRef.current?.sessionId === sessionId)
374
+ setSelectedSession(null);
375
+ releaseSession(deletingIdentity);
365
376
  void refreshSessionList().finally(() => {
366
377
  pendingDeletedIdsRef.current.delete(sessionId);
367
378
  deleteRequestVersionsRef.current.delete(sessionId);
@@ -379,6 +390,7 @@ export function useChatSessions(params) {
379
390
  refs,
380
391
  setSession,
381
392
  resetActiveProjection,
393
+ releaseSession,
382
394
  refreshSessionList,
383
395
  setError,
384
396
  sessionOwnerQuery,
@@ -388,25 +400,45 @@ export function useChatSessions(params) {
388
400
  const active = refs.sessionRef.current;
389
401
  const listed = sessionListRef.current.find((item) => item.sessionId === sessionId);
390
402
  const scope = ownerWorkspaceId ?? listed?.workspaceId ?? active?.workspaceId ?? workspaceId;
403
+ const identity = {
404
+ sessionId,
405
+ ...(scope ? { workspaceId: scope } : {}),
406
+ };
391
407
  forgetActiveChatSession(window.localStorage, scope, sessionId);
408
+ releaseSession(identity);
392
409
  pendingDeletedIdsRef.current.add(sessionId);
393
410
  const { next } = removeSessionById(sessionListRef.current, sessionId);
394
411
  sessionListRef.current = next;
395
412
  setSessionList(next);
396
- if (active?.sessionId === sessionId) {
413
+ if (active?.sessionId === sessionId ||
414
+ selectedSessionRef.current?.sessionId === sessionId) {
397
415
  sessionSelectionVersionRef.current += 1;
398
416
  refs.sessionRef.current = null;
417
+ setSelectedSession(null);
399
418
  resetActiveProjection();
400
419
  setSession(null);
401
420
  }
402
421
  void refreshSessionList();
403
- }, [refs, resetActiveProjection, setSession, workspaceId, refreshSessionList]);
422
+ }, [refs, releaseSession, resetActiveProjection, setSession, workspaceId, refreshSessionList]);
404
423
  const activateSession = useCallback(async (sessionId, ownerWorkspaceId) => {
405
424
  const selectionVersion = ++sessionSelectionVersionRef.current;
425
+ const listedOwner = sessionListRef.current.find((item) => item.sessionId === sessionId)?.workspaceId;
426
+ const requestedOwner = ownerWorkspaceId ?? listedOwner;
427
+ const requested = {
428
+ sessionId,
429
+ ...(requestedOwner ? { workspaceId: requestedOwner } : {}),
430
+ };
431
+ // DSH-style staging: selection identity changes synchronously; detail
432
+ // hydration follows. The previous session loses send authority now.
433
+ setSelectedSession(requested);
434
+ resetActiveProjection({ keepStreams: true });
435
+ refs.sessionRef.current = null;
436
+ setSession(null);
437
+ setError(null);
406
438
  // AC-002: activation targets the session's durable owning workspace —
407
439
  // never the global active workspace (no cross-workspace leak when
408
440
  // Operate/Inspect switch their scope while a session stays open).
409
- const ownerQuery = sessionOwnerQuery(ownerWorkspaceId);
441
+ const ownerQuery = sessionOwnerQuery(requested.workspaceId);
410
442
  try {
411
443
  const res = await fetch(`${origin}/api/operator/v1/chat/sessions/${encodeURIComponent(sessionId)}${ownerQuery}`, { credentials: "include" });
412
444
  const body = (await res.json());
@@ -427,7 +459,7 @@ export function useChatSessions(params) {
427
459
  // AC-002: session switch (same or cross workspace) never aborts other
428
460
  // sessions' background request controllers; the single /events owner is
429
461
  // re-bound by useChatStream's session effect.
430
- resetActiveProjection({ keepStreams: true });
462
+ setSelectedSession(next);
431
463
  refs.sessionRef.current = next;
432
464
  refs.activeTurnIdRef.current = body.session.activeTurnId ?? null;
433
465
  const visuallyStopped = body.session.turnPhase === "stopping";
@@ -447,9 +479,8 @@ export function useChatSessions(params) {
447
479
  refs.assistantIdRef.current = null;
448
480
  refs.abortRef.current =
449
481
  refs.sessionAbortControllersRef.current.get(sessionId) ?? null;
450
- setPendingImages([]);
451
482
  setPreviews([]);
452
- setInput(body.session.composerDraft ?? "");
483
+ seedSession(next, body.session.composerDraft ?? "");
453
484
  const hydrated = hydrateSessionThread(body.session.messages ?? []);
454
485
  hydrated.messages = mergeCompactionMessages(hydrated.messages, body.compactions);
455
486
  applyActivatedSession({
@@ -482,9 +513,8 @@ export function useChatSessions(params) {
482
513
  origin,
483
514
  refs,
484
515
  setSession,
485
- setPendingImages,
486
516
  setPreviews,
487
- setInput,
517
+ seedSession,
488
518
  applyActivatedSession,
489
519
  resetActiveProjection,
490
520
  setActivePreviewPath,
@@ -518,9 +548,7 @@ export function useChatSessions(params) {
518
548
  }
519
549
  const watchSessionTitle = useCallback((sessionId) => titleWatcherRef.current?.startIfEligible(sessionId), []);
520
550
  useEffect(() => {
521
- titleWatcherRef.current?.reconcile(sessionList
522
- .filter((item) => item.state !== "archived" && !item.title)
523
- .map((item) => item.sessionId));
551
+ titleWatcherRef.current?.reconcile(sessionList.flatMap((item) => item.state !== "archived" && !item.title ? [item.sessionId] : []));
524
552
  }, [sessionList]);
525
553
  useEffect(() => () => titleWatcherRef.current?.dispose(), []);
526
554
  // Browser refresh/workspace-switch recovery: selection is client state keyed
@@ -537,15 +565,22 @@ export function useChatSessions(params) {
537
565
  setSession(null);
538
566
  const savedSessionId = readActiveChatSessionId(window.localStorage, workspaceId);
539
567
  if (!savedSessionId) {
568
+ setSelectedSession(null);
540
569
  // Keep the per-workspace map intact, but clear the legacy global pointer
541
570
  // so Operate/Models cannot link a session from another workspace.
542
571
  forgetActiveChatSession(window.localStorage, workspaceId);
543
572
  return;
544
573
  }
574
+ setSelectedSession({
575
+ sessionId: savedSessionId,
576
+ ...(workspaceId ? { workspaceId } : {}),
577
+ });
545
578
  void (async () => {
546
579
  try {
547
580
  const res = await fetch(`${origin}/api/operator/v1/chat/sessions/${encodeURIComponent(savedSessionId)}${workspaceQuery}`, { credentials: "include" });
548
581
  if (!res.ok) {
582
+ if (selectionVersion === sessionSelectionVersionRef.current)
583
+ setSelectedSession(null);
549
584
  forgetActiveChatSession(window.localStorage, workspaceId, savedSessionId);
550
585
  return;
551
586
  }
@@ -557,6 +592,7 @@ export function useChatSessions(params) {
557
592
  return;
558
593
  const restored = projectActiveChatSession(body.session, workspaceId);
559
594
  refs.sessionGenerationRef.current += 1;
595
+ setSelectedSession(restored);
560
596
  refs.sessionRef.current = restored;
561
597
  // Do not seed Last-Event-ID here: session messages are restored
562
598
  // from the durable store, but tool/gate/operation projections live
@@ -577,8 +613,8 @@ export function useChatSessions(params) {
577
613
  refs.settledTurnIdsRef.current = new Set();
578
614
  refs.lastEventIdRef.current = null;
579
615
  setSession(restored);
616
+ seedSession(restored, body.session.composerDraft ?? "");
580
617
  rememberActiveChatSession(window.localStorage, restored.workspaceId ?? workspaceId, restored.sessionId);
581
- setInput(body.session.composerDraft ?? "");
582
618
  const hydrated = hydrateSessionThread(body.session.messages ?? []);
583
619
  hydrated.messages = mergeCompactionMessages(hydrated.messages, body.compactions);
584
620
  applyActivatedSession({
@@ -610,7 +646,7 @@ export function useChatSessions(params) {
610
646
  workspaceQuery,
611
647
  refs,
612
648
  setSession,
613
- setInput,
649
+ seedSession,
614
650
  applyActivatedSession,
615
651
  blocked,
616
652
  resetActiveProjection,
@@ -622,7 +658,7 @@ export function useChatSessions(params) {
622
658
  // either explicit (sidebar create-in-workspace) or the current valid
623
659
  // selection. No scope → fail closed client-side (the server would reject
624
660
  // with WORKSPACE_REQUIRED anyway); never a default-workspace fallback.
625
- const createSessionAndPrimeTitleWatcherForHook = useCallback((options) => {
661
+ const createSessionAndPrimeTitleWatcherForHook = useCallback(async (options) => {
626
662
  const scope = options?.workspaceId?.trim() ?? workspaceId?.trim();
627
663
  if (!scope) {
628
664
  setError("创建会话需要选择一个有效的工作区(不会回退默认工作区)");
@@ -644,25 +680,27 @@ export function useChatSessions(params) {
644
680
  const optimisticList = insertOptimisticSession(sessionListRef.current, optimisticRow);
645
681
  sessionListRef.current = optimisticList;
646
682
  setSessionList(optimisticList);
647
- return createSessionAndPrimeTitleWatcher({
648
- origin,
649
- sessionListRef,
650
- setSessionList,
651
- titleWatcher: titleWatcherRef.current,
652
- refreshSessionList,
653
- options: { ...options, workspaceId: scope, optimisticSessionId },
654
- setCreatingSession,
655
- creatingRef: creatingSessionRef,
656
- }).then((created) => {
683
+ try {
684
+ const created = await createSessionAndPrimeTitleWatcher({
685
+ origin,
686
+ sessionListRef,
687
+ setSessionList,
688
+ titleWatcher: titleWatcherRef.current,
689
+ refreshSessionList,
690
+ options: { ...options, workspaceId: scope, optimisticSessionId },
691
+ setCreatingSession,
692
+ creatingRef: creatingSessionRef,
693
+ });
657
694
  pendingCreatedRowsRef.current.delete(optimisticSessionId);
658
695
  return created;
659
- }).catch((error) => {
696
+ }
697
+ catch (error) {
660
698
  pendingCreatedRowsRef.current.delete(optimisticSessionId);
661
699
  const cleaned = sessionListRef.current.filter((item) => item.sessionId !== optimisticSessionId);
662
700
  sessionListRef.current = cleaned;
663
701
  setSessionList(cleaned);
664
702
  throw error;
665
- });
703
+ }
666
704
  }, [origin, refreshSessionList, workspaceId, setError]);
667
705
  const createSession = useCallback(async (options) => {
668
706
  // Suppress concurrent create from any entry (sidebar / Landing / chip).
@@ -708,6 +746,7 @@ export function useChatSessions(params) {
708
746
  // createSession().then(sendPrompt) reads the new session even
709
747
  // though sendPrompt's closure captured the stale (null) value.
710
748
  refs.sessionRef.current = created;
749
+ setSelectedSession(created);
711
750
  rememberActiveChatSession(window.localStorage, created.workspaceId ?? options?.workspaceId ?? workspaceId, created.sessionId);
712
751
  setSession(created);
713
752
  return created;
@@ -834,6 +873,7 @@ export function useChatSessions(params) {
834
873
  const activeSessionId = refs.sessionRef.current.sessionId;
835
874
  sessionSelectionVersionRef.current += 1;
836
875
  refs.sessionRef.current = null;
876
+ setSelectedSession(null);
837
877
  resetActiveProjection();
838
878
  setSession(null);
839
879
  forgetActiveChatSession(window.localStorage, activeWorkspaceId, activeSessionId);
@@ -841,6 +881,9 @@ export function useChatSessions(params) {
841
881
  };
842
882
  return {
843
883
  sessionList,
884
+ selectedSession,
885
+ sessionSwitching: Boolean(selectedSession &&
886
+ refs.sessionRef.current?.sessionId !== selectedSession.sessionId),
844
887
  creatingSession,
845
888
  activateSession,
846
889
  refreshSessionList,