Show prompt titles immediately for new workspaces (#1567)

* Set workspace prompt titles at creation

* Cover existing workspace prompt titles

* Fix rewind E2E prompt selector
This commit is contained in:
Mohamed Boudra
2026-06-17 15:30:32 +08:00
committed by GitHub
parent 1340a80144
commit 0c9802b22b
6 changed files with 304 additions and 35 deletions
@@ -132,7 +132,7 @@ interface SessionTestAccess {
emitWorkspaceUpdatesForWorkspaceIds(...args: unknown[]): Promise<unknown>;
applyGeneratedWorkspaceTitle(
workspaceId: string,
input: { title: string; branch?: string | null },
input: { title: string; branch?: string | null; promptTitle?: string | null },
): Promise<void>;
emit(message: unknown): void;
onMessage(message: unknown): void;
@@ -791,6 +791,122 @@ test("create_agent_request keeps requested child cwd when grouped under an exist
}
});
test("create_agent_request writes the first prompt title onto an untitled existing workspace", async () => {
const workdir = mkdtempSync(path.join(tmpdir(), "paseo-create-agent-existing-title-"));
try {
const cwd = path.join(workdir, "repo");
mkdirSync(cwd, { recursive: true });
const logger = {
child: () => logger,
trace: vi.fn(),
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
};
const agentStorage = new AgentStorage(path.join(workdir, "agents"), asSessionLogger(logger));
const agentManager = new AgentManager({
clients: { codex: new CreateAgentTestClient() },
registry: agentStorage,
logger: asSessionLogger(logger),
idFactory: () => "00000000-0000-4000-8000-000000000552",
});
const projectRegistry = new FileBackedProjectRegistry(
path.join(workdir, "projects.json"),
asSessionLogger(logger),
);
const workspaceRegistry = new FileBackedWorkspaceRegistry(
path.join(workdir, "workspaces.json"),
asSessionLogger(logger),
);
await projectRegistry.upsert(
createPersistedProjectRecord({
projectId: "proj-existing",
rootPath: cwd,
kind: "git",
displayName: "repo",
createdAt: "2026-05-07T00:00:00.000Z",
updatedAt: "2026-05-07T00:00:00.000Z",
}),
);
await workspaceRegistry.upsert(
createPersistedWorkspaceRecord({
workspaceId: "ws-existing",
projectId: "proj-existing",
cwd,
kind: "local_checkout",
displayName: "repo",
title: null,
createdAt: "2026-05-07T00:00:00.000Z",
updatedAt: "2026-05-07T00:00:00.000Z",
}),
);
const session = asTestSession(
new Session({
clientId: "test-client",
appVersion: null,
onMessage: vi.fn(),
logger: asSessionLogger(logger),
downloadTokenStore: asDownloadTokenStore(),
pushTokenStore: asPushTokenStore(),
paseoHome: path.join(workdir, "paseo-home"),
agentManager,
agentStorage,
projectRegistry,
workspaceRegistry,
chatService: asChatService(),
scheduleService: asScheduleService(),
loopService: asLoopService(),
checkoutDiffManager: asCheckoutDiffManager({
subscribe: async () => ({
initial: { cwd, files: [], error: null },
unsubscribe: () => {},
}),
scheduleRefreshForCwd: () => {},
onWorkspaceStateMayHaveChanged: () => {},
getMetrics: () => ({
checkoutDiffTargetCount: 0,
checkoutDiffSubscriptionCount: 0,
checkoutDiffWatcherCount: 0,
checkoutDiffFallbackRefreshTargetCount: 0,
}),
dispose: () => {},
}),
workspaceGitService: createNoopWorkspaceGitService(),
daemonConfigStore: asDaemonConfigStore({
get: () => ({ mcp: { injectIntoAgents: false }, providers: {} }),
onChange: () => () => {},
}),
mcpBaseUrl: null,
stt: null,
tts: null,
providerSnapshotManager: createProviderSnapshotManagerStub().manager,
terminalManager: null,
}),
);
await session.handleMessage({
type: "create_agent_request",
requestId: "req-create-existing-title",
workspaceId: "ws-existing",
config: { provider: "codex", cwd },
initialPrompt: "Fix login bug\nwith better validation",
attachments: [],
});
const [createdAgent] = agentManager.listAgents();
expect(createdAgent?.workspaceId).toBe("ws-existing");
await expect(workspaceRegistry.get("ws-existing")).resolves.toMatchObject({
title: "Fix login bug",
});
} finally {
rmSync(workdir, { recursive: true, force: true });
}
});
test("unsupported persisted agents are excluded from active lists but preserved in history payloads", async () => {
const session = createSessionForWorkspaceTests({ appVersion: "0.1.45" });
const storedRecord = {
@@ -6266,6 +6382,46 @@ test("applyGeneratedWorkspaceTitle writes branch metadata and does not clobber c
expect(saved?.title).toBe("User-set title");
});
test("applyGeneratedWorkspaceTitle replaces the unchanged prompt title", async () => {
const session = createSessionForWorkspaceTests();
const workspace = createPersistedWorkspaceRecord({
workspaceId: "ws-prompt-title",
projectId: "proj-prompt-title",
cwd: REPO_CWD,
kind: "directory",
displayName: "repo",
title: "Fix login bug",
createdAt: "2026-03-01T12:00:00.000Z",
updatedAt: "2026-03-01T12:00:00.000Z",
});
const stored = new Map([[workspace.workspaceId, workspace]]);
session.workspaceRegistry.get = async (id: string) => stored.get(id) ?? null;
session.workspaceRegistry.upsert = async (record: unknown) => {
const parsed = record as typeof workspace;
stored.set(parsed.workspaceId, parsed);
};
await session.applyGeneratedWorkspaceTitle(workspace.workspaceId, {
title: "Generated login fix",
promptTitle: "Fix login bug",
});
expect(stored.get(workspace.workspaceId)?.title).toBe("Generated login fix");
stored.set(workspace.workspaceId, {
...workspace,
title: "User rename",
updatedAt: "2026-03-01T12:01:00.000Z",
});
await session.applyGeneratedWorkspaceTitle(workspace.workspaceId, {
title: "Generated login fix",
promptTitle: "Fix login bug",
});
expect(stored.get(workspace.workspaceId)?.title).toBe("User rename");
});
// Phase 7: branch is a git fact derived per-descriptor from each workspace's own
// live git snapshot, and reconciliation re-persists `branch` per workspace from
// its own cwd. handleCheckoutRenameBranchRequest renames the git branch and
@@ -6327,3 +6483,42 @@ test("checkout.rename_branch.request renames the branch without a denormalized b
expect(persisted?.displayName).toBe("Refactor auth flow");
expect(persisted?.title).toBe("Refactor auth flow");
});
test("workspace.create.response persists the first prompt as the initial title", async () => {
const emitted: SessionOutboundMessage[] = [];
const workspaces = new Map<string, ReturnType<typeof createPersistedWorkspaceRecord>>();
const session = createSessionForWorkspaceTests({
onMessage: (message) => emitted.push(message),
workspaceRegistry: {
initialize: async () => {},
existsOnDisk: async () => true,
list: async () => Array.from(workspaces.values()),
get: async (workspaceId: string) => workspaces.get(workspaceId) ?? null,
upsert: async (workspace) => {
workspaces.set(workspace.workspaceId, workspace);
},
archive: async () => {},
remove: async () => {},
},
});
session.listAgentPayloads = async () => [];
await session.handleMessage({
type: "workspace.create.request",
requestId: "req-create-first-prompt",
source: { kind: "directory", path: REPO_CWD },
firstAgentContext: {
prompt: "Add retries to the payments flow\nwith exponential backoff",
},
});
const response = findByType(emitted, "workspace.create.response");
expect(response?.payload.error).toBeNull();
expect(response?.payload.workspace?.title).toBe("Add retries to the payments flow");
expect(response?.payload.workspace?.name).toBe("Add retries to the payments flow");
const workspaceId = response?.payload.workspace?.id;
expect(workspaceId).toBeDefined();
const persisted = await session.workspaceRegistry.get(workspaceId as string);
expect(persisted?.title).toBe("Add retries to the payments flow");
});