Agent: resolve resumed session agent scope before run

This commit is contained in:
Vignesh Natarajan
2026-02-22 22:37:54 -08:00
parent 7fab4d128a
commit 5ad5ea53cd
3 changed files with 40 additions and 8 deletions

View File

@@ -202,6 +202,7 @@ Docs: https://docs.openclaw.ai
- Agents/Subagents: honor `tools.subagents.tools.alsoAllow` and explicit subagent `allow` entries when resolving built-in subagent deny defaults, so explicitly granted tools (for example `sessions_send`) are no longer blocked unless re-denied in `tools.subagents.tools.deny`. (#23359) Thanks @goren-beehero.
- Agents/Subagents: make announce call timeouts configurable via `agents.defaults.subagents.announceTimeoutMs` and restore a 60s default to prevent false timeout failures on slower announce paths. (#22719) Thanks @Valadon.
- Agents/Diagnostics: include resolved lifecycle error text in `embedded run agent end` warnings so UI/TUI “Connection error” runs expose actionable provider failure reasons in gateway logs. (#23054) Thanks @Raize.
- Agents/Auth profiles: resolve `agentCommand` session scope before choosing `agentDir`/workspace so resumed runs no longer read auth from `agents/main/agent` when the resolved session belongs to a different/default agent (for example `agent:exec:*` sessions). (#24016) Thanks @abersonFAC.
- Agents/Auth profiles: skip auth-profile cooldown writes for timeout failures in embedded runner rotation so model/network timeouts do not poison same-provider fallback model selection while still allowing in-turn account rotation. (#22622) Thanks @vageeshkumar.
- Plugins/Hooks: run legacy `before_agent_start` once per agent turn and reuse that result across model-resolve and prompt-build compatibility paths, preventing duplicate hook side effects (for example duplicate external API calls). (#23289) Thanks @ksato8710.
- Models/Config: default missing Anthropic provider/model `api` fields to `anthropic-messages` during config validation so custom relay model entries are preserved instead of being dropped by runtime model registry validation. (#23332) Thanks @bigbigmonkey123.

View File

@@ -205,6 +205,31 @@ describe("agentCommand", () => {
});
});
it("uses the resumed session agent scope when sessionId resolves to another agent store", async () => {
await withTempHome(async (home) => {
const storePattern = path.join(home, "sessions", "{agentId}", "sessions.json");
const execStore = path.join(home, "sessions", "exec", "sessions.json");
writeSessionStoreSeed(execStore, {
"agent:exec:hook:gmail:thread-1": {
sessionId: "session-exec-hook",
updatedAt: Date.now(),
systemSent: true,
},
});
mockConfig(home, storePattern, undefined, undefined, [
{ id: "dev" },
{ id: "exec", default: true },
]);
await agentCommand({ message: "resume me", sessionId: "session-exec-hook" }, runtime);
const callArgs = vi.mocked(runEmbeddedPiAgent).mock.calls.at(-1)?.[0];
expect(callArgs?.sessionKey).toBe("agent:exec:hook:gmail:thread-1");
expect(callArgs?.agentId).toBe("exec");
expect(callArgs?.agentDir).toContain(`${path.sep}agents${path.sep}exec${path.sep}agent`);
});
});
it("resolves resumed session transcript path from custom session store directory", async () => {
await withTempHome(async (home) => {
const customStoreDir = path.join(home, "custom-state");

View File

@@ -3,6 +3,7 @@ import {
resolveAgentDir,
resolveEffectiveModelFallbacks,
resolveAgentModelPrimary,
resolveSessionAgentId,
resolveAgentSkillsFilter,
resolveAgentWorkspaceDir,
} from "../agents/agent-scope.js";
@@ -218,14 +219,6 @@ export async function agentCommand(
}
}
const agentCfg = cfg.agents?.defaults;
const sessionAgentId = agentIdOverride ?? resolveAgentIdFromSessionKey(opts.sessionKey?.trim());
const workspaceDirRaw = resolveAgentWorkspaceDir(cfg, sessionAgentId);
const agentDir = resolveAgentDir(cfg, sessionAgentId);
const workspace = await ensureAgentWorkspace({
dir: workspaceDirRaw,
ensureBootstrapFiles: !agentCfg?.skipBootstrap,
});
const workspaceDir = workspace.dir;
const configuredModel = resolveConfiguredModelRef({
cfg,
defaultProvider: DEFAULT_PROVIDER,
@@ -284,6 +277,19 @@ export async function agentCommand(
persistedThinking,
persistedVerbose,
} = sessionResolution;
const sessionAgentId =
agentIdOverride ??
resolveSessionAgentId({
sessionKey: sessionKey ?? opts.sessionKey?.trim(),
config: cfg,
});
const workspaceDirRaw = resolveAgentWorkspaceDir(cfg, sessionAgentId);
const agentDir = resolveAgentDir(cfg, sessionAgentId);
const workspace = await ensureAgentWorkspace({
dir: workspaceDirRaw,
ensureBootstrapFiles: !agentCfg?.skipBootstrap,
});
const workspaceDir = workspace.dir;
let sessionEntry = resolvedSessionEntry;
const runId = opts.runId?.trim() || sessionId;