fix(app): source composer cwd from agent state, not draft hook
The GitHub picker showed "No results found." for any running agent after
the first message was sent. useAgentInputDraft owned a local cwd state
that clear("sent") wiped to "", so the composer read cwd: "" even though
the agent's real cwd was correct on the wire.
Drop cwd ownership from the hook. Composer takes cwd as a prop sourced
by the parent — agentState.cwd for running agents, composerState.workingDir
for drafts, sourceDirectory for setup flows. The hook now persists only
{text, attachments}. Legacy persisted drafts with a cwd field still
hydrate; tagged COMPAT(draft-cwd) for removal after 2026-11-09.
Also moves attachment pills back inside the composer's bordered surface
via a new attachmentSlot prop on MessageInput, and shrinks pill height
from 48 to 32 — the subagents-track tuck previously had attachments
floating between it and the input.
Adds one Playwright spec asserting the draft-create-agent path submits
the workspace cwd.
This commit is contained in:
@@ -4,54 +4,6 @@ import type { DraftCommandConfig } from "@/hooks/use-agent-commands-query";
|
||||
import type { UseAgentFormStateResult } from "@/hooks/use-agent-form-state";
|
||||
import type { AgentModelDefinition, AgentProvider } from "@server/server/agent/agent-sdk-types";
|
||||
|
||||
export interface DraftStorage {
|
||||
getItem(key: string): Promise<string | null>;
|
||||
setItem(key: string, value: string): Promise<void>;
|
||||
removeItem(key: string): Promise<void>;
|
||||
}
|
||||
|
||||
export interface StoredDraft {
|
||||
text: string;
|
||||
attachments: UserComposerAttachment[];
|
||||
cwd: string;
|
||||
}
|
||||
|
||||
export interface AgentInputDraftCore {
|
||||
load(initialCwd?: string): Promise<StoredDraft | null>;
|
||||
save(draft: StoredDraft): Promise<void>;
|
||||
clear(): Promise<void>;
|
||||
}
|
||||
|
||||
export function createAgentInputDraftCore(input: {
|
||||
storage: DraftStorage;
|
||||
storageKey: string;
|
||||
}): AgentInputDraftCore {
|
||||
return {
|
||||
async load(initialCwd?: string) {
|
||||
const json = await input.storage.getItem(input.storageKey);
|
||||
if (!json) return null;
|
||||
try {
|
||||
const parsed = JSON.parse(json) as Record<string, unknown>;
|
||||
return {
|
||||
text: typeof parsed.text === "string" ? parsed.text : "",
|
||||
attachments: Array.isArray(parsed.attachments)
|
||||
? (parsed.attachments as UserComposerAttachment[])
|
||||
: [],
|
||||
cwd: typeof parsed.cwd === "string" ? parsed.cwd : (initialCwd ?? ""),
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
async save(draft) {
|
||||
await input.storage.setItem(input.storageKey, JSON.stringify(draft));
|
||||
},
|
||||
async clear() {
|
||||
await input.storage.removeItem(input.storageKey);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export interface DraftKeyContext {
|
||||
selectedServerId: string | null;
|
||||
}
|
||||
@@ -152,11 +104,8 @@ export function buildDraftStatusControls(input: {
|
||||
export function hasDraftContent(input: {
|
||||
text: string;
|
||||
attachments: UserComposerAttachment[];
|
||||
cwd: string;
|
||||
}): boolean {
|
||||
return (
|
||||
input.text.trim().length > 0 || input.attachments.length > 0 || input.cwd.trim().length > 0
|
||||
);
|
||||
return input.text.trim().length > 0 || input.attachments.length > 0;
|
||||
}
|
||||
|
||||
export function areAttachmentsEqual(input: {
|
||||
|
||||
Reference in New Issue
Block a user