fix(cli): align agent exec with system owner (#124697)

This commit is contained in:
Leon-SK668
2026-08-17 11:04:03 +08:00
committed by GitHub
parent 0c01534426
commit 45cd2bae66
2 changed files with 95 additions and 2 deletions
@@ -0,0 +1,80 @@
import fs from "node:fs/promises";
import path from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js";
import { resolveSessionStorePathCore } from "../config/sessions/paths.js";
import { resolveSqliteScope } from "../config/sessions/session-accessor.sqlite-scope.js";
import type { RuntimeEnv } from "../runtime.js";
import { agentExecCommand } from "./agent-exec.js";
const tempDirs = useAutoCleanupTempDirTracker(afterEach);
const runtime: RuntimeEnv = {
log: vi.fn(),
error: vi.fn(),
exit: vi.fn(),
};
afterEach(() => {
vi.restoreAllMocks();
});
describe("agent exec owner selection", () => {
it.each([
{
name: "a migrated legacy default",
config: {
agents: {
entries: {
alpha: { default: true },
beta: {},
},
},
},
},
{
name: "the canonical system-agent owner",
config: {
agents: {
ownership: "explicit",
defaults: { systemAgent: { agentId: "alpha" } },
entries: { alpha: {}, beta: {} },
},
},
},
])("uses $name for the run and its SQLite store scope", async ({ config }) => {
const configRoot = tempDirs.make("openclaw-agent-exec-default-agent-");
const configPath = path.join(configRoot, "openclaw.json");
await fs.writeFile(configPath, JSON.stringify(config), "utf8");
const runAgent = vi.fn(async (options: Record<string, unknown>) => {
const requestedAgentId = typeof options.agentId === "string" ? options.agentId : "main";
const sessionId = String(options.sessionId);
const storePath = resolveSessionStorePathCore(undefined, {
agentId: "alpha",
env: process.env,
});
// Exercise the production guard that rejects keys owned by another agent.
expect(() =>
resolveSqliteScope({
agentId: requestedAgentId,
defaultAgentId: "alpha",
env: process.env,
sessionKey: `agent:${requestedAgentId}:explicit:${sessionId}`,
storePath,
}),
).not.toThrow();
expect(requestedAgentId).toBe("alpha");
return {
payloads: [{ text: "done" }],
meta: { durationMs: 1 },
};
});
const result = await agentExecCommand("inspect", { config: configPath }, runtime, { runAgent });
expect(result.envelope.error).toBeUndefined();
expect(result.exitCode).toBe(0);
expect(runAgent).toHaveBeenCalledOnce();
});
});
+15 -2
View File
@@ -635,7 +635,11 @@ export async function agentExecCommand(
const pluginInstallRoots = pluginInstallContext?.resolvePluginInstallRoots();
const timeout = normalizeTimeoutSeconds(opts.timeout);
const fallbacks = normalizeFallbacks(opts.model, opts.fallback);
const { resolveDefaultAgentDir } = await import("../agents/agent-scope-config.js");
const {
resolveAgentDir,
resolveSystemAgentTargetAgentId,
tryResolveLegacyCompatibilityAgentId,
} = await import("../agents/agent-scope-config.js");
// Resolve from the inherited config, not `{}`: the default agent may declare
// its own `agentDir`, and that is where its stored auth profiles live. This
// reads `baseConfig` rather than `runConfig` because the run config
@@ -643,7 +647,15 @@ export async function agentExecCommand(
// credential ownership must still follow the operator's configuration.
// Computed before the environment repoints the state dir so the unconfigured
// case still resolves against the real one.
const storedAuthAgentDir = resolveDefaultAgentDir(baseConfig);
const execAgentId =
tryResolveLegacyCompatibilityAgentId(baseConfig) ??
resolveSystemAgentTargetAgentId(baseConfig, undefined, {
surface: "agent exec",
hint: "Set agents.defaults.systemAgent.agentId.",
});
// Auth, session keys, and SQLite ownership must share one resolved owner.
// Splitting these paths can select an agent's store but emit a `main` key.
const storedAuthAgentDir = resolveAgentDir(baseConfig, execAgentId);
restoreEnvironment = setAgentExecEnvironment({ stateDir, cwd });
runtimePaths = await import("../config/paths.js");
runtimePaths.pinRuntimePaths();
@@ -698,6 +710,7 @@ export async function agentExecCommand(
{
message: prompt,
sessionId,
agentId: execAgentId,
workspaceDir: cwd,
cwd,
model: opts.model,