mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-22 14:41:34 +00:00
Reply: defer direct secret resolution
This commit is contained in:
@@ -22,6 +22,7 @@ const createReplyToModeFilterForChannelMock = vi.fn();
|
||||
const createReplyMediaPathNormalizerMock = vi.fn();
|
||||
const runPreflightCompactionIfNeededMock = vi.fn();
|
||||
const runMemoryFlushIfNeededMock = vi.fn();
|
||||
const enqueueFollowupRunMock = vi.fn();
|
||||
|
||||
vi.mock("./agent-runner-utils.js", () => ({
|
||||
resolveQueuedReplyExecutionConfig: (...args: unknown[]) =>
|
||||
@@ -45,6 +46,14 @@ vi.mock("./agent-runner-memory.js", () => ({
|
||||
runMemoryFlushIfNeeded: (...args: unknown[]) => runMemoryFlushIfNeededMock(...args),
|
||||
}));
|
||||
|
||||
vi.mock("./queue.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("./queue.js")>("./queue.js");
|
||||
return {
|
||||
...actual,
|
||||
enqueueFollowupRun: (...args: unknown[]) => enqueueFollowupRunMock(...args),
|
||||
};
|
||||
});
|
||||
|
||||
const { runReplyAgent } = await import("./agent-runner.js");
|
||||
|
||||
describe("runReplyAgent runtime config", () => {
|
||||
@@ -55,6 +64,7 @@ describe("runReplyAgent runtime config", () => {
|
||||
createReplyMediaPathNormalizerMock.mockReset();
|
||||
runPreflightCompactionIfNeededMock.mockReset();
|
||||
runMemoryFlushIfNeededMock.mockReset();
|
||||
enqueueFollowupRunMock.mockReset();
|
||||
|
||||
resolveQueuedReplyExecutionConfigMock.mockResolvedValue(freshCfg);
|
||||
resolveReplyToModeMock.mockReturnValue("default");
|
||||
@@ -140,4 +150,76 @@ describe("runReplyAgent runtime config", () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("does not resolve secrets before the enqueue-followup queue path", async () => {
|
||||
const followupRun = {
|
||||
prompt: "hello",
|
||||
summaryLine: "hello",
|
||||
enqueuedAt: Date.now(),
|
||||
run: {
|
||||
sessionId: "session-1",
|
||||
sessionKey: "agent:main:telegram:default:direct:test",
|
||||
messageProvider: "telegram",
|
||||
sessionFile: "/tmp/session.jsonl",
|
||||
workspaceDir: "/tmp",
|
||||
config: staleCfg,
|
||||
skillsSnapshot: {},
|
||||
provider: "openai",
|
||||
model: "gpt-5.4",
|
||||
thinkLevel: "low",
|
||||
verboseLevel: "off",
|
||||
elevatedLevel: "off",
|
||||
bashElevated: {
|
||||
enabled: false,
|
||||
allowed: false,
|
||||
defaultLevel: "off",
|
||||
},
|
||||
timeoutMs: 1_000,
|
||||
blockReplyBreak: "message_end",
|
||||
},
|
||||
} as unknown as FollowupRun;
|
||||
|
||||
const resolvedQueue = { mode: "interrupt" } as QueueSettings;
|
||||
const typing = createMockTypingController();
|
||||
const sessionCtx = {
|
||||
Provider: "telegram",
|
||||
OriginatingChannel: "telegram",
|
||||
OriginatingTo: "12345",
|
||||
AccountId: "default",
|
||||
ChatType: "dm",
|
||||
MessageSid: "msg-1",
|
||||
} as unknown as TemplateContext;
|
||||
|
||||
await expect(
|
||||
runReplyAgent({
|
||||
commandBody: "hello",
|
||||
followupRun,
|
||||
queueKey: "main",
|
||||
resolvedQueue,
|
||||
shouldSteer: false,
|
||||
shouldFollowup: true,
|
||||
isActive: true,
|
||||
isStreaming: false,
|
||||
typing,
|
||||
sessionCtx,
|
||||
defaultModel: "openai/gpt-5.4",
|
||||
resolvedVerboseLevel: "off",
|
||||
isNewSession: false,
|
||||
blockStreamingEnabled: false,
|
||||
resolvedBlockStreamingBreak: "message_end",
|
||||
shouldInjectGroupIntro: false,
|
||||
typingMode: "instant",
|
||||
}),
|
||||
).resolves.toBeUndefined();
|
||||
|
||||
expect(resolveQueuedReplyExecutionConfigMock).not.toHaveBeenCalled();
|
||||
expect(enqueueFollowupRunMock).toHaveBeenCalledWith(
|
||||
"main",
|
||||
followupRun,
|
||||
resolvedQueue,
|
||||
"message-id",
|
||||
expect.any(Function),
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -163,43 +163,6 @@ export async function runReplyAgent(params: {
|
||||
|
||||
const pendingToolTasks = new Set<Promise<void>>();
|
||||
const blockReplyTimeoutMs = opts?.blockReplyTimeoutMs ?? BLOCK_REPLY_SEND_TIMEOUT_MS;
|
||||
followupRun.run.config = await resolveQueuedReplyExecutionConfig(followupRun.run.config);
|
||||
|
||||
const replyToChannel = resolveOriginMessageProvider({
|
||||
originatingChannel: sessionCtx.OriginatingChannel,
|
||||
provider: sessionCtx.Surface ?? sessionCtx.Provider,
|
||||
}) as OriginatingChannelType | undefined;
|
||||
const replyToMode = resolveReplyToMode(
|
||||
followupRun.run.config,
|
||||
replyToChannel,
|
||||
sessionCtx.AccountId,
|
||||
sessionCtx.ChatType,
|
||||
);
|
||||
const applyReplyToMode = createReplyToModeFilterForChannel(replyToMode, replyToChannel);
|
||||
const cfg = followupRun.run.config;
|
||||
const normalizeReplyMediaPaths = createReplyMediaPathNormalizer({
|
||||
cfg,
|
||||
sessionKey,
|
||||
workspaceDir: followupRun.run.workspaceDir,
|
||||
});
|
||||
const blockReplyCoalescing =
|
||||
blockStreamingEnabled && opts?.onBlockReply
|
||||
? resolveEffectiveBlockStreamingConfig({
|
||||
cfg,
|
||||
provider: sessionCtx.Provider,
|
||||
accountId: sessionCtx.AccountId,
|
||||
chunking: blockReplyChunking,
|
||||
}).coalescing
|
||||
: undefined;
|
||||
const blockReplyPipeline =
|
||||
blockStreamingEnabled && opts?.onBlockReply
|
||||
? createBlockReplyPipeline({
|
||||
onBlockReply: opts.onBlockReply,
|
||||
timeoutMs: blockReplyTimeoutMs,
|
||||
coalescing: blockReplyCoalescing,
|
||||
buffer: createAudioAsVoiceBuffer({ isAudioPayload }),
|
||||
})
|
||||
: null;
|
||||
const touchActiveSessionEntry = async () => {
|
||||
if (!activeSessionEntry || !activeSessionStore || !sessionKey) {
|
||||
return;
|
||||
@@ -271,6 +234,44 @@ export async function runReplyAgent(params: {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
followupRun.run.config = await resolveQueuedReplyExecutionConfig(followupRun.run.config);
|
||||
|
||||
const replyToChannel = resolveOriginMessageProvider({
|
||||
originatingChannel: sessionCtx.OriginatingChannel,
|
||||
provider: sessionCtx.Surface ?? sessionCtx.Provider,
|
||||
}) as OriginatingChannelType | undefined;
|
||||
const replyToMode = resolveReplyToMode(
|
||||
followupRun.run.config,
|
||||
replyToChannel,
|
||||
sessionCtx.AccountId,
|
||||
sessionCtx.ChatType,
|
||||
);
|
||||
const applyReplyToMode = createReplyToModeFilterForChannel(replyToMode, replyToChannel);
|
||||
const cfg = followupRun.run.config;
|
||||
const normalizeReplyMediaPaths = createReplyMediaPathNormalizer({
|
||||
cfg,
|
||||
sessionKey,
|
||||
workspaceDir: followupRun.run.workspaceDir,
|
||||
});
|
||||
const blockReplyCoalescing =
|
||||
blockStreamingEnabled && opts?.onBlockReply
|
||||
? resolveEffectiveBlockStreamingConfig({
|
||||
cfg,
|
||||
provider: sessionCtx.Provider,
|
||||
accountId: sessionCtx.AccountId,
|
||||
chunking: blockReplyChunking,
|
||||
}).coalescing
|
||||
: undefined;
|
||||
const blockReplyPipeline =
|
||||
blockStreamingEnabled && opts?.onBlockReply
|
||||
? createBlockReplyPipeline({
|
||||
onBlockReply: opts.onBlockReply,
|
||||
timeoutMs: blockReplyTimeoutMs,
|
||||
coalescing: blockReplyCoalescing,
|
||||
buffer: createAudioAsVoiceBuffer({ isAudioPayload }),
|
||||
})
|
||||
: null;
|
||||
|
||||
const replySessionKey = sessionKey ?? followupRun.run.sessionKey;
|
||||
let replyOperation: ReplyOperation;
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user