fix(server): treat pi aborts as cancellations
This commit is contained in:
@@ -0,0 +1,61 @@
|
|||||||
|
import { describe, expect, test, vi } from "vitest";
|
||||||
|
|
||||||
|
import type { AgentStreamEvent } from "../agent-sdk-types.js";
|
||||||
|
import { PiDirectAgentSession, type PiDirectSessionAdapter } from "./pi-direct-agent.js";
|
||||||
|
|
||||||
|
function createPiSession(prompt: () => Promise<void>): PiDirectSessionAdapter {
|
||||||
|
return {
|
||||||
|
sessionId: "pi-session-1",
|
||||||
|
thinkingLevel: "medium",
|
||||||
|
model: undefined,
|
||||||
|
messages: [],
|
||||||
|
extensionRunner: undefined,
|
||||||
|
promptTemplates: [],
|
||||||
|
resourceLoader: {
|
||||||
|
getSkills: () => ({ skills: [] }),
|
||||||
|
},
|
||||||
|
agent: {
|
||||||
|
state: {
|
||||||
|
systemPrompt: "",
|
||||||
|
errorMessage: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sessionManager: {
|
||||||
|
getSessionFile: () => "/tmp/pi-session.json",
|
||||||
|
getCwd: () => "/tmp/paseo-pi-test",
|
||||||
|
},
|
||||||
|
subscribe: vi.fn(),
|
||||||
|
prompt,
|
||||||
|
abort: vi.fn(),
|
||||||
|
dispose: vi.fn(),
|
||||||
|
getSessionStats: vi.fn(() => ({})),
|
||||||
|
setThinkingLevel: vi.fn(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("PiDirectAgentSession", () => {
|
||||||
|
test("treats SDK request abort rejections as turn cancellations", async () => {
|
||||||
|
const session = new PiDirectAgentSession(
|
||||||
|
createPiSession(() => Promise.reject(new Error("Request was aborted."))),
|
||||||
|
{ find: vi.fn(), getAll: vi.fn(() => []) },
|
||||||
|
{
|
||||||
|
provider: "pi",
|
||||||
|
cwd: "/tmp/paseo-pi-test",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const events: AgentStreamEvent[] = [];
|
||||||
|
session.subscribe((event) => events.push(event));
|
||||||
|
|
||||||
|
const { turnId } = await session.startTurn("hello");
|
||||||
|
await Promise.resolve();
|
||||||
|
|
||||||
|
expect(events).toEqual([
|
||||||
|
{
|
||||||
|
type: "turn_canceled",
|
||||||
|
provider: "pi",
|
||||||
|
turnId,
|
||||||
|
reason: "Request was aborted.",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -100,6 +100,28 @@ interface StartTurnResult {
|
|||||||
turnId: string;
|
turnId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type PiDirectSessionAdapter = Pick<
|
||||||
|
PiAgentSession,
|
||||||
|
| "abort"
|
||||||
|
| "agent"
|
||||||
|
| "dispose"
|
||||||
|
| "extensionRunner"
|
||||||
|
| "getSessionStats"
|
||||||
|
| "messages"
|
||||||
|
| "model"
|
||||||
|
| "prompt"
|
||||||
|
| "promptTemplates"
|
||||||
|
| "resourceLoader"
|
||||||
|
| "sessionId"
|
||||||
|
| "sessionManager"
|
||||||
|
| "setModel"
|
||||||
|
| "setThinkingLevel"
|
||||||
|
| "subscribe"
|
||||||
|
| "thinkingLevel"
|
||||||
|
>;
|
||||||
|
|
||||||
|
type PiDirectModelRegistry = Pick<ModelRegistry, "find" | "getAll">;
|
||||||
|
|
||||||
interface PiToolResultObject {
|
interface PiToolResultObject {
|
||||||
output?: string;
|
output?: string;
|
||||||
stdout?: string;
|
stdout?: string;
|
||||||
@@ -666,7 +688,7 @@ function mapSkillCommand(skill: Skill): AgentSlashCommand {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildSlashCommands(session: PiAgentSession): AgentSlashCommand[] {
|
function buildSlashCommands(session: PiDirectSessionAdapter): AgentSlashCommand[] {
|
||||||
const commands: AgentSlashCommand[] = [];
|
const commands: AgentSlashCommand[] = [];
|
||||||
const extensionCommands = session.extensionRunner?.getRegisteredCommands() ?? [];
|
const extensionCommands = session.extensionRunner?.getRegisteredCommands() ?? [];
|
||||||
|
|
||||||
@@ -749,6 +771,14 @@ function getStreamEventTurnId(event: AgentStreamEvent): string | undefined {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isPiRequestAbortError(error: unknown): boolean {
|
||||||
|
if (error instanceof Error && error.name === "AbortError") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return /\brequest was aborted\b/i.test(stringifyUnknownError(error));
|
||||||
|
}
|
||||||
|
|
||||||
function resolveThinkingOptionId(
|
function resolveThinkingOptionId(
|
||||||
cachedThinkingOptionId: string | null,
|
cachedThinkingOptionId: string | null,
|
||||||
sessionThinkingLevel: ThinkingLevel,
|
sessionThinkingLevel: ThinkingLevel,
|
||||||
@@ -773,7 +803,7 @@ function mapThinkingOption(option: (typeof PI_THINKING_OPTIONS)[number]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function findModelInRegistry(
|
function findModelInRegistry(
|
||||||
registry: ModelRegistry,
|
registry: PiDirectModelRegistry,
|
||||||
parsedReference: PiModelReference,
|
parsedReference: PiModelReference,
|
||||||
): Model<Api> | undefined {
|
): Model<Api> | undefined {
|
||||||
if (parsedReference.provider) {
|
if (parsedReference.provider) {
|
||||||
@@ -799,8 +829,8 @@ export class PiDirectAgentSession implements AgentSession {
|
|||||||
private latestUsage: AgentUsage | undefined;
|
private latestUsage: AgentUsage | undefined;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly session: PiAgentSession,
|
private readonly session: PiDirectSessionAdapter,
|
||||||
private readonly modelRegistry: ModelRegistry,
|
private readonly modelRegistry: PiDirectModelRegistry,
|
||||||
private readonly config: AgentSessionConfig,
|
private readonly config: AgentSessionConfig,
|
||||||
) {
|
) {
|
||||||
this.lastKnownThinkingOptionId =
|
this.lastKnownThinkingOptionId =
|
||||||
@@ -1076,6 +1106,15 @@ export class PiDirectAgentSession implements AgentSession {
|
|||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
const failedTurnId = this.activeTurnId ?? turnId;
|
const failedTurnId = this.activeTurnId ?? turnId;
|
||||||
this.activeTurnId = null;
|
this.activeTurnId = null;
|
||||||
|
if (isPiRequestAbortError(error)) {
|
||||||
|
this.emit({
|
||||||
|
type: "turn_canceled",
|
||||||
|
provider: PI_PROVIDER,
|
||||||
|
turnId: failedTurnId,
|
||||||
|
reason: stringifyUnknownError(error),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.emit({
|
this.emit({
|
||||||
type: "turn_failed",
|
type: "turn_failed",
|
||||||
provider: PI_PROVIDER,
|
provider: PI_PROVIDER,
|
||||||
|
|||||||
Reference in New Issue
Block a user