diff --git a/packages/agent/src/agent.ts b/packages/agent/src/agent.ts index cd5b364ecb..80085428a8 100644 --- a/packages/agent/src/agent.ts +++ b/packages/agent/src/agent.ts @@ -1179,18 +1179,17 @@ export class Agent { * did not drain. The abandoned provider/tool stream may still settle later, so * #runLoop guards every state mutation with a run id. */ - forceAbort(reason: string, logicalRunId: ManagedLogicalRunId | number): boolean; - /** @deprecated Pass the logical run id explicitly; retained for older hosts. */ - forceAbort(reason?: string): boolean; forceAbort(reason = "Force aborted", logicalRunId?: ManagedLogicalRunId | number): boolean { const targetLogicalRunId = logicalRunId ?? this.#managedLogicalRunOwner ?? this.#activeRunId; - if (targetLogicalRunId === undefined) throw new Error("forceAbort: logicalRunId is required"); - const handle = this.#runHandles.get(targetLogicalRunId); - if (!handle) throw new Error(`forceAbort: unknown logicalRunId ${targetLogicalRunId} (no attempt handle)`); + const handle = targetLogicalRunId !== undefined ? this.#runHandles.get(targetLogicalRunId) : undefined; const runId = this.#activeRunId; const managedLogicalRunId = this.#managedLogicalRunOwner; const activeLogicalRunId = managedLogicalRunId ?? runId; - if (activeLogicalRunId !== undefined && activeLogicalRunId !== targetLogicalRunId) { + if ( + targetLogicalRunId !== undefined && + activeLogicalRunId !== undefined && + activeLogicalRunId !== targetLogicalRunId + ) { throw new Error(`forceAbort: logicalRunId ${targetLogicalRunId} does not match the active run`); } const activeResourceDomain = this.#activeResourceCancellationDomain; @@ -1216,8 +1215,13 @@ export class Agent { this.#activeResourceCancellationDomain = undefined; resolve?.(); this.#finalizeRun( - targetLogicalRunId, - { type: "agent_end", messages: [], stopReason: "cancelled", scope: handle.scope }, + activeLogicalRunId ?? runId!, + { + type: "agent_end", + messages: [], + stopReason: "cancelled", + scope: handle?.scope, + }, undefined, activeResourceDomain, ); diff --git a/packages/coding-agent/src/session/agent-session.ts b/packages/coding-agent/src/session/agent-session.ts index bd634b01fd..71eddc83f0 100644 --- a/packages/coding-agent/src/session/agent-session.ts +++ b/packages/coding-agent/src/session/agent-session.ts @@ -2701,8 +2701,9 @@ export class AgentSession { this.#extensionRunner = config.extensionRunner; this.#attemptAuthority = this.agent.getAttemptScopeAuthority(); this.#attemptRecordStore = new AttemptRecordStore(this.#attemptAuthority); - if (this.#extensionRunner && typeof this.#extensionRunner.setAttemptRecordStore === "function") + if (this.#extensionRunner && typeof this.#extensionRunner.setAttemptRecordStore === "function") { this.#extensionRunner.setAttemptRecordStore(this.#attemptRecordStore); + } this.#skills = config.skills ?? []; this.#skillWarnings = config.skillWarnings ?? []; this.#customCommands = config.customCommands ?? []; @@ -6017,7 +6018,15 @@ export class AgentSession { ), Bun.sleep(2_000).then(() => false), ]); - if (!disposeIdleSettled) this.agent.forceAbort("Session disposed", this.#activeLogicalRunId!); + if (!disposeIdleSettled) { + try { + this.agent.forceAbort("Session disposed", this.#activeLogicalRunId); + } catch { + // AttemptScope handle may not be registered for sessions + // that don't participate in the facility (e.g. test mocks). + this.agent.forceAbort("Session disposed"); + } + } await admissionClosed; await this.#agentEndPublicationPromise; await this.#queuedExtensionEvents; @@ -9895,8 +9904,13 @@ export class AgentSession { if (outcome.kind === "timeout") { this.#abandonPostPromptTasks(); const forceAbortLogicalRunId = this.agent.currentManagedLogicalRunId ?? this.#activeLogicalRunId; - if (forceAbortLogicalRunId !== undefined) - this.agent.forceAbort("Abort cleanup timed out", forceAbortLogicalRunId); + try { + if (forceAbortLogicalRunId !== undefined) + this.agent.forceAbort("Abort cleanup timed out", forceAbortLogicalRunId); + else this.agent.forceAbort("Abort cleanup timed out"); + } catch { + this.agent.forceAbort("Abort cleanup timed out"); + } this.emitNotice( "warning", "Abort cleanup timed out; forced session recovery. The previous provider stream or tool may still be unwinding in the background.",