@shepai/cli 1.164.0-pr514.db33061 → 1.164.0

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 (206) hide show
  1. package/dist/packages/core/src/application/ports/output/agents/interactive-agent-executor.interface.d.ts +1 -3
  2. package/dist/packages/core/src/application/ports/output/agents/interactive-agent-executor.interface.d.ts.map +1 -1
  3. package/dist/packages/core/src/application/ports/output/repositories/interactive-session-repository.interface.d.ts +0 -19
  4. package/dist/packages/core/src/application/ports/output/repositories/interactive-session-repository.interface.d.ts.map +1 -1
  5. package/dist/packages/core/src/application/ports/output/services/interactive-session-service.interface.d.ts +1 -7
  6. package/dist/packages/core/src/application/ports/output/services/interactive-session-service.interface.d.ts.map +1 -1
  7. package/dist/packages/core/src/application/use-cases/interactive/send-interactive-message.use-case.d.ts +0 -2
  8. package/dist/packages/core/src/application/use-cases/interactive/send-interactive-message.use-case.d.ts.map +1 -1
  9. package/dist/packages/core/src/application/use-cases/interactive/send-interactive-message.use-case.js +1 -1
  10. package/dist/packages/core/src/infrastructure/repositories/sqlite-interactive-session.repository.d.ts +0 -12
  11. package/dist/packages/core/src/infrastructure/repositories/sqlite-interactive-session.repository.d.ts.map +1 -1
  12. package/dist/packages/core/src/infrastructure/repositories/sqlite-interactive-session.repository.js +0 -31
  13. package/dist/packages/core/src/infrastructure/services/agents/common/executors/claude-code-interactive-executor.service.d.ts +7 -38
  14. package/dist/packages/core/src/infrastructure/services/agents/common/executors/claude-code-interactive-executor.service.d.ts.map +1 -1
  15. package/dist/packages/core/src/infrastructure/services/agents/common/executors/claude-code-interactive-executor.service.js +45 -152
  16. package/dist/packages/core/src/infrastructure/services/interactive/interactive-session.service.d.ts +1 -17
  17. package/dist/packages/core/src/infrastructure/services/interactive/interactive-session.service.d.ts.map +1 -1
  18. package/dist/packages/core/src/infrastructure/services/interactive/interactive-session.service.js +47 -131
  19. package/dist/src/presentation/web/app/api/interactive/chat/[featureId]/messages/route.d.ts.map +1 -1
  20. package/dist/src/presentation/web/app/api/interactive/chat/[featureId]/messages/route.js +1 -3
  21. package/dist/src/presentation/web/components/assistant-ui/thread.js +1 -8
  22. package/dist/src/presentation/web/components/features/chat/ChatTab.d.ts.map +1 -1
  23. package/dist/src/presentation/web/components/features/chat/ChatTab.js +7 -13
  24. package/dist/src/presentation/web/components/features/chat/useChatRuntime.d.ts +0 -16
  25. package/dist/src/presentation/web/components/features/chat/useChatRuntime.d.ts.map +1 -1
  26. package/dist/src/presentation/web/components/features/chat/useChatRuntime.js +7 -98
  27. package/dist/src/presentation/web/components/features/settings/AgentModelPicker/index.d.ts.map +1 -1
  28. package/dist/src/presentation/web/components/features/settings/AgentModelPicker/index.js +1 -1
  29. package/dist/tsconfig.build.tsbuildinfo +1 -1
  30. package/package.json +2 -2
  31. package/web/.next/BUILD_ID +1 -1
  32. package/web/.next/build-manifest.json +2 -2
  33. package/web/.next/fallback-build-manifest.json +2 -2
  34. package/web/.next/prerender-manifest.json +3 -3
  35. package/web/.next/required-server-files.js +3 -3
  36. package/web/.next/required-server-files.json +3 -3
  37. package/web/.next/server/app/(dashboard)/@drawer/adopt/page/server-reference-manifest.json +29 -29
  38. package/web/.next/server/app/(dashboard)/@drawer/adopt/page.js.nft.json +1 -1
  39. package/web/.next/server/app/(dashboard)/@drawer/adopt/page_client-reference-manifest.js +1 -1
  40. package/web/.next/server/app/(dashboard)/@drawer/chat/page/server-reference-manifest.json +27 -27
  41. package/web/.next/server/app/(dashboard)/@drawer/chat/page.js.nft.json +1 -1
  42. package/web/.next/server/app/(dashboard)/@drawer/chat/page_client-reference-manifest.js +1 -1
  43. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +30 -30
  44. package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  45. package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  46. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +37 -37
  47. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  48. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  49. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +37 -37
  50. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  51. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  52. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/[tab]/page/server-reference-manifest.json +28 -28
  53. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/[tab]/page.js.nft.json +1 -1
  54. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/[tab]/page_client-reference-manifest.js +1 -1
  55. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +28 -28
  56. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  57. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  58. package/web/.next/server/app/(dashboard)/chat/page/server-reference-manifest.json +27 -27
  59. package/web/.next/server/app/(dashboard)/chat/page.js.nft.json +1 -1
  60. package/web/.next/server/app/(dashboard)/chat/page_client-reference-manifest.js +1 -1
  61. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +30 -30
  62. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  63. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  64. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +37 -37
  65. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  66. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  67. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +37 -37
  68. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  69. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  70. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +27 -27
  71. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  72. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  73. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/[tab]/page/server-reference-manifest.json +28 -28
  74. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/[tab]/page.js.nft.json +1 -1
  75. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/[tab]/page_client-reference-manifest.js +1 -1
  76. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +28 -28
  77. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  78. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  79. package/web/.next/server/app/_global-error.html +2 -2
  80. package/web/.next/server/app/_global-error.rsc +1 -1
  81. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  82. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  83. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  84. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  85. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  86. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +6 -6
  87. package/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  88. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  89. package/web/.next/server/app/settings/page/server-reference-manifest.json +9 -9
  90. package/web/.next/server/app/settings/page.js.nft.json +1 -1
  91. package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  92. package/web/.next/server/app/skills/page/server-reference-manifest.json +11 -11
  93. package/web/.next/server/app/skills/page.js.nft.json +1 -1
  94. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  95. package/web/.next/server/app/tools/page/server-reference-manifest.json +11 -11
  96. package/web/.next/server/app/tools/page.js.nft.json +1 -1
  97. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  98. package/web/.next/server/app/version/page/server-reference-manifest.json +6 -6
  99. package/web/.next/server/app/version/page.js.nft.json +1 -1
  100. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  101. package/web/.next/server/chunks/[root-of-the-server]__2b71641f._.js +1 -1
  102. package/web/.next/server/chunks/[root-of-the-server]__2b71641f._.js.map +1 -1
  103. package/web/.next/server/chunks/[root-of-the-server]__a402b567._.js +1 -1
  104. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  105. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  106. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_feature-drawer-client_tsx_e9755fc8._.js +2 -2
  107. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_feature-drawer-client_tsx_e9755fc8._.js.map +1 -1
  108. package/web/.next/server/chunks/ssr/[root-of-the-server]__1abe77bb._.js +2 -2
  109. package/web/.next/server/chunks/ssr/[root-of-the-server]__1abe77bb._.js.map +1 -1
  110. package/web/.next/server/chunks/ssr/[root-of-the-server]__1f389e5d._.js +1 -1
  111. package/web/.next/server/chunks/ssr/[root-of-the-server]__1f389e5d._.js.map +1 -1
  112. package/web/.next/server/chunks/ssr/[root-of-the-server]__357d99f9._.js +1 -1
  113. package/web/.next/server/chunks/ssr/[root-of-the-server]__563e4faf._.js +1 -1
  114. package/web/.next/server/chunks/ssr/[root-of-the-server]__563e4faf._.js.map +1 -1
  115. package/web/.next/server/chunks/ssr/[root-of-the-server]__7562afc6._.js +2 -2
  116. package/web/.next/server/chunks/ssr/[root-of-the-server]__7562afc6._.js.map +1 -1
  117. package/web/.next/server/chunks/ssr/[root-of-the-server]__821a11c1._.js +1 -1
  118. package/web/.next/server/chunks/ssr/[root-of-the-server]__821a11c1._.js.map +1 -1
  119. package/web/.next/server/chunks/ssr/[root-of-the-server]__86ff0bc5._.js +2 -2
  120. package/web/.next/server/chunks/ssr/[root-of-the-server]__86ff0bc5._.js.map +1 -1
  121. package/web/.next/server/chunks/ssr/[root-of-the-server]__8b0aac03._.js +1 -1
  122. package/web/.next/server/chunks/ssr/[root-of-the-server]__98740ee4._.js +1 -1
  123. package/web/.next/server/chunks/ssr/[root-of-the-server]__98740ee4._.js.map +1 -1
  124. package/web/.next/server/chunks/ssr/[root-of-the-server]__b7b96453._.js +1 -1
  125. package/web/.next/server/chunks/ssr/[root-of-the-server]__b7b96453._.js.map +1 -1
  126. package/web/.next/server/chunks/ssr/[root-of-the-server]__ba9f9e11._.js +1 -1
  127. package/web/.next/server/chunks/ssr/[root-of-the-server]__ba9f9e11._.js.map +1 -1
  128. package/web/.next/server/chunks/ssr/[root-of-the-server]__e0be67c7._.js +1 -1
  129. package/web/.next/server/chunks/ssr/[root-of-the-server]__e0be67c7._.js.map +1 -1
  130. package/web/.next/server/chunks/ssr/_02e01240._.js +1 -1
  131. package/web/.next/server/chunks/ssr/_02e01240._.js.map +1 -1
  132. package/web/.next/server/chunks/ssr/_05c23ad9._.js +1 -1
  133. package/web/.next/server/chunks/ssr/_05c23ad9._.js.map +1 -1
  134. package/web/.next/server/chunks/ssr/_0727935d._.js +1 -1
  135. package/web/.next/server/chunks/ssr/_0727935d._.js.map +1 -1
  136. package/web/.next/server/chunks/ssr/_16eb4fec._.js +1 -1
  137. package/web/.next/server/chunks/ssr/_16eb4fec._.js.map +1 -1
  138. package/web/.next/server/chunks/ssr/_18886033._.js +1 -1
  139. package/web/.next/server/chunks/ssr/_18886033._.js.map +1 -1
  140. package/web/.next/server/chunks/ssr/_22e00a14._.js +1 -1
  141. package/web/.next/server/chunks/ssr/_22e00a14._.js.map +1 -1
  142. package/web/.next/server/chunks/ssr/_2f8f89fb._.js +3 -0
  143. package/web/.next/server/chunks/ssr/{_7a6f6a76._.js.map → _2f8f89fb._.js.map} +1 -1
  144. package/web/.next/server/chunks/ssr/{_8276397a._.js → _52403a07._.js} +2 -2
  145. package/web/.next/server/chunks/ssr/{_8276397a._.js.map → _52403a07._.js.map} +1 -1
  146. package/web/.next/server/chunks/ssr/_56b9d60f._.js +1 -1
  147. package/web/.next/server/chunks/ssr/_56b9d60f._.js.map +1 -1
  148. package/web/.next/server/chunks/ssr/{_0020fddd._.js → _8b57edb8._.js} +2 -2
  149. package/web/.next/server/chunks/ssr/_8b57edb8._.js.map +1 -0
  150. package/web/.next/server/chunks/ssr/_9215e9ec._.js +1 -1
  151. package/web/.next/server/chunks/ssr/_9215e9ec._.js.map +1 -1
  152. package/web/.next/server/chunks/ssr/_a5a5901d._.js +1 -1
  153. package/web/.next/server/chunks/ssr/_a5a5901d._.js.map +1 -1
  154. package/web/.next/server/chunks/ssr/_ad09f271._.js +1 -1
  155. package/web/.next/server/chunks/ssr/_ad09f271._.js.map +1 -1
  156. package/web/.next/server/chunks/ssr/_c3f595c6._.js +1 -1
  157. package/web/.next/server/chunks/ssr/_c3f595c6._.js.map +1 -1
  158. package/web/.next/server/chunks/ssr/{_fdc356bf._.js → _c9f903f2._.js} +2 -2
  159. package/web/.next/server/chunks/ssr/{_fdc356bf._.js.map → _c9f903f2._.js.map} +1 -1
  160. package/web/.next/server/chunks/ssr/_ea9e1556._.js +1 -1
  161. package/web/.next/server/chunks/ssr/_ea9e1556._.js.map +1 -1
  162. package/web/.next/server/chunks/ssr/_f1ba9be6._.js +2 -2
  163. package/web/.next/server/chunks/ssr/_f1ba9be6._.js.map +1 -1
  164. package/web/.next/server/chunks/ssr/_f33cd07e._.js +2 -2
  165. package/web/.next/server/chunks/ssr/_f33cd07e._.js.map +1 -1
  166. package/web/.next/server/chunks/ssr/_f8b45233._.js +1 -1
  167. package/web/.next/server/chunks/ssr/_f8b45233._.js.map +1 -1
  168. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
  169. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
  170. package/web/.next/server/chunks/ssr/f3a1f_components_common_control-center-drawer_repository-drawer-client_tsx_39a00c03._.js +1 -1
  171. package/web/.next/server/chunks/ssr/f3a1f_components_common_control-center-drawer_repository-drawer-client_tsx_39a00c03._.js.map +1 -1
  172. package/web/.next/server/chunks/ssr/src_presentation_web_app_actions_open-ide_ts_baaca5d5._.js +1 -1
  173. package/web/.next/server/chunks/ssr/src_presentation_web_components_895e5bfa._.js +1 -1
  174. package/web/.next/server/chunks/ssr/src_presentation_web_components_895e5bfa._.js.map +1 -1
  175. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  176. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  177. package/web/.next/server/pages/500.html +2 -2
  178. package/web/.next/server/server-reference-manifest.js +1 -1
  179. package/web/.next/server/server-reference-manifest.json +47 -47
  180. package/web/.next/static/chunks/{d6a1a06ee3c12489.js → 25e894a1de46b5fb.js} +1 -1
  181. package/web/.next/static/chunks/{1ad664f7081cf4c0.js → 2609c8fc6f14cb26.js} +1 -1
  182. package/web/.next/static/chunks/{16eea21868510afd.js → 29f0d874b1fde3d6.js} +2 -2
  183. package/web/.next/static/chunks/400e93efac983a76.css +1 -0
  184. package/web/.next/static/chunks/{5d8c83c576ec1a7b.js → 498e45f8e05a5491.js} +1 -1
  185. package/web/.next/static/chunks/{ee9fe376b60c3cab.js → 56955fa252a9f3ed.js} +2 -2
  186. package/web/.next/static/chunks/7f491899a2fe2fd7.js +1 -0
  187. package/web/.next/static/chunks/{dedf6ca63c5468fa.js → a8243f8d06bdcef0.js} +2 -2
  188. package/web/.next/static/chunks/{4788a37d8c608e30.js → ce0316338f7e01e6.js} +1 -1
  189. package/web/.next/static/chunks/{e77d053610055e0c.js → ce87ded6cc38b4de.js} +1 -1
  190. package/web/.next/static/chunks/{9162bb869b3543e2.js → cf000ba1a3f11439.js} +1 -1
  191. package/web/.next/static/chunks/{940bca62e44f3e90.js → cf75fade434602c6.js} +2 -2
  192. package/web/.next/static/chunks/{8094762f5cda4934.js → d9e4d90ef254da84.js} +1 -1
  193. package/web/.next/static/chunks/{7d40e79e524c8d18.js → dc21cf85bfa262a7.js} +2 -2
  194. package/web/.next/static/chunks/e6398f94cffe9bc2.js +1 -0
  195. package/web/.next/static/chunks/{4f7d9c08a205bc4e.js → ecfd93d61bf4d933.js} +1 -1
  196. package/dist/packages/core/src/infrastructure/persistence/sqlite/migrations/051-add-session-usage-tracking.d.ts +0 -18
  197. package/dist/packages/core/src/infrastructure/persistence/sqlite/migrations/051-add-session-usage-tracking.d.ts.map +0 -1
  198. package/dist/packages/core/src/infrastructure/persistence/sqlite/migrations/051-add-session-usage-tracking.js +0 -32
  199. package/web/.next/server/chunks/ssr/_0020fddd._.js.map +0 -1
  200. package/web/.next/server/chunks/ssr/_7a6f6a76._.js +0 -3
  201. package/web/.next/static/chunks/7cec51e9bd7d1f4f.js +0 -1
  202. package/web/.next/static/chunks/a760d5829014058d.js +0 -1
  203. package/web/.next/static/chunks/eeadf13c0ea6cbe0.css +0 -1
  204. /package/web/.next/static/{lQkUWQQS5aR4vq6WUZ2yr → rav6zzO3q2NtCKwg9XZXP}/_buildManifest.js +0 -0
  205. /package/web/.next/static/{lQkUWQQS5aR4vq6WUZ2yr → rav6zzO3q2NtCKwg9XZXP}/_clientMiddlewareManifest.json +0 -0
  206. /package/web/.next/static/{lQkUWQQS5aR4vq6WUZ2yr → rav6zzO3q2NtCKwg9XZXP}/_ssgManifest.js +0 -0
@@ -44,14 +44,6 @@ export class InteractiveSessionService {
44
44
  sessions = new Map();
45
45
  /** Cached agentSessionIds from stopped sessions, keyed by featureId. */
46
46
  stoppedAgentSessionIds = new Map();
47
- /**
48
- * Feature-level subscribers that survive session restarts.
49
- *
50
- * Unlike session-level subscribers (in SessionState.subscribers), these
51
- * persist when a session dies and a new one boots. SSE connections
52
- * subscribe here so they continue receiving events from new sessions.
53
- */
54
- featureSubscribers = new Map();
55
47
  constructor(sessionRepo, messageRepo, executorFactory, featureRepo, contextBuilder) {
56
48
  this.sessionRepo = sessionRepo;
57
49
  this.messageRepo = messageRepo;
@@ -189,10 +181,9 @@ export class InteractiveSessionService {
189
181
  // Create the interactive executor and session
190
182
  const executor = this.executorFactory.createInteractiveExecutor(resolvedAgentType, authConfig);
191
183
  let handle;
192
- const previousAgentSessionId = state.agentSessionId;
193
- if (previousAgentSessionId) {
184
+ if (state.agentSessionId) {
194
185
  // Resume existing SDK session
195
- handle = await executor.resumeSession(previousAgentSessionId, {
186
+ handle = await executor.resumeSession(state.agentSessionId, {
196
187
  cwd: worktreePath,
197
188
  model: state.model,
198
189
  systemPrompt: context,
@@ -227,7 +218,7 @@ export class InteractiveSessionService {
227
218
  if (event.content) {
228
219
  greetingText += event.content;
229
220
  state.currentAssistantBuffer += event.content;
230
- this.notify(state, { delta: event.content, done: false });
221
+ state.subscribers.forEach((sub) => sub({ delta: event.content, done: false }));
231
222
  }
232
223
  break;
233
224
  case 'tool_use':
@@ -235,12 +226,12 @@ export class InteractiveSessionService {
235
226
  const toolLabel = event.label;
236
227
  const toolDetail = event.detail;
237
228
  void this.persistToolEvent(state, toolLabel, toolDetail);
238
- this.notify(state, {
229
+ state.subscribers.forEach((sub) => sub({
239
230
  delta: '',
240
231
  done: false,
241
232
  log: `Using tool: ${toolLabel}`,
242
233
  activity: { kind: 'tool_use', label: toolLabel, detail: toolDetail },
243
- });
234
+ }));
244
235
  }
245
236
  break;
246
237
  case 'tool_result':
@@ -248,18 +239,18 @@ export class InteractiveSessionService {
248
239
  const resultLabel = event.label;
249
240
  const resultDetail = event.detail;
250
241
  void this.persistToolEvent(state, resultLabel, resultDetail);
251
- this.notify(state, {
242
+ state.subscribers.forEach((sub) => sub({
252
243
  delta: '',
253
244
  done: false,
254
245
  log: `Completed: ${resultLabel}`,
255
246
  activity: { kind: 'tool_result', label: resultLabel, detail: resultDetail },
256
- });
247
+ }));
257
248
  }
258
249
  break;
259
250
  case 'status':
260
251
  if (event.content) {
261
252
  const statusContent = event.content;
262
- this.notify(state, { delta: '', done: false, log: statusContent });
253
+ state.subscribers.forEach((sub) => sub({ delta: '', done: false, log: statusContent }));
263
254
  }
264
255
  break;
265
256
  case 'done': {
@@ -268,15 +259,6 @@ export class InteractiveSessionService {
268
259
  // Capture the SDK session ID (available after first message exchange)
269
260
  const sdkSessionId = handle.sessionId;
270
261
  if (sdkSessionId) {
271
- // Detect CWD mismatch: if we tried to resume but got a different
272
- // session ID, the SDK silently created a fresh session (typically
273
- // because the cwd changed or session JSONL was lost).
274
- if (previousAgentSessionId && sdkSessionId !== previousAgentSessionId) {
275
- // eslint-disable-next-line no-console
276
- console.warn(`[InteractiveSession] Session resume mismatch for feature ${featureId}: ` +
277
- `expected ${previousAgentSessionId}, got ${sdkSessionId}. ` +
278
- `SDK created a fresh session (likely cwd changed or session expired).`);
279
- }
280
262
  state.agentSessionId = sdkSessionId;
281
263
  // Persist to DB so it survives service restarts
282
264
  void this.sessionRepo.updateAgentSessionId(state.sessionId, sdkSessionId);
@@ -301,7 +283,7 @@ export class InteractiveSessionService {
301
283
  state.currentAssistantBuffer = '';
302
284
  state.toolEventsLog = [];
303
285
  // Notify subscribers of end-of-turn
304
- this.notify(state, { delta: '', done: true });
286
+ state.subscribers.forEach((sub) => sub({ delta: '', done: true }));
305
287
  // Start idle timer now that the session is live
306
288
  this.resetTimer(state);
307
289
  return; // Boot complete
@@ -452,7 +434,7 @@ export class InteractiveSessionService {
452
434
  if (event.content) {
453
435
  responseText += event.content;
454
436
  state.currentAssistantBuffer += event.content;
455
- this.notify(state, { delta: event.content, done: false });
437
+ state.subscribers.forEach((sub) => sub({ delta: event.content, done: false }));
456
438
  }
457
439
  break;
458
440
  case 'tool_use':
@@ -460,12 +442,12 @@ export class InteractiveSessionService {
460
442
  const toolLabel = event.label;
461
443
  const toolDetail = event.detail;
462
444
  void this.persistToolEvent(state, toolLabel, toolDetail);
463
- this.notify(state, {
445
+ state.subscribers.forEach((sub) => sub({
464
446
  delta: '',
465
447
  done: false,
466
448
  log: `Using tool: ${toolLabel}`,
467
449
  activity: { kind: 'tool_use', label: toolLabel, detail: toolDetail },
468
- });
450
+ }));
469
451
  }
470
452
  break;
471
453
  case 'tool_result':
@@ -473,18 +455,18 @@ export class InteractiveSessionService {
473
455
  const resultLabel = event.label;
474
456
  const resultDetail = event.detail;
475
457
  void this.persistToolEvent(state, resultLabel, resultDetail);
476
- this.notify(state, {
458
+ state.subscribers.forEach((sub) => sub({
477
459
  delta: '',
478
460
  done: false,
479
461
  log: `Completed: ${resultLabel}`,
480
462
  activity: { kind: 'tool_result', label: resultLabel, detail: resultDetail },
481
- });
463
+ }));
482
464
  }
483
465
  break;
484
466
  case 'status':
485
467
  if (event.content) {
486
468
  const statusContent = event.content;
487
- this.notify(state, { delta: '', done: false, log: statusContent });
469
+ state.subscribers.forEach((sub) => sub({ delta: '', done: false, log: statusContent }));
488
470
  }
489
471
  break;
490
472
  case 'done': {
@@ -504,39 +486,17 @@ export class InteractiveSessionService {
504
486
  await this.messageRepo.create(msg);
505
487
  state.currentAssistantBuffer = '';
506
488
  state.toolEventsLog = [];
507
- // Accumulate usage from this turn
508
- if (event.usage) {
509
- void this.sessionRepo.accumulateUsage(state.sessionId, {
510
- costUsd: event.usage.costUsd ?? 0,
511
- inputTokens: event.usage.inputTokens ?? 0,
512
- outputTokens: event.usage.outputTokens ?? 0,
513
- turns: event.usage.numTurns ?? 1,
514
- });
515
- }
516
489
  // Mark as unread — if user has the chat open, the frontend
517
490
  // will immediately call markRead to clear it
518
491
  void this.sessionRepo.updateTurnStatus(state.sessionId, 'unread');
519
492
  // Notify subscribers of end-of-turn
520
- this.notify(state, { delta: '', done: true });
493
+ state.subscribers.forEach((sub) => sub({ delta: '', done: true }));
521
494
  return; // Turn complete
522
495
  }
523
496
  case 'error':
524
497
  // eslint-disable-next-line no-console
525
498
  console.error(`[InteractiveSession] agent error during turn for session ${state.sessionId}:`, event.content);
526
- // Accumulate usage even on errors cost was still incurred
527
- if (event.usage) {
528
- void this.sessionRepo.accumulateUsage(state.sessionId, {
529
- costUsd: event.usage.costUsd ?? 0,
530
- inputTokens: event.usage.inputTokens ?? 0,
531
- outputTokens: event.usage.outputTokens ?? 0,
532
- turns: event.usage.numTurns ?? 1,
533
- });
534
- }
535
- this.notify(state, {
536
- delta: '',
537
- done: true,
538
- log: `Error: ${event.content ?? 'unknown'}`,
539
- });
499
+ state.subscribers.forEach((sub) => sub({ delta: '', done: true, log: `Error: ${event.content ?? 'unknown'}` }));
540
500
  break;
541
501
  case 'init':
542
502
  // The SDK emits init on every turn, but we only show "Session started"
@@ -544,36 +504,32 @@ export class InteractiveSessionService {
544
504
  // spamming the chat with repeated session-started messages.
545
505
  break;
546
506
  case 'api_retry':
547
- this.notify(state, {
548
- delta: '',
549
- done: false,
550
- log: event.content ?? 'Retrying API call...',
551
- });
507
+ state.subscribers.forEach((sub) => sub({ delta: '', done: false, log: event.content ?? 'Retrying API call...' }));
552
508
  break;
553
509
  case 'rate_limit':
554
- this.notify(state, { delta: '', done: false, log: event.content ?? 'Rate limited' });
510
+ state.subscribers.forEach((sub) => sub({ delta: '', done: false, log: event.content ?? 'Rate limited' }));
555
511
  break;
556
512
  case 'task_started':
557
513
  if (event.content) {
558
514
  void this.persistToolEvent(state, 'Subtask started', event.content);
559
- this.notify(state, {
515
+ state.subscribers.forEach((sub) => sub({
560
516
  delta: '',
561
517
  done: false,
562
518
  log: `Subtask: ${event.content}`,
563
519
  activity: { kind: 'system', label: 'Subtask started', detail: event.content },
564
- });
520
+ }));
565
521
  }
566
522
  break;
567
523
  case 'task_progress':
568
524
  if (event.content) {
569
- this.notify(state, { delta: '', done: false, log: `Subtask: ${event.content}` });
525
+ state.subscribers.forEach((sub) => sub({ delta: '', done: false, log: `Subtask: ${event.content}` }));
570
526
  }
571
527
  break;
572
528
  case 'task_done':
573
529
  if (event.content) {
574
530
  const taskStatus = event.detail ?? 'completed';
575
531
  void this.persistToolEvent(state, `Subtask ${taskStatus}`, event.content);
576
- this.notify(state, {
532
+ state.subscribers.forEach((sub) => sub({
577
533
  delta: '',
578
534
  done: false,
579
535
  log: `Subtask ${taskStatus}: ${event.content}`,
@@ -582,7 +538,7 @@ export class InteractiveSessionService {
582
538
  label: `Subtask ${taskStatus}`,
583
539
  detail: event.content,
584
540
  },
585
- });
541
+ }));
586
542
  }
587
543
  break;
588
544
  }
@@ -607,18 +563,14 @@ export class InteractiveSessionService {
607
563
  await this.messageRepo.create(msg);
608
564
  state.currentAssistantBuffer = '';
609
565
  state.toolEventsLog = [];
610
- this.notify(state, { delta: '', done: true });
566
+ state.subscribers.forEach((sub) => sub({ delta: '', done: true }));
611
567
  }
612
568
  else if (!responseText) {
613
569
  // Stream ended without any response — SDK session likely died.
614
570
  // Mark as error so the next message triggers a fresh session.
615
571
  // eslint-disable-next-line no-console
616
572
  console.error(`[InteractiveSession] stream ended without response for session ${state.sessionId} — session may have died`);
617
- this.notify(state, {
618
- delta: '',
619
- done: true,
620
- log: 'Session disconnected — will restart on next message',
621
- });
573
+ state.subscribers.forEach((sub) => sub({ delta: '', done: true, log: 'Session disconnected — will restart on next message' }));
622
574
  if (state.agentSessionId) {
623
575
  this.stoppedAgentSessionIds.set(state.featureId, state.agentSessionId);
624
576
  }
@@ -677,7 +629,7 @@ export class InteractiveSessionService {
677
629
  // ---------------------------------------------------------------------------
678
630
  // Feature-scoped API (frontend doesn't manage sessions)
679
631
  // ---------------------------------------------------------------------------
680
- async sendUserMessage(featureId, content, worktreePath, model, agentType) {
632
+ async sendUserMessage(featureId, content, worktreePath) {
681
633
  // 1. Persist user message to DB immediately — this is the source of truth
682
634
  const now = new Date();
683
635
  const userMsg = {
@@ -690,21 +642,7 @@ export class InteractiveSessionService {
690
642
  };
691
643
  await this.messageRepo.create(userMsg);
692
644
  // 2. Find active session for this feature
693
- let state = this.findActiveStateForFeature(featureId);
694
- // If the caller requested a different model/agent than the running session,
695
- // silently stop the current session so a new one boots with the new config.
696
- // Also clear the cached agentSessionId so we create a fresh SDK session
697
- // instead of resuming the old one (which would keep the old model).
698
- if (state && model && state.model !== model) {
699
- await this.stopSession(state.sessionId);
700
- this.stoppedAgentSessionIds.delete(featureId);
701
- state = undefined;
702
- }
703
- else if (state && agentType && state.agentType !== agentType) {
704
- await this.stopSession(state.sessionId);
705
- this.stoppedAgentSessionIds.delete(featureId);
706
- state = undefined;
707
- }
645
+ const state = this.findActiveStateForFeature(featureId);
708
646
  if (state) {
709
647
  const dbSession = await this.sessionRepo.findById(state.sessionId);
710
648
  if (dbSession?.status === InteractiveSessionStatus.ready) {
@@ -736,7 +674,7 @@ export class InteractiveSessionService {
736
674
  await this.sessionRepo.updateStatus(dbSession.id, InteractiveSessionStatus.stopped, new Date());
737
675
  }
738
676
  // Boot a new session — startSession will find the agentSessionId from DB
739
- const session = await this.startSession(featureId, worktreePath, model, agentType);
677
+ const session = await this.startSession(featureId, worktreePath);
740
678
  const newState = this.sessions.get(session.id);
741
679
  if (newState) {
742
680
  newState.pendingUserContent = content;
@@ -760,7 +698,6 @@ export class InteractiveSessionService {
760
698
  }
761
699
  // Resolve model display: explicit override > default
762
700
  const displayModel = state.model ?? 'claude-sonnet-4-6';
763
- const usage = await this.sessionRepo.getUsage(state.sessionId);
764
701
  sessionInfo = {
765
702
  pid: null, // SDK manages process internally
766
703
  sessionId: state.agentSessionId ?? state.sessionId,
@@ -772,9 +709,6 @@ export class InteractiveSessionService {
772
709
  lastActivityAt: dbSession?.lastActivityAt
773
710
  ? new Date(dbSession.lastActivityAt).toISOString()
774
711
  : new Date().toISOString(),
775
- totalCostUsd: usage?.totalCostUsd ?? null,
776
- totalInputTokens: usage?.totalInputTokens ?? null,
777
- totalOutputTokens: usage?.totalOutputTokens ?? null,
778
712
  };
779
713
  }
780
714
  else {
@@ -785,7 +719,6 @@ export class InteractiveSessionService {
785
719
  // Show DB info even without live process (process was lost on restart)
786
720
  if (latest.status !== InteractiveSessionStatus.stopped &&
787
721
  latest.status !== InteractiveSessionStatus.error) {
788
- const latestUsage = await this.sessionRepo.getUsage(latest.id);
789
722
  sessionInfo = {
790
723
  pid: null,
791
724
  sessionId: latest.id,
@@ -797,9 +730,6 @@ export class InteractiveSessionService {
797
730
  lastActivityAt: latest.lastActivityAt
798
731
  ? new Date(latest.lastActivityAt).toISOString()
799
732
  : new Date().toISOString(),
800
- totalCostUsd: latestUsage?.totalCostUsd ?? null,
801
- totalInputTokens: latestUsage?.totalInputTokens ?? null,
802
- totalOutputTokens: latestUsage?.totalOutputTokens ?? null,
803
733
  };
804
734
  }
805
735
  }
@@ -822,26 +752,29 @@ export class InteractiveSessionService {
822
752
  return { messages, sessionStatus, streamingText, sessionInfo, turnStatus };
823
753
  }
824
754
  subscribeByFeature(featureId, onChunk) {
825
- // Subscribe at the feature level so the callback survives session restarts.
826
- // When a session dies (idle timeout, error) and a new one boots, the SSE
827
- // connection keeps receiving events from the new session automatically.
828
- let subs = this.featureSubscribers.get(featureId);
829
- if (!subs) {
830
- subs = new Set();
831
- this.featureSubscribers.set(featureId, subs);
832
- }
833
- subs.add(onChunk);
834
- return () => {
835
- subs.delete(onChunk);
836
- if (subs.size === 0) {
837
- this.featureSubscribers.delete(featureId);
838
- }
839
- };
755
+ const state = this.findActiveStateForFeature(featureId);
756
+ if (!state) {
757
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
758
+ return () => { };
759
+ }
760
+ state.subscribers.add(onChunk);
761
+ return () => state.subscribers.delete(onChunk);
840
762
  }
841
763
  async stopByFeature(featureId) {
842
764
  const state = this.findActiveStateForFeature(featureId);
843
765
  if (!state)
844
766
  return;
767
+ // Persist a system message before killing
768
+ const msg = {
769
+ id: crypto.randomUUID(),
770
+ featureId,
771
+ sessionId: state.sessionId,
772
+ role: InteractiveMessageRole.assistant,
773
+ content: '**Session stopped by user**',
774
+ createdAt: new Date(),
775
+ updatedAt: new Date(),
776
+ };
777
+ await this.messageRepo.create(msg);
845
778
  await this.stopSession(state.sessionId);
846
779
  }
847
780
  async markRead(featureId) {
@@ -921,23 +854,6 @@ export class InteractiveSessionService {
921
854
  }
922
855
  }
923
856
  // ---------------------------------------------------------------------------
924
- // Event dispatch
925
- // ---------------------------------------------------------------------------
926
- /**
927
- * Dispatch a StreamChunk to all subscribers for a session.
928
- *
929
- * Sends to both session-level subscribers (legacy, for sessionId-based
930
- * subscribe()) and feature-level subscribers (for SSE connections that
931
- * must survive session restarts).
932
- */
933
- notify(state, chunk) {
934
- state.subscribers.forEach((sub) => sub(chunk));
935
- const featureSubs = this.featureSubscribers.get(state.featureId);
936
- if (featureSubs) {
937
- featureSubs.forEach((sub) => sub(chunk));
938
- }
939
- }
940
- // ---------------------------------------------------------------------------
941
857
  // Timer helpers
942
858
  // ---------------------------------------------------------------------------
943
859
  /** Start or restart the idle timeout timer for a session. */
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/presentation/web/app/api/interactive/chat/[featureId]/messages/route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAO3C,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAIvC,UAAU,WAAW;IACnB,MAAM,EAAE,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxC;AAED,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CA8C/F;AAED,wBAAsB,MAAM,CAC1B,QAAQ,EAAE,WAAW,EACrB,EAAE,MAAM,EAAE,EAAE,WAAW,GACtB,OAAO,CAAC,YAAY,CAAC,CAgBvB;AAED,wBAAsB,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAe/F"}
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/presentation/web/app/api/interactive/chat/[featureId]/messages/route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAO3C,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAIvC,UAAU,WAAW;IACnB,MAAM,EAAE,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxC;AAED,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAuC/F;AAED,wBAAsB,MAAM,CAC1B,QAAQ,EAAE,WAAW,EACrB,EAAE,MAAM,EAAE,EAAE,WAAW,GACtB,OAAO,CAAC,YAAY,CAAC,CAgBvB;AAED,wBAAsB,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAe/F"}
@@ -18,7 +18,7 @@ export async function POST(request, { params }) {
18
18
  try {
19
19
  const { featureId } = await params;
20
20
  const body = (await request.json());
21
- const { content, worktreePath, model, agentType } = body;
21
+ const { content, worktreePath } = body;
22
22
  if (!content || typeof content !== 'string' || content.trim().length === 0) {
23
23
  return NextResponse.json({ error: 'content must be a non-empty string' }, { status: 400 });
24
24
  }
@@ -35,8 +35,6 @@ export async function POST(request, { params }) {
35
35
  featureId,
36
36
  content,
37
37
  worktreePath: resolvedWorktreePath,
38
- model: typeof model === 'string' ? model : undefined,
39
- agentType: typeof agentType === 'string' ? agentType : undefined,
40
38
  });
41
39
  return NextResponse.json({ message }, { status: 201 });
42
40
  }
@@ -152,12 +152,6 @@ function ThinkingIndicator({ booting }) {
152
152
  // ── Message metadata (timestamp + relative time) ────────────────────────────
153
153
  function MessageMeta() {
154
154
  const message = useMessage();
155
- // Tick every 30s so relative timestamps stay fresh
156
- const [tick, setTick] = useState(0);
157
- useEffect(() => {
158
- const id = setInterval(() => setTick((t) => t + 1), 30_000);
159
- return () => clearInterval(id);
160
- }, []);
161
155
  const meta = useMemo(() => {
162
156
  if (!message?.createdAt)
163
157
  return null;
@@ -168,8 +162,7 @@ function MessageMeta() {
168
162
  time: date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
169
163
  relative: formatRelativeTime(date),
170
164
  };
171
- // eslint-disable-next-line react-hooks/exhaustive-deps
172
- }, [message?.createdAt, tick]);
165
+ }, [message?.createdAt]);
173
166
  if (!meta)
174
167
  return null;
175
168
  return (_jsx("span", { className: "text-muted-foreground/60 text-[10px]", title: meta.time, children: meta.relative }));
@@ -1 +1 @@
1
- {"version":3,"file":"ChatTab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/chat/ChatTab.tsx"],"names":[],"mappings":"AAcA,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAID,wBAAgB,OAAO,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,EAAE,YAAY,2CAwGhE"}
1
+ {"version":3,"file":"ChatTab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/chat/ChatTab.tsx"],"names":[],"mappings":"AAcA,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,OAAO,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,EAAE,YAAY,2CAgGhE"}
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { useCallback, useState } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
  import { AssistantRuntimeProvider } from '@assistant-ui/react';
6
- import { Trash2, Cpu } from 'lucide-react';
6
+ import { Trash2, Square, Cpu } from 'lucide-react';
7
7
  import { cn } from '../../../lib/utils.js';
8
8
  import { Thread } from '../../assistant-ui/thread.js';
9
9
  import { useAttachments } from '../../../hooks/use-attachments.js';
@@ -11,20 +11,12 @@ import { composeUserInput } from '../../../app/actions/compose-user-input.js';
11
11
  import { AgentModelPicker } from '../../features/settings/AgentModelPicker/index.js';
12
12
  import { useChatRuntime } from './useChatRuntime.js';
13
13
  import { ChatComposer } from './ChatComposer.js';
14
- const IS_DEV = process.env.NODE_ENV === 'development';
15
14
  export function ChatTab({ featureId, worktreePath }) {
16
15
  const [overrideAgent, setOverrideAgent] = useState(undefined);
17
16
  const [overrideModel, setOverrideModel] = useState(undefined);
18
- const [debugMode, setDebugMode] = useState(false);
19
17
  const att = useAttachments();
20
18
  const contentTransform = useCallback((content) => composeUserInput(content, att.completedAttachments.map((a) => ({ path: a.path, name: a.name, notes: a.notes }))), [att.completedAttachments]);
21
- const { runtime, status, clearChat, sessionInfo, isChatLoading } = useChatRuntime(featureId, worktreePath, {
22
- contentTransform,
23
- onMessageSent: att.clearAttachments,
24
- model: overrideModel,
25
- agentType: overrideAgent,
26
- debugMode,
27
- });
19
+ const { runtime, status, clearChat, stopAgent, sessionInfo, isChatLoading } = useChatRuntime(featureId, worktreePath, { contentTransform, onMessageSent: att.clearAttachments });
28
20
  const handlePickFiles = useCallback(async () => {
29
21
  try {
30
22
  const res = await fetch('/api/dialog/pick-files');
@@ -54,16 +46,18 @@ export function ChatTab({ featureId, worktreePath }) {
54
46
  setOverrideAgent(agent);
55
47
  setOverrideModel(model);
56
48
  }, className: "w-55" }) }));
57
- return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsx(ChatHeader, { sessionInfo: sessionInfo, isAgentActive: status.isRunning, onClear: clearChat, debugMode: debugMode, onDebugToggle: IS_DEV ? setDebugMode : undefined }), _jsx("div", { className: "flex min-h-0 flex-1 flex-col", children: isChatLoading ? (_jsx(ChatSkeleton, {})) : (_jsx(AssistantRuntimeProvider, { runtime: runtime, children: _jsx(Thread, { composer: composer }) })) })] }));
49
+ return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsx(ChatHeader, { sessionInfo: sessionInfo, isAgentActive: status.isRunning, onClear: clearChat, onStop: stopAgent }), _jsx("div", { className: "flex min-h-0 flex-1 flex-col", children: isChatLoading ? (_jsx(ChatSkeleton, {})) : (_jsx(AssistantRuntimeProvider, { runtime: runtime, children: _jsx(Thread, { composer: composer }) })) })] }));
58
50
  }
59
51
  // ── Loading skeleton ────────────────────────────────────────────────────────
60
52
  function ChatSkeleton() {
61
53
  return (_jsxs("div", { className: "flex flex-1 flex-col gap-3 p-4 pt-6", children: [_jsxs("div", { className: "flex items-start gap-2.5", children: [_jsx("div", { className: "bg-muted h-6 w-6 animate-pulse rounded-full" }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx("div", { className: "bg-muted h-4 w-48 animate-pulse rounded-lg" }), _jsx("div", { className: "bg-muted h-4 w-72 animate-pulse rounded-lg" }), _jsx("div", { className: "bg-muted h-4 w-36 animate-pulse rounded-lg" })] })] }), _jsxs("div", { className: "flex items-start gap-2.5", children: [_jsx("div", { className: "bg-muted h-6 w-6 animate-pulse rounded-full" }), _jsx("div", { className: "bg-muted h-4 w-32 animate-pulse rounded-lg" })] }), _jsxs("div", { className: "flex items-start gap-2.5", children: [_jsx("div", { className: "bg-muted h-6 w-6 animate-pulse rounded-full" }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx("div", { className: "bg-muted h-4 w-56 animate-pulse rounded-lg" }), _jsx("div", { className: "bg-muted h-4 w-64 animate-pulse rounded-lg" })] })] })] }));
62
54
  }
63
55
  // ── Chat header — compact session info + actions ─────────────────────────────
64
- function ChatHeader({ sessionInfo, isAgentActive, onClear, debugMode, onDebugToggle, }) {
56
+ function ChatHeader({ sessionInfo, isAgentActive, onClear, onStop, }) {
65
57
  const { t } = useTranslation('web');
66
- return (_jsxs("div", { className: "flex h-8 shrink-0 items-center border-b px-3", children: [_jsx("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: sessionInfo ? (_jsxs(_Fragment, { children: [isAgentActive ? (_jsx("span", { className: "h-1.5 w-1.5 shrink-0 animate-pulse rounded-full bg-emerald-500" })) : (_jsx(Cpu, { className: "text-muted-foreground/40 h-3 w-3 shrink-0" })), _jsxs("span", { className: "text-muted-foreground font-mono text-[10px]", children: [sessionInfo.model ?? 'agent', sessionInfo.sessionId ? ` · ${sessionInfo.sessionId.slice(0, 8)}` : ''] })] })) : (_jsx("span", { className: "text-muted-foreground/40 text-[11px]", children: t('chat.noSession') })) }), _jsxs("div", { className: "flex items-center gap-1 ps-2", children: [onDebugToggle ? (_jsxs("label", { className: "text-muted-foreground/60 flex cursor-pointer items-center gap-1 text-[10px]", children: [_jsx("input", { type: "checkbox", checked: debugMode, onChange: (e) => onDebugToggle(e.target.checked), className: "h-3 w-3 cursor-pointer rounded" }), "Debug"] })) : null, _jsxs(ToolbarButton, { onClick: () => {
58
+ return (_jsxs("div", { className: "flex h-8 shrink-0 items-center border-b px-3", children: [_jsx("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: sessionInfo ? (_jsxs(_Fragment, { children: [isAgentActive ? (_jsx("span", { className: "h-1.5 w-1.5 shrink-0 animate-pulse rounded-full bg-emerald-500" })) : (_jsx(Cpu, { className: "text-muted-foreground/40 h-3 w-3 shrink-0" })), _jsxs("span", { className: "text-muted-foreground font-mono text-[10px]", children: [sessionInfo.model ?? 'agent', sessionInfo.sessionId ? ` · ${sessionInfo.sessionId.slice(0, 8)}` : ''] })] })) : (_jsx("span", { className: "text-muted-foreground/40 text-[11px]", children: t('chat.noSession') })) }), _jsxs("div", { className: "flex items-center gap-1 ps-2", children: [sessionInfo ? (_jsxs(_Fragment, { children: [_jsxs(ToolbarButton, { onClick: () => {
59
+ void onStop();
60
+ }, title: t('chat.forceStopAgent'), variant: "danger", children: [_jsx(Square, { className: "h-2.5 w-2.5 fill-current" }), _jsx("span", { children: t('chat.stop') })] }), _jsx("span", { className: "text-border mx-0.5", children: "|" })] })) : null, _jsxs(ToolbarButton, { onClick: () => {
67
61
  void onClear();
68
62
  }, title: t('chat.clearChatHistory'), children: [_jsx(Trash2, { className: "h-2.5 w-2.5" }), _jsx("span", { children: t('chat.clear') })] })] })] }));
69
63
  }
@@ -5,9 +5,6 @@ interface SessionInfo {
5
5
  startedAt: string;
6
6
  idleTimeoutMinutes: number;
7
7
  lastActivityAt: string;
8
- totalCostUsd: number | null;
9
- totalInputTokens: number | null;
10
- totalOutputTokens: number | null;
11
8
  }
12
9
  export interface ChatStatus {
13
10
  /** Whether the agent is actively working (booting, thinking, streaming). */
@@ -20,19 +17,6 @@ export interface ChatRuntimeOptions {
20
17
  contentTransform?: (content: string) => string;
21
18
  /** Called after a message is successfully sent (e.g. clear attachments). */
22
19
  onMessageSent?: () => void;
23
- /** Override model for new sessions (e.g. 'claude-sonnet-4-6'). */
24
- model?: string;
25
- /** Override agent type for new sessions (e.g. 'claude-code'). */
26
- agentType?: string;
27
- /** When true, inject debug bubbles showing SSE events, session info, etc. */
28
- debugMode?: boolean;
29
- }
30
- /** A debug event captured from SSE for display in debug mode. */
31
- export interface DebugEvent {
32
- id: string;
33
- timestamp: Date;
34
- label: string;
35
- detail?: string;
36
20
  }
37
21
  /**
38
22
  * `featureId` is a polymorphic scope key: a feature UUID, "repo-<id>", or "global".
@@ -1 +1 @@
1
- {"version":3,"file":"useChatRuntime.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/chat/useChatRuntime.ts"],"names":[],"mappings":"AAkBA,UAAU,WAAW;IACnB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAkDD,MAAM,WAAW,UAAU;IACzB,4EAA4E;IAC5E,SAAS,EAAE,OAAO,CAAC;IACnB,qFAAqF;IACrF,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAID,MAAM,WAAW,kBAAkB;IACjC,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/C,4EAA4E;IAC5E,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,iEAAiE;AACjE,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,IAAI,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,YAAY,CAAC,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,kBAAkB;;;;;;;EAgW7B"}
1
+ {"version":3,"file":"useChatRuntime.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/chat/useChatRuntime.ts"],"names":[],"mappings":"AAkBA,UAAU,WAAW;IACnB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;CACxB;AAgDD,MAAM,WAAW,UAAU;IACzB,4EAA4E;IAC5E,SAAS,EAAE,OAAO,CAAC;IACnB,qFAAqF;IACrF,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAID,MAAM,WAAW,kBAAkB;IACjC,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/C,4EAA4E;IAC5E,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,YAAY,CAAC,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,kBAAkB;;;;;;;EAiQ7B"}