@tea-agent/loop-agent 0.43.0-next.17 → 0.43.0-next.3

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 (187) hide show
  1. package/CHANGELOG.md +9 -140
  2. package/dist/application/task-lifecycle/advance.js +4 -17
  3. package/dist/application/task-lifecycle/observe.js +29 -43
  4. package/dist/application/task-lifecycle/plan-transitions.js +4 -5
  5. package/dist/build-stamp.json +3 -3
  6. package/dist/commands/client-recovery.js +55 -20
  7. package/dist/commands/new-task.js +5 -0
  8. package/dist/commands/status.js +57 -0
  9. package/dist/commands/task-contract.js +269 -0
  10. package/dist/executors/dag-pi-executor.js +230 -269
  11. package/dist/executors/pi-executor.js +0 -2
  12. package/dist/executors/pi-sdk-executor.js +39 -77
  13. package/dist/executors/shell-executor.js +2 -3
  14. package/dist/executors/shell-write-guard.js +32 -27
  15. package/dist/shared/pi-context-pressure/extension.js +16 -26
  16. package/dist/shared/pi-retry-settings.js +0 -40
  17. package/dist/shared/prompts.js +26 -0
  18. package/dist/task/config.js +2 -0
  19. package/dist/task/contract/validate-draft.js +106 -0
  20. package/dist/task/goal.js +3 -0
  21. package/dist/task/index.js +12 -0
  22. package/dist/task/lifecycle.js +1 -0
  23. package/dist/task/operator/capabilities.js +6 -0
  24. package/dist/task/operator/envelope.js +2 -0
  25. package/dist/task/operator/index.js +5 -0
  26. package/dist/task/operator/registry.js +2 -0
  27. package/dist/task/operator/types.js +1 -0
  28. package/dist/task/paths.js +1 -0
  29. package/dist/task/source-prepare/source-fidelity-pi.js +2 -2
  30. package/dist/task/source-references.js +23 -48
  31. package/dist/task/source-state.js +1 -0
  32. package/dist/task/subagent-guidance.js +1 -0
  33. package/dist/worker/console/chat/assistant-content.js +2 -23
  34. package/dist/worker/console/chat/explore-tools.js +0 -13
  35. package/dist/worker/console/chat/pi-runtime.js +1182 -221
  36. package/dist/worker/console/chat/routes.js +71 -213
  37. package/dist/worker/console/chat/sdd-data-alignment.js +9 -65
  38. package/dist/worker/console/chat/session-catalog.js +0 -32
  39. package/dist/worker/console/chat/session-store.js +6 -22
  40. package/dist/worker/console/chat/shortcuts.js +0 -6
  41. package/dist/worker/console/chat/sift-bridge.js +1 -0
  42. package/dist/worker/console/chat/tool-preview.js +0 -11
  43. package/dist/worker/console/chat/tools.js +0 -6
  44. package/dist/worker/console/console-update-and-init.js +19 -1
  45. package/dist/worker/console/console-update-runtime.js +20 -5
  46. package/dist/worker/console/index.js +20 -0
  47. package/dist/worker/console/operator-actions.js +1 -13
  48. package/dist/worker/console/routes.js +0 -8
  49. package/dist/worker/console/server.js +29 -11
  50. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C4NXyedy.js → abnfDiagram-N423BO3Z-hUmM_GRf.js} +1 -1
  51. package/dist/worker/console/static/assets/{arc-NZ3qXvBI.js → arc-Bt-xuieW.js} +1 -1
  52. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-COZSRvml.js → architectureDiagram-T3A2C74G-DTqkL4DJ.js} +1 -1
  53. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-Ck0Gxi9C.js → blockDiagram-VBNYF7ZC-Cy0MNnzV.js} +1 -1
  54. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BX5HQ4Ox.js → c4Diagram-5PPSVZJV-DDdlT0Pd.js} +1 -1
  55. package/dist/worker/console/static/assets/channel-BpzRnZmU.js +1 -0
  56. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-CoviemSL.js → chunk-2GRJ4B5K-3JawwNZ6.js} +1 -1
  57. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DbOdOkbZ.js → chunk-2Q5K7J3B-DoTp7Cri.js} +1 -1
  58. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BsG2pmdQ.js → chunk-5RXB4S5H-CcirhZaS.js} +1 -1
  59. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-DGdvrNm8.js → chunk-5VM5RSS4-VrG21gnP.js} +1 -1
  60. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BAy7Yd6y.js → chunk-6Q2QTUOP-DDOgIS70.js} +1 -1
  61. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-D9UA7hUb.js → chunk-GF5L2VYU-CBjmgvJ_.js} +1 -1
  62. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-DPz7SZ8V.js → chunk-JWPE2WC7-CCQSht8M.js} +1 -1
  63. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BmuEOIh7.js → chunk-KBJHAD2P-BpGMkUUV.js} +1 -1
  64. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CIhBWnd8.js → chunk-RYQCIY6F-DNDT41QL.js} +1 -1
  65. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CScR6mDD.js → chunk-XXDRQBXY-euXnDFbn.js} +1 -1
  66. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BhlMVqCc.js +1 -0
  67. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BhlMVqCc.js +1 -0
  68. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BjpfWaUr.js → cose-bilkent-JH36ORCC-BJ6NAeql.js} +1 -1
  69. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BFQohJgT.js → cynefin-VYW2F7L2-F5JKIvIq.js} +1 -1
  70. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-D3gzRJnS.js → cynefinDiagram-MW4NZA55-Cwmkl_Gi.js} +1 -1
  71. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-5eojkhoA.js → dagre-VZM6K2ZE-BmCZ8815.js} +1 -1
  72. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-CpD8LWu_.js → diagram-7IWD3JNH-zWA3hUnX.js} +1 -1
  73. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B8_4qJ2a.js → diagram-B4RE2ZJO-BzA-888F.js} +1 -1
  74. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-HOsXf0eu.js → diagram-LBJQPF4R-tPNheoch.js} +1 -1
  75. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-BqwRwiWP.js → diagram-Q27KOJAE-Cdi7Fyqp.js} +1 -1
  76. package/dist/worker/console/static/assets/{diagram-UB23O5K3-BRVCNZe1.js → diagram-UB23O5K3-DFK9VBf1.js} +1 -1
  77. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-Cvb1TgGh.js → ebnfDiagram-BXEA7PRR-xCUr1kpg.js} +1 -1
  78. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DS0O0qG9.js → erDiagram-JOGREHBK-BANQHY_A.js} +1 -1
  79. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-BHIRvRr3.js → flowDiagram-UKHOOZJN-VHJYEMkP.js} +1 -1
  80. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-DcCJ1JWx.js → ganttDiagram-PKOTCBZU-DiyZQ0C6.js} +1 -1
  81. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-B9ia5ony.js → gitGraphDiagram-DS77QQ5N-CZtIeBBa.js} +1 -1
  82. package/dist/worker/console/static/assets/index-BlN6ytLr.css +1 -0
  83. package/dist/worker/console/static/assets/index-CA-jfWlE.js +449 -0
  84. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-3ogpgegn.js → infoDiagram-6WML65LV-BuSspC0X.js} +1 -1
  85. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-4tgpQ5SL.js → ishikawaDiagram-WSZJBQD7-DRJZyPFy.js} +1 -1
  86. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DNfgHqFx.js → journeyDiagram-NVQOT4AX-DIfXIVuE.js} +1 -1
  87. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Dz7ngC8z.js → kanban-definition-27J2QSJJ-CT10_7Lt.js} +1 -1
  88. package/dist/worker/console/static/assets/{linear-DI7Jv62E.js → linear-ufemj74H.js} +1 -1
  89. package/dist/worker/console/static/assets/{mermaid.core-CQA4Nzmo.js → mermaid.core-DEQe8-_m.js} +5 -5
  90. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-CtsMc8Mf.js → mindmap-definition-FAOFIHXS-BRLUrbrC.js} +1 -1
  91. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-ByppE9yH.js → pegDiagram-VL7TDLO6-BgibKtiF.js} +1 -1
  92. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-B5PtgFgt.js → pieDiagram-7S7Q4E2Y-D3eyfdV9.js} +1 -1
  93. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-Cq4p0k2K.js → quadrantDiagram-CIZ2JOQS-DXeTk5Px.js} +1 -1
  94. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-DjfkyljN.js → railroadDiagram-AXF67PYL-BgXdFEe7.js} +1 -1
  95. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DzynNRSl.js → requirementDiagram-LRYGKXZP-CseC2A7R.js} +1 -1
  96. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Cj7TCpTJ.js → sankeyDiagram-W5VNT64P-D80PFzfJ.js} +1 -1
  97. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-C73C6ykA.js → sequenceDiagram-SI44F4Z6-BWzHW7LS.js} +1 -1
  98. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-BiNLgN2x.js → sizeCapture-X5ZJPWSS-xWQYTLAn.js} +1 -1
  99. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-UwvjS64-.js → stateDiagram-OKZ733FA-CLxtpZeL.js} +1 -1
  100. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-CiVoq4G-.js +1 -0
  101. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CC7DH98r.js → swimlanes-SLNWSIFB-DwugtmWV.js} +2 -2
  102. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BkdgdpfR.js +8 -0
  103. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-LSBXTR70.js → timeline-definition-Z64GVDOM-DHEOwHIe.js} +1 -1
  104. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BAuxWwGP.js → vennDiagram-T6HMQDX7-pmbZKlBK.js} +1 -1
  105. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y--E5_gjZx.js → wardleyDiagram-T6FBY63Y-CivHoM5q.js} +1 -1
  106. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-dJWNmyzF.js → xychartDiagram-ELKLHX3M-BfHIE7o6.js} +1 -1
  107. package/dist/worker/console/static/index.html +2 -2
  108. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +10 -122
  109. package/dist/worker/console/static-src/operator-chat/compaction-message.js +0 -7
  110. package/dist/worker/console/static-src/operator-chat/pending-user-message.js +15 -35
  111. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +62 -136
  112. package/dist/worker/console/static-src/operator-chat/useChatStream.js +68 -159
  113. package/dist/worker/console/static-src/operator-chat/useChatThread.js +56 -75
  114. package/dist/worker/console/static-src/operator-chat/useComposer.js +78 -206
  115. package/dist/worker/console/workspace-context.js +1 -21
  116. package/dist/worker/observe/static/router.js +1 -1
  117. package/dist/worker/observe/static/styles.css +4 -7
  118. package/dist/worker/observe/static/views/dag-inspector.js +19 -12
  119. package/dist/worker/observe/static/views/dag.js +2 -2
  120. package/dist/worker/scheduler/index.js +25 -0
  121. package/dist/workflows/dag/facts.js +4 -0
  122. package/dist/workflows/dag/frontend-closeout.js +1 -1
  123. package/dist/workflows/dag/frontend-design-policy.js +1 -2
  124. package/dist/workflows/dag/frontend-implementation-contract.js +4 -8
  125. package/dist/workflows/dag/frontend-prewrite-gate.js +3 -3
  126. package/dist/workflows/dag/frontend-repair.js +76 -55
  127. package/dist/workflows/dag/frontend-review-context.js +1 -2
  128. package/dist/workflows/dag/frontend-review-scopes.js +12 -34
  129. package/dist/workflows/dag/frontend-shape-facts.js +2 -12
  130. package/dist/workflows/dag/frontend-verification-trace.js +42 -8
  131. package/dist/workflows/dag/index.js +6 -0
  132. package/dist/workflows/dag/init-hybrid.js +160 -97
  133. package/dist/workflows/dynamic/index.js +7 -0
  134. package/docs/architecture/runtime-boundaries.md +3 -3
  135. package/docs/templates/frontend-implementation-dag.json +8 -9
  136. package/package.json +3 -7
  137. package/skills/frontend-bounded-implement/SKILL.md +3 -4
  138. package/skills/frontend-bounded-implement/references/code-standards.md +3 -3
  139. package/skills/frontend-contract/references/contract-protocol.md +1 -3
  140. package/skills/frontend-design-review/SKILL.md +7 -12
  141. package/skills/frontend-design-review/references/review-checklist.md +7 -7
  142. package/skills/frontend-plan/SKILL.md +0 -19
  143. package/skills/frontend-plan/references/decision-contract.md +19 -26
  144. package/skills/frontend-review/SKILL.md +18 -12
  145. package/skills/frontend-review/references/review-findings.md +43 -24
  146. package/dist/shared/pi-provider-recovery.js +0 -182
  147. package/dist/worker/console/chat/browser-automation.js +0 -363
  148. package/dist/worker/console/chat/browser-policy.js +0 -113
  149. package/dist/worker/console/chat/browser-routes.js +0 -162
  150. package/dist/worker/console/chat/pi-runtime/custom-tools/ask-user-question.js +0 -44
  151. package/dist/worker/console/chat/pi-runtime/custom-tools/browser-tools.js +0 -89
  152. package/dist/worker/console/chat/pi-runtime/custom-tools/explore-tools.js +0 -202
  153. package/dist/worker/console/chat/pi-runtime/custom-tools/goal-tools.js +0 -79
  154. package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +0 -86
  155. package/dist/worker/console/chat/pi-runtime/custom-tools/todo-write.js +0 -38
  156. package/dist/worker/console/chat/pi-runtime/inspection-types.js +0 -28
  157. package/dist/worker/console/chat/pi-runtime/inspection.js +0 -439
  158. package/dist/worker/console/chat/pi-runtime/sdk-bindings.js +0 -153
  159. package/dist/worker/console/chat/pi-runtime/sdk-session.js +0 -5
  160. package/dist/worker/console/chat/scm-routes.js +0 -217
  161. package/dist/worker/console/chat/scm-service.js +0 -283
  162. package/dist/worker/console/chat/scm-tools.js +0 -111
  163. package/dist/worker/console/chat/session-mode-view.js +0 -49
  164. package/dist/worker/console/chat/session-mode.js +0 -220
  165. package/dist/worker/console/chat/terminal-routes.js +0 -216
  166. package/dist/worker/console/chat/terminal-sessions.js +0 -348
  167. package/dist/worker/console/chat/terminal-tools.js +0 -211
  168. package/dist/worker/console/static/assets/channel-BxdTlb_5.js +0 -1
  169. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-_ByA1Nf8.js +0 -1
  170. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-_ByA1Nf8.js +0 -1
  171. package/dist/worker/console/static/assets/index-B9goN3S5.css +0 -1
  172. package/dist/worker/console/static/assets/index-wXlQohNs.js +0 -469
  173. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-nLgMZ0D3.js +0 -1
  174. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BcYqTady.js +0 -8
  175. package/dist/worker/console/static-src/operator-chat/active-turn-tail.js +0 -10
  176. package/dist/worker/console/static-src/operator-chat/chat-link.js +0 -94
  177. package/dist/worker/console/static-src/operator-chat/chat-scroll-position.js +0 -24
  178. package/dist/worker/console/static-src/operator-chat/composer-session-ledger.js +0 -192
  179. package/dist/worker/console/static-src/operator-chat/process-label.js +0 -41
  180. package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +0 -13
  181. package/dist/worker/console/static-src/operator-chat/turn-process-disclosure.js +0 -10
  182. package/dist/worker/console/static-src/operator-chat/use-searchable-hidden.js +0 -51
  183. package/dist/worker/console/static-src/operator-chat/user-turn-anchor-equality.js +0 -21
  184. package/dist/worker/console/workspace-initialization.js +0 -209
  185. package/dist/workflows/dag/frontend-capacity.js +0 -9
  186. package/dist/workflows/dag/frontend-plan-completeness.js +0 -186
  187. package/dist/workflows/dag/frontend-plan-recovery-policy.js +0 -18
@@ -2,10 +2,9 @@ import { useCallback, useEffect, useRef, useState } from "react";
2
2
  import { runtimeSelectionFromRecord } from "../../chat/runtime-selection.js";
3
3
  import { hydrateSessionThread } from "../../chat/turn-process.js";
4
4
  import { forgetActiveChatSession, readActiveChatSessionId, rememberActiveChatSession, } from "../../chat/workspace-landing.js";
5
- import { DEFAULT_CONSOLE_CHAT_SESSION_MODE } from "../../chat/session-mode-view.js";
6
5
  import { confirmationToken } from "./format.js";
7
6
  import { isLostChatSessionError } from "./runtime-snapshot-store.js";
8
- import { upsertCompactionMessage, } from "./compaction-message.js";
7
+ import { upsertCompactionMessage } from "./compaction-message.js";
9
8
  import { describeMutationGateFailure, isMutationGateFailureCode, isStaleTokenFailure, } from "./mutation-gate.js";
10
9
  import { SessionTitleWatcher } from "./session-title-watcher.js";
11
10
  /** Pure list mutations keep optimistic UI deterministic and easy to verify. */
@@ -68,8 +67,6 @@ function sameSessionSummary(a, b) {
68
67
  a.model?.modelId === b.model?.modelId &&
69
68
  a.thinkingLevel === b.thinkingLevel &&
70
69
  a.isBlank === b.isBlank &&
71
- (a.sessionMode ?? DEFAULT_CONSOLE_CHAT_SESSION_MODE) ===
72
- (b.sessionMode ?? DEFAULT_CONSOLE_CHAT_SESSION_MODE) &&
73
70
  a.optimisticState === b.optimisticState &&
74
71
  sameQueueSummary(a, b));
75
72
  }
@@ -86,18 +83,8 @@ export function stabilizeSessionList(previous, next) {
86
83
  });
87
84
  return changed ? stabilized : previous;
88
85
  }
89
- /** Project a session detail/list record into the active ChatSessionInfo. */
90
- export function projectActiveChatSession(session, fallbackWorkspaceId) {
91
- const workspaceId = session.workspaceId?.trim() || fallbackWorkspaceId?.trim() || "";
92
- return {
93
- sessionId: session.sessionId,
94
- ...(workspaceId ? { workspaceId } : {}),
95
- sessionMode: session.sessionMode ?? DEFAULT_CONSOLE_CHAT_SESSION_MODE,
96
- ...runtimeSelectionFromRecord(session),
97
- };
98
- }
99
86
  /** Select a server-confirmed blank session owned by the requested workspace. */
100
- export function findReusableBlankSession(list, workspaceId, sessionMode = DEFAULT_CONSOLE_CHAT_SESSION_MODE) {
87
+ export function findReusableBlankSession(list, workspaceId) {
101
88
  const scope = workspaceId.trim();
102
89
  if (!scope)
103
90
  return undefined;
@@ -106,8 +93,7 @@ export function findReusableBlankSession(list, workspaceId, sessionMode = DEFAUL
106
93
  item.isBlank === true &&
107
94
  item.turnPhase === "idle" &&
108
95
  !item.activeTurnId &&
109
- !item.optimisticState &&
110
- (item.sessionMode ?? DEFAULT_CONSOLE_CHAT_SESSION_MODE) === sessionMode);
96
+ !item.optimisticState);
111
97
  }
112
98
  export async function createSessionAndPrimeTitleWatcher(input) {
113
99
  if (input.creatingRef?.current) {
@@ -141,13 +127,7 @@ export async function createSessionAndPrimeTitleWatcher(input) {
141
127
  const body = (await res.json());
142
128
  if (!body.session)
143
129
  throw new Error("创建会话响应缺少 durable session");
144
- const durable = {
145
- ...body.session,
146
- isBlank: true,
147
- sessionMode: body.session.sessionMode ??
148
- input.options?.sessionMode ??
149
- DEFAULT_CONSOLE_CHAT_SESSION_MODE,
150
- };
130
+ const durable = { ...body.session, isBlank: true };
151
131
  const created = {
152
132
  sessionId: durable.sessionId,
153
133
  ...(durable.workspaceId || input.options?.workspaceId
@@ -155,7 +135,6 @@ export async function createSessionAndPrimeTitleWatcher(input) {
155
135
  workspaceId: durable.workspaceId ?? input.options?.workspaceId,
156
136
  }
157
137
  : {}),
158
- sessionMode: durable.sessionMode,
159
138
  ...runtimeSelectionFromRecord(durable),
160
139
  };
161
140
  const nextList = replaceOptimisticSession(input.sessionListRef.current, input.options?.optimisticSessionId, durable);
@@ -179,8 +158,8 @@ function mergeCompactionMessages(messages, compactions) {
179
158
  * compact, plus browser-refresh recovery from the durable session store. */
180
159
  export function useChatSessions(params) {
181
160
  const { origin, refs, setSession, thread, composer, browser, setError, setNotice, onCompactionChange, setProcessOpen, setAutoScroll, } = params;
182
- const { applyActivatedSession, resetForNewSession, setMessages } = thread;
183
- const { seedSession, releaseSession } = composer;
161
+ const { applyActivatedSession, resetForNewSession } = thread;
162
+ const { setInput, setPendingImages } = composer;
184
163
  const { setPreviews, setActivePreviewPath } = browser;
185
164
  const { workspaceId, blocked } = params;
186
165
  const workspaceQuery = workspaceId
@@ -198,11 +177,7 @@ export function useChatSessions(params) {
198
177
  return owner ? `?workspaceId=${encodeURIComponent(owner)}` : "";
199
178
  }, [refs]);
200
179
  const [sessionList, setSessionList] = useState([]);
201
- const [selectedSession, setSelectedSession] = useState(null);
202
- const selectedSessionRef = useRef(null);
203
- selectedSessionRef.current = selectedSession;
204
180
  const sessionListRef = useRef([]);
205
- const sessionListRequestVersionRef = useRef(0);
206
181
  const pendingDeletedIdsRef = useRef(new Set());
207
182
  const pendingCreatedRowsRef = useRef(new Map());
208
183
  const sessionMutationVersionRef = useRef(0);
@@ -244,6 +219,8 @@ export function useChatSessions(params) {
244
219
  refs.settledTurnIdsRef.current = new Set();
245
220
  refs.activeTurnIdRef.current = null;
246
221
  resetForNewSession();
222
+ setInput("");
223
+ setPendingImages([]);
247
224
  setPreviews([]);
248
225
  setActivePreviewPath(null);
249
226
  setProcessOpen(false);
@@ -251,6 +228,8 @@ export function useChatSessions(params) {
251
228
  }, [
252
229
  refs,
253
230
  resetForNewSession,
231
+ setInput,
232
+ setPendingImages,
254
233
  setPreviews,
255
234
  setActivePreviewPath,
256
235
  setProcessOpen,
@@ -259,7 +238,6 @@ export function useChatSessions(params) {
259
238
  const refreshSessionList = useCallback(async () => {
260
239
  if (blocked)
261
240
  return; // AC-R5-002: fail-closed, zero session requests
262
- const version = ++sessionListRequestVersionRef.current;
263
241
  try {
264
242
  // The sidebar is a multi-workspace index. Keep one unscoped summary
265
243
  // collection so expanding or switching a workspace never projects the
@@ -269,8 +247,6 @@ export function useChatSessions(params) {
269
247
  credentials: "include",
270
248
  });
271
249
  const body = (await res.json().catch(() => ({})));
272
- if (version !== sessionListRequestVersionRef.current)
273
- return;
274
250
  if (!res.ok) {
275
251
  setError(body.error?.message ?? `会话列表加载失败(HTTP ${res.status})`);
276
252
  return;
@@ -288,8 +264,6 @@ export function useChatSessions(params) {
288
264
  }
289
265
  }
290
266
  catch (error) {
291
- if (version !== sessionListRequestVersionRef.current)
292
- return;
293
267
  setError(`会话列表加载失败:${error instanceof Error ? error.message : String(error)}`);
294
268
  }
295
269
  }, [origin, blocked, setError]);
@@ -301,22 +275,12 @@ export function useChatSessions(params) {
301
275
  const { next: removedList, removed, index: removedIndex } = removeSessionById(currentList, sessionId);
302
276
  if (!removed || removedIndex < 0)
303
277
  return false;
304
- // No pre-mutation snapshot may overwrite this deletion or its rollback.
305
- sessionListRequestVersionRef.current += 1;
306
278
  pendingDeletedIdsRef.current.add(sessionId);
307
279
  deleteRequestVersionsRef.current.set(sessionId, requestId);
308
280
  const activeSession = refs.sessionRef.current?.sessionId === sessionId
309
281
  ? refs.sessionRef.current
310
282
  : null;
311
- const selectedForDelete = selectedSessionRef.current?.sessionId === sessionId
312
- ? selectedSessionRef.current
313
- : null;
314
- const deletingIdentity = selectedForDelete ??
315
- activeSession ?? {
316
- sessionId,
317
- ...(removed.workspaceId ? { workspaceId: removed.workspaceId } : {}),
318
- };
319
- const activeWorkspaceId = deletingIdentity.workspaceId ?? workspaceId;
283
+ const activeWorkspaceId = activeSession?.workspaceId ?? workspaceId;
320
284
  const deletingRow = {
321
285
  ...removed,
322
286
  optimisticState: "deleting",
@@ -335,17 +299,16 @@ export function useChatSessions(params) {
335
299
  const restore = () => {
336
300
  if (deleteRequestVersionsRef.current.get(sessionId) !== requestId)
337
301
  return;
338
- sessionListRequestVersionRef.current += 1;
339
302
  pendingDeletedIdsRef.current.delete(sessionId);
340
303
  deleteRequestVersionsRef.current.delete(sessionId);
341
- const restored = restoreSessionAt(sessionListRef.current.filter((item) => item.sessionId !== sessionId), removed, removedIndex);
304
+ const restored = restoreSessionAt(sessionListRef.current, removed, removedIndex);
342
305
  if (restored === sessionListRef.current)
343
306
  return;
344
307
  sessionListRef.current = restored;
345
308
  setSessionList(restored);
346
309
  };
347
310
  let deletionSelectionVersion = null;
348
- if (activeSession || selectedForDelete) {
311
+ if (activeSession) {
349
312
  deletionSelectionVersion = ++sessionSelectionVersionRef.current;
350
313
  refs.sessionRef.current = null;
351
314
  resetActiveProjection();
@@ -353,16 +316,17 @@ export function useChatSessions(params) {
353
316
  forgetActiveChatSession(window.localStorage, activeWorkspaceId, sessionId);
354
317
  }
355
318
  const restoreActiveSession = async () => {
356
- if ((activeSession || selectedForDelete) &&
319
+ if (activeSession &&
357
320
  deletionSelectionVersion === sessionSelectionVersionRef.current &&
358
321
  !refs.sessionRef.current) {
359
- await activateSessionRef.current?.(deletingIdentity.sessionId, deletingIdentity.workspaceId);
322
+ await activateSessionRef.current?.(activeSession.sessionId, activeSession.workspaceId);
360
323
  }
361
324
  };
362
325
  try {
363
326
  // AC-002: delete follows the session's durable owner, not the
364
327
  // global active workspace.
365
- const ownerQuery = sessionOwnerQuery(removed.workspaceId ?? null);
328
+ const ownerQuery = sessionOwnerQuery(sessionListRef.current.find((item) => item.sessionId === sessionId)
329
+ ?.workspaceId ?? null);
366
330
  const res = await fetch(`${origin}/api/operator/v1/chat/sessions/${encodeURIComponent(sessionId)}${ownerQuery}`, {
367
331
  method: "DELETE",
368
332
  credentials: "include",
@@ -377,13 +341,6 @@ export function useChatSessions(params) {
377
341
  return false;
378
342
  }
379
343
  titleWatcherRef.current?.cancel(sessionId);
380
- const next = sessionListRef.current.filter((item) => item.sessionId !== sessionId);
381
- sessionListRef.current = next;
382
- setSessionList(next);
383
- if (deletionSelectionVersion === sessionSelectionVersionRef.current &&
384
- selectedSessionRef.current?.sessionId === sessionId)
385
- setSelectedSession(null);
386
- releaseSession(deletingIdentity);
387
344
  void refreshSessionList().finally(() => {
388
345
  pendingDeletedIdsRef.current.delete(sessionId);
389
346
  deleteRequestVersionsRef.current.delete(sessionId);
@@ -401,7 +358,6 @@ export function useChatSessions(params) {
401
358
  refs,
402
359
  setSession,
403
360
  resetActiveProjection,
404
- releaseSession,
405
361
  refreshSessionList,
406
362
  setError,
407
363
  sessionOwnerQuery,
@@ -411,45 +367,25 @@ export function useChatSessions(params) {
411
367
  const active = refs.sessionRef.current;
412
368
  const listed = sessionListRef.current.find((item) => item.sessionId === sessionId);
413
369
  const scope = ownerWorkspaceId ?? listed?.workspaceId ?? active?.workspaceId ?? workspaceId;
414
- const identity = {
415
- sessionId,
416
- ...(scope ? { workspaceId: scope } : {}),
417
- };
418
370
  forgetActiveChatSession(window.localStorage, scope, sessionId);
419
- releaseSession(identity);
420
371
  pendingDeletedIdsRef.current.add(sessionId);
421
372
  const { next } = removeSessionById(sessionListRef.current, sessionId);
422
373
  sessionListRef.current = next;
423
374
  setSessionList(next);
424
- if (active?.sessionId === sessionId ||
425
- selectedSessionRef.current?.sessionId === sessionId) {
375
+ if (active?.sessionId === sessionId) {
426
376
  sessionSelectionVersionRef.current += 1;
427
377
  refs.sessionRef.current = null;
428
- setSelectedSession(null);
429
378
  resetActiveProjection();
430
379
  setSession(null);
431
380
  }
432
381
  void refreshSessionList();
433
- }, [refs, releaseSession, resetActiveProjection, setSession, workspaceId, refreshSessionList]);
382
+ }, [refs, resetActiveProjection, setSession, workspaceId, refreshSessionList]);
434
383
  const activateSession = useCallback(async (sessionId, ownerWorkspaceId) => {
435
384
  const selectionVersion = ++sessionSelectionVersionRef.current;
436
- const listedOwner = sessionListRef.current.find((item) => item.sessionId === sessionId)?.workspaceId;
437
- const requestedOwner = ownerWorkspaceId ?? listedOwner;
438
- const requested = {
439
- sessionId,
440
- ...(requestedOwner ? { workspaceId: requestedOwner } : {}),
441
- };
442
- // DSH-style staging: selection identity changes synchronously; detail
443
- // hydration follows. The previous session loses send authority now.
444
- setSelectedSession(requested);
445
- resetActiveProjection({ keepStreams: true });
446
- refs.sessionRef.current = null;
447
- setSession(null);
448
- setError(null);
449
385
  // AC-002: activation targets the session's durable owning workspace —
450
386
  // never the global active workspace (no cross-workspace leak when
451
387
  // Operate/Inspect switch their scope while a session stays open).
452
- const ownerQuery = sessionOwnerQuery(requested.workspaceId);
388
+ const ownerQuery = sessionOwnerQuery(ownerWorkspaceId);
453
389
  try {
454
390
  const res = await fetch(`${origin}/api/operator/v1/chat/sessions/${encodeURIComponent(sessionId)}${ownerQuery}`, { credentials: "include" });
455
391
  const body = (await res.json());
@@ -466,11 +402,15 @@ export function useChatSessions(params) {
466
402
  }
467
403
  if (selectionVersion !== sessionSelectionVersionRef.current)
468
404
  return false;
469
- const next = projectActiveChatSession(body.session);
405
+ const next = {
406
+ sessionId: body.session.sessionId,
407
+ ...(body.session.workspaceId ? { workspaceId: body.session.workspaceId } : {}),
408
+ ...runtimeSelectionFromRecord(body.session),
409
+ };
470
410
  // AC-002: session switch (same or cross workspace) never aborts other
471
411
  // sessions' background request controllers; the single /events owner is
472
412
  // re-bound by useChatStream's session effect.
473
- setSelectedSession(next);
413
+ resetActiveProjection({ keepStreams: true });
474
414
  refs.sessionRef.current = next;
475
415
  refs.activeTurnIdRef.current = body.session.activeTurnId ?? null;
476
416
  const visuallyStopped = body.session.turnPhase === "stopping";
@@ -490,8 +430,9 @@ export function useChatSessions(params) {
490
430
  refs.assistantIdRef.current = null;
491
431
  refs.abortRef.current =
492
432
  refs.sessionAbortControllersRef.current.get(sessionId) ?? null;
433
+ setPendingImages([]);
493
434
  setPreviews([]);
494
- seedSession(next, body.session.composerDraft ?? "");
435
+ setInput(body.session.composerDraft ?? "");
495
436
  const hydrated = hydrateSessionThread(body.session.messages ?? []);
496
437
  hydrated.messages = mergeCompactionMessages(hydrated.messages, body.compactions);
497
438
  applyActivatedSession({
@@ -524,8 +465,9 @@ export function useChatSessions(params) {
524
465
  origin,
525
466
  refs,
526
467
  setSession,
468
+ setPendingImages,
527
469
  setPreviews,
528
- seedSession,
470
+ setInput,
529
471
  applyActivatedSession,
530
472
  resetActiveProjection,
531
473
  setActivePreviewPath,
@@ -559,7 +501,9 @@ export function useChatSessions(params) {
559
501
  }
560
502
  const watchSessionTitle = useCallback((sessionId) => titleWatcherRef.current?.startIfEligible(sessionId), []);
561
503
  useEffect(() => {
562
- titleWatcherRef.current?.reconcile(sessionList.flatMap((item) => item.state !== "archived" && !item.title ? [item.sessionId] : []));
504
+ titleWatcherRef.current?.reconcile(sessionList
505
+ .filter((item) => item.state !== "archived" && !item.title)
506
+ .map((item) => item.sessionId));
563
507
  }, [sessionList]);
564
508
  useEffect(() => () => titleWatcherRef.current?.dispose(), []);
565
509
  // Browser refresh/workspace-switch recovery: selection is client state keyed
@@ -576,22 +520,15 @@ export function useChatSessions(params) {
576
520
  setSession(null);
577
521
  const savedSessionId = readActiveChatSessionId(window.localStorage, workspaceId);
578
522
  if (!savedSessionId) {
579
- setSelectedSession(null);
580
523
  // Keep the per-workspace map intact, but clear the legacy global pointer
581
524
  // so Operate/Models cannot link a session from another workspace.
582
525
  forgetActiveChatSession(window.localStorage, workspaceId);
583
526
  return;
584
527
  }
585
- setSelectedSession({
586
- sessionId: savedSessionId,
587
- ...(workspaceId ? { workspaceId } : {}),
588
- });
589
528
  void (async () => {
590
529
  try {
591
530
  const res = await fetch(`${origin}/api/operator/v1/chat/sessions/${encodeURIComponent(savedSessionId)}${workspaceQuery}`, { credentials: "include" });
592
531
  if (!res.ok) {
593
- if (selectionVersion === sessionSelectionVersionRef.current)
594
- setSelectedSession(null);
595
532
  forgetActiveChatSession(window.localStorage, workspaceId, savedSessionId);
596
533
  return;
597
534
  }
@@ -601,9 +538,16 @@ export function useChatSessions(params) {
601
538
  selectionVersion !== sessionSelectionVersionRef.current ||
602
539
  !body.session)
603
540
  return;
604
- const restored = projectActiveChatSession(body.session, workspaceId);
541
+ const restored = {
542
+ sessionId: body.session.sessionId,
543
+ ...(body.session.workspaceId
544
+ ? { workspaceId: body.session.workspaceId }
545
+ : workspaceId
546
+ ? { workspaceId }
547
+ : {}),
548
+ ...runtimeSelectionFromRecord(body.session),
549
+ };
605
550
  refs.sessionGenerationRef.current += 1;
606
- setSelectedSession(restored);
607
551
  refs.sessionRef.current = restored;
608
552
  // Do not seed Last-Event-ID here: session messages are restored
609
553
  // from the durable store, but tool/gate/operation projections live
@@ -624,8 +568,8 @@ export function useChatSessions(params) {
624
568
  refs.settledTurnIdsRef.current = new Set();
625
569
  refs.lastEventIdRef.current = null;
626
570
  setSession(restored);
627
- seedSession(restored, body.session.composerDraft ?? "");
628
571
  rememberActiveChatSession(window.localStorage, restored.workspaceId ?? workspaceId, restored.sessionId);
572
+ setInput(body.session.composerDraft ?? "");
629
573
  const hydrated = hydrateSessionThread(body.session.messages ?? []);
630
574
  hydrated.messages = mergeCompactionMessages(hydrated.messages, body.compactions);
631
575
  applyActivatedSession({
@@ -657,7 +601,7 @@ export function useChatSessions(params) {
657
601
  workspaceQuery,
658
602
  refs,
659
603
  setSession,
660
- seedSession,
604
+ setInput,
661
605
  applyActivatedSession,
662
606
  blocked,
663
607
  resetActiveProjection,
@@ -669,13 +613,12 @@ export function useChatSessions(params) {
669
613
  // either explicit (sidebar create-in-workspace) or the current valid
670
614
  // selection. No scope → fail closed client-side (the server would reject
671
615
  // with WORKSPACE_REQUIRED anyway); never a default-workspace fallback.
672
- const createSessionAndPrimeTitleWatcherForHook = useCallback(async (options) => {
616
+ const createSessionAndPrimeTitleWatcherForHook = useCallback((options) => {
673
617
  const scope = options?.workspaceId?.trim() ?? workspaceId?.trim();
674
618
  if (!scope) {
675
619
  setError("创建会话需要选择一个有效的工作区(不会回退默认工作区)");
676
620
  return Promise.reject(new Error("WORKSPACE_REQUIRED"));
677
621
  }
678
- const sessionMode = options?.sessionMode ?? DEFAULT_CONSOLE_CHAT_SESSION_MODE;
679
622
  const optimisticSessionId = `optimistic-chat-${Date.now()}-${Math.random().toString(16).slice(2)}`;
680
623
  const now = new Date().toISOString();
681
624
  const optimisticRow = {
@@ -684,34 +627,31 @@ export function useChatSessions(params) {
684
627
  state: "active",
685
628
  updatedAt: now,
686
629
  turnPhase: "idle",
687
- sessionMode,
688
630
  optimisticState: "creating",
689
631
  };
690
632
  pendingCreatedRowsRef.current.set(optimisticSessionId, optimisticRow);
691
633
  const optimisticList = insertOptimisticSession(sessionListRef.current, optimisticRow);
692
634
  sessionListRef.current = optimisticList;
693
635
  setSessionList(optimisticList);
694
- try {
695
- const created = await createSessionAndPrimeTitleWatcher({
696
- origin,
697
- sessionListRef,
698
- setSessionList,
699
- titleWatcher: titleWatcherRef.current,
700
- refreshSessionList,
701
- options: { ...options, workspaceId: scope, optimisticSessionId },
702
- setCreatingSession,
703
- creatingRef: creatingSessionRef,
704
- });
636
+ return createSessionAndPrimeTitleWatcher({
637
+ origin,
638
+ sessionListRef,
639
+ setSessionList,
640
+ titleWatcher: titleWatcherRef.current,
641
+ refreshSessionList,
642
+ options: { ...options, workspaceId: scope, optimisticSessionId },
643
+ setCreatingSession,
644
+ creatingRef: creatingSessionRef,
645
+ }).then((created) => {
705
646
  pendingCreatedRowsRef.current.delete(optimisticSessionId);
706
647
  return created;
707
- }
708
- catch (error) {
648
+ }).catch((error) => {
709
649
  pendingCreatedRowsRef.current.delete(optimisticSessionId);
710
650
  const cleaned = sessionListRef.current.filter((item) => item.sessionId !== optimisticSessionId);
711
651
  sessionListRef.current = cleaned;
712
652
  setSessionList(cleaned);
713
653
  throw error;
714
- }
654
+ });
715
655
  }, [origin, refreshSessionList, workspaceId, setError]);
716
656
  const createSession = useCallback(async (options) => {
717
657
  // Suppress concurrent create from any entry (sidebar / Landing / chip).
@@ -729,10 +669,9 @@ export function useChatSessions(params) {
729
669
  }
730
670
  // A caller-provided snapshot is an explicit new-session boundary; reusing
731
671
  // an existing shell would silently drop that context suffix.
732
- const sessionMode = options?.sessionMode ?? DEFAULT_CONSOLE_CHAT_SESSION_MODE;
733
672
  const reusable = options?.snapshot
734
673
  ? undefined
735
- : findReusableBlankSession(sessionListRef.current, scope, sessionMode);
674
+ : findReusableBlankSession(sessionListRef.current, scope);
736
675
  if (reusable) {
737
676
  if (previousSession?.sessionId === reusable.sessionId)
738
677
  return previousSession;
@@ -742,7 +681,6 @@ export function useChatSessions(params) {
742
681
  return {
743
682
  sessionId: reusable.sessionId,
744
683
  ...(reusable.workspaceId ? { workspaceId: reusable.workspaceId } : {}),
745
- sessionMode: reusable.sessionMode ?? sessionMode,
746
684
  ...runtimeSelectionFromRecord(reusable),
747
685
  };
748
686
  }
@@ -757,7 +695,6 @@ export function useChatSessions(params) {
757
695
  // createSession().then(sendPrompt) reads the new session even
758
696
  // though sendPrompt's closure captured the stale (null) value.
759
697
  refs.sessionRef.current = created;
760
- setSelectedSession(created);
761
698
  rememberActiveChatSession(window.localStorage, created.workspaceId ?? options?.workspaceId ?? workspaceId, created.sessionId);
762
699
  setSession(created);
763
700
  return created;
@@ -826,18 +763,11 @@ export function useChatSessions(params) {
826
763
  return;
827
764
  }
828
765
  setError(null);
829
- // Keep the current projection and converge the lifecycle row in place.
830
- // Re-activating here would clear the timeline and replay the full event ring.
766
+ // pi-web parity: reload the compacted session, then leave a distinct
767
+ // compaction summary card in the timeline plus a transient toast.
831
768
  const summary = body.snapshot?.summary?.trim();
832
769
  const summaryText = summary || "历史上下文已精简,关键决策与当前任务已保留。";
833
- const compaction = body.compaction;
834
- if (compaction &&
835
- refs.sessionRef.current?.sessionId === current.sessionId) {
836
- if (compaction.eventId) {
837
- refs.seenEventIdsRef.current.add(compaction.eventId);
838
- }
839
- setMessages((messages) => upsertCompactionMessage(messages, compaction));
840
- }
770
+ await activateSession(current.sessionId);
841
771
  setNotice({
842
772
  id: Date.now(),
843
773
  message: `已压缩上下文:${summaryText.slice(0, 72)}${summaryText.length > 72 ? "…" : ""}`,
@@ -855,7 +785,7 @@ export function useChatSessions(params) {
855
785
  setError,
856
786
  setNotice,
857
787
  onCompactionChange,
858
- setMessages,
788
+ activateSession,
859
789
  sessionOwnerQuery,
860
790
  ]);
861
791
  const updateSession = async (sessionId, patch) => {
@@ -884,7 +814,6 @@ export function useChatSessions(params) {
884
814
  const activeSessionId = refs.sessionRef.current.sessionId;
885
815
  sessionSelectionVersionRef.current += 1;
886
816
  refs.sessionRef.current = null;
887
- setSelectedSession(null);
888
817
  resetActiveProjection();
889
818
  setSession(null);
890
819
  forgetActiveChatSession(window.localStorage, activeWorkspaceId, activeSessionId);
@@ -892,9 +821,6 @@ export function useChatSessions(params) {
892
821
  };
893
822
  return {
894
823
  sessionList,
895
- selectedSession,
896
- sessionSwitching: Boolean(selectedSession &&
897
- refs.sessionRef.current?.sessionId !== selectedSession.sessionId),
898
824
  creatingSession,
899
825
  activateSession,
900
826
  refreshSessionList,