fix(cron): preserve untrusted awareness event labels (#68210)

* fix(cron): preserve untrusted awareness event labels

Keep isolated cron awareness summaries untrusted when they are promoted into the main session, and forward explicit trust downgrades through the gateway cron wrapper. Add focused regression coverage for both paths.

* changelog: note cron awareness untrusted-label preservation (#68210)
This commit is contained in:
Devin Robison
2026-04-17 12:43:48 -06:00
committed by GitHub
parent 2745e5b3bd
commit f61896b03c
6 changed files with 50 additions and 2 deletions

View File

@@ -324,6 +324,7 @@ describe("dispatchCronDelivery — double-announce guard", () => {
expect(enqueueSystemEvent).toHaveBeenCalledWith("Morning briefing complete.", {
sessionKey: "agent:main:main",
contextKey: "cron-direct-delivery:v1:run-123:telegram::123456:",
trusted: false,
});
});

View File

@@ -351,6 +351,7 @@ async function queueCronAwarenessSystemEvent(params: {
agentId: params.agentId,
}),
contextKey: params.deliveryIdempotencyKey,
trusted: false,
});
} catch (err) {
await logCronDeliveryWarn(

View File

@@ -62,7 +62,7 @@ export type CronServiceDeps = {
maxMissedJobsPerRestart?: number;
enqueueSystemEvent: (
text: string,
opts?: { agentId?: string; sessionKey?: string; contextKey?: string },
opts?: { agentId?: string; sessionKey?: string; contextKey?: string; trusted?: boolean },
) => void;
requestHeartbeatNow: (opts?: { reason?: string; agentId?: string; sessionKey?: string }) => void;
runHeartbeatOnce?: (opts?: {

View File

@@ -140,6 +140,47 @@ describe("buildGatewayCronService", () => {
}
});
it("preserves trust downgrades when cron enqueues system events", () => {
const cfg = createCronConfig("server-cron-untrusted");
loadConfigMock.mockReturnValue(cfg);
const state = buildGatewayCronService({
cfg,
deps: {} as CliDeps,
broadcast: () => {},
});
try {
const cronDeps = (
state.cron as unknown as {
state?: {
deps?: {
enqueueSystemEvent?: (optsText: string, opts?: {
agentId?: string;
sessionKey?: string;
contextKey?: string;
trusted?: boolean;
}) => void;
};
};
}
).state?.deps;
cronDeps?.enqueueSystemEvent?.("hello", {
sessionKey: "discord:channel:ops",
contextKey: "cron:test",
trusted: false,
});
expect(enqueueSystemEventMock).toHaveBeenCalledWith("hello", {
sessionKey: "agent:main:discord:channel:ops",
contextKey: "cron:test",
trusted: false,
});
} finally {
state.cron.stop();
}
});
it("blocks private webhook URLs via SSRF-guarded fetch", async () => {
const cfg = createCronConfig("server-cron-ssrf");
loadConfigMock.mockReturnValue(cfg);

View File

@@ -285,7 +285,11 @@ export function buildGatewayCronService(params: {
agentId,
requestedSessionKey: opts?.sessionKey,
});
enqueueSystemEvent(text, { sessionKey, contextKey: opts?.contextKey });
enqueueSystemEvent(text, {
sessionKey,
contextKey: opts?.contextKey,
trusted: opts?.trusted,
});
},
requestHeartbeatNow: (opts) => {
const { agentId, sessionKey } = resolveCronWakeTarget(opts);