Merge branch 'master' into worktree/command-row-copy
This commit is contained in:
@@ -1042,6 +1042,56 @@ export function createFixtureApi(options: FixtureOptions = {}): ApiProxy {
|
||||
const appended = logOf(sessionId).at(-1) as SessionEvent
|
||||
return ok(request, { title: normalized, seq: appended.seq })
|
||||
},
|
||||
fork: (request) => {
|
||||
const { sessionId, atSeq } = request.payload
|
||||
const source = summaryOf(sessionId)
|
||||
if (source === undefined) {
|
||||
return err(request, {
|
||||
code: 'session-not-found',
|
||||
message: `no session ${sessionId}`,
|
||||
details: { sessionId },
|
||||
})
|
||||
}
|
||||
const log = logs.get(sessionId) ?? []
|
||||
const lastSeq = log.at(-1)?.seq ?? -1
|
||||
const anchoredBoundary = atSeq === undefined
|
||||
? undefined
|
||||
: log.find(e => e.type === 'turn/end' && e.seq >= atSeq)
|
||||
const boundary = anchoredBoundary
|
||||
?? (atSeq === undefined || atSeq > lastSeq
|
||||
? log.findLast(e => e.type === 'turn/end')
|
||||
: undefined)
|
||||
if (boundary === undefined) {
|
||||
return err(request, {
|
||||
code: 'fork-unavailable',
|
||||
message: atSeq !== undefined && atSeq <= lastSeq
|
||||
? `session ${sessionId} has not completed the turn containing event ${String(atSeq)}`
|
||||
: `session ${sessionId} has no completed turn`,
|
||||
details: { sessionId },
|
||||
})
|
||||
}
|
||||
let cut = boundary.seq + 1
|
||||
while (cut < log.length && log[cut]?.type !== 'turn/start') cut++
|
||||
const child: SessionSummary = {
|
||||
sessionId: sid(`fx-${nextSession++}`), updatedAt: Date.now(), running: false, blank: false,
|
||||
parentSessionId: sessionId,
|
||||
...source.cwd === undefined ? {} : { cwd: source.cwd },
|
||||
}
|
||||
logs.set(child.sessionId, log.slice(0, cut))
|
||||
sessions.push(child)
|
||||
emitHost({
|
||||
type: 'host/session-added', sessionId: child.sessionId, blank: false,
|
||||
parentSessionId: sessionId,
|
||||
...source.cwd === undefined ? {} : { cwd: source.cwd },
|
||||
})
|
||||
const workspace = workspaces.find(w => w.sessionIds.includes(sessionId))
|
||||
if (workspace !== undefined) {
|
||||
workspace.sessionIds = [child.sessionId, ...workspace.sessionIds]
|
||||
workspace.updatedAt = new Date().toISOString()
|
||||
emitHost({ type: 'host/workspace-changed', workspace: { ...workspace } })
|
||||
}
|
||||
return ok(request, { sessionId: child.sessionId })
|
||||
},
|
||||
history: async (request) => {
|
||||
const log = logs.get(request.payload.sessionId) ?? []
|
||||
// Snapshot at request time, deliver after the transit delay (mirrors a real host under latency).
|
||||
@@ -1591,6 +1641,7 @@ export class FixtureApiClient extends AbstractApiClient {
|
||||
case 'session.models': return this.api.sessions.models(request)
|
||||
case 'session.selectModel': return this.api.sessions.selectModel(request)
|
||||
case 'session.rename': return this.api.sessions.rename(request)
|
||||
case 'session.fork': return this.api.sessions.fork(request)
|
||||
case 'session.prompt': return this.api.sessions.prompt(request)
|
||||
case 'session.updateQueue': return this.api.sessions.updateQueue(request)
|
||||
case 'session.cancel': return this.api.sessions.cancel(request)
|
||||
|
||||
@@ -46,6 +46,7 @@ export class FakeApiClient implements IApiClient {
|
||||
onList: (payload: unknown) => Promise<RpcResponse<{ items: never[] }>> = () => Promise.resolve(ok({ items: [] }))
|
||||
onCreate: (payload: unknown) => Promise<RpcResponse<{ sessionId: SessionId }>> = () => Promise.resolve(ok({ sessionId: 'fk-new' as SessionId }))
|
||||
onRename: (payload: unknown) => Promise<RpcResponse<{ title: string; seq: number }>> = () => Promise.resolve(ok({ title: 'fk-renamed', seq: 0 }))
|
||||
onFork: (payload: unknown) => Promise<RpcResponse<{ sessionId: SessionId }>> = () => Promise.resolve(ok({ sessionId: 'fk-fork' as SessionId }))
|
||||
onHistory: (payload: { sessionId: SessionId; beforeSeq?: number; maxMessages?: number })
|
||||
=> Promise<RpcResponse<{ events: never[]; hasMore: boolean; modelTarget: ModelTarget }>> =
|
||||
() => Promise.resolve(ok({
|
||||
@@ -99,6 +100,7 @@ export class FakeApiClient implements IApiClient {
|
||||
selectModel: (payload: ModelTarget & { sessionId: SessionId }) =>
|
||||
this.record('session.selectModel', payload, this.onSelectModel(payload)),
|
||||
rename: (payload: unknown) => this.record('session.rename', payload, this.onRename(payload)),
|
||||
fork: (payload: unknown) => this.record('session.fork', payload, this.onFork(payload)),
|
||||
prompt: (payload: unknown) => this.record('session.prompt', payload, this.onPrompt(payload)),
|
||||
updateQueue: (payload: unknown) => this.record('session.updateQueue', payload, this.onUpdateQueue(payload)),
|
||||
cancel: (payload: unknown) => this.record('session.cancel', payload, this.onCancel(payload)),
|
||||
|
||||
Reference in New Issue
Block a user