Merge remote-tracking branch 'origin/feat/server-refactor' into feat/cloud-question
This commit is contained in:
commit
59d3460554
|
|
@ -1116,6 +1116,7 @@ export class QueryEngine {
|
||||||
duration_ms: Date.now() - startTime,
|
duration_ms: Date.now() - startTime,
|
||||||
duration_api_ms: getTotalAPIDuration(),
|
duration_api_ms: getTotalAPIDuration(),
|
||||||
is_error: true,
|
is_error: true,
|
||||||
|
is_interrupted: this.abortController.signal.aborted,
|
||||||
num_turns: turnCount,
|
num_turns: turnCount,
|
||||||
stop_reason: lastStopReason,
|
stop_reason: lastStopReason,
|
||||||
session_id: getSessionId(),
|
session_id: getSessionId(),
|
||||||
|
|
@ -1128,11 +1129,6 @@ export class QueryEngine {
|
||||||
initialAppState.fastMode,
|
initialAppState.fastMode,
|
||||||
),
|
),
|
||||||
uuid: randomUUID(),
|
uuid: randomUUID(),
|
||||||
// Diagnostic prefix: these are what isResultSuccessful() checks — if
|
|
||||||
// the result type isn't assistant-with-text/thinking or user-with-
|
|
||||||
// tool_result, and stop_reason isn't end_turn, that's why this fired.
|
|
||||||
// errors[] is turn-scoped via the watermark; previously it dumped the
|
|
||||||
// entire process's logError buffer (ripgrep timeouts, ENOENT, etc).
|
|
||||||
errors: (() => {
|
errors: (() => {
|
||||||
const all = getInMemoryErrors()
|
const all = getInMemoryErrors()
|
||||||
const start = errorLogWatermark
|
const start = errorLogWatermark
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,17 @@ export function createInfoRoutes(sessionManager: SessionManager): Hono {
|
||||||
})
|
})
|
||||||
.get('/agent', c => {
|
.get('/agent', c => {
|
||||||
const allAgents = getBuiltInAgents()
|
const allAgents = getBuiltInAgents()
|
||||||
return c.json(
|
const defaultMode = {
|
||||||
allAgents.map(a => ({
|
name: 'build',
|
||||||
|
description: 'Default mode for software engineering tasks: writing code, editing files, running commands, and building projects.',
|
||||||
|
mode: 'primary' as const,
|
||||||
|
hidden: false,
|
||||||
|
options: {},
|
||||||
|
permission: [],
|
||||||
|
}
|
||||||
|
return c.json([
|
||||||
|
defaultMode,
|
||||||
|
...allAgents.map(a => ({
|
||||||
name: a.agentType,
|
name: a.agentType,
|
||||||
description: a.whenToUse,
|
description: a.whenToUse,
|
||||||
mode: a.isMainThread ? ('primary' as const) : ('subagent' as const),
|
mode: a.isMainThread ? ('primary' as const) : ('subagent' as const),
|
||||||
|
|
@ -54,7 +63,7 @@ export function createInfoRoutes(sessionManager: SessionManager): Hono {
|
||||||
options: {},
|
options: {},
|
||||||
permission: [],
|
permission: [],
|
||||||
})),
|
})),
|
||||||
)
|
])
|
||||||
})
|
})
|
||||||
.get('/mcp', async c => {
|
.get('/mcp', async c => {
|
||||||
for (const handle of sessionManager.getAllSessions()) {
|
for (const handle of sessionManager.getAllSessions()) {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export function createMessageRoutes(sessionManager: SessionManager): Hono {
|
||||||
|
|
||||||
const { messages, nextCursor } = await readSessionMessages({
|
const { messages, nextCursor } = await readSessionMessages({
|
||||||
sessionId: id,
|
sessionId: id,
|
||||||
cwd: handle?.cwd,
|
cwd: handle?.spawnCwd ?? handle?.cwd,
|
||||||
limit,
|
limit,
|
||||||
before,
|
before,
|
||||||
includeSystem,
|
includeSystem,
|
||||||
|
|
@ -39,7 +39,7 @@ export function createMessageRoutes(sessionManager: SessionManager): Hono {
|
||||||
|
|
||||||
const todos = await readSessionTodos({
|
const todos = await readSessionTodos({
|
||||||
sessionId: id,
|
sessionId: id,
|
||||||
cwd: handle?.cwd,
|
cwd: handle?.spawnCwd ?? handle?.cwd,
|
||||||
})
|
})
|
||||||
|
|
||||||
return c.json({ todos })
|
return c.json({ todos })
|
||||||
|
|
@ -53,7 +53,7 @@ export function createMessageRoutes(sessionManager: SessionManager): Hono {
|
||||||
|
|
||||||
const { diffs } = await readSessionDiff({
|
const { diffs } = await readSessionDiff({
|
||||||
sessionId: id,
|
sessionId: id,
|
||||||
cwd: handle?.cwd,
|
cwd: handle?.spawnCwd ?? handle?.cwd,
|
||||||
messageID,
|
messageID,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,30 +11,13 @@ import {
|
||||||
} from '../errors.js'
|
} from '../errors.js'
|
||||||
import { listSessionsImpl } from '../../utils/listSessionsImpl.js'
|
import { listSessionsImpl } from '../../utils/listSessionsImpl.js'
|
||||||
|
|
||||||
async function getOrCreateSession(
|
/** 从磁盘历史记录查找 session 的原始 cwd,用于 resume 时恢复正确的工作目录 */
|
||||||
sessionManager: SessionManager,
|
async function getHistoryCwd(sessionId: string): Promise<string | undefined> {
|
||||||
id: string,
|
|
||||||
): Promise<import('../sessionHandle.js').SessionHandle> {
|
|
||||||
const existing = sessionManager.getSession(id)
|
|
||||||
if (existing) return existing
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const handle = await sessionManager.createSession({
|
const list = await listSessionsImpl({ limit: 1000 })
|
||||||
resumeSessionId: id,
|
return list.find(s => s.sessionId === sessionId)?.cwd
|
||||||
execPath: process.execPath,
|
} catch {
|
||||||
scriptArgs: getScriptArgsForChild(),
|
return undefined
|
||||||
})
|
|
||||||
await handle.start()
|
|
||||||
return handle
|
|
||||||
} catch (err) {
|
|
||||||
const msg = err instanceof Error ? err.message : 'Failed to create session'
|
|
||||||
if (msg.includes('Maximum concurrent')) {
|
|
||||||
throw tooManySessions(msg)
|
|
||||||
}
|
|
||||||
if (msg.includes('Working directory does not exist')) {
|
|
||||||
throw sessionError(msg)
|
|
||||||
}
|
|
||||||
throw notFound('session not found')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -178,6 +161,9 @@ export function createSessionRoutes(
|
||||||
scriptArgs: getScriptArgsForChild(),
|
scriptArgs: getScriptArgsForChild(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 等待子进程初始化完成,确保 ready 后再返回,避免立即发 prompt 时子进程还未就绪
|
||||||
|
await handle.waitReady(30000)
|
||||||
|
|
||||||
const info = handle.getInfo()
|
const info = handle.getInfo()
|
||||||
return c.json(
|
return c.json(
|
||||||
{
|
{
|
||||||
|
|
@ -214,10 +200,7 @@ export function createSessionRoutes(
|
||||||
let historySessions: Awaited<ReturnType<typeof listSessionsImpl>> = []
|
let historySessions: Awaited<ReturnType<typeof listSessionsImpl>> = []
|
||||||
try {
|
try {
|
||||||
historySessions = await listSessionsImpl({ dir, limit: limit + offset })
|
historySessions = await listSessionsImpl({ dir, limit: limit + offset })
|
||||||
process.stderr.write(`[server:session] listSessionsImpl dir=${dir ?? 'all'} found=${historySessions.length}\n`)
|
} catch {}
|
||||||
} catch (err) {
|
|
||||||
process.stderr.write(`[server:session] listSessionsImpl error: ${err}\n`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 内存中活跃的 handle,用于覆盖运行时状态
|
// 内存中活跃的 handle,用于覆盖运行时状态
|
||||||
const handleMap = new Map(
|
const handleMap = new Map(
|
||||||
|
|
@ -272,7 +255,6 @@ export function createSessionRoutes(
|
||||||
filtered.sort((a, b) => (b.last_active_at ?? 0) - (a.last_active_at ?? 0))
|
filtered.sort((a, b) => (b.last_active_at ?? 0) - (a.last_active_at ?? 0))
|
||||||
|
|
||||||
const sessions = filtered.slice(offset, offset + limit)
|
const sessions = filtered.slice(offset, offset + limit)
|
||||||
process.stderr.write(`[server:session] GET /session -> history=${historySessions.length} active=${handleMap.size} merged=${merged.length} returned=${sessions.length}\n`)
|
|
||||||
return c.json({ sessions })
|
return c.json({ sessions })
|
||||||
})
|
})
|
||||||
.get('/session/status', async c => {
|
.get('/session/status', async c => {
|
||||||
|
|
@ -309,7 +291,7 @@ export function createSessionRoutes(
|
||||||
status: st.status,
|
status: st.status,
|
||||||
state: st.status,
|
state: st.status,
|
||||||
has_pending_permission: st.has_pending_permission,
|
has_pending_permission: st.has_pending_permission,
|
||||||
type: st.status === 'running' ? 'busy' : 'idle',
|
type: st.prompting ? 'busy' : 'idle',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -382,7 +364,22 @@ export function createSessionRoutes(
|
||||||
})
|
})
|
||||||
.post('/session/:sessionID/prompt', async c => {
|
.post('/session/:sessionID/prompt', async c => {
|
||||||
const id = c.req.param('sessionID')
|
const id = c.req.param('sessionID')
|
||||||
const handle = await getOrCreateSession(sessionManager, id)
|
let handle = sessionManager.getSession(id)
|
||||||
|
if (!handle) {
|
||||||
|
const cwd = await getHistoryCwd(id)
|
||||||
|
try {
|
||||||
|
handle = await sessionManager.createSession({
|
||||||
|
cwd,
|
||||||
|
sessionId: id,
|
||||||
|
resumeSessionId: id,
|
||||||
|
execPath: process.execPath,
|
||||||
|
scriptArgs: getScriptArgsForChild(),
|
||||||
|
})
|
||||||
|
await handle.waitReady(30000)
|
||||||
|
} catch (err) {
|
||||||
|
throw sessionError(err instanceof Error ? err.message : 'Failed to resume session')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const body = await c.req.json<{
|
const body = await c.req.json<{
|
||||||
content?: string
|
content?: string
|
||||||
|
|
@ -408,7 +405,24 @@ export function createSessionRoutes(
|
||||||
.post('/session/:sessionID/prompt_async', async c => {
|
.post('/session/:sessionID/prompt_async', async c => {
|
||||||
const id = c.req.param('sessionID')
|
const id = c.req.param('sessionID')
|
||||||
|
|
||||||
const handle = await getOrCreateSession(sessionManager, id)
|
let handle = sessionManager.getSession(id)
|
||||||
|
|
||||||
|
// 历史 session 不在内存中,自动恢复
|
||||||
|
if (!handle) {
|
||||||
|
const cwd = await getHistoryCwd(id)
|
||||||
|
try {
|
||||||
|
handle = await sessionManager.createSession({
|
||||||
|
cwd,
|
||||||
|
sessionId: id,
|
||||||
|
resumeSessionId: id,
|
||||||
|
execPath: process.execPath,
|
||||||
|
scriptArgs: getScriptArgsForChild(),
|
||||||
|
})
|
||||||
|
await handle.waitReady(30000)
|
||||||
|
} catch (err) {
|
||||||
|
throw sessionError(err instanceof Error ? err.message : 'Failed to resume session')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const body = await c.req.json<{
|
const body = await c.req.json<{
|
||||||
content?: string
|
content?: string
|
||||||
|
|
@ -445,13 +459,15 @@ export function createSessionRoutes(
|
||||||
})
|
})
|
||||||
.post('/session/:sessionID/abort', async c => {
|
.post('/session/:sessionID/abort', async c => {
|
||||||
const id = c.req.param('sessionID')
|
const id = c.req.param('sessionID')
|
||||||
const handle = await getOrCreateSession(sessionManager, id)
|
const handle = sessionManager.getSession(id)
|
||||||
|
if (!handle) throw notFound('session not found')
|
||||||
await handle.abort()
|
await handle.abort()
|
||||||
return c.json({ aborted: true })
|
return c.json({ aborted: true })
|
||||||
})
|
})
|
||||||
.post('/session/:sessionID/shell', async c => {
|
.post('/session/:sessionID/shell', async c => {
|
||||||
const id = c.req.param('sessionID')
|
const id = c.req.param('sessionID')
|
||||||
const handle = await getOrCreateSession(sessionManager, id)
|
const handle = sessionManager.getSession(id)
|
||||||
|
if (!handle) throw notFound('session not found')
|
||||||
|
|
||||||
const body = await c.req.json<{ command: string }>()
|
const body = await c.req.json<{ command: string }>()
|
||||||
if (!body.command) throw badRequest('command is required')
|
if (!body.command) throw badRequest('command is required')
|
||||||
|
|
@ -459,7 +475,8 @@ export function createSessionRoutes(
|
||||||
})
|
})
|
||||||
.post('/session/:sessionID/command', async c => {
|
.post('/session/:sessionID/command', async c => {
|
||||||
const id = c.req.param('sessionID')
|
const id = c.req.param('sessionID')
|
||||||
const handle = await getOrCreateSession(sessionManager, id)
|
const handle = sessionManager.getSession(id)
|
||||||
|
if (!handle) throw notFound('session not found')
|
||||||
|
|
||||||
const body = await c.req.json<{ command: string }>()
|
const body = await c.req.json<{ command: string }>()
|
||||||
if (!body.command) throw badRequest('command is required')
|
if (!body.command) throw badRequest('command is required')
|
||||||
|
|
@ -467,7 +484,8 @@ export function createSessionRoutes(
|
||||||
})
|
})
|
||||||
.post('/session/:sessionID/command_async', async c => {
|
.post('/session/:sessionID/command_async', async c => {
|
||||||
const id = c.req.param('sessionID')
|
const id = c.req.param('sessionID')
|
||||||
const handle = await getOrCreateSession(sessionManager, id)
|
const handle = sessionManager.getSession(id)
|
||||||
|
if (!handle) throw notFound('session not found')
|
||||||
|
|
||||||
const body = await c.req.json<{ command: string }>()
|
const body = await c.req.json<{ command: string }>()
|
||||||
if (!body.command) throw badRequest('command is required')
|
if (!body.command) throw badRequest('command is required')
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,8 @@ function loadChildSpawnPrefix(): { execPath: string; scriptArgs: string[]; defin
|
||||||
export class SessionHandle {
|
export class SessionHandle {
|
||||||
readonly sessionId: string
|
readonly sessionId: string
|
||||||
readonly cwd: string
|
readonly cwd: string
|
||||||
|
/** 子进程实际的工作目录,dev 模式下与 cwd 不同(csc 根目录),用于定位 transcript 文件 */
|
||||||
|
private _spawnCwd: string | undefined
|
||||||
private child: ChildProcess | null = null
|
private child: ChildProcess | null = null
|
||||||
private _status: SessionState = 'starting'
|
private _status: SessionState = 'starting'
|
||||||
private _model?: string
|
private _model?: string
|
||||||
|
|
@ -189,6 +191,10 @@ export class SessionHandle {
|
||||||
return this.opts.silent ?? false
|
return this.opts.silent ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get spawnCwd(): string {
|
||||||
|
return this._spawnCwd ?? this.cwd
|
||||||
|
}
|
||||||
|
|
||||||
async waitReady(timeoutMs = 30000): Promise<void> {
|
async waitReady(timeoutMs = 30000): Promise<void> {
|
||||||
if (this._status === 'running') return
|
if (this._status === 'running') return
|
||||||
if (this._status === 'stopped') {
|
if (this._status === 'stopped') {
|
||||||
|
|
@ -199,11 +205,17 @@ export class SessionHandle {
|
||||||
reject(new Error(`Session ${this.sessionId} init timed out`))
|
reject(new Error(`Session ${this.sessionId} init timed out`))
|
||||||
}, timeoutMs)
|
}, timeoutMs)
|
||||||
const origResolve = this.initResolve
|
const origResolve = this.initResolve
|
||||||
|
const origReject = this.initReject
|
||||||
this.initResolve = (data: InitData) => {
|
this.initResolve = (data: InitData) => {
|
||||||
clearTimeout(timer)
|
clearTimeout(timer)
|
||||||
origResolve?.(data)
|
origResolve?.(data)
|
||||||
resolve()
|
resolve()
|
||||||
}
|
}
|
||||||
|
this.initReject = (err: unknown) => {
|
||||||
|
clearTimeout(timer)
|
||||||
|
origReject?.(err)
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,8 +245,8 @@ export class SessionHandle {
|
||||||
'stream-json',
|
'stream-json',
|
||||||
'--output-format',
|
'--output-format',
|
||||||
'stream-json',
|
'stream-json',
|
||||||
'--session-id',
|
// resume 时不传 --session-id,csc 会继承历史 session 的 id
|
||||||
this.sessionId,
|
...(this.opts.resumeSessionId ? [] : ['--session-id', this.sessionId]),
|
||||||
...(this.opts.model ? ['--model', this.opts.model] : []),
|
...(this.opts.model ? ['--model', this.opts.model] : []),
|
||||||
'--permission-mode',
|
'--permission-mode',
|
||||||
this._permissionMode,
|
this._permissionMode,
|
||||||
|
|
@ -264,6 +276,7 @@ export class SessionHandle {
|
||||||
const featureArgs = saved?.featureArgs ?? []
|
const featureArgs = saved?.featureArgs ?? []
|
||||||
const spawnArgs = [...defineArgs, ...featureArgs, ...this.opts.scriptArgs, ...printArgs]
|
const spawnArgs = [...defineArgs, ...featureArgs, ...this.opts.scriptArgs, ...printArgs]
|
||||||
|
|
||||||
|
this._spawnCwd = this.opts.cwd
|
||||||
this.child = spawn(this.opts.execPath, spawnArgs, {
|
this.child = spawn(this.opts.execPath, spawnArgs, {
|
||||||
cwd: this.opts.cwd,
|
cwd: this.opts.cwd,
|
||||||
stdio: ['pipe', 'pipe', 'pipe'],
|
stdio: ['pipe', 'pipe', 'pipe'],
|
||||||
|
|
@ -283,8 +296,12 @@ export class SessionHandle {
|
||||||
signal: signal ?? null,
|
signal: signal ?? null,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (this.initResolve) {
|
if (this.initReject) {
|
||||||
|
const reject = this.initReject
|
||||||
this.initResolve = null
|
this.initResolve = null
|
||||||
|
this.initReject = null
|
||||||
|
process.stderr.write(`[serve:${this.sessionId}] exit_code=${code} cwd=${this.cwd} stderr:\n${this.lastStderr.slice(-5).join('\n')}\n`)
|
||||||
|
reject(new Error(`Process exited with code ${code}`))
|
||||||
}
|
}
|
||||||
if (this.promptReject) {
|
if (this.promptReject) {
|
||||||
this.promptReject(new Error(`Process exited with code ${code}`))
|
this.promptReject(new Error(`Process exited with code ${code}`))
|
||||||
|
|
@ -294,8 +311,11 @@ export class SessionHandle {
|
||||||
this.child.on('error', err => {
|
this.child.on('error', err => {
|
||||||
logError(err)
|
logError(err)
|
||||||
this._status = 'stopped'
|
this._status = 'stopped'
|
||||||
if (this.initResolve) {
|
if (this.initReject) {
|
||||||
|
const reject = this.initReject
|
||||||
this.initResolve = null
|
this.initResolve = null
|
||||||
|
this.initReject = null
|
||||||
|
reject(err)
|
||||||
}
|
}
|
||||||
if (this.promptReject) {
|
if (this.promptReject) {
|
||||||
this.promptReject(err)
|
this.promptReject(err)
|
||||||
|
|
@ -306,13 +326,17 @@ export class SessionHandle {
|
||||||
this.sendInitialize()
|
this.sendInitialize()
|
||||||
|
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
this.initResolve = null
|
if (this.initReject) {
|
||||||
this.initReject = null
|
const reject = this.initReject
|
||||||
this._status = 'stopped'
|
this.initResolve = null
|
||||||
this.emitEvent('deleted', {
|
this.initReject = null
|
||||||
status: 'stopped',
|
this._status = 'stopped'
|
||||||
reason: 'init_timeout',
|
this.emitEvent('deleted', {
|
||||||
})
|
status: 'stopped',
|
||||||
|
reason: 'init_timeout',
|
||||||
|
})
|
||||||
|
reject(new Error(`Session ${this.sessionId} init timed out`))
|
||||||
|
}
|
||||||
}, 30000)
|
}, 30000)
|
||||||
|
|
||||||
this.initResolve = (data: InitData) => {
|
this.initResolve = (data: InitData) => {
|
||||||
|
|
@ -685,18 +709,19 @@ export class SessionHandle {
|
||||||
})
|
})
|
||||||
this.writeStdin(interrupt)
|
this.writeStdin(interrupt)
|
||||||
|
|
||||||
|
if (!this.promptResolve) return
|
||||||
|
|
||||||
await new Promise<void>(resolve => {
|
await new Promise<void>(resolve => {
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
this.kill()
|
this.kill()
|
||||||
resolve()
|
resolve()
|
||||||
}, 5000)
|
}, 2000)
|
||||||
const check = setInterval(() => {
|
const originalResolve = this.promptResolve
|
||||||
if (!this.child || this.child.killed) {
|
this.promptResolve = (value) => {
|
||||||
clearTimeout(timeout)
|
clearTimeout(timeout)
|
||||||
clearInterval(check)
|
originalResolve?.(value)
|
||||||
resolve()
|
resolve()
|
||||||
}
|
}
|
||||||
}, 200)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -128,16 +128,17 @@ export class SessionManager {
|
||||||
|
|
||||||
getSessionStatuses(): Record<
|
getSessionStatuses(): Record<
|
||||||
string,
|
string,
|
||||||
{ status: SessionState; has_pending_permission: boolean }
|
{ status: SessionState; has_pending_permission: boolean; prompting: boolean }
|
||||||
> {
|
> {
|
||||||
const result: Record<
|
const result: Record<
|
||||||
string,
|
string,
|
||||||
{ status: SessionState; has_pending_permission: boolean }
|
{ status: SessionState; has_pending_permission: boolean; prompting: boolean }
|
||||||
> = {}
|
> = {}
|
||||||
for (const [id, handle] of this.sessions) {
|
for (const [id, handle] of this.sessions) {
|
||||||
result[id] = {
|
result[id] = {
|
||||||
status: handle.status,
|
status: handle.status,
|
||||||
has_pending_permission: handle.getPendingPermissions().length > 0,
|
has_pending_permission: handle.getPendingPermissions().length > 0,
|
||||||
|
prompting: handle.prompting,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|
@ -154,6 +155,7 @@ export class SessionManager {
|
||||||
execPath: string
|
execPath: string
|
||||||
scriptArgs: string[]
|
scriptArgs: string[]
|
||||||
silent?: boolean
|
silent?: boolean
|
||||||
|
sessionId?: string
|
||||||
}): Promise<SessionHandle> {
|
}): Promise<SessionHandle> {
|
||||||
if (this.maxSessions > 0 && this.sessions.size >= this.maxSessions) {
|
if (this.maxSessions > 0 && this.sessions.size >= this.maxSessions) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
@ -161,7 +163,7 @@ export class SessionManager {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const sessionId = crypto.randomUUID()
|
const sessionId = opts.sessionId ?? crypto.randomUUID()
|
||||||
let cwd = opts.cwd || this.defaultWorkspace || process.cwd()
|
let cwd = opts.cwd || this.defaultWorkspace || process.cwd()
|
||||||
if (!isAbsolute(cwd)) {
|
if (!isAbsolute(cwd)) {
|
||||||
cwd = resolve(process.cwd(), cwd)
|
cwd = resolve(process.cwd(), cwd)
|
||||||
|
|
|
||||||
|
|
@ -63,10 +63,13 @@ async function resolveTranscriptPath(
|
||||||
): Promise<string | null> {
|
): Promise<string | null> {
|
||||||
const cacheKey = `${sessionId}:${cwd ?? ''}`
|
const cacheKey = `${sessionId}:${cwd ?? ''}`
|
||||||
const cached = pathCache.get(cacheKey)
|
const cached = pathCache.get(cacheKey)
|
||||||
if (cached !== undefined) return cached
|
// 只缓存找到的路径,不缓存 null(文件可能还未写入)
|
||||||
|
if (cached !== undefined && cached !== null) return cached
|
||||||
|
|
||||||
const result = await resolveTranscriptPathUncached(sessionId, cwd)
|
const result = await resolveTranscriptPathUncached(sessionId, cwd)
|
||||||
pathCache.set(cacheKey, result)
|
if (result !== null) {
|
||||||
|
pathCache.set(cacheKey, result)
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
{
|
{
|
||||||
"extends": "./tsconfig.base.json",
|
"extends": "./tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"ignoreDeprecations": "6.0",
|
||||||
"paths": {
|
"paths": {
|
||||||
"src/*": ["./src/*"],
|
"src/*": ["./src/*"],
|
||||||
"@claude-code-best/builtin-tools/*": ["./packages/builtin-tools/src/*"],
|
"@claude-code-best/builtin-tools/*": ["./packages/builtin-tools/src/*"],
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user