refactor: return agent on create and resume instead of waiting
This commit is contained in:
@@ -121,7 +121,6 @@ Run `npx expo-doctor` to diagnose version mismatches and native module issues.
|
||||
- **Launch agents in the most permissive mode**: Use full access or bypass permissions mode.
|
||||
- **Set cwd to the repository root** - The agent's working directory should usually be the repo root
|
||||
|
||||
|
||||
**CRITICAL: ALWAYS RUN TYPECHECK AFTER EVERY CHANGE.**
|
||||
|
||||
## Agent Authentication
|
||||
@@ -132,7 +131,6 @@ All agent providers (Claude, Codex, OpenCode) handle their own authentication ou
|
||||
|
||||
## NEVER DO THESE THINGS
|
||||
|
||||
- **NEVER restart the main Paseo daemon on port 6767** - This is the production daemon that launches and manages agents. If you are reading this, you are probably running as an agent under it. Restarting it will kill your own process and all other running agents. The daemon is managed by the user in Tmux.
|
||||
- **NEVER kill or restart processes in Tmux** without explicit user permission
|
||||
- **NEVER restart the main Paseo daemon on port 6767 without permission** - This is the production daemon that launches and manages agents. If you are reading this, you are probably running as an agent under it. Restarting it will kill your own process and all other running agents. The daemon is managed by the user in Tmux.
|
||||
- **NEVER assume a timeout means the service needs restarting** - Timeouts can be transient network issues, not service failures
|
||||
- **NEVER add authentication checks to tests** - Agent providers handle their own auth. If tests fail due to auth issues, report it rather than adding conditional skips or env var checks
|
||||
|
||||
@@ -200,9 +200,6 @@ type SubscribeTerminalPayload = SubscribeTerminalResponse["payload"];
|
||||
type TerminalOutputPayload = TerminalOutput["payload"];
|
||||
type KillTerminalPayload = KillTerminalResponse["payload"];
|
||||
|
||||
type AgentCreateFailedStatusPayload = z.infer<
|
||||
typeof AgentCreateFailedStatusPayloadSchema
|
||||
>;
|
||||
type AgentRefreshedStatusPayload = z.infer<
|
||||
typeof AgentRefreshedStatusPayloadSchema
|
||||
>;
|
||||
@@ -889,43 +886,7 @@ export class DaemonClientV2 {
|
||||
throw new Error(status.error);
|
||||
}
|
||||
|
||||
return this.waitForAgentUpsert(
|
||||
status.agentId,
|
||||
(snapshot) => snapshot.status === "idle",
|
||||
60000
|
||||
);
|
||||
}
|
||||
|
||||
async createAgentExpectFail(
|
||||
options: CreateAgentRequestOptions
|
||||
): Promise<AgentCreateFailedStatusPayload> {
|
||||
const requestId = this.createRequestId(options.requestId);
|
||||
const config = resolveAgentConfig(options);
|
||||
|
||||
const message = SessionInboundMessageSchema.parse({
|
||||
type: "create_agent_request",
|
||||
requestId,
|
||||
config,
|
||||
...(options.initialPrompt ? { initialPrompt: options.initialPrompt } : {}),
|
||||
});
|
||||
|
||||
const response = this.waitFor(
|
||||
(msg) => {
|
||||
if (msg.type !== "status") {
|
||||
return null;
|
||||
}
|
||||
const failed = AgentCreateFailedStatusPayloadSchema.safeParse(msg.payload);
|
||||
if (failed.success && failed.data.requestId === requestId) {
|
||||
return failed.data;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
10000,
|
||||
{ skipQueue: true }
|
||||
);
|
||||
|
||||
await this.sendSessionMessageOrThrow(message);
|
||||
return response;
|
||||
return status.agent;
|
||||
}
|
||||
|
||||
async deleteAgent(agentId: string): Promise<void> {
|
||||
@@ -1007,11 +968,7 @@ export class DaemonClientV2 {
|
||||
await this.sendSessionMessageOrThrow(message);
|
||||
const status = await statusPromise;
|
||||
|
||||
return this.waitForAgentUpsert(
|
||||
status.agentId,
|
||||
(snapshot) => snapshot.status === "idle",
|
||||
60000
|
||||
);
|
||||
return status.agent;
|
||||
}
|
||||
|
||||
async refreshAgent(
|
||||
|
||||
@@ -185,13 +185,14 @@ describe("daemon client v2 E2E", () => {
|
||||
return unsubscribe;
|
||||
});
|
||||
|
||||
const failResult = await ctx.client.createAgentExpectFail({
|
||||
...getFullAccessConfig("codex"),
|
||||
cwd: "/this/path/does/not/exist/12345",
|
||||
title: "Should Fail",
|
||||
requestId: failRequestId,
|
||||
});
|
||||
expect(failResult.error).toContain("Working directory does not exist");
|
||||
await expect(
|
||||
ctx.client.createAgent({
|
||||
...getFullAccessConfig("codex"),
|
||||
cwd: "/this/path/does/not/exist/12345",
|
||||
title: "Should Fail",
|
||||
requestId: failRequestId,
|
||||
})
|
||||
).rejects.toThrow("Working directory does not exist");
|
||||
await failedStatusPromise;
|
||||
|
||||
let sawRefresh = false;
|
||||
|
||||
@@ -93,14 +93,13 @@ describe("daemon E2E", () => {
|
||||
test("fails to create agent with non-existent cwd", async () => {
|
||||
const nonExistentCwd = "/this/path/does/not/exist/12345";
|
||||
|
||||
const result = await ctx.client.createAgentExpectFail({
|
||||
provider: "codex", model: CODEX_TEST_MODEL, reasoningEffort: CODEX_TEST_REASONING_EFFORT,
|
||||
cwd: nonExistentCwd,
|
||||
title: "Should Fail Agent",
|
||||
});
|
||||
|
||||
expect(result.error).toContain("Working directory does not exist");
|
||||
expect(result.error).toContain(nonExistentCwd);
|
||||
await expect(
|
||||
ctx.client.createAgent({
|
||||
provider: "codex", model: CODEX_TEST_MODEL, reasoningEffort: CODEX_TEST_REASONING_EFFORT,
|
||||
cwd: nonExistentCwd,
|
||||
title: "Should Fail Agent",
|
||||
})
|
||||
).rejects.toThrow(nonExistentCwd);
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -1435,12 +1435,17 @@ export class Session {
|
||||
}
|
||||
|
||||
if (requestId) {
|
||||
const agentPayload = await this.getAgentPayloadById(snapshot.id);
|
||||
if (!agentPayload) {
|
||||
throw new Error(`Agent ${snapshot.id} not found after creation`);
|
||||
}
|
||||
this.emit({
|
||||
type: "status",
|
||||
payload: {
|
||||
status: "agent_created",
|
||||
agentId: snapshot.id,
|
||||
requestId,
|
||||
agent: agentPayload,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1510,6 +1515,10 @@ export class Session {
|
||||
await this.forwardAgentUpdate(snapshot);
|
||||
const timelineSize = this.emitAgentTimelineSnapshot(snapshot);
|
||||
if (requestId) {
|
||||
const agentPayload = await this.getAgentPayloadById(snapshot.id);
|
||||
if (!agentPayload) {
|
||||
throw new Error(`Agent ${snapshot.id} not found after resume`);
|
||||
}
|
||||
this.emit({
|
||||
type: "status",
|
||||
payload: {
|
||||
@@ -1517,6 +1526,7 @@ export class Session {
|
||||
agentId: snapshot.id,
|
||||
requestId,
|
||||
timelineSize,
|
||||
agent: agentPayload,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -979,6 +979,7 @@ const AgentStatusWithTimelineSchema = AgentStatusWithRequestSchema.extend({
|
||||
export const AgentCreatedStatusPayloadSchema = z
|
||||
.object({
|
||||
status: z.literal("agent_created"),
|
||||
agent: AgentSnapshotPayloadSchema,
|
||||
})
|
||||
.extend(AgentStatusWithRequestSchema.shape);
|
||||
|
||||
@@ -991,6 +992,7 @@ export const AgentCreateFailedStatusPayloadSchema = z.object({
|
||||
export const AgentResumedStatusPayloadSchema = z
|
||||
.object({
|
||||
status: z.literal("agent_resumed"),
|
||||
agent: AgentSnapshotPayloadSchema,
|
||||
})
|
||||
.extend(AgentStatusWithTimelineSchema.shape);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user