Make chat work directory per-run instead of global

The work directory was stored in a single shared config file, so setting
it in one chat changed it for every chat and new chats inherited a stale
value. Store it as run-scoped metadata: captured on the start event at
creation and updatable mid-chat via an appended workdir-changed event.
The runtime reads the per-run value from AgentState, and the chat input
reads/writes it against the run (pending chats pass it into runs:create).
This commit is contained in:
Gagancreates 2026-05-26 18:23:32 +00:00
parent b9c4099c3b
commit ce1913d4e0
No known key found for this signature in database
9 changed files with 113 additions and 40 deletions

View file

@ -234,6 +234,15 @@ const ipcSchemas = {
}),
res: Run,
},
'runs:setWorkdir': {
req: z.object({
runId: z.string(),
workingDirectory: z.string().nullable(),
}),
res: z.object({
success: z.literal(true),
}),
},
'runs:list': {
req: z.object({
cursor: z.string().optional(),

View file

@ -31,6 +31,9 @@ export const StartEvent = BaseRunEvent.extend({
"knowledge_sync",
]).optional(),
subUseCase: z.string().optional(),
// Per-run work directory chosen at creation. Optional: a run may have none,
// and it can be changed later via WorkdirChangedEvent.
workingDirectory: z.string().optional(),
});
export const SpawnSubFlowEvent = BaseRunEvent.extend({
@ -105,6 +108,12 @@ export const RunStoppedEvent = BaseRunEvent.extend({
reason: z.enum(["user-requested", "force-stopped"]).optional(),
});
export const WorkdirChangedEvent = BaseRunEvent.extend({
type: z.literal("workdir-changed"),
// Absent/empty means the work directory was cleared.
workingDirectory: z.string().optional(),
});
export const RunEvent = z.union([
RunProcessingStartEvent,
RunProcessingEndEvent,
@ -121,6 +130,7 @@ export const RunEvent = z.union([
ToolPermissionResponseEvent,
RunErrorEvent,
RunStoppedEvent,
WorkdirChangedEvent,
]);
export const ToolPermissionAuthorizePayload = ToolPermissionResponseEvent.pick({
@ -153,6 +163,7 @@ export const Run = z.object({
provider: z.string(),
useCase: UseCase.optional(),
subUseCase: z.string().optional(),
workingDirectory: z.string().optional(),
log: z.array(RunEvent),
});
@ -172,4 +183,5 @@ export const CreateRunOptions = z.object({
provider: z.string().optional(),
useCase: UseCase.optional(),
subUseCase: z.string().optional(),
workingDirectory: z.string().optional(),
});