summaryrefslogtreecommitdiffhomepage
path: root/packages/api
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-05-23 05:25:03 +0900
committerAdam Malczewski <[email protected]>2026-05-23 05:25:03 +0900
commit225d3ea65cfc35d211fc66e30cf05cbc693d37e4 (patch)
tree1d765743942eb8c5d519fea37763ed6a36b90eec /packages/api
parent9287cccb29d135ea19f2612c26f3090c94820d8c (diff)
downloaddispatch-225d3ea65cfc35d211fc66e30cf05cbc693d37e4.tar.gz
dispatch-225d3ea65cfc35d211fc66e30cf05cbc693d37e4.zip
feat: relative working directory support and subagent tab cwd propagation
- Resolve relative cwd paths (e.g. ./subtask) against parent's working directory at runtime - check-dir endpoint resolves relative paths and returns the resolved absolute path - AgentBuilder shows resolved path below input for relative paths, updated helper text - tab-created event now includes workingDirectory so subagent tabs display their cwd in sidebar - Add workingDirectory to tab-created AgentEvent type definition - spawnChildAgent stores resolved absolute path instead of raw relative path
Diffstat (limited to 'packages/api')
-rw-r--r--packages/api/src/agent-manager.ts17
-rw-r--r--packages/api/src/routes/agents.ts9
2 files changed, 23 insertions, 3 deletions
diff --git a/packages/api/src/agent-manager.ts b/packages/api/src/agent-manager.ts
index 74995b2..d473950 100644
--- a/packages/api/src/agent-manager.ts
+++ b/packages/api/src/agent-manager.ts
@@ -329,6 +329,15 @@ export class AgentManager {
workingDirectory = join(homedir(), workingDirectory.slice(1));
}
+ // Resolve relative paths against the default working directory
+ // (e.g. subagent cwd "./subtask" resolves relative to the parent's effective dir)
+ {
+ const { isAbsolute, resolve } = await import("node:path");
+ if (!isAbsolute(workingDirectory)) {
+ workingDirectory = resolve(defaultWorkDir, workingDirectory);
+ }
+ }
+
// Auto-create the working directory if it doesn't exist
try {
const { mkdirSync, existsSync } = await import("node:fs");
@@ -668,6 +677,8 @@ export class AgentManager {
parentEffectiveDir = join(homedir(), parentEffectiveDir.slice(1));
}
+ // Resolve and validate child working directory against parent's effective dir
+ let resolvedWorkingDirectory = options.workingDirectory;
if (options.workingDirectory) {
const { isAbsolute, relative, resolve, join } = await import("node:path");
// Expand ~ in child working directory
@@ -685,6 +696,9 @@ export class AgentManager {
`Working directory "${options.workingDirectory}" is outside the parent's working directory "${parentDir}".`,
);
}
+ // Store the resolved absolute path so downstream code doesn't
+ // re-resolve against the wrong base directory
+ resolvedWorkingDirectory = resolved;
}
// Intersect requested tools with parent's allowed tools to prevent privilege escalation
@@ -696,7 +710,7 @@ export class AgentManager {
// Create the tab agent entry with overrides
const tabAgent = this._getOrCreateTabAgent(tabId);
tabAgent.toolsOverride = childTools;
- tabAgent.workingDirectoryOverride = options.workingDirectory;
+ tabAgent.workingDirectoryOverride = resolvedWorkingDirectory;
tabAgent.keyId = options.parentKeyId ?? null;
tabAgent.modelId = options.parentModelId ?? null;
tabAgent.finalOutput = "";
@@ -727,6 +741,7 @@ export class AgentManager {
keyId: tabAgent.keyId,
modelId: tabAgent.modelId,
parentTabId: options.parentTabId ?? null,
+ workingDirectory: resolvedWorkingDirectory ?? null,
},
tabId,
);
diff --git a/packages/api/src/routes/agents.ts b/packages/api/src/routes/agents.ts
index d3ca23f..1627674 100644
--- a/packages/api/src/routes/agents.ts
+++ b/packages/api/src/routes/agents.ts
@@ -99,11 +99,16 @@ agentsRoutes.get("/check-dir", (c) => {
if (dirPath === "~" || dirPath.startsWith("~/")) {
dirPath = path.join(os.homedir(), dirPath.slice(1));
}
+ // Resolve relative paths against the project root
+ if (!path.isAbsolute(dirPath)) {
+ const projectDir = process.env.DISPATCH_WORKING_DIR || process.cwd();
+ dirPath = path.resolve(projectDir, dirPath);
+ }
try {
const stat = fs.statSync(dirPath);
- return c.json({ exists: stat.isDirectory() });
+ return c.json({ exists: stat.isDirectory(), resolved: dirPath });
} catch {
- return c.json({ exists: false });
+ return c.json({ exists: false, resolved: dirPath });
}
});