test: reduce auth and subagent control hotspots

This commit is contained in:
Peter Steinberger
2026-04-18 20:44:45 +01:00
parent a2b093cf6a
commit b928f360a1
3 changed files with 247 additions and 326 deletions

View File

@@ -1,7 +1,7 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { describe, expect, it, vi } from "vitest";
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
vi.mock("./cli-credentials.js", () => ({
readCodexCliCredentialsCached: () => null,
@@ -20,36 +20,51 @@ import { calculateAuthProfileCooldownMs, markAuthProfileFailure } from "./auth-p
type AuthProfileStore = ReturnType<typeof ensureAuthProfileStore>;
let tempRoot = "";
let tempCaseIndex = 0;
beforeAll(() => {
tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-auth-"));
});
afterAll(() => {
clearRuntimeAuthProfileStoreSnapshots();
fs.rmSync(tempRoot, { recursive: true, force: true });
});
function makeAgentDir(label = "case") {
tempCaseIndex += 1;
const agentDir = path.join(tempRoot, `${tempCaseIndex}-${label}`);
fs.mkdirSync(agentDir, { recursive: true });
return agentDir;
}
async function withAuthProfileStore(
fn: (ctx: { agentDir: string; store: AuthProfileStore }) => Promise<void>,
): Promise<void> {
const agentDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-auth-"));
try {
const authPath = path.join(agentDir, "auth-profiles.json");
fs.writeFileSync(
authPath,
JSON.stringify({
version: 1,
profiles: {
"anthropic:default": {
type: "api_key",
provider: "anthropic",
key: "sk-default",
},
"openrouter:default": {
type: "api_key",
provider: "openrouter",
key: "sk-or-default",
},
const agentDir = makeAgentDir("store");
const authPath = path.join(agentDir, "auth-profiles.json");
fs.writeFileSync(
authPath,
JSON.stringify({
version: 1,
profiles: {
"anthropic:default": {
type: "api_key",
provider: "anthropic",
key: "sk-default",
},
}),
);
"openrouter:default": {
type: "api_key",
provider: "openrouter",
key: "sk-or-default",
},
},
}),
);
const store = ensureAuthProfileStore(agentDir);
await fn({ agentDir, store });
} finally {
fs.rmSync(agentDir, { recursive: true, force: true });
}
const store = ensureAuthProfileStore(agentDir);
await fn({ agentDir, store });
}
function expectCooldownInRange(remainingMs: number, minMs: number, maxMs: number): void {
@@ -59,24 +74,11 @@ function expectCooldownInRange(remainingMs: number, minMs: number, maxMs: number
describe("markAuthProfileFailure", () => {
it("does not overwrite fresher on-disk credentials with a stale runtime snapshot", async () => {
const agentDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-auth-"));
try {
const authPath = path.join(agentDir, "auth-profiles.json");
fs.writeFileSync(
authPath,
JSON.stringify({
version: 1,
profiles: {
"openai:default": {
type: "api_key",
provider: "openai",
key: "sk-expired-old",
},
},
}),
);
const staleRuntimeStore: AuthProfileStore = {
const agentDir = makeAgentDir("stale-snapshot");
const authPath = path.join(agentDir, "auth-profiles.json");
fs.writeFileSync(
authPath,
JSON.stringify({
version: 1,
profiles: {
"openai:default": {
@@ -85,47 +87,55 @@ describe("markAuthProfileFailure", () => {
key: "sk-expired-old",
},
},
};
}),
);
fs.writeFileSync(
authPath,
JSON.stringify({
version: 1,
profiles: {
"openai:default": {
type: "api_key",
provider: "openai",
key: "sk-fresh-new",
},
const staleRuntimeStore: AuthProfileStore = {
version: 1,
profiles: {
"openai:default": {
type: "api_key",
provider: "openai",
key: "sk-expired-old",
},
},
};
fs.writeFileSync(
authPath,
JSON.stringify({
version: 1,
profiles: {
"openai:default": {
type: "api_key",
provider: "openai",
key: "sk-fresh-new",
},
}),
);
},
}),
);
const staleCredential = staleRuntimeStore.profiles["openai:default"];
expect(staleCredential?.type).toBe("api_key");
expect(staleCredential && "key" in staleCredential ? staleCredential.key : undefined).toBe(
"sk-expired-old",
);
const staleCredential = staleRuntimeStore.profiles["openai:default"];
expect(staleCredential?.type).toBe("api_key");
expect(staleCredential && "key" in staleCredential ? staleCredential.key : undefined).toBe(
"sk-expired-old",
);
await markAuthProfileFailure({
store: staleRuntimeStore,
profileId: "openai:default",
reason: "rate_limit",
agentDir,
});
await markAuthProfileFailure({
store: staleRuntimeStore,
profileId: "openai:default",
reason: "rate_limit",
agentDir,
});
clearRuntimeAuthProfileStoreSnapshots();
const reloaded = ensureAuthProfileStore(agentDir);
const reloadedCredential = reloaded.profiles["openai:default"];
expect(reloadedCredential?.type).toBe("api_key");
expect(
reloadedCredential && "key" in reloadedCredential ? reloadedCredential.key : undefined,
).toBe("sk-fresh-new");
expect(typeof reloaded.usageStats?.["openai:default"]?.cooldownUntil).toBe("number");
} finally {
clearRuntimeAuthProfileStoreSnapshots();
fs.rmSync(agentDir, { recursive: true, force: true });
}
clearRuntimeAuthProfileStoreSnapshots();
const reloaded = ensureAuthProfileStore(agentDir);
const reloadedCredential = reloaded.profiles["openai:default"];
expect(reloadedCredential?.type).toBe("api_key");
expect(
reloadedCredential && "key" in reloadedCredential ? reloadedCredential.key : undefined,
).toBe("sk-fresh-new");
expect(typeof reloaded.usageStats?.["openai:default"]?.cooldownUntil).toBe("number");
});
it("disables billing failures for ~5 hours by default", async () => {
@@ -255,99 +265,91 @@ describe("markAuthProfileFailure", () => {
});
});
it("resets backoff counters outside the failure window", async () => {
const agentDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-auth-"));
try {
const authPath = path.join(agentDir, "auth-profiles.json");
const now = Date.now();
fs.writeFileSync(
authPath,
JSON.stringify({
version: 1,
profiles: {
"anthropic:default": {
type: "api_key",
provider: "anthropic",
key: "sk-default",
},
const agentDir = makeAgentDir("reset-window");
const authPath = path.join(agentDir, "auth-profiles.json");
const now = Date.now();
fs.writeFileSync(
authPath,
JSON.stringify({
version: 1,
profiles: {
"anthropic:default": {
type: "api_key",
provider: "anthropic",
key: "sk-default",
},
usageStats: {
"anthropic:default": {
errorCount: 9,
failureCounts: { billing: 3 },
lastFailureAt: now - 48 * 60 * 60 * 1000,
},
},
usageStats: {
"anthropic:default": {
errorCount: 9,
failureCounts: { billing: 3 },
lastFailureAt: now - 48 * 60 * 60 * 1000,
},
}),
);
},
}),
);
const store = ensureAuthProfileStore(agentDir);
await markAuthProfileFailure({
store,
profileId: "anthropic:default",
reason: "billing",
agentDir,
cfg: {
auth: { cooldowns: { failureWindowHours: 24 } },
} as never,
});
const store = ensureAuthProfileStore(agentDir);
await markAuthProfileFailure({
store,
profileId: "anthropic:default",
reason: "billing",
agentDir,
cfg: {
auth: { cooldowns: { failureWindowHours: 24 } },
} as never,
});
expect(store.usageStats?.["anthropic:default"]?.errorCount).toBe(1);
expect(store.usageStats?.["anthropic:default"]?.failureCounts?.billing).toBe(1);
} finally {
fs.rmSync(agentDir, { recursive: true, force: true });
}
expect(store.usageStats?.["anthropic:default"]?.errorCount).toBe(1);
expect(store.usageStats?.["anthropic:default"]?.failureCounts?.billing).toBe(1);
});
it("resets error count when previous cooldown has expired to prevent escalation", async () => {
const agentDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-auth-"));
try {
const authPath = path.join(agentDir, "auth-profiles.json");
const now = Date.now();
// Simulate state left on disk after 3 rapid failures within a 1-min cooldown
// window. The cooldown has since expired, but clearExpiredCooldowns() only
// ran in-memory and never persisted — so disk still carries errorCount: 3.
fs.writeFileSync(
authPath,
JSON.stringify({
version: 1,
profiles: {
"anthropic:default": {
type: "api_key",
provider: "anthropic",
key: "sk-default",
},
const agentDir = makeAgentDir("expired-cooldown");
const authPath = path.join(agentDir, "auth-profiles.json");
const now = Date.now();
// Simulate state left on disk after 3 rapid failures within a 1-min cooldown
// window. The cooldown has since expired, but clearExpiredCooldowns() only
// ran in-memory and never persisted - so disk still carries errorCount: 3.
fs.writeFileSync(
authPath,
JSON.stringify({
version: 1,
profiles: {
"anthropic:default": {
type: "api_key",
provider: "anthropic",
key: "sk-default",
},
usageStats: {
"anthropic:default": {
errorCount: 3,
failureCounts: { rate_limit: 3 },
lastFailureAt: now - 120_000, // 2 minutes ago
cooldownUntil: now - 60_000, // expired 1 minute ago
},
},
usageStats: {
"anthropic:default": {
errorCount: 3,
failureCounts: { rate_limit: 3 },
lastFailureAt: now - 120_000, // 2 minutes ago
cooldownUntil: now - 60_000, // expired 1 minute ago
},
}),
);
},
}),
);
const store = ensureAuthProfileStore(agentDir);
await markAuthProfileFailure({
store,
profileId: "anthropic:default",
reason: "rate_limit",
agentDir,
});
const store = ensureAuthProfileStore(agentDir);
await markAuthProfileFailure({
store,
profileId: "anthropic:default",
reason: "rate_limit",
agentDir,
});
const stats = store.usageStats?.["anthropic:default"];
// Error count should reset to 1 (not escalate to 4) because the
// previous cooldown expired. Cooldown should be ~30s, not ~5 min.
expect(stats?.errorCount).toBe(1);
expect(stats?.failureCounts?.rate_limit).toBe(1);
const cooldownMs = (stats?.cooldownUntil ?? 0) - now;
// calculateAuthProfileCooldownMs(1) = 30_000 (stepped: 30s 1m 5m)
expect(cooldownMs).toBeLessThan(60_000);
expect(cooldownMs).toBeGreaterThan(0);
} finally {
fs.rmSync(agentDir, { recursive: true, force: true });
}
const stats = store.usageStats?.["anthropic:default"];
// Error count should reset to 1 (not escalate to 4) because the
// previous cooldown expired. Cooldown should be ~30s, not ~5 min.
expect(stats?.errorCount).toBe(1);
expect(stats?.failureCounts?.rate_limit).toBe(1);
const cooldownMs = (stats?.cooldownUntil ?? 0) - now;
// calculateAuthProfileCooldownMs(1) = 30_000 (stepped: 30s -> 1m -> 5m)
expect(cooldownMs).toBeLessThan(60_000);
expect(cooldownMs).toBeGreaterThan(0);
});
it("does not persist cooldown windows for OpenRouter profiles", async () => {

View File

@@ -1,7 +1,7 @@
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { resetFileLockStateForTest } from "../../infra/file-lock.js";
import { captureEnv } from "../../test-utils/env.js";
import { resolveApiKeyForProfile, resetOAuthRefreshQueuesForTest } from "./oauth.js";
@@ -107,6 +107,11 @@ describe("OAuth refresh in-process queue", () => {
]);
let tempRoot = "";
let agentDir = "";
let caseIndex = 0;
beforeAll(async () => {
tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-oauth-queue-"));
});
beforeEach(async () => {
resetFileLockStateForTest();
@@ -115,9 +120,9 @@ describe("OAuth refresh in-process queue", () => {
formatProviderAuthProfileApiKeyWithPluginMock.mockReset();
formatProviderAuthProfileApiKeyWithPluginMock.mockReturnValue(undefined);
clearRuntimeAuthProfileStoreSnapshots();
tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-oauth-queue-"));
process.env.OPENCLAW_STATE_DIR = tempRoot;
agentDir = path.join(tempRoot, "agents", "main", "agent");
const caseRoot = path.join(tempRoot, `case-${++caseIndex}`);
process.env.OPENCLAW_STATE_DIR = caseRoot;
agentDir = path.join(caseRoot, "agents", "main", "agent");
process.env.OPENCLAW_AGENT_DIR = agentDir;
process.env.PI_CODING_AGENT_DIR = agentDir;
await fs.mkdir(agentDir, { recursive: true });
@@ -129,57 +134,10 @@ describe("OAuth refresh in-process queue", () => {
resetFileLockStateForTest();
clearRuntimeAuthProfileStoreSnapshots();
resetOAuthRefreshQueuesForTest();
if (tempRoot) {
await fs.rm(tempRoot, { recursive: true, force: true });
}
});
it("serializes concurrent same-PID callers FIFO", async () => {
const profileId = "openai-codex:default";
const provider = "openai-codex";
saveAuthProfileStore(createExpiredOauthStore({ profileId, provider }), agentDir);
const order: number[] = [];
let seq = 0;
refreshProviderOAuthCredentialWithPluginMock.mockImplementation(async () => {
const n = ++seq;
order.push(n);
// Small delay so concurrent callers have time to interleave if they can.
await new Promise((r) => setTimeout(r, 10));
return {
type: "oauth",
provider,
access: `refreshed-${n}`,
refresh: `refreshed-refresh-${n}`,
// Each refresh returns a token already expired again, so the next
// queued caller also proceeds to refresh (proves the queue releases
// cleanly and the next caller actually runs).
expires: Date.now() - 1_000,
} as never;
});
// Fire three resolves concurrently against the same agent+profile.
const results = await Promise.all([
resolveApiKeyForProfileInTest({
store: ensureAuthProfileStore(agentDir),
profileId,
agentDir,
}).catch((e) => e),
resolveApiKeyForProfileInTest({
store: ensureAuthProfileStore(agentDir),
profileId,
agentDir,
}).catch((e) => e),
resolveApiKeyForProfileInTest({
store: ensureAuthProfileStore(agentDir),
profileId,
agentDir,
}).catch((e) => e),
]);
// All three should have completed in order (FIFO queue).
expect(order).toEqual([1, 2, 3]);
expect(results).toHaveLength(3);
afterAll(async () => {
await fs.rm(tempRoot, { recursive: true, force: true });
});
it("releases the queue even when the refresh throws", async () => {
@@ -254,8 +212,8 @@ describe("OAuth refresh in-process queue", () => {
startOrder.push(n);
inFlight += 1;
maxInFlight = Math.max(maxInFlight, inFlight);
// Small delay so any non-serialized overlap would be observable.
await new Promise((r) => setTimeout(r, 5));
// Yield once so any non-serialized overlap is observable without wall-clock sleep.
await Promise.resolve();
inFlight -= 1;
endOrder.push(n);
return {

View File

@@ -1,7 +1,7 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import * as sessionStore from "../config/sessions/store.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import type { CallGatewayOptions } from "../gateway/call.js";
@@ -115,6 +115,34 @@ function setSubagentControlDepsForTest(
});
}
let tempRoot = "";
let tempStoreIndex = 0;
beforeAll(() => {
tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-subagent-control-"));
});
afterAll(() => {
fs.rmSync(tempRoot, { recursive: true, force: true });
});
function nextSessionStorePath(label: string) {
tempStoreIndex += 1;
return path.join(tempRoot, `${tempStoreIndex}-${label}.json`);
}
function cfgWithSessionStore(storePath = nextSessionStorePath("sessions")): OpenClawConfig {
return {
session: { store: storePath },
} as OpenClawConfig;
}
function writeSessionStoreFixture(label: string, store: Record<string, unknown>) {
const storePath = nextSessionStorePath(label);
fs.writeFileSync(storePath, JSON.stringify(store, null, 2), "utf-8");
return storePath;
}
beforeEach(() => {
setSubagentControlDepsForTest();
});
@@ -472,24 +500,13 @@ describe("killSubagentRunAdmin", () => {
});
it("kills a subagent by session key without requester ownership checks", async () => {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-subagent-admin-kill-"));
const storePath = path.join(tmpDir, "sessions.json");
const childSessionKey = "agent:main:subagent:worker";
fs.writeFileSync(
storePath,
JSON.stringify(
{
[childSessionKey]: {
sessionId: "sess-worker",
updatedAt: Date.now(),
},
},
null,
2,
),
"utf-8",
);
const storePath = writeSessionStoreFixture("admin-kill", {
[childSessionKey]: {
sessionId: "sess-worker",
updatedAt: Date.now(),
},
});
addSubagentRunForTests({
runId: "run-worker",
@@ -503,9 +520,7 @@ describe("killSubagentRunAdmin", () => {
startedAt: Date.now() - 4_000,
});
const cfg = {
session: { store: storePath },
} as OpenClawConfig;
const cfg = cfgWithSessionStore(storePath);
const result = await killSubagentRunAdmin({
cfg,
@@ -523,7 +538,7 @@ describe("killSubagentRunAdmin", () => {
it("returns found=false when the session key is not tracked as a subagent run", async () => {
const result = await killSubagentRunAdmin({
cfg: {} as OpenClawConfig,
cfg: cfgWithSessionStore(),
sessionKey: "agent:main:subagent:missing",
});
@@ -559,7 +574,7 @@ describe("killSubagentRunAdmin", () => {
});
const result = await killSubagentRunAdmin({
cfg: {} as OpenClawConfig,
cfg: cfgWithSessionStore(),
sessionKey: childSessionKey,
});
@@ -572,24 +587,13 @@ describe("killSubagentRunAdmin", () => {
});
it("still terminates the run when session store persistence fails during kill", async () => {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-subagent-admin-kill-store-"));
const storePath = path.join(tmpDir, "sessions.json");
const childSessionKey = "agent:main:subagent:worker-store-fail";
fs.writeFileSync(
storePath,
JSON.stringify(
{
[childSessionKey]: {
sessionId: "sess-worker-store-fail",
updatedAt: Date.now(),
},
},
null,
2,
),
"utf-8",
);
const storePath = writeSessionStoreFixture("admin-kill-store-fail", {
[childSessionKey]: {
sessionId: "sess-worker-store-fail",
updatedAt: Date.now(),
},
});
addSubagentRunForTests({
runId: "run-worker-store-fail",
@@ -609,9 +613,7 @@ describe("killSubagentRunAdmin", () => {
try {
const result = await killSubagentRunAdmin({
cfg: {
session: { store: storePath },
} as OpenClawConfig,
cfg: cfgWithSessionStore(storePath),
sessionKey: childSessionKey,
});
@@ -635,23 +637,12 @@ describe("killControlledSubagentRun", () => {
});
it("does not mutate the live session when the caller passes a stale run entry", async () => {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-subagent-stale-kill-"));
const storePath = path.join(tmpDir, "sessions.json");
const childSessionKey = "agent:main:subagent:stale-kill-worker";
fs.writeFileSync(
storePath,
JSON.stringify(
{
[childSessionKey]: {
updatedAt: Date.now(),
},
},
null,
2,
),
"utf-8",
);
const storePath = writeSessionStoreFixture("stale-kill", {
[childSessionKey]: {
updatedAt: Date.now(),
},
});
addSubagentRunForTests({
runId: "run-current",
@@ -666,9 +657,7 @@ describe("killControlledSubagentRun", () => {
});
const result = await killControlledSubagentRun({
cfg: {
session: { store: storePath },
} as OpenClawConfig,
cfg: cfgWithSessionStore(storePath),
controller: {
controllerSessionKey: "agent:main:main",
callerSessionKey: "agent:main:main",
@@ -758,7 +747,7 @@ describe("killControlledSubagentRun", () => {
});
const result = await killControlledSubagentRun({
cfg: {} as OpenClawConfig,
cfg: cfgWithSessionStore(),
controller: {
controllerSessionKey: "agent:main:main",
callerSessionKey: "agent:main:main",
@@ -859,7 +848,7 @@ describe("killControlledSubagentRun", () => {
});
const result = await killControlledSubagentRun({
cfg: {} as OpenClawConfig,
cfg: cfgWithSessionStore(),
controller: {
controllerSessionKey: "agent:main:main",
callerSessionKey: "agent:main:main",
@@ -899,23 +888,12 @@ describe("killAllControlledSubagentRuns", () => {
});
it("ignores stale run snapshots in bulk kill requests", async () => {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-subagent-stale-kill-all-"));
const storePath = path.join(tmpDir, "sessions.json");
const childSessionKey = "agent:main:subagent:stale-kill-all-worker";
fs.writeFileSync(
storePath,
JSON.stringify(
{
[childSessionKey]: {
updatedAt: Date.now(),
},
},
null,
2,
),
"utf-8",
);
const storePath = writeSessionStoreFixture("stale-kill-all", {
[childSessionKey]: {
updatedAt: Date.now(),
},
});
addSubagentRunForTests({
runId: "run-current-bulk",
@@ -930,9 +908,7 @@ describe("killAllControlledSubagentRuns", () => {
});
const result = await killAllControlledSubagentRuns({
cfg: {
session: { store: storePath },
} as OpenClawConfig,
cfg: cfgWithSessionStore(storePath),
controller: {
controllerSessionKey: "agent:main:main",
callerSessionKey: "agent:main:main",
@@ -968,25 +944,12 @@ describe("killAllControlledSubagentRuns", () => {
});
it("does not let a stale bulk entry suppress the current live entry for the same child key", async () => {
const tmpDir = fs.mkdtempSync(
path.join(os.tmpdir(), "openclaw-subagent-stale-kill-all-shadow-"),
);
const storePath = path.join(tmpDir, "sessions.json");
const childSessionKey = "agent:main:subagent:stale-kill-all-shadow-worker";
fs.writeFileSync(
storePath,
JSON.stringify(
{
[childSessionKey]: {
updatedAt: Date.now(),
},
},
null,
2,
),
"utf-8",
);
const storePath = writeSessionStoreFixture("stale-kill-all-shadow", {
[childSessionKey]: {
updatedAt: Date.now(),
},
});
addSubagentRunForTests({
runId: "run-current-shadow",
@@ -1001,9 +964,7 @@ describe("killAllControlledSubagentRuns", () => {
});
const result = await killAllControlledSubagentRuns({
cfg: {
session: { store: storePath },
} as OpenClawConfig,
cfg: cfgWithSessionStore(storePath),
controller: {
controllerSessionKey: "agent:main:main",
callerSessionKey: "agent:main:main",
@@ -1073,7 +1034,7 @@ describe("killAllControlledSubagentRuns", () => {
});
const result = await killAllControlledSubagentRuns({
cfg: {} as OpenClawConfig,
cfg: cfgWithSessionStore(),
controller: {
controllerSessionKey: "agent:main:main",
callerSessionKey: "agent:main:main",
@@ -1145,7 +1106,7 @@ describe("killAllControlledSubagentRuns", () => {
});
const result = await killAllControlledSubagentRuns({
cfg: {} as OpenClawConfig,
cfg: cfgWithSessionStore(),
controller: {
controllerSessionKey: "agent:main:main",
callerSessionKey: "agent:main:main",
@@ -1215,7 +1176,7 @@ describe("steerControlledSubagentRun", () => {
try {
const result = await steerControlledSubagentRun({
cfg: {} as OpenClawConfig,
cfg: cfgWithSessionStore(),
controller: {
controllerSessionKey: "agent:main:main",
callerSessionKey: "agent:main:main",
@@ -1260,7 +1221,7 @@ describe("steerControlledSubagentRun", () => {
});
const result = await steerControlledSubagentRun({
cfg: {} as OpenClawConfig,
cfg: cfgWithSessionStore(),
controller: {
controllerSessionKey: "agent:main:main",
callerSessionKey: "agent:main:main",
@@ -1340,7 +1301,7 @@ describe("steerControlledSubagentRun", () => {
});
const result = await steerControlledSubagentRun({
cfg: {} as OpenClawConfig,
cfg: cfgWithSessionStore(),
controller: {
controllerSessionKey: "agent:main:main",
callerSessionKey: "agent:main:main",