@jacexh/claude-web-console 0.11.0 → 0.11.2

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jacexh/claude-web-console",
3
- "version": "0.11.0",
3
+ "version": "0.11.2",
4
4
  "description": "Web-based console for Claude Code — manage sessions, switch models, preview artifacts",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,7 +11,7 @@ import { describe, it, expect } from 'vitest'
11
11
  * We extract the logic into a pure function so it can be tested
12
12
  * without mocking the full SessionManager.
13
13
  */
14
- import { shouldBroadcastTurnStarted } from '../turn-lifecycle'
14
+ import { shouldBroadcastTurnStarted, shouldResetToIdleOnStreamEnd } from '../turn-lifecycle'
15
15
 
16
16
  describe('shouldBroadcastTurnStarted', () => {
17
17
  it('returns true for the first message of a turn', () => {
@@ -51,3 +51,17 @@ describe('shouldBroadcastTurnStarted', () => {
51
51
  expect(shouldBroadcastTurnStarted(state)).toBe(false)
52
52
  })
53
53
  })
54
+
55
+ describe('shouldResetToIdleOnStreamEnd', () => {
56
+ it('returns true when current status is running (stream ended without result)', () => {
57
+ expect(shouldResetToIdleOnStreamEnd('running')).toBe(true)
58
+ })
59
+
60
+ it('returns false when current status is idle (result already set idle)', () => {
61
+ expect(shouldResetToIdleOnStreamEnd('idle')).toBe(false)
62
+ })
63
+
64
+ it('returns false when current status is stopped (session closed)', () => {
65
+ expect(shouldResetToIdleOnStreamEnd('stopped')).toBe(false)
66
+ })
67
+ })
@@ -17,7 +17,7 @@ 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'
20
+ import { shouldBroadcastTurnStarted, shouldResetToIdleOnStreamEnd, type TurnState } from './turn-lifecycle.js'
21
21
  import { SessionStatusTracker } from './session-status.js'
22
22
 
23
23
  type PermissionResolver = {
@@ -548,6 +548,10 @@ export class SessionManager {
548
548
  let sessionId: string
549
549
  try { sessionId = session.sessionId } catch { break }
550
550
  if (this.closedSessionIds.has(sessionId)) break
551
+ // Reset to idle if stream ended without a result (e.g. init-only stream)
552
+ if (shouldResetToIdleOnStreamEnd(this.sessionStatus.get(sessionId))) {
553
+ this.sessionStatus.set(sessionId, 'idle')
554
+ }
551
555
  // Wait briefly before re-entering stream() for next turn
552
556
  await new Promise((r) => setTimeout(r, 50))
553
557
  }
@@ -0,0 +1,15 @@
1
+ export interface TurnState {
2
+ turnStarted: boolean
3
+ }
4
+
5
+ /** Returns true (and flips the flag) on the first call per turn. */
6
+ export function shouldBroadcastTurnStarted(state: TurnState): boolean {
7
+ if (state.turnStarted) return false
8
+ state.turnStarted = true
9
+ return true
10
+ }
11
+
12
+ /** Returns true if status should be reset to idle when a stream ends without a result. */
13
+ export function shouldResetToIdleOnStreamEnd(currentStatus: string): boolean {
14
+ return currentStatus === 'running'
15
+ }