fix(agents): land #39247 from @jasonQin6 (subagent workspace inheritance)

Propagate parent workspace directories into spawned subagent runs, keep workspace override internal-only, and add regression tests for forwarding boundaries.

Co-authored-by: jasonQin6 <991262382@qq.com>
This commit is contained in:
Peter Steinberger
2026-03-07 23:55:51 +00:00
parent eeba93d63d
commit ab54532c8f
10 changed files with 80 additions and 2 deletions

View File

@@ -110,6 +110,7 @@ export const AgentParamsSchema = Type.Object(
idempotencyKey: NonEmptyString,
label: Type.Optional(SessionLabelString),
spawnedBy: Type.Optional(Type.String()),
workspaceDir: Type.Optional(Type.String()),
},
{ additionalProperties: false },
);

View File

@@ -409,6 +409,39 @@ describe("gateway agent handler", () => {
expect(callArgs.bestEffortDeliver).toBe(false);
});
it("only forwards workspaceDir for spawned subagent runs", async () => {
primeMainAgentRun();
mocks.agentCommand.mockClear();
await invokeAgent(
{
message: "normal run",
sessionKey: "agent:main:main",
workspaceDir: "/tmp/ignored",
idempotencyKey: "workspace-ignored",
},
{ reqId: "workspace-ignored-1" },
);
await vi.waitFor(() => expect(mocks.agentCommand).toHaveBeenCalled());
const normalCall = mocks.agentCommand.mock.calls.at(-1)?.[0] as { workspaceDir?: string };
expect(normalCall.workspaceDir).toBeUndefined();
mocks.agentCommand.mockClear();
await invokeAgent(
{
message: "spawned run",
sessionKey: "agent:main:main",
spawnedBy: "agent:main:subagent:parent",
workspaceDir: "/tmp/inherited",
idempotencyKey: "workspace-forwarded",
},
{ reqId: "workspace-forwarded-1" },
);
await vi.waitFor(() => expect(mocks.agentCommand).toHaveBeenCalled());
const spawnedCall = mocks.agentCommand.mock.calls.at(-1)?.[0] as { workspaceDir?: string };
expect(spawnedCall.workspaceDir).toBe("/tmp/inherited");
});
it("keeps origin messageChannel as webchat while delivery channel uses last session channel", async () => {
mockMainSessionEntry({
sessionId: "existing-session-id",

View File

@@ -211,6 +211,7 @@ export const agentHandlers: GatewayRequestHandlers = {
label?: string;
spawnedBy?: string;
inputProvenance?: InputProvenance;
workspaceDir?: string;
};
const senderIsOwner = resolveSenderIsOwnerFromClient(client);
const cfg = loadConfig();
@@ -645,6 +646,8 @@ export const agentHandlers: GatewayRequestHandlers = {
extraSystemPrompt: request.extraSystemPrompt,
internalEvents: request.internalEvents,
inputProvenance,
// Internal-only: allow workspace override for spawned subagent runs.
workspaceDir: spawnedByValue ? request.workspaceDir : undefined,
senderIsOwner,
},
defaultRuntime,