Gateway: keep spawned workspace overrides internal (#43801)

* Gateway: keep spawned workspace overrides internal

* Changelog: note GHSA-2rqg agent boundary fix

* Gateway: persist spawned workspace inheritance in sessions

* Agents: clean failed lineage spawn state

* Tests: cover lineage attachment cleanup

* Tests: cover lineage thread cleanup
This commit is contained in:
Vincent Koc
2026-03-12 04:20:00 -04:00
committed by GitHub
parent 97683071b5
commit 46a332385d
12 changed files with 226 additions and 35 deletions

View File

@@ -153,6 +153,25 @@ async function cleanupProvisionalSession(
}
}
async function cleanupFailedSpawnBeforeAgentStart(params: {
childSessionKey: string;
attachmentAbsDir?: string;
emitLifecycleHooks?: boolean;
deleteTranscript?: boolean;
}): Promise<void> {
if (params.attachmentAbsDir) {
try {
await fs.rm(params.attachmentAbsDir, { recursive: true, force: true });
} catch {
// Best-effort cleanup only.
}
}
await cleanupProvisionalSession(params.childSessionKey, {
emitLifecycleHooks: params.emitLifecycleHooks,
deleteTranscript: params.deleteTranscript,
});
}
function resolveSpawnMode(params: {
requestedMode?: SpawnSubagentMode;
threadRequested: boolean;
@@ -561,10 +580,32 @@ export async function spawnSubagentDirect(
explicitWorkspaceDir: toolSpawnMetadata.workspaceDir,
}),
});
const spawnLineagePatchError = await patchChildSession({
spawnedBy: spawnedByKey,
...(spawnedMetadata.workspaceDir ? { spawnedWorkspaceDir: spawnedMetadata.workspaceDir } : {}),
});
if (spawnLineagePatchError) {
await cleanupFailedSpawnBeforeAgentStart({
childSessionKey,
attachmentAbsDir,
emitLifecycleHooks: threadBindingReady,
deleteTranscript: true,
});
return {
status: "error",
error: spawnLineagePatchError,
childSessionKey,
};
}
const childIdem = crypto.randomUUID();
let childRunId: string = childIdem;
try {
const {
spawnedBy: _spawnedBy,
workspaceDir: _workspaceDir,
...publicSpawnedMetadata
} = spawnedMetadata;
const response = await callGateway<{ runId: string }>({
method: "agent",
params: {
@@ -581,7 +622,7 @@ export async function spawnSubagentDirect(
thinking: thinkingOverride,
timeout: runTimeoutSeconds,
label: label || undefined,
...spawnedMetadata,
...publicSpawnedMetadata,
},
timeoutMs: 10_000,
});