Skip to content

Commit cd54814

Browse files
committed
feat: session/close support
1 parent f640d4c commit cd54814

10 files changed

Lines changed: 2206 additions & 158 deletions

src/CodexAcpClient.ts

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,18 +378,44 @@ export class CodexAcpClient {
378378
this.codexClient.onElicitationRequest(sessionId, elicitationHandler);
379379
}
380380

381+
disposeSession(sessionId: string): void {
382+
this.codexClient.clearSessionHandlers(sessionId);
383+
}
384+
385+
async closeSession(sessionId: string): Promise<void> {
386+
await this.codexClient.threadUnsubscribe({ threadId: sessionId });
387+
}
388+
381389
async sendPrompt(
382390
request: acp.PromptRequest,
383391
agentMode: AgentMode,
384392
modelId: ModelId,
385393
serviceTier: ServiceTier | null,
386394
disableSummary: boolean,
387395
cwd: string,
396+
onTurnStarted?: (turnId: string) => void,
397+
onTurnStartRequested?: () => void,
398+
isCancelled?: () => boolean,
399+
cancellation?: Promise<TurnCompletedNotification>,
388400
): Promise<TurnCompletedNotification> {
389401
const input = buildPromptItems(request.prompt);
390402
const effort = modelId.effort as ReasoningEffort | null; //TODO remove unsafe conversion
391403

392-
await this.refreshSkills(cwd, request._meta);
404+
if (cancellation) {
405+
const refreshResult = await Promise.race([
406+
this.refreshSkills(cwd, request._meta).then(() => null),
407+
cancellation,
408+
]);
409+
if (refreshResult !== null) {
410+
return refreshResult;
411+
}
412+
} else {
413+
await this.refreshSkills(cwd, request._meta);
414+
}
415+
if (isCancelled?.()) {
416+
return createInterruptedTurnCompleted(request.sessionId, "cancelled-before-turn");
417+
}
418+
onTurnStartRequested?.();
393419
return await this.codexClient.runTurn({
394420
threadId: request.sessionId,
395421
input: input,
@@ -399,7 +425,7 @@ export class CodexAcpClient {
399425
effort: effort,
400426
model: modelId.model,
401427
serviceTier: serviceTier,
402-
});
428+
}, onTurnStarted, cancellation);
403429
}
404430

405431
async listSkills(params?: SkillsListParams): Promise<SkillsListResponse> {
@@ -524,6 +550,10 @@ export class CodexAcpClient {
524550
});
525551
}
526552

553+
resolveInterruptedTurn(params: { threadId: string, turnId: string }): void {
554+
this.codexClient.resolveTurnInterrupted(params.threadId, params.turnId);
555+
}
556+
527557
async fetchAvailableModels(): Promise<Model[]> {
528558
const models: Model[] = [];
529559
let cursor: string | null = null;
@@ -575,6 +605,21 @@ export type SessionMetadataWithThread = SessionMetadata & {
575605
thread: Thread,
576606
}
577607

608+
function createInterruptedTurnCompleted(threadId: string, turnId: string): TurnCompletedNotification {
609+
return {
610+
threadId,
611+
turn: {
612+
id: turnId,
613+
items: [],
614+
status: "interrupted",
615+
error: null,
616+
startedAt: null,
617+
completedAt: null,
618+
durationMs: null,
619+
},
620+
};
621+
}
622+
578623
function buildPromptItems(prompt: acp.ContentBlock[]): UserInput[] {
579624
return prompt.map((block): UserInput | null => {
580625
switch (block.type) {

0 commit comments

Comments
 (0)