mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
Fix live agent refresh without persistence
This commit is contained in:
@@ -14,6 +14,14 @@ import {
|
||||
} from "./test-utils/index.js";
|
||||
import { getFullAccessConfig, getAskModeConfig } from "./daemon-e2e/agent-configs.js";
|
||||
import { chunkPcm16, parsePcm16MonoWav, wordSimilarity } from "./test-utils/dictation-e2e.js";
|
||||
import type {
|
||||
AgentClient,
|
||||
AgentPersistenceHandle,
|
||||
AgentRunResult,
|
||||
AgentSession,
|
||||
AgentSessionConfig,
|
||||
AgentStreamEvent,
|
||||
} from "./agent/agent-sdk-types.js";
|
||||
|
||||
const openaiApiKey = process.env.OPENAI_API_KEY ?? null;
|
||||
|
||||
@@ -94,6 +102,116 @@ function waitForSignal<T>(
|
||||
});
|
||||
}
|
||||
|
||||
class NonPersistentReloadSession implements AgentSession {
|
||||
readonly provider = "claude" as const;
|
||||
readonly id = null;
|
||||
readonly capabilities = {
|
||||
supportsStreaming: false,
|
||||
supportsSessionPersistence: true,
|
||||
supportsDynamicModes: false,
|
||||
supportsMcpServers: false,
|
||||
supportsReasoningStream: false,
|
||||
supportsToolInvocations: false,
|
||||
} as const;
|
||||
|
||||
constructor(private readonly onClose: () => void) {}
|
||||
|
||||
async run(): Promise<AgentRunResult> {
|
||||
return {
|
||||
sessionId: "non-persistent",
|
||||
finalText: "",
|
||||
timeline: [],
|
||||
};
|
||||
}
|
||||
|
||||
async startTurn(): Promise<{ turnId: string }> {
|
||||
return { turnId: "non-persistent-turn" };
|
||||
}
|
||||
|
||||
subscribe(_callback: (event: AgentStreamEvent) => void): () => void {
|
||||
return () => undefined;
|
||||
}
|
||||
|
||||
async *streamHistory(): AsyncGenerator<AgentStreamEvent> {
|
||||
return;
|
||||
}
|
||||
|
||||
async getRuntimeInfo() {
|
||||
return {
|
||||
provider: "claude" as const,
|
||||
sessionId: null,
|
||||
model: null,
|
||||
modeId: null,
|
||||
};
|
||||
}
|
||||
|
||||
async getAvailableModes(): Promise<[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
async getCurrentMode(): Promise<string | null> {
|
||||
return null;
|
||||
}
|
||||
|
||||
async setMode(_modeId: string): Promise<void> {}
|
||||
|
||||
getPendingPermissions() {
|
||||
return [];
|
||||
}
|
||||
|
||||
async respondToPermission(): Promise<void> {}
|
||||
|
||||
describePersistence(): AgentPersistenceHandle | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
async interrupt(): Promise<void> {}
|
||||
|
||||
async close(): Promise<void> {
|
||||
this.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
class NonPersistentReloadClient implements AgentClient {
|
||||
readonly provider = "claude" as const;
|
||||
readonly capabilities = {
|
||||
supportsStreaming: false,
|
||||
supportsSessionPersistence: true,
|
||||
supportsDynamicModes: false,
|
||||
supportsMcpServers: false,
|
||||
supportsReasoningStream: false,
|
||||
supportsToolInvocations: false,
|
||||
} as const;
|
||||
createSessionCalls = 0;
|
||||
resumeSessionCalls = 0;
|
||||
closeCalls = 0;
|
||||
|
||||
async isAvailable(): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
async createSession(_config: AgentSessionConfig): Promise<AgentSession> {
|
||||
this.createSessionCalls += 1;
|
||||
return new NonPersistentReloadSession(() => {
|
||||
this.closeCalls += 1;
|
||||
});
|
||||
}
|
||||
|
||||
async resumeSession(
|
||||
_handle: AgentPersistenceHandle,
|
||||
_overrides?: Partial<AgentSessionConfig>,
|
||||
): Promise<AgentSession> {
|
||||
this.resumeSessionCalls += 1;
|
||||
return new NonPersistentReloadSession(() => {
|
||||
this.closeCalls += 1;
|
||||
});
|
||||
}
|
||||
|
||||
async listModels() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
describe("daemon client E2E", () => {
|
||||
let ctx: DaemonTestContext;
|
||||
|
||||
@@ -279,6 +397,38 @@ describe("daemon client E2E", () => {
|
||||
}
|
||||
}, 120000);
|
||||
|
||||
test("refresh_agent rebuilds a live agent even when it has no persistence handle", async () => {
|
||||
const cwd = tmpCwd();
|
||||
const client = new NonPersistentReloadClient();
|
||||
const localCtx = await createDaemonTestContext({
|
||||
agentClients: {
|
||||
claude: client,
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const created = await localCtx.client.createAgent({
|
||||
config: {
|
||||
provider: "claude",
|
||||
cwd,
|
||||
},
|
||||
});
|
||||
|
||||
expect(client.createSessionCalls).toBe(1);
|
||||
expect(client.resumeSessionCalls).toBe(0);
|
||||
expect(client.closeCalls).toBe(0);
|
||||
|
||||
await localCtx.client.refreshAgent(created.id);
|
||||
|
||||
expect(client.createSessionCalls).toBe(2);
|
||||
expect(client.resumeSessionCalls).toBe(0);
|
||||
expect(client.closeCalls).toBe(1);
|
||||
} finally {
|
||||
await localCtx.cleanup();
|
||||
rmSync(cwd, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("resume_agent auto-unarchives archived agents", async () => {
|
||||
const cwd = tmpCwd();
|
||||
try {
|
||||
|
||||
@@ -3095,11 +3095,7 @@ export class Session {
|
||||
const existing = this.agentManager.getAgent(agentId);
|
||||
if (existing) {
|
||||
await this.interruptAgentIfRunning(agentId);
|
||||
if (existing.persistence) {
|
||||
snapshot = await this.agentManager.reloadAgentSession(agentId);
|
||||
} else {
|
||||
snapshot = existing;
|
||||
}
|
||||
snapshot = await this.agentManager.reloadAgentSession(agentId);
|
||||
} else {
|
||||
const record = await this.agentStorage.get(agentId);
|
||||
if (!record) {
|
||||
|
||||
Reference in New Issue
Block a user