@jacexh/claude-web-console 0.10.6 → 0.11.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 (64) hide show
  1. package/client/dist/assets/{_baseUniq-68MegVue.js → _baseUniq-C8KadaZm.js} +1 -1
  2. package/client/dist/assets/{arc-DGs2oxvI.js → arc-B2W9x-cV.js} +1 -1
  3. package/client/dist/assets/{architectureDiagram-Q4EWVU46-9f3V68WI.js → architectureDiagram-Q4EWVU46-BRjvIRM_.js} +1 -1
  4. package/client/dist/assets/{blockDiagram-DXYQGD6D-nw_XrJPn.js → blockDiagram-DXYQGD6D-BcTkMEef.js} +1 -1
  5. package/client/dist/assets/{c4Diagram-AHTNJAMY-DtmpkbCH.js → c4Diagram-AHTNJAMY-egjSISEz.js} +1 -1
  6. package/client/dist/assets/channel-De3V3xku.js +1 -0
  7. package/client/dist/assets/{chunk-4BX2VUAB-CXkBrbXl.js → chunk-4BX2VUAB-5ky7wtNG.js} +1 -1
  8. package/client/dist/assets/{chunk-4TB4RGXK-Cpwuap5g.js → chunk-4TB4RGXK-CT-lH0qn.js} +1 -1
  9. package/client/dist/assets/{chunk-55IACEB6-MDkMv1Vj.js → chunk-55IACEB6-D9CKt7OU.js} +1 -1
  10. package/client/dist/assets/{chunk-EDXVE4YY-fWFZkL3K.js → chunk-EDXVE4YY-NwC01K04.js} +1 -1
  11. package/client/dist/assets/{chunk-FMBD7UC4-aLTS4ppS.js → chunk-FMBD7UC4-BCG3_zqC.js} +1 -1
  12. package/client/dist/assets/{chunk-OYMX7WX6-CdiOjMj9.js → chunk-OYMX7WX6-DJSGZ6jV.js} +1 -1
  13. package/client/dist/assets/{chunk-QZHKN3VN-B9w_-JI6.js → chunk-QZHKN3VN-BBIVA3HS.js} +1 -1
  14. package/client/dist/assets/{chunk-YZCP3GAM-BCHcb4hM.js → chunk-YZCP3GAM-C09sqCat.js} +1 -1
  15. package/client/dist/assets/classDiagram-6PBFFD2Q-CqsWJaxt.js +1 -0
  16. package/client/dist/assets/classDiagram-v2-HSJHXN6E-CqsWJaxt.js +1 -0
  17. package/client/dist/assets/clone-5Qb140gW.js +1 -0
  18. package/client/dist/assets/{cose-bilkent-S5V4N54A-D-D1pyZ4.js → cose-bilkent-S5V4N54A-35P9iw0b.js} +1 -1
  19. package/client/dist/assets/{dagre-KV5264BT-6Cz3n9So.js → dagre-KV5264BT-CXV-WCZD.js} +1 -1
  20. package/client/dist/assets/{diagram-5BDNPKRD-DFjJzsE0.js → diagram-5BDNPKRD-Dp3u_O_t.js} +1 -1
  21. package/client/dist/assets/{diagram-G4DWMVQ6-BS9PCHKv.js → diagram-G4DWMVQ6-BtWAfxpb.js} +1 -1
  22. package/client/dist/assets/{diagram-MMDJMWI5-CsF9E5G3.js → diagram-MMDJMWI5-hf_41SmM.js} +1 -1
  23. package/client/dist/assets/{diagram-TYMM5635-CZ413KNX.js → diagram-TYMM5635-BjK1YnVW.js} +1 -1
  24. package/client/dist/assets/{erDiagram-SMLLAGMA-WO8fXuNp.js → erDiagram-SMLLAGMA-CmcBJzib.js} +1 -1
  25. package/client/dist/assets/{flowDiagram-DWJPFMVM-CzBUR3uU.js → flowDiagram-DWJPFMVM-D90oEHbY.js} +1 -1
  26. package/client/dist/assets/{ganttDiagram-T4ZO3ILL-B60qcaB5.js → ganttDiagram-T4ZO3ILL-BkpoWXoe.js} +1 -1
  27. package/client/dist/assets/{gitGraphDiagram-UUTBAWPF-CnB_Lftl.js → gitGraphDiagram-UUTBAWPF-C8zLE_7k.js} +1 -1
  28. package/client/dist/assets/{graph-CwsRpftd.js → graph-Bh3K-muc.js} +1 -1
  29. package/client/dist/assets/{index-BlOeGePC.js → index-DBmP29uk.js} +77 -77
  30. package/client/dist/assets/{infoDiagram-42DDH7IO-CPfOWmw3.js → infoDiagram-42DDH7IO-BM0WXxrt.js} +1 -1
  31. package/client/dist/assets/{ishikawaDiagram-UXIWVN3A-DbLJUVid.js → ishikawaDiagram-UXIWVN3A-UAHaEHNp.js} +1 -1
  32. package/client/dist/assets/{journeyDiagram-VCZTEJTY-Dbl8zh4b.js → journeyDiagram-VCZTEJTY-Cafrpd2t.js} +1 -1
  33. package/client/dist/assets/{kanban-definition-6JOO6SKY-Csb1o0yW.js → kanban-definition-6JOO6SKY-D0ZRwwaM.js} +1 -1
  34. package/client/dist/assets/{layout-BK4Q6R8I.js → layout-D9SZ1QBf.js} +1 -1
  35. package/client/dist/assets/{linear-CvJiszRa.js → linear-CpQg9EBQ.js} +1 -1
  36. package/client/dist/assets/{mermaid.core-B3-wKFnz.js → mermaid.core-Cff27cfj.js} +4 -4
  37. package/client/dist/assets/{min-D5RjKPqt.js → min-CCDTUlLD.js} +1 -1
  38. package/client/dist/assets/{mindmap-definition-QFDTVHPH-HPPx8VRM.js → mindmap-definition-QFDTVHPH-1_xvkSC-.js} +1 -1
  39. package/client/dist/assets/{pieDiagram-DEJITSTG-BBd6S7qX.js → pieDiagram-DEJITSTG-CXJh-BmH.js} +1 -1
  40. package/client/dist/assets/{quadrantDiagram-34T5L4WZ-5mFLAEO_.js → quadrantDiagram-34T5L4WZ-IIET-zJK.js} +1 -1
  41. package/client/dist/assets/{requirementDiagram-MS252O5E-D_j8Btvg.js → requirementDiagram-MS252O5E-CydtzCWy.js} +1 -1
  42. package/client/dist/assets/{sankeyDiagram-XADWPNL6-D-mJMVBg.js → sankeyDiagram-XADWPNL6-ByQ8OATo.js} +1 -1
  43. package/client/dist/assets/{sequenceDiagram-FGHM5R23-Du_HXzIS.js → sequenceDiagram-FGHM5R23-D1qYShIQ.js} +1 -1
  44. package/client/dist/assets/{stateDiagram-FHFEXIEX-DK1zMyaT.js → stateDiagram-FHFEXIEX-CLldWrCV.js} +1 -1
  45. package/client/dist/assets/stateDiagram-v2-QKLJ7IA2-B5cjRsZb.js +1 -0
  46. package/client/dist/assets/{timeline-definition-GMOUNBTQ-C4sXzll7.js → timeline-definition-GMOUNBTQ-DnPj25Y5.js} +1 -1
  47. package/client/dist/assets/{vennDiagram-DHZGUBPP-CxaV9frx.js → vennDiagram-DHZGUBPP-CnaH-R-v.js} +1 -1
  48. package/client/dist/assets/{wardley-RL74JXVD-Ci_gt1QK.js → wardley-RL74JXVD-D-Yc5POx.js} +1 -1
  49. package/client/dist/assets/{wardleyDiagram-NUSXRM2D-BVBkkfaG.js → wardleyDiagram-NUSXRM2D-BqXhvnsn.js} +1 -1
  50. package/client/dist/assets/{xychartDiagram-5P7HB3ND-Dgg3kv43.js → xychartDiagram-5P7HB3ND--56nlnda.js} +1 -1
  51. package/client/dist/index.html +1 -1
  52. package/package.json +1 -1
  53. package/server/src/__tests__/session-status-forward.test.ts +39 -0
  54. package/server/src/__tests__/session-status.test.ts +71 -0
  55. package/server/src/__tests__/turn-started-broadcast.test.ts +53 -0
  56. package/server/src/session-manager.ts +25 -9
  57. package/server/src/session-status.ts +30 -0
  58. package/server/src/types.ts +9 -1
  59. package/server/src/ws-handler.ts +6 -0
  60. package/client/dist/assets/channel-C81EZqNp.js +0 -1
  61. package/client/dist/assets/classDiagram-6PBFFD2Q-Bwl-Yyn3.js +0 -1
  62. package/client/dist/assets/classDiagram-v2-HSJHXN6E-Bwl-Yyn3.js +0 -1
  63. package/client/dist/assets/clone-DwEzo7pK.js +0 -1
  64. package/client/dist/assets/stateDiagram-v2-QKLJ7IA2-BHkgOSON.js +0 -1
@@ -17,6 +17,8 @@ import { join } from 'node:path'
17
17
  import { homedir } from 'node:os'
18
18
  import type { FastifyBaseLogger } from 'fastify'
19
19
  import type { SessionInfo, EffortLevel } from './types.js'
20
+ import { shouldBroadcastTurnStarted, type TurnState } from './turn-lifecycle.js'
21
+ import { SessionStatusTracker } from './session-status.js'
20
22
 
21
23
  type PermissionResolver = {
22
24
  resolve: (approved: boolean, reason?: string, updatedPermissions?: import('@anthropic-ai/claude-agent-sdk').PermissionUpdate[]) => void
@@ -147,7 +149,11 @@ export class SessionManager {
147
149
  private sessions = new Map<string, SDKSession>()
148
150
  private pendingPermissions = new Map<string, PermissionResolver>()
149
151
  private pendingElicitations = new Map<string, { resolve: (result: ElicitationResult) => void; sessionId: string }>()
150
- private runningSessionIds = new Set<string>()
152
+ private sessionStatus = new SessionStatusTracker((sessionId, status) => {
153
+ this.broadcast(sessionId, (l) => l.onMessage(sessionId, {
154
+ type: 'session_status', sessionId, status,
155
+ } as unknown as SDKMessage))
156
+ })
151
157
  private closedSessionIds = new Set<string>()
152
158
  private streamingSessionIds = new Set<string>()
153
159
  private sessionListeners = new Map<string, Set<SessionListener>>()
@@ -368,6 +374,7 @@ export class SessionManager {
368
374
  // Use a temporary ID, then remap when real sessionId arrives.
369
375
  const tempId = `pending-${Date.now()}`
370
376
  this.sessions.set(tempId, session)
377
+ this.sessionStatus.set(tempId, 'idle')
371
378
  this.sessionCwds.set(tempId, cwd)
372
379
  this.sessionCreationOptions.set(tempId, {
373
380
  model: options?.model,
@@ -425,6 +432,7 @@ export class SessionManager {
425
432
  while (true) {
426
433
  this.log.info({ sessionId: currentSessionId }, 'consumeStream: entering stream()')
427
434
  const query = session.stream()
435
+ const turnState: TurnState = { turnStarted: false }
428
436
  // Store query reference so control requests (setModel etc.) can use it
429
437
  try {
430
438
  const sid = session.sessionId
@@ -489,6 +497,7 @@ export class SessionManager {
489
497
  } else {
490
498
  this.log.info({ cost: (msgAny as Record<string, unknown>).total_cost_usd }, 'Turn complete')
491
499
  }
500
+ this.sessionStatus.set(currentSessionId, 'idle')
492
501
  }
493
502
 
494
503
  let sessionId: string
@@ -514,6 +523,9 @@ export class SessionManager {
514
523
  if (listeners) { this.sessionListeners.delete(tempId); this.sessionListeners.set(sessionId, listeners) }
515
524
  this.streamingSessionIds.delete(tempId)
516
525
  this.streamingSessionIds.add(sessionId)
526
+ const prevStatus = this.sessionStatus.get(tempId)
527
+ this.sessionStatus.delete(tempId)
528
+ if (prevStatus) this.sessionStatus.set(sessionId, prevStatus)
517
529
  const q = this.activeQueries.get(tempId)
518
530
  if (q) { this.activeQueries.delete(tempId); this.activeQueries.set(sessionId, q) }
519
531
  this.pendingRemaps.delete(tempId)
@@ -525,7 +537,10 @@ export class SessionManager {
525
537
  } as unknown as SDKMessage))
526
538
  }
527
539
 
528
- this.runningSessionIds.add(sessionId)
540
+ // Set session status to running on first message of each turn
541
+ if (shouldBroadcastTurnStarted(turnState)) {
542
+ this.sessionStatus.set(sessionId, 'running')
543
+ }
529
544
  this.broadcast(sessionId, (l) => l.onMessage(sessionId, msg))
530
545
  }
531
546
  this.log.info({ sessionId: currentSessionId }, 'consumeStream: stream() ended (turn complete)')
@@ -544,12 +559,12 @@ export class SessionManager {
544
559
  } finally {
545
560
  try {
546
561
  const sessionId = session.sessionId
547
- this.runningSessionIds.delete(sessionId)
562
+ this.sessionStatus.set(sessionId, 'stopped')
548
563
  this.streamingSessionIds.delete(sessionId)
549
564
  this.activeQueries.delete(sessionId)
550
565
  // Clean up stale session object if stream ended unexpectedly (not via explicit closeSession).
551
566
  // Without this, the session stays in this.sessions (appears "already running")
552
- // while runningSessionIds shows it as idle — making resume impossible.
567
+ // while sessionStatus shows it as idle — making resume impossible.
553
568
  if (this.sessions.has(sessionId) && !this.closedSessionIds.has(sessionId)) {
554
569
  const s = this.sessions.get(sessionId)
555
570
  this.sessions.delete(sessionId)
@@ -558,7 +573,7 @@ export class SessionManager {
558
573
  this.broadcast(sessionId, (l) => l.onEnd(sessionId))
559
574
  } catch {
560
575
  // Session never initialized — try with our tracked id
561
- this.runningSessionIds.delete(currentSessionId)
576
+ this.sessionStatus.set(currentSessionId, 'stopped')
562
577
  this.streamingSessionIds.delete(currentSessionId)
563
578
  this.activeQueries.delete(currentSessionId)
564
579
  if (this.sessions.has(currentSessionId) && !this.closedSessionIds.has(currentSessionId)) {
@@ -642,10 +657,11 @@ export class SessionManager {
642
657
  } as unknown as SDKMessage))
643
658
  }
644
659
 
645
- getSessionState(sessionId: string): { model?: string; effortLevel?: EffortLevel } {
660
+ getSessionState(sessionId: string): { model?: string; effortLevel?: EffortLevel; status: 'idle' | 'running' | 'stopped' } {
646
661
  return {
647
662
  model: this.sessionModels.get(sessionId),
648
663
  effortLevel: this.sessionEffortLevels.get(sessionId),
664
+ status: this.sessionStatus.get(sessionId),
649
665
  }
650
666
  }
651
667
 
@@ -743,7 +759,7 @@ export class SessionManager {
743
759
  sessionId: s.sessionId,
744
760
  summary: s.summary || 'Untitled',
745
761
  lastModified: s.lastModified,
746
- status: this.runningSessionIds.has(s.sessionId) ? 'running' as const : 'idle' as const,
762
+ status: this.sessionStatus.get(s.sessionId),
747
763
  cwd: (s as Record<string, unknown>).cwd as string | undefined,
748
764
  }))
749
765
  }
@@ -814,7 +830,7 @@ export class SessionManager {
814
830
  const session = unstable_v2_resumeSession(sessionId, sessionOptions)
815
831
  try { process.chdir(originalCwd) } catch { /* ignore */ }
816
832
  this.sessions.set(sessionId, session)
817
- this.runningSessionIds.add(sessionId)
833
+ this.sessionStatus.set(sessionId, 'idle')
818
834
 
819
835
  // Broadcast session_resumed to all listeners so other connections update their UI
820
836
  this.broadcast(sessionId, (l) => l.onMessage(sessionId, {
@@ -832,7 +848,7 @@ export class SessionManager {
832
848
  this.closedSessionIds.add(sessionId)
833
849
  session.close()
834
850
  this.sessions.delete(sessionId)
835
- this.runningSessionIds.delete(sessionId)
851
+ this.sessionStatus.set(sessionId, 'stopped')
836
852
  this.streamingSessionIds.delete(sessionId)
837
853
 
838
854
  // Clean up idle timer
@@ -0,0 +1,30 @@
1
+ export type SessionStatus = 'idle' | 'running' | 'stopped'
2
+
3
+ export class SessionStatusTracker {
4
+ private map = new Map<string, SessionStatus>()
5
+ private onChange?: (sessionId: string, status: SessionStatus) => void
6
+
7
+ constructor(onChange?: (sessionId: string, status: SessionStatus) => void) {
8
+ this.onChange = onChange
9
+ }
10
+
11
+ get(sessionId: string): SessionStatus {
12
+ return this.map.get(sessionId) ?? 'stopped'
13
+ }
14
+
15
+ set(sessionId: string, status: SessionStatus): void {
16
+ const prev = this.map.get(sessionId)
17
+ this.map.set(sessionId, status)
18
+ if (prev !== status && this.onChange) {
19
+ this.onChange(sessionId, status)
20
+ }
21
+ }
22
+
23
+ delete(sessionId: string): void {
24
+ this.map.delete(sessionId)
25
+ }
26
+
27
+ getAll(): Map<string, SessionStatus> {
28
+ return new Map(this.map)
29
+ }
30
+ }
@@ -158,7 +158,7 @@ export interface SessionInfo {
158
158
  sessionId: string
159
159
  summary: string
160
160
  lastModified: number
161
- status: 'idle' | 'running'
161
+ status: 'idle' | 'running' | 'stopped'
162
162
  cwd?: string
163
163
  }
164
164
 
@@ -277,6 +277,7 @@ export interface SessionStateMessage {
277
277
  sessionId: string
278
278
  model?: string
279
279
  effortLevel?: EffortLevel
280
+ status?: 'idle' | 'running' | 'stopped'
280
281
  }
281
282
 
282
283
  export interface SubagentMessagesMessage {
@@ -302,6 +303,12 @@ export interface SessionSettingsMessage {
302
303
  settings: Record<string, unknown>
303
304
  }
304
305
 
306
+ export interface SessionStatusMessage {
307
+ type: 'session_status'
308
+ sessionId: string
309
+ status: string
310
+ }
311
+
305
312
  export type ServerMessage =
306
313
  | SessionCreatedMessage
307
314
  | SessionListMessage
@@ -325,3 +332,4 @@ export type ServerMessage =
325
332
  | SubagentMessagesMessage
326
333
  | ElicitationRequestMessage
327
334
  | SessionSettingsMessage
335
+ | SessionStatusMessage
@@ -135,6 +135,12 @@ export function createWsHandler(sessionManager: SessionManager, log: FastifyBase
135
135
  return // don't forward synthetic message
136
136
  }
137
137
 
138
+ // Forward session_status as top-level message (not wrapped in sdk_message)
139
+ if (msg.type === 'session_status') {
140
+ send({ type: 'session_status', sessionId: msg.sessionId as string, status: msg.status as string })
141
+ return
142
+ }
143
+
138
144
  send({ type: 'sdk_message', sessionId: sid, message })
139
145
  },
140
146
  onPermissionRequest(sid, toolUseId, toolName, input, meta) {
@@ -1 +0,0 @@
1
- import{aq as o,ar as n}from"./mermaid.core-B3-wKFnz.js";const t=(r,a)=>o.lang.round(n.parse(r)[a]);export{t as c};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-4TB4RGXK-Cpwuap5g.js";import{_ as i}from"./mermaid.core-B3-wKFnz.js";import"./chunk-FMBD7UC4-aLTS4ppS.js";import"./chunk-YZCP3GAM-BCHcb4hM.js";import"./chunk-55IACEB6-MDkMv1Vj.js";import"./chunk-EDXVE4YY-fWFZkL3K.js";import"./index-BlOeGePC.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-4TB4RGXK-Cpwuap5g.js";import{_ as i}from"./mermaid.core-B3-wKFnz.js";import"./chunk-FMBD7UC4-aLTS4ppS.js";import"./chunk-YZCP3GAM-BCHcb4hM.js";import"./chunk-55IACEB6-MDkMv1Vj.js";import"./chunk-EDXVE4YY-fWFZkL3K.js";import"./index-BlOeGePC.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
@@ -1 +0,0 @@
1
- import{b as r}from"./graph-CwsRpftd.js";var e=4;function a(o){return r(o,e)}export{a as c};
@@ -1 +0,0 @@
1
- import{s as e,b as r,a,S as s}from"./chunk-OYMX7WX6-CdiOjMj9.js";import{_ as i}from"./mermaid.core-B3-wKFnz.js";import"./chunk-55IACEB6-MDkMv1Vj.js";import"./chunk-EDXVE4YY-fWFZkL3K.js";import"./index-BlOeGePC.js";var p={parser:a,get db(){return new s(2)},renderer:r,styles:e,init:i(t=>{t.state||(t.state={}),t.state.arrowMarkerAbsolute=t.arrowMarkerAbsolute},"init")};export{p as diagram};