refactor: share agent snapshot and scope test fixtures

This commit is contained in:
Peter Steinberger 2026-03-13 18:19:46 +00:00
parent 05a1b0c3ae
commit e003038261
2 changed files with 210 additions and 324 deletions

View File

@ -16,47 +16,137 @@ import { readGeneratedModelsJson } from "./models-config.test-utils.js";
installModelsConfigTestHooks();
function createOpenAiApiKeySourceConfig(): OpenClawConfig {
return {
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
apiKey: { source: "env", provider: "default", id: "OPENAI_API_KEY" }, // pragma: allowlist secret
api: "openai-completions" as const,
models: [],
},
},
},
};
}
function createOpenAiApiKeyRuntimeConfig(): OpenClawConfig {
return {
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
apiKey: "sk-runtime-resolved", // pragma: allowlist secret
api: "openai-completions" as const,
models: [],
},
},
},
};
}
function createOpenAiHeaderSourceConfig(): OpenClawConfig {
return {
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
api: "openai-completions" as const,
headers: {
Authorization: {
source: "env",
provider: "default",
id: "OPENAI_HEADER_TOKEN", // pragma: allowlist secret
},
"X-Tenant-Token": {
source: "file",
provider: "vault",
id: "/providers/openai/tenantToken",
},
},
models: [],
},
},
},
};
}
function createOpenAiHeaderRuntimeConfig(): OpenClawConfig {
return {
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
api: "openai-completions" as const,
headers: {
Authorization: "Bearer runtime-openai-token",
"X-Tenant-Token": "runtime-tenant-token",
},
models: [],
},
},
},
};
}
function withGatewayTokenMode(config: OpenClawConfig): OpenClawConfig {
return {
...config,
gateway: {
auth: {
mode: "token",
},
},
};
}
async function withGeneratedModelsFromRuntimeSource(
params: {
sourceConfig: OpenClawConfig;
runtimeConfig: OpenClawConfig;
candidateConfig?: OpenClawConfig;
},
runAssertions: () => Promise<void>,
) {
await withTempHome(async () => {
try {
setRuntimeConfigSnapshot(params.runtimeConfig, params.sourceConfig);
await ensureOpenClawModelsJson(params.candidateConfig ?? loadConfig());
await runAssertions();
} finally {
clearRuntimeConfigSnapshot();
clearConfigCache();
}
});
}
async function expectGeneratedProviderApiKey(providerId: string, expected: string) {
const parsed = await readGeneratedModelsJson<{
providers: Record<string, { apiKey?: string }>;
}>();
expect(parsed.providers[providerId]?.apiKey).toBe(expected);
}
async function expectGeneratedOpenAiHeaderMarkers() {
const parsed = await readGeneratedModelsJson<{
providers: Record<string, { headers?: Record<string, string> }>;
}>();
expect(parsed.providers.openai?.headers?.Authorization).toBe(
"secretref-env:OPENAI_HEADER_TOKEN", // pragma: allowlist secret
);
expect(parsed.providers.openai?.headers?.["X-Tenant-Token"]).toBe(NON_ENV_SECRETREF_MARKER);
}
describe("models-config runtime source snapshot", () => {
it("uses runtime source snapshot markers when passed the active runtime config", async () => {
await withTempHome(async () => {
const sourceConfig: OpenClawConfig = {
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
apiKey: { source: "env", provider: "default", id: "OPENAI_API_KEY" }, // pragma: allowlist secret
api: "openai-completions" as const,
models: [],
},
},
},
};
const runtimeConfig: OpenClawConfig = {
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
apiKey: "sk-runtime-resolved", // pragma: allowlist secret
api: "openai-completions" as const,
models: [],
},
},
},
};
try {
setRuntimeConfigSnapshot(runtimeConfig, sourceConfig);
await ensureOpenClawModelsJson(loadConfig());
const parsed = await readGeneratedModelsJson<{
providers: Record<string, { apiKey?: string }>;
}>();
expect(parsed.providers.openai?.apiKey).toBe("OPENAI_API_KEY"); // pragma: allowlist secret
} finally {
clearRuntimeConfigSnapshot();
clearConfigCache();
}
});
await withGeneratedModelsFromRuntimeSource(
{
sourceConfig: createOpenAiApiKeySourceConfig(),
runtimeConfig: createOpenAiApiKeyRuntimeConfig(),
},
async () => expectGeneratedProviderApiKey("openai", "OPENAI_API_KEY"), // pragma: allowlist secret
);
});
it("uses non-env marker from runtime source snapshot for file refs", async () => {
@ -103,30 +193,8 @@ describe("models-config runtime source snapshot", () => {
it("projects cloned runtime configs onto source snapshot when preserving provider auth", async () => {
await withTempHome(async () => {
const sourceConfig: OpenClawConfig = {
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
apiKey: { source: "env", provider: "default", id: "OPENAI_API_KEY" }, // pragma: allowlist secret
api: "openai-completions" as const,
models: [],
},
},
},
};
const runtimeConfig: OpenClawConfig = {
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
apiKey: "sk-runtime-resolved", // pragma: allowlist secret
api: "openai-completions" as const,
models: [],
},
},
},
};
const sourceConfig = createOpenAiApiKeySourceConfig();
const runtimeConfig = createOpenAiApiKeyRuntimeConfig();
const clonedRuntimeConfig: OpenClawConfig = {
...runtimeConfig,
agents: {
@ -139,11 +207,7 @@ describe("models-config runtime source snapshot", () => {
try {
setRuntimeConfigSnapshot(runtimeConfig, sourceConfig);
await ensureOpenClawModelsJson(clonedRuntimeConfig);
const parsed = await readGeneratedModelsJson<{
providers: Record<string, { apiKey?: string }>;
}>();
expect(parsed.providers.openai?.apiKey).toBe("OPENAI_API_KEY"); // pragma: allowlist secret
await expectGeneratedProviderApiKey("openai", "OPENAI_API_KEY"); // pragma: allowlist secret
} finally {
clearRuntimeConfigSnapshot();
clearConfigCache();
@ -152,121 +216,27 @@ describe("models-config runtime source snapshot", () => {
});
it("uses header markers from runtime source snapshot instead of resolved runtime values", async () => {
await withTempHome(async () => {
const sourceConfig: OpenClawConfig = {
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
api: "openai-completions" as const,
headers: {
Authorization: {
source: "env",
provider: "default",
id: "OPENAI_HEADER_TOKEN", // pragma: allowlist secret
},
"X-Tenant-Token": {
source: "file",
provider: "vault",
id: "/providers/openai/tenantToken",
},
},
models: [],
},
},
},
};
const runtimeConfig: OpenClawConfig = {
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
api: "openai-completions" as const,
headers: {
Authorization: "Bearer runtime-openai-token",
"X-Tenant-Token": "runtime-tenant-token",
},
models: [],
},
},
},
};
try {
setRuntimeConfigSnapshot(runtimeConfig, sourceConfig);
await ensureOpenClawModelsJson(loadConfig());
const parsed = await readGeneratedModelsJson<{
providers: Record<string, { headers?: Record<string, string> }>;
}>();
expect(parsed.providers.openai?.headers?.Authorization).toBe(
"secretref-env:OPENAI_HEADER_TOKEN", // pragma: allowlist secret
);
expect(parsed.providers.openai?.headers?.["X-Tenant-Token"]).toBe(NON_ENV_SECRETREF_MARKER);
} finally {
clearRuntimeConfigSnapshot();
clearConfigCache();
}
});
await withGeneratedModelsFromRuntimeSource(
{
sourceConfig: createOpenAiHeaderSourceConfig(),
runtimeConfig: createOpenAiHeaderRuntimeConfig(),
},
expectGeneratedOpenAiHeaderMarkers,
);
});
it("keeps source markers when runtime projection is skipped for incompatible top-level shape", async () => {
await withTempHome(async () => {
const sourceConfig: OpenClawConfig = {
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
apiKey: { source: "env", provider: "default", id: "OPENAI_API_KEY" }, // pragma: allowlist secret
api: "openai-completions" as const,
models: [],
},
},
},
gateway: {
auth: {
mode: "token",
},
},
};
const runtimeConfig: OpenClawConfig = {
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
apiKey: "sk-runtime-resolved", // pragma: allowlist secret
api: "openai-completions" as const,
models: [],
},
},
},
gateway: {
auth: {
mode: "token",
},
},
};
const sourceConfig = withGatewayTokenMode(createOpenAiApiKeySourceConfig());
const runtimeConfig = withGatewayTokenMode(createOpenAiApiKeyRuntimeConfig());
const incompatibleCandidate: OpenClawConfig = {
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
apiKey: "sk-runtime-resolved", // pragma: allowlist secret
api: "openai-completions" as const,
models: [],
},
},
},
...createOpenAiApiKeyRuntimeConfig(),
};
try {
setRuntimeConfigSnapshot(runtimeConfig, sourceConfig);
await ensureOpenClawModelsJson(incompatibleCandidate);
const parsed = await readGeneratedModelsJson<{
providers: Record<string, { apiKey?: string }>;
}>();
expect(parsed.providers.openai?.apiKey).toBe("OPENAI_API_KEY"); // pragma: allowlist secret
await expectGeneratedProviderApiKey("openai", "OPENAI_API_KEY"); // pragma: allowlist secret
} finally {
clearRuntimeConfigSnapshot();
clearConfigCache();
@ -276,81 +246,16 @@ describe("models-config runtime source snapshot", () => {
it("keeps source header markers when runtime projection is skipped for incompatible top-level shape", async () => {
await withTempHome(async () => {
const sourceConfig: OpenClawConfig = {
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
api: "openai-completions" as const,
headers: {
Authorization: {
source: "env",
provider: "default",
id: "OPENAI_HEADER_TOKEN", // pragma: allowlist secret
},
"X-Tenant-Token": {
source: "file",
provider: "vault",
id: "/providers/openai/tenantToken",
},
},
models: [],
},
},
},
gateway: {
auth: {
mode: "token",
},
},
};
const runtimeConfig: OpenClawConfig = {
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
api: "openai-completions" as const,
headers: {
Authorization: "Bearer runtime-openai-token",
"X-Tenant-Token": "runtime-tenant-token",
},
models: [],
},
},
},
gateway: {
auth: {
mode: "token",
},
},
};
const sourceConfig = withGatewayTokenMode(createOpenAiHeaderSourceConfig());
const runtimeConfig = withGatewayTokenMode(createOpenAiHeaderRuntimeConfig());
const incompatibleCandidate: OpenClawConfig = {
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
api: "openai-completions" as const,
headers: {
Authorization: "Bearer runtime-openai-token",
"X-Tenant-Token": "runtime-tenant-token",
},
models: [],
},
},
},
...createOpenAiHeaderRuntimeConfig(),
};
try {
setRuntimeConfigSnapshot(runtimeConfig, sourceConfig);
await ensureOpenClawModelsJson(incompatibleCandidate);
const parsed = await readGeneratedModelsJson<{
providers: Record<string, { headers?: Record<string, string> }>;
}>();
expect(parsed.providers.openai?.headers?.Authorization).toBe(
"secretref-env:OPENAI_HEADER_TOKEN", // pragma: allowlist secret
);
expect(parsed.providers.openai?.headers?.["X-Tenant-Token"]).toBe(NON_ENV_SECRETREF_MARKER);
await expectGeneratedOpenAiHeaderMarkers();
} finally {
clearRuntimeConfigSnapshot();
clearConfigCache();

View File

@ -17,6 +17,63 @@ function writeStore(storePath: string, store: Record<string, unknown>) {
fs.writeFileSync(storePath, JSON.stringify(store, null, 2), "utf-8");
}
function seedLeafOwnedChildSession(storePath: string, leafKey = "agent:main:subagent:leaf") {
const childKey = `${leafKey}:subagent:child`;
writeStore(storePath, {
[leafKey]: {
sessionId: "leaf-session",
updatedAt: Date.now(),
spawnedBy: "agent:main:main",
subagentRole: "leaf",
subagentControlScope: "none",
},
[childKey]: {
sessionId: "child-session",
updatedAt: Date.now(),
spawnedBy: leafKey,
subagentRole: "leaf",
subagentControlScope: "none",
},
});
addSubagentRunForTests({
runId: "run-child",
childSessionKey: childKey,
controllerSessionKey: leafKey,
requesterSessionKey: leafKey,
requesterDisplayKey: leafKey,
task: "impossible child",
cleanup: "keep",
createdAt: Date.now() - 30_000,
startedAt: Date.now() - 30_000,
});
return {
childKey,
tool: createSubagentsTool({ agentSessionKey: leafKey }),
};
}
async function expectLeafSubagentControlForbidden(params: {
storePath: string;
action: "kill" | "steer";
callId: string;
message?: string;
}) {
const { childKey, tool } = seedLeafOwnedChildSession(params.storePath);
const result = await tool.execute(params.callId, {
action: params.action,
target: childKey,
...(params.message ? { message: params.message } : {}),
});
expect(result.details).toMatchObject({
status: "forbidden",
error: "Leaf subagents cannot control other sessions.",
});
expect(callGatewayMock).not.toHaveBeenCalled();
}
describe("openclaw-tools: subagents scope isolation", () => {
let storePath = "";
@ -151,95 +208,19 @@ describe("openclaw-tools: subagents scope isolation", () => {
});
it("leaf subagents cannot kill even explicitly-owned child sessions", async () => {
const leafKey = "agent:main:subagent:leaf";
const childKey = `${leafKey}:subagent:child`;
writeStore(storePath, {
[leafKey]: {
sessionId: "leaf-session",
updatedAt: Date.now(),
spawnedBy: "agent:main:main",
subagentRole: "leaf",
subagentControlScope: "none",
},
[childKey]: {
sessionId: "child-session",
updatedAt: Date.now(),
spawnedBy: leafKey,
subagentRole: "leaf",
subagentControlScope: "none",
},
});
addSubagentRunForTests({
runId: "run-child",
childSessionKey: childKey,
controllerSessionKey: leafKey,
requesterSessionKey: leafKey,
requesterDisplayKey: leafKey,
task: "impossible child",
cleanup: "keep",
createdAt: Date.now() - 30_000,
startedAt: Date.now() - 30_000,
});
const tool = createSubagentsTool({ agentSessionKey: leafKey });
const result = await tool.execute("call-leaf-kill", {
await expectLeafSubagentControlForbidden({
storePath,
action: "kill",
target: childKey,
callId: "call-leaf-kill",
});
expect(result.details).toMatchObject({
status: "forbidden",
error: "Leaf subagents cannot control other sessions.",
});
expect(callGatewayMock).not.toHaveBeenCalled();
});
it("leaf subagents cannot steer even explicitly-owned child sessions", async () => {
const leafKey = "agent:main:subagent:leaf";
const childKey = `${leafKey}:subagent:child`;
writeStore(storePath, {
[leafKey]: {
sessionId: "leaf-session",
updatedAt: Date.now(),
spawnedBy: "agent:main:main",
subagentRole: "leaf",
subagentControlScope: "none",
},
[childKey]: {
sessionId: "child-session",
updatedAt: Date.now(),
spawnedBy: leafKey,
subagentRole: "leaf",
subagentControlScope: "none",
},
});
addSubagentRunForTests({
runId: "run-child",
childSessionKey: childKey,
controllerSessionKey: leafKey,
requesterSessionKey: leafKey,
requesterDisplayKey: leafKey,
task: "impossible child",
cleanup: "keep",
createdAt: Date.now() - 30_000,
startedAt: Date.now() - 30_000,
});
const tool = createSubagentsTool({ agentSessionKey: leafKey });
const result = await tool.execute("call-leaf-steer", {
await expectLeafSubagentControlForbidden({
storePath,
action: "steer",
target: childKey,
callId: "call-leaf-steer",
message: "continue",
});
expect(result.details).toMatchObject({
status: "forbidden",
error: "Leaf subagents cannot control other sessions.",
});
expect(callGatewayMock).not.toHaveBeenCalled();
});
});