feat: serve 模式支持 resume 历史会话继续对话
- prompt/prompt_async 接口找不到内存 session 时自动从磁盘历史恢复 - 从 transcript 读取历史 session 的原始 cwd,保证子进程在正确目录启动 - resume 时不传 --session-id 避免与 --resume 冲突(csc 限制) - sessionManager.createSession 支持传入指定 sessionId,resume 时复用历史 id Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
f34325f60a
commit
550dbb5532
|
|
@ -11,6 +11,16 @@ import {
|
|||
} from '../errors.js'
|
||||
import { listSessionsImpl } from '../../utils/listSessionsImpl.js'
|
||||
|
||||
/** 从磁盘历史记录查找 session 的原始 cwd,用于 resume 时恢复正确的工作目录 */
|
||||
async function getHistoryCwd(sessionId: string): Promise<string | undefined> {
|
||||
try {
|
||||
const list = await listSessionsImpl({ limit: 1000 })
|
||||
return list.find(s => s.sessionId === sessionId)?.cwd
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
function ssePrompt(
|
||||
handle: import('../sessionHandle.js').SessionHandle,
|
||||
id: string,
|
||||
|
|
@ -354,8 +364,22 @@ export function createSessionRoutes(
|
|||
})
|
||||
.post('/session/:sessionID/prompt', async c => {
|
||||
const id = c.req.param('sessionID')
|
||||
const handle = sessionManager.getSession(id)
|
||||
if (!handle) throw notFound('session not found')
|
||||
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<{
|
||||
content?: string
|
||||
|
|
@ -381,9 +405,23 @@ export function createSessionRoutes(
|
|||
.post('/session/:sessionID/prompt_async', async c => {
|
||||
const id = c.req.param('sessionID')
|
||||
|
||||
const handle = sessionManager.getSession(id)
|
||||
let handle = sessionManager.getSession(id)
|
||||
|
||||
// 历史 session 不在内存中,自动恢复
|
||||
if (!handle) {
|
||||
throw notFound('session not found')
|
||||
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<{
|
||||
|
|
|
|||
|
|
@ -245,8 +245,8 @@ export class SessionHandle {
|
|||
'stream-json',
|
||||
'--output-format',
|
||||
'stream-json',
|
||||
'--session-id',
|
||||
this.sessionId,
|
||||
// resume 时不传 --session-id,csc 会继承历史 session 的 id
|
||||
...(this.opts.resumeSessionId ? [] : ['--session-id', this.sessionId]),
|
||||
...(this.opts.model ? ['--model', this.opts.model] : []),
|
||||
'--permission-mode',
|
||||
this._permissionMode,
|
||||
|
|
@ -299,6 +299,7 @@ export class SessionHandle {
|
|||
const reject = this.initReject
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ export class SessionManager {
|
|||
execPath: string
|
||||
scriptArgs: string[]
|
||||
silent?: boolean
|
||||
sessionId?: string
|
||||
}): Promise<SessionHandle> {
|
||||
if (this.maxSessions > 0 && this.sessions.size >= this.maxSessions) {
|
||||
throw new Error(
|
||||
|
|
@ -162,7 +163,7 @@ export class SessionManager {
|
|||
)
|
||||
}
|
||||
|
||||
const sessionId = crypto.randomUUID()
|
||||
const sessionId = opts.sessionId ?? crypto.randomUUID()
|
||||
let cwd = opts.cwd || this.defaultWorkspace || process.cwd()
|
||||
if (!isAbsolute(cwd)) {
|
||||
cwd = resolve(process.cwd(), cwd)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user