mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
fix(cron): preserve runtime snapshot for isolated delivery
Fix isolated cron delivery so agent-default derivation keeps using the paired runtime config snapshot, preserving resolved channel credentials such as Discord SecretRefs. Fixes #86545.
This commit is contained in:
committed by
GitHub
parent
c55bee5ec7
commit
a98660eebd
@@ -1,8 +1,9 @@
|
||||
import "./isolated-agent.mocks.js";
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { runSubagentAnnounceFlow } from "../agents/subagent-announce.js";
|
||||
import type { ChannelOutboundAdapter, ChannelOutboundContext } from "../channels/plugins/types.js";
|
||||
import type { CliDeps } from "../cli/deps.js";
|
||||
import { clearRuntimeConfigSnapshot, setRuntimeConfigSnapshot } from "../config/config.js";
|
||||
import { resolveOutboundSendDep } from "../infra/outbound/send-deps.js";
|
||||
import { setActivePluginRegistry } from "../plugins/runtime.js";
|
||||
import { createOutboundTestPlugin, createTestRegistry } from "../test-utils/channel-plugins.js";
|
||||
@@ -297,6 +298,10 @@ describe("runCronIsolatedAgentTurn core-channel direct delivery", () => {
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
clearRuntimeConfigSnapshot();
|
||||
});
|
||||
|
||||
for (const testCase of CASES) {
|
||||
it(`routes ${testCase.name} text-only announce delivery through the outbound adapter`, async () => {
|
||||
await expectCoreChannelAnnounceDelivery({
|
||||
@@ -316,6 +321,51 @@ describe("runCronIsolatedAgentTurn core-channel direct delivery", () => {
|
||||
});
|
||||
|
||||
if (testCase.channel === "discord") {
|
||||
it("keeps isolated Discord delivery on the active runtime snapshot after agent-default derivation", async () => {
|
||||
await withTempCronHome(async (home) => {
|
||||
const storePath = await writeSessionStore(home, { lastProvider: "webchat", lastTo: "" });
|
||||
const sourceCfg = makeCfg(home, storePath, {
|
||||
channels: {
|
||||
discord: {
|
||||
accounts: {
|
||||
default: {
|
||||
token: { provider: "default", source: "env", id: "DISCORD_BOT_TOKEN" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const runtimeCfg = makeCfg(home, storePath, {
|
||||
channels: {
|
||||
discord: {
|
||||
accounts: { default: { token: "resolved-discord-token" } },
|
||||
},
|
||||
},
|
||||
});
|
||||
setRuntimeConfigSnapshot(runtimeCfg, sourceCfg);
|
||||
const deps = createCliDeps();
|
||||
mockAgentPayloads([{ text: "hello from cron" }]);
|
||||
|
||||
const res = await runExplicitAnnounceTurn({
|
||||
cfg: sourceCfg,
|
||||
deps,
|
||||
channel: "discord",
|
||||
to: testCase.to,
|
||||
});
|
||||
|
||||
expect(res.status).toBe("ok");
|
||||
expect(res.delivered).toBe(true);
|
||||
expect(deps.sendMessageDiscord).toHaveBeenCalledTimes(1);
|
||||
expect(deps.sendMessageDiscord).toHaveBeenCalledWith(
|
||||
testCase.expectedTo,
|
||||
"hello from cron",
|
||||
expect.objectContaining({
|
||||
cfg: expect.objectContaining({ channels: runtimeCfg.channels }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("collapses Discord text-only announce delivery to the final assistant text", async () => {
|
||||
await expectCoreChannelAnnounceDelivery({
|
||||
testCase,
|
||||
|
||||
@@ -5,6 +5,11 @@ import { retireSessionMcpRuntime } from "../../agents/pi-bundle-mcp-tools.js";
|
||||
import type { SkillSnapshot } from "../../agents/skills.js";
|
||||
import type { ThinkLevel } from "../../auto-reply/thinking.js";
|
||||
import type { CliDeps } from "../../cli/outbound-send-deps.js";
|
||||
import {
|
||||
getRuntimeConfigSnapshot,
|
||||
getRuntimeConfigSourceSnapshot,
|
||||
selectApplicableRuntimeConfig,
|
||||
} from "../../config/config.js";
|
||||
import type { AgentDefaultsConfig } from "../../config/types.agent-defaults.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { clearAgentRunContext } from "../../infra/agent-events.js";
|
||||
@@ -480,12 +485,28 @@ type CronPreparationResult =
|
||||
| { ok: true; context: PreparedCronRunContext }
|
||||
| { ok: false; result: RunCronAgentTurnResult };
|
||||
|
||||
function resolveCronActiveRuntimeConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
const runtimeConfig = getRuntimeConfigSnapshot();
|
||||
const runtimeSourceConfig = getRuntimeConfigSourceSnapshot();
|
||||
if (!runtimeConfig || !runtimeSourceConfig) {
|
||||
return cfg;
|
||||
}
|
||||
return (
|
||||
selectApplicableRuntimeConfig({
|
||||
inputConfig: cfg,
|
||||
runtimeConfig,
|
||||
runtimeSourceConfig,
|
||||
}) ?? cfg
|
||||
);
|
||||
}
|
||||
|
||||
async function prepareCronRunContext(params: {
|
||||
input: RunCronAgentTurnParams;
|
||||
isFastTestEnv: boolean;
|
||||
}): Promise<CronPreparationResult> {
|
||||
const { input } = params;
|
||||
const defaultAgentId = resolveDefaultAgentId(input.cfg);
|
||||
const runtimeCfg = resolveCronActiveRuntimeConfig(input.cfg);
|
||||
const defaultAgentId = resolveDefaultAgentId(runtimeCfg);
|
||||
const requestedAgentId =
|
||||
typeof input.agentId === "string" && input.agentId.trim()
|
||||
? input.agentId
|
||||
@@ -494,16 +515,16 @@ async function prepareCronRunContext(params: {
|
||||
: undefined;
|
||||
const normalizedRequested = requestedAgentId ? normalizeAgentId(requestedAgentId) : undefined;
|
||||
const agentConfigOverride = normalizedRequested
|
||||
? resolveAgentConfig(input.cfg, normalizedRequested)
|
||||
? resolveAgentConfig(runtimeCfg, normalizedRequested)
|
||||
: undefined;
|
||||
const agentId = normalizedRequested ?? defaultAgentId;
|
||||
const agentCfg: AgentDefaultsConfig = buildCronAgentDefaultsConfig({
|
||||
defaults: input.cfg.agents?.defaults,
|
||||
defaults: runtimeCfg.agents?.defaults,
|
||||
agentConfigOverride,
|
||||
});
|
||||
const cfgWithAgentDefaults: OpenClawConfig = {
|
||||
...input.cfg,
|
||||
agents: Object.assign({}, input.cfg.agents, { defaults: agentCfg }),
|
||||
...runtimeCfg,
|
||||
agents: Object.assign({}, runtimeCfg.agents, { defaults: agentCfg }),
|
||||
};
|
||||
let catalog: Awaited<ReturnType<CronModelCatalogRuntime["loadModelCatalog"]>> | undefined;
|
||||
const loadCatalog = async () => {
|
||||
|
||||
Reference in New Issue
Block a user