mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
Fix commands not loading for Pi agent (#225)
This commit is contained in:
@@ -415,10 +415,39 @@ describe("transformPiModels", () => {
|
||||
});
|
||||
|
||||
describe("ACPAgentSession slash commands", () => {
|
||||
test("caches ACP available commands for listCommands", async () => {
|
||||
test("returns immediately for ACP sessions that do not wait for async command discovery", async () => {
|
||||
const session = createSession();
|
||||
|
||||
expect(await session.listCommands()).toEqual([]);
|
||||
await expect(session.listCommands()).resolves.toEqual([]);
|
||||
});
|
||||
|
||||
test("waits for async available_commands_update when enabled", async () => {
|
||||
const session = new ACPAgentSession(
|
||||
{
|
||||
provider: "pi",
|
||||
cwd: "/tmp/paseo-acp-test",
|
||||
},
|
||||
{
|
||||
provider: "pi",
|
||||
logger: createTestLogger(),
|
||||
defaultCommand: ["pi-acp"],
|
||||
defaultModes: [],
|
||||
modelTransformer: transformPiModels,
|
||||
sessionResponseTransformer: transformPiSessionResponse,
|
||||
capabilities: {
|
||||
supportsStreaming: true,
|
||||
supportsSessionPersistence: true,
|
||||
supportsDynamicModes: true,
|
||||
supportsMcpServers: false,
|
||||
supportsReasoningStream: true,
|
||||
supportsToolInvocations: true,
|
||||
},
|
||||
waitForInitialCommands: true,
|
||||
initialCommandsWaitTimeoutMs: 1500,
|
||||
},
|
||||
);
|
||||
|
||||
const listCommandsPromise = session.listCommands();
|
||||
|
||||
(session as any).translateSessionUpdate({
|
||||
sessionUpdate: "available_commands_update",
|
||||
@@ -434,6 +463,19 @@ describe("ACPAgentSession slash commands", () => {
|
||||
],
|
||||
});
|
||||
|
||||
expect(await listCommandsPromise).toEqual([
|
||||
{
|
||||
name: "research_codebase",
|
||||
description: "Search the workspace for relevant files",
|
||||
argumentHint: "",
|
||||
},
|
||||
{
|
||||
name: "create_plan",
|
||||
description: "Draft a plan for the requested work",
|
||||
argumentHint: "",
|
||||
},
|
||||
]);
|
||||
|
||||
expect(await session.listCommands()).toEqual([
|
||||
{
|
||||
name: "research_codebase",
|
||||
|
||||
@@ -125,6 +125,8 @@ type ACPAgentClientOptions = {
|
||||
thinkingOptionId: string,
|
||||
) => Promise<void>;
|
||||
capabilities?: AgentCapabilityFlags;
|
||||
waitForInitialCommands?: boolean;
|
||||
initialCommandsWaitTimeoutMs?: number;
|
||||
};
|
||||
|
||||
type ACPAgentSessionOptions = {
|
||||
@@ -144,6 +146,8 @@ type ACPAgentSessionOptions = {
|
||||
capabilities: AgentCapabilityFlags;
|
||||
handle?: AgentPersistenceHandle;
|
||||
launchEnv?: Record<string, string>;
|
||||
waitForInitialCommands?: boolean;
|
||||
initialCommandsWaitTimeoutMs?: number;
|
||||
};
|
||||
|
||||
type SpawnedACPProcess = {
|
||||
@@ -302,6 +306,8 @@ export class ACPAgentClient implements AgentClient {
|
||||
sessionId: string,
|
||||
thinkingOptionId: string,
|
||||
) => Promise<void>;
|
||||
private readonly waitForInitialCommands: boolean;
|
||||
private readonly initialCommandsWaitTimeoutMs: number;
|
||||
|
||||
constructor(options: ACPAgentClientOptions) {
|
||||
this.provider = options.provider;
|
||||
@@ -314,6 +320,8 @@ export class ACPAgentClient implements AgentClient {
|
||||
this.sessionResponseTransformer = options.sessionResponseTransformer;
|
||||
this.toolSnapshotTransformer = options.toolSnapshotTransformer;
|
||||
this.thinkingOptionWriter = options.thinkingOptionWriter;
|
||||
this.waitForInitialCommands = options.waitForInitialCommands ?? false;
|
||||
this.initialCommandsWaitTimeoutMs = options.initialCommandsWaitTimeoutMs ?? 1500;
|
||||
}
|
||||
|
||||
async createSession(
|
||||
@@ -335,6 +343,8 @@ export class ACPAgentClient implements AgentClient {
|
||||
thinkingOptionWriter: this.thinkingOptionWriter,
|
||||
capabilities: this.capabilities,
|
||||
launchEnv: launchContext?.env,
|
||||
waitForInitialCommands: this.waitForInitialCommands,
|
||||
initialCommandsWaitTimeoutMs: this.initialCommandsWaitTimeoutMs,
|
||||
},
|
||||
);
|
||||
await session.initializeNewSession();
|
||||
@@ -375,6 +385,8 @@ export class ACPAgentClient implements AgentClient {
|
||||
capabilities: this.capabilities,
|
||||
handle,
|
||||
launchEnv: launchContext?.env,
|
||||
waitForInitialCommands: this.waitForInitialCommands,
|
||||
initialCommandsWaitTimeoutMs: this.initialCommandsWaitTimeoutMs,
|
||||
});
|
||||
await session.initializeResumedSession();
|
||||
return session;
|
||||
@@ -617,6 +629,10 @@ export class ACPAgentSession implements AgentSession, ACPClient {
|
||||
private lastActivityAt: string | null = null;
|
||||
private configOptions: SessionConfigOption[] = [];
|
||||
private cachedCommands: AgentSlashCommand[] = [];
|
||||
private commandsReadyDeferred: { promise: Promise<void>; resolve: () => void } | null = null;
|
||||
private commandsReadySettled = false;
|
||||
private waitForInitialCommands: boolean;
|
||||
private initialCommandsWaitTimeoutMs: number;
|
||||
private currentTurnUsage: AgentUsage | undefined;
|
||||
private activeForegroundTurnId: string | null = null;
|
||||
private closed = false;
|
||||
@@ -645,6 +661,8 @@ export class ACPAgentSession implements AgentSession, ACPClient {
|
||||
this.currentModel = config.model ?? null;
|
||||
this.thinkingOptionId = config.thinkingOptionId ?? null;
|
||||
this.currentTitle = config.title ?? null;
|
||||
this.waitForInitialCommands = options.waitForInitialCommands ?? false;
|
||||
this.initialCommandsWaitTimeoutMs = options.initialCommandsWaitTimeoutMs ?? 1500;
|
||||
}
|
||||
|
||||
get id(): string | null {
|
||||
@@ -876,7 +894,59 @@ export class ACPAgentSession implements AgentSession, ACPClient {
|
||||
return this.currentMode;
|
||||
}
|
||||
|
||||
private ensureCommandsReadyDeferred(): void {
|
||||
if (this.commandsReadyDeferred || this.commandsReadySettled || this.cachedCommands.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let resolve!: () => void;
|
||||
const promise = new Promise<void>((r) => {
|
||||
resolve = r;
|
||||
});
|
||||
this.commandsReadyDeferred = { promise, resolve };
|
||||
}
|
||||
|
||||
private settleCommandsReady(): void {
|
||||
if (this.commandsReadySettled) {
|
||||
return;
|
||||
}
|
||||
this.commandsReadySettled = true;
|
||||
this.commandsReadyDeferred?.resolve();
|
||||
this.commandsReadyDeferred = null;
|
||||
}
|
||||
|
||||
private async waitForCommandsReady(): Promise<void> {
|
||||
const deferred = this.commandsReadyDeferred;
|
||||
if (!deferred) {
|
||||
return;
|
||||
}
|
||||
|
||||
let timer: ReturnType<typeof setTimeout> | null = null;
|
||||
try {
|
||||
await Promise.race([
|
||||
deferred.promise,
|
||||
new Promise<void>((resolve) => {
|
||||
timer = setTimeout(resolve, this.initialCommandsWaitTimeoutMs);
|
||||
}),
|
||||
]);
|
||||
} finally {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async listCommands(): Promise<AgentSlashCommand[]> {
|
||||
if (this.cachedCommands.length > 0) {
|
||||
return this.cachedCommands;
|
||||
}
|
||||
if (!this.waitForInitialCommands || this.closed) {
|
||||
return this.cachedCommands;
|
||||
}
|
||||
|
||||
this.ensureCommandsReadyDeferred();
|
||||
await this.waitForCommandsReady();
|
||||
this.settleCommandsReady();
|
||||
return this.cachedCommands;
|
||||
}
|
||||
|
||||
@@ -1046,6 +1116,8 @@ export class ACPAgentSession implements AgentSession, ACPClient {
|
||||
}
|
||||
this.closed = true;
|
||||
|
||||
this.settleCommandsReady();
|
||||
|
||||
for (const pending of this.pendingPermissions.values()) {
|
||||
pending.resolve({ outcome: { outcome: "cancelled" } });
|
||||
}
|
||||
@@ -1392,6 +1464,7 @@ export class ACPAgentSession implements AgentSession, ACPClient {
|
||||
description: command.description,
|
||||
argumentHint: "",
|
||||
}));
|
||||
this.settleCommandsReady();
|
||||
return [];
|
||||
default:
|
||||
return [];
|
||||
|
||||
@@ -298,6 +298,8 @@ export class PiACPAgentClient extends ACPAgentClient {
|
||||
await connection.setSessionMode({ sessionId, modeId: thinkingOptionId });
|
||||
},
|
||||
capabilities: PI_CAPABILITIES,
|
||||
waitForInitialCommands: true,
|
||||
initialCommandsWaitTimeoutMs: 1500,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user