From b23b4002121c04b84c7a229a982f1175a1b1d1d3 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Mon, 13 Jul 2026 13:26:20 +0200 Subject: [PATCH] fix(hub): fail creates when prompts cannot start --- .../hub/relationship-owned-executions.test.ts | 14 ++++++++++++++ .../server/hub/relationship-owned-executions.ts | 7 ++++++- .../server/hub/test-utils/relationship-harness.ts | 13 ++++++++++++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/server/src/server/hub/relationship-owned-executions.test.ts b/packages/server/src/server/hub/relationship-owned-executions.test.ts index 099c8f5220..0aac8b5477 100644 --- a/packages/server/src/server/hub/relationship-owned-executions.test.ts +++ b/packages/server/src/server/hub/relationship-owned-executions.test.ts @@ -53,3 +53,17 @@ test("a failed Hub create removes its auto-created worktree", async () => { expect(await hub.listedWorktrees()).toHaveLength(1); expect(await hub.durableOwnedAgentIds()).toEqual([]); }); + +test("a Hub create fails when its initial prompt cannot start", async () => { + const hub = await launchRelationship(); + hub.failNextProviderPromptStart(); + hub.beginOwnedCreate("failed-prompt-create", "failed-prompt-execution"); + + const response = await hub.ownedCreateResult("failed-prompt-create"); + + expect(response).toMatchObject({ + type: "hub.agent.create.response", + payload: { success: false, executionId: "failed-prompt-execution" }, + }); + expect(await hub.durableOwnedAgentIds()).toEqual([]); +}); diff --git a/packages/server/src/server/hub/relationship-owned-executions.ts b/packages/server/src/server/hub/relationship-owned-executions.ts index 66ac10e74f..d787494a6d 100644 --- a/packages/server/src/server/hub/relationship-owned-executions.ts +++ b/packages/server/src/server/hub/relationship-owned-executions.ts @@ -137,6 +137,7 @@ export class RelationshipOwnedExecutions implements HubExecutions { provider: input.model ? `${input.provider}/${input.model}` : input.provider, title: input.prompt, initialPrompt: input.prompt, + promptFailure: "throw", cwd: input.cwd, workspaceId: input.workspaceId, mode: input.modeId, @@ -156,7 +157,11 @@ export class RelationshipOwnedExecutions implements HubExecutions { }, }); } catch (error) { - await this.cleanupFailedCreate({ createdWorktree, createdAgentId }); + if (createdAgentId) { + await this.agentManager.closeAgent(createdAgentId); + await this.agentStorage.remove(createdAgentId); + } + await this.cleanupFailedCreate({ createdWorktree, createdAgentId: null }); throw error; } diff --git a/packages/server/src/server/hub/test-utils/relationship-harness.ts b/packages/server/src/server/hub/test-utils/relationship-harness.ts index 4fe418195f..0396f90df0 100644 --- a/packages/server/src/server/hub/test-utils/relationship-harness.ts +++ b/packages/server/src/server/hub/test-utils/relationship-harness.ts @@ -424,12 +424,19 @@ export class HubRelationshipHarness { private host = ""; private readonly logs: string[] = []; private readonly providerPrompts: AgentPromptInput[] = []; + private failNextPromptStart = false; private observedEnrollments = 0; private observedSockets = 0; private reconcileRequests = 0; private readonly codex = new ControlledAgentClient( createTestAgentClients({ - onStartTurn: (prompt) => this.providerPrompts.push(prompt), + onStartTurn: (prompt) => { + if (this.failNextPromptStart) { + this.failNextPromptStart = false; + throw new Error("Requested provider prompt startup failure"); + } + this.providerPrompts.push(prompt); + }, }).codex, ); @@ -534,6 +541,10 @@ export class HubRelationshipHarness { this.remote.failRevocations(count); } + failNextProviderPromptStart(): void { + this.failNextPromptStart = true; + } + async socketDialed(): Promise { await this.remote.socketAt(this.observedSockets++); }