@jacexh/claude-web-console 0.12.2 → 0.12.4
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
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
+
import { waitForSessionId } from '../session-id-resolver'
|
|
3
|
+
|
|
4
|
+
describe('waitForSessionId', () => {
|
|
5
|
+
it('resolves when session.sessionId becomes available', async () => {
|
|
6
|
+
let ready = false
|
|
7
|
+
const session = {
|
|
8
|
+
get sessionId() {
|
|
9
|
+
if (!ready) throw new Error('not ready')
|
|
10
|
+
return 'real-session-id'
|
|
11
|
+
},
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Simulate SDK resolving sessionId after 100ms
|
|
15
|
+
setTimeout(() => { ready = true }, 100)
|
|
16
|
+
|
|
17
|
+
const result = await waitForSessionId(session as any, 5000)
|
|
18
|
+
expect(result).toBe('real-session-id')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('resolves immediately when sessionId is already available', async () => {
|
|
22
|
+
const session = { sessionId: 'already-ready' }
|
|
23
|
+
const result = await waitForSessionId(session as any, 5000)
|
|
24
|
+
expect(result).toBe('already-ready')
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('rejects on timeout if sessionId never becomes available', async () => {
|
|
28
|
+
const session = {
|
|
29
|
+
get sessionId(): string { throw new Error('not ready') },
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
await expect(waitForSessionId(session as any, 200)).rejects.toThrow('Session init timed out')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('ignores pending- prefixed session IDs', async () => {
|
|
36
|
+
let callCount = 0
|
|
37
|
+
const session = {
|
|
38
|
+
get sessionId() {
|
|
39
|
+
callCount++
|
|
40
|
+
if (callCount < 3) return 'pending-123'
|
|
41
|
+
return 'real-id'
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const result = await waitForSessionId(session as any, 5000)
|
|
46
|
+
expect(result).toBe('real-id')
|
|
47
|
+
})
|
|
48
|
+
})
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { SDKSession } from '@anthropic-ai/claude-agent-sdk'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Polls session.sessionId until the SDK resolves the real ID.
|
|
5
|
+
* The SDK sets sessionId asynchronously after process init.
|
|
6
|
+
*/
|
|
7
|
+
export function waitForSessionId(session: SDKSession, timeoutMs: number): Promise<string> {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
const timeout = setTimeout(() => {
|
|
10
|
+
clearInterval(poll)
|
|
11
|
+
reject(new Error('Session init timed out'))
|
|
12
|
+
}, timeoutMs)
|
|
13
|
+
|
|
14
|
+
const poll = setInterval(() => {
|
|
15
|
+
try {
|
|
16
|
+
const id = session.sessionId
|
|
17
|
+
if (id && !id.startsWith('pending-')) {
|
|
18
|
+
clearInterval(poll)
|
|
19
|
+
clearTimeout(timeout)
|
|
20
|
+
resolve(id)
|
|
21
|
+
}
|
|
22
|
+
} catch {
|
|
23
|
+
// sessionId not ready yet, keep polling
|
|
24
|
+
}
|
|
25
|
+
}, 50)
|
|
26
|
+
|
|
27
|
+
// Check immediately (no need to wait 50ms if already ready)
|
|
28
|
+
try {
|
|
29
|
+
const id = session.sessionId
|
|
30
|
+
if (id && !id.startsWith('pending-')) {
|
|
31
|
+
clearInterval(poll)
|
|
32
|
+
clearTimeout(timeout)
|
|
33
|
+
resolve(id)
|
|
34
|
+
}
|
|
35
|
+
} catch {
|
|
36
|
+
// not ready, poll will handle it
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
}
|
|
@@ -19,6 +19,7 @@ import type { FastifyBaseLogger } from 'fastify'
|
|
|
19
19
|
import type { SessionInfo, EffortLevel } from './types.js'
|
|
20
20
|
import { shouldBroadcastTurnStarted, shouldResetToIdleOnStreamEnd, isTurnMessage, type TurnState } from './turn-lifecycle.js'
|
|
21
21
|
import { SessionStatusTracker } from './session-status.js'
|
|
22
|
+
import { waitForSessionId } from './session-id-resolver.js'
|
|
22
23
|
|
|
23
24
|
type PermissionResolver = {
|
|
24
25
|
resolve: (approved: boolean, reason?: string, updatedPermissions?: import('@anthropic-ai/claude-agent-sdk').PermissionUpdate[]) => void
|
|
@@ -158,7 +159,7 @@ export class SessionManager {
|
|
|
158
159
|
private streamingSessionIds = new Set<string>()
|
|
159
160
|
private sessionListeners = new Map<string, Set<SessionListener>>()
|
|
160
161
|
private idleTimers = new Map<string, ReturnType<typeof setTimeout>>()
|
|
161
|
-
private pendingRemaps = new Map<string, { sessionIdRef: { current: string }
|
|
162
|
+
private pendingRemaps = new Map<string, { sessionIdRef: { current: string } }>()
|
|
162
163
|
// Track cwd for each session so we can resume in the correct project
|
|
163
164
|
private sessionCwds = new Map<string, string>()
|
|
164
165
|
/** User-supplied creation options that must survive resume cycles */
|
|
@@ -384,21 +385,14 @@ export class SessionManager {
|
|
|
384
385
|
})
|
|
385
386
|
|
|
386
387
|
// Store tempId in pendingRemaps for remap inside consumeStream
|
|
387
|
-
|
|
388
|
-
this.pendingRemaps.set(tempId, { sessionIdRef, resolve })
|
|
389
|
-
})
|
|
388
|
+
this.pendingRemaps.set(tempId, { sessionIdRef })
|
|
390
389
|
|
|
391
390
|
// For new sessions, start stream immediately — SDK may emit init messages
|
|
392
391
|
this.startStreamConsumer(tempId, session)
|
|
393
392
|
|
|
394
|
-
//
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
sessionIdReady,
|
|
398
|
-
new Promise<string>((_, reject) => setTimeout(() => reject(new Error('Session init timed out')), timeoutMs)),
|
|
399
|
-
])
|
|
400
|
-
|
|
401
|
-
return sessionId
|
|
393
|
+
// SDK requires send() before sessionId is available.
|
|
394
|
+
// Return tempId now; real sessionId arrives via session_id_resolved WS event after first send().
|
|
395
|
+
return tempId
|
|
402
396
|
}
|
|
403
397
|
|
|
404
398
|
private fetchAndBroadcastModels(sessionId: string, session: SDKSession, currentModel?: string): void {
|
|
@@ -537,8 +531,6 @@ export class SessionManager {
|
|
|
537
531
|
if (prevStatus) this.sessionStatus.set(sessionId, prevStatus)
|
|
538
532
|
const q = this.activeQueries.get(tempId)
|
|
539
533
|
if (q) { this.activeQueries.delete(tempId); this.activeQueries.set(sessionId, q) }
|
|
540
|
-
// Resolve the sessionId promise before cleaning up the remap entry
|
|
541
|
-
if (remap.resolve) remap.resolve(sessionId)
|
|
542
534
|
this.pendingRemaps.delete(tempId)
|
|
543
535
|
// Update our tracking variable
|
|
544
536
|
currentSessionId = sessionId
|