mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-12 09:41:11 +00:00
fix: normalize subagent registry session keys
This commit is contained in:
@@ -274,6 +274,14 @@ export function createSubagentRunManager(params: {
|
||||
attachmentsRootDir?: string;
|
||||
retainAttachmentsOnKeep?: boolean;
|
||||
}) => {
|
||||
const runId = registerParams.runId.trim();
|
||||
const childSessionKey = registerParams.childSessionKey.trim();
|
||||
const requesterSessionKey = registerParams.requesterSessionKey.trim();
|
||||
const controllerSessionKey =
|
||||
registerParams.controllerSessionKey?.trim() || requesterSessionKey;
|
||||
if (!runId || !childSessionKey || !requesterSessionKey) {
|
||||
return;
|
||||
}
|
||||
const now = Date.now();
|
||||
const cfg = params.loadConfig();
|
||||
const archiveAfterMs = resolveArchiveAfterMs(cfg);
|
||||
@@ -287,12 +295,11 @@ export function createSubagentRunManager(params: {
|
||||
const runTimeoutSeconds = registerParams.runTimeoutSeconds ?? 0;
|
||||
const waitTimeoutMs = params.resolveSubagentWaitTimeoutMs(cfg, runTimeoutSeconds);
|
||||
const requesterOrigin = normalizeDeliveryContext(registerParams.requesterOrigin);
|
||||
params.runs.set(registerParams.runId, {
|
||||
runId: registerParams.runId,
|
||||
childSessionKey: registerParams.childSessionKey,
|
||||
controllerSessionKey:
|
||||
registerParams.controllerSessionKey ?? registerParams.requesterSessionKey,
|
||||
requesterSessionKey: registerParams.requesterSessionKey,
|
||||
params.runs.set(runId, {
|
||||
runId,
|
||||
childSessionKey,
|
||||
controllerSessionKey,
|
||||
requesterSessionKey,
|
||||
requesterOrigin,
|
||||
requesterDisplayKey: registerParams.requesterDisplayKey,
|
||||
task: registerParams.task,
|
||||
@@ -318,12 +325,12 @@ export function createSubagentRunManager(params: {
|
||||
try {
|
||||
createRunningTaskRun({
|
||||
runtime: "subagent",
|
||||
sourceId: registerParams.runId,
|
||||
ownerKey: registerParams.requesterSessionKey,
|
||||
sourceId: runId,
|
||||
ownerKey: requesterSessionKey,
|
||||
scopeKind: "session",
|
||||
requesterOrigin,
|
||||
childSessionKey: registerParams.childSessionKey,
|
||||
runId: registerParams.runId,
|
||||
childSessionKey,
|
||||
runId,
|
||||
label: registerParams.label,
|
||||
task: registerParams.task,
|
||||
deliveryStatus:
|
||||
@@ -343,7 +350,7 @@ export function createSubagentRunManager(params: {
|
||||
params.startSweeper();
|
||||
// Wait for subagent completion via gateway RPC (cross-process).
|
||||
// The in-process lifecycle listener is a fallback for embedded runs.
|
||||
void waitForSubagentCompletion(registerParams.runId, waitTimeoutMs);
|
||||
void waitForSubagentCompletion(runId, waitTimeoutMs);
|
||||
};
|
||||
|
||||
const releaseSubagentRun = (runId: string) => {
|
||||
|
||||
@@ -319,6 +319,78 @@ describe("subagent registry persistence", () => {
|
||||
expect(after.version).toBe(2);
|
||||
});
|
||||
|
||||
it("normalizes persisted and newly registered session keys to canonical trimmed values", async () => {
|
||||
const persisted = {
|
||||
version: 2,
|
||||
runs: {
|
||||
"run-spaced": {
|
||||
runId: "run-spaced",
|
||||
childSessionKey: " agent:main:subagent:spaced-child ",
|
||||
controllerSessionKey: " agent:main:subagent:controller ",
|
||||
requesterSessionKey: " agent:main:main ",
|
||||
requesterDisplayKey: "main",
|
||||
task: "spaced persisted keys",
|
||||
cleanup: "keep",
|
||||
createdAt: 1,
|
||||
startedAt: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
await writePersistedRegistry(persisted, { seedChildSessions: false });
|
||||
|
||||
const restored = loadSubagentRegistryFromDisk();
|
||||
const restoredEntry = restored.get("run-spaced");
|
||||
expect(restoredEntry).toMatchObject({
|
||||
childSessionKey: "agent:main:subagent:spaced-child",
|
||||
controllerSessionKey: "agent:main:subagent:controller",
|
||||
requesterSessionKey: "agent:main:main",
|
||||
});
|
||||
|
||||
resetSubagentRegistryForTests({ persist: false });
|
||||
addSubagentRunForTests(restoredEntry as never);
|
||||
expect(listSubagentRunsForRequester("agent:main:main")).toEqual([
|
||||
expect.objectContaining({
|
||||
runId: "run-spaced",
|
||||
}),
|
||||
]);
|
||||
expect(getSubagentRunByChildSessionKey("agent:main:subagent:spaced-child")).toMatchObject({
|
||||
runId: "run-spaced",
|
||||
});
|
||||
|
||||
resetSubagentRegistryForTests({ persist: false });
|
||||
tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-subagent-"));
|
||||
process.env.OPENCLAW_STATE_DIR = tempStateDir;
|
||||
|
||||
const mod = await import(`./subagent-registry.ts?t=${Date.now()}`);
|
||||
vi.mocked(callGateway).mockResolvedValue({
|
||||
status: "ok",
|
||||
startedAt: 111,
|
||||
endedAt: 222,
|
||||
});
|
||||
|
||||
mod.registerSubagentRun({
|
||||
runId: " run-live ",
|
||||
childSessionKey: " agent:main:subagent:live-child ",
|
||||
controllerSessionKey: " agent:main:subagent:live-controller ",
|
||||
requesterSessionKey: " agent:main:main ",
|
||||
requesterDisplayKey: "main",
|
||||
task: "live spaced keys",
|
||||
cleanup: "keep",
|
||||
});
|
||||
|
||||
expect(mod.listSubagentRunsForRequester("agent:main:main")).toEqual([
|
||||
expect.objectContaining({
|
||||
runId: "run-live",
|
||||
childSessionKey: "agent:main:subagent:live-child",
|
||||
controllerSessionKey: "agent:main:subagent:live-controller",
|
||||
requesterSessionKey: "agent:main:main",
|
||||
}),
|
||||
]);
|
||||
expect(mod.getSubagentRunByChildSessionKey("agent:main:subagent:live-child")).toMatchObject({
|
||||
runId: "run-live",
|
||||
});
|
||||
});
|
||||
|
||||
it("retries cleanup announce after a failed announce", async () => {
|
||||
const persisted = createPersistedEndedRun({
|
||||
runId: "run-3",
|
||||
|
||||
@@ -89,6 +89,13 @@ export function loadSubagentRegistryFromDisk(): Map<string, SubagentRunRecord> {
|
||||
accountId: readStringValue(typed.requesterAccountId),
|
||||
},
|
||||
);
|
||||
const childSessionKey = readStringValue(typed.childSessionKey)?.trim() ?? "";
|
||||
const requesterSessionKey = readStringValue(typed.requesterSessionKey)?.trim() ?? "";
|
||||
const controllerSessionKey =
|
||||
readStringValue(typed.controllerSessionKey)?.trim() || requesterSessionKey;
|
||||
if (!childSessionKey || !requesterSessionKey) {
|
||||
continue;
|
||||
}
|
||||
const {
|
||||
announceCompletedAt: _announceCompletedAt,
|
||||
announceHandled: _announceHandled,
|
||||
@@ -98,6 +105,9 @@ export function loadSubagentRegistryFromDisk(): Map<string, SubagentRunRecord> {
|
||||
} = typed;
|
||||
out.set(runId, {
|
||||
...rest,
|
||||
childSessionKey,
|
||||
requesterSessionKey,
|
||||
controllerSessionKey,
|
||||
requesterOrigin,
|
||||
cleanupCompletedAt,
|
||||
cleanupHandled,
|
||||
|
||||
Reference in New Issue
Block a user