
























@@ -21,6 +21,60 @@ export type CronPayloadOutcome = {
2121embeddedRunError?: string;
2222};
232324+type CronDenialSignal = {
25+token: string;
26+field: string;
27+};
28+29+const CRON_DENIAL_EXACT_TOKENS = ["SYSTEM_RUN_DENIED", "INVALID_REQUEST"] as const;
30+const CRON_DENIAL_CASE_INSENSITIVE_TOKENS = [
31+"approval cannot safely bind",
32+"runtime denied",
33+"could not run",
34+"did not run",
35+"was denied",
36+] as const;
37+38+export function detectCronDenialToken(text: string | undefined): string | undefined {
39+const normalized = normalizeOptionalString(text);
40+if (!normalized) {
41+return undefined;
42+}
43+for (const token of CRON_DENIAL_EXACT_TOKENS) {
44+if (normalized.includes(token)) {
45+return token;
46+}
47+}
48+const lowerText = normalized.toLowerCase();
49+for (const token of CRON_DENIAL_CASE_INSENSITIVE_TOKENS) {
50+if (lowerText.includes(token)) {
51+return token;
52+}
53+}
54+return undefined;
55+}
56+57+function resolveCronDenialSignal(
58+fields: Array<{ field: string; text?: string | undefined }>,
59+): CronDenialSignal | undefined {
60+const seen = new Set<string>();
61+for (const { field, text } of fields) {
62+if (seen.has(field)) {
63+continue;
64+}
65+seen.add(field);
66+const token = detectCronDenialToken(text);
67+if (token) {
68+return { token, field };
69+}
70+}
71+return undefined;
72+}
73+74+function formatCronDenialSignal(signal: CronDenialSignal): string {
75+return `cron classifier: denial token "${signal.token}" detected in ${signal.field}`;
76+}
77+2478export function pickSummaryFromOutput(text: string | undefined) {
2579const clean = (text ?? "").trim();
2680if (!clean) {
@@ -157,7 +211,7 @@ export function resolveCronPayloadOutcome(params: {
157211params.payloads
158212.slice(lastErrorPayloadIndex + 1)
159213.some((payload) => payload?.isError !== true && Boolean(payload?.text?.trim()));
160-const hasFatalErrorPayload = hasErrorPayload && !hasSuccessfulPayloadAfterLastError;
214+const hasFatalStructuredErrorPayload = hasErrorPayload && !hasSuccessfulPayloadAfterLastError;
161215const normalizedFinalAssistantVisibleText = normalizeOptionalString(
162216params.finalAssistantVisibleText,
163217);
@@ -169,7 +223,7 @@ export function resolveCronPayloadOutcome(params: {
169223const shouldUseFinalAssistantVisibleText =
170224params.preferFinalAssistantVisibleText === true &&
171225normalizedFinalAssistantVisibleText !== undefined &&
172-!hasFatalErrorPayload &&
226+!hasFatalStructuredErrorPayload &&
173227!hasStructuredDeliveryPayloads;
174228const summary = shouldUseFinalAssistantVisibleText
175229 ? (pickSummaryFromOutput(normalizedFinalAssistantVisibleText) ?? fallbackSummary)
@@ -189,6 +243,18 @@ export function resolveCronPayloadOutcome(params: {
189243.toReversed()
190244.find((payload) => payload?.isError === true && Boolean(payload?.text?.trim()))
191245?.text?.trim();
246+const denialSignal = resolveCronDenialSignal([
247+{ field: "summary", text: summary },
248+{ field: "outputText", text: outputText },
249+{ field: "synthesizedText", text: synthesizedText },
250+{ field: "fallbackSummary", text: fallbackSummary },
251+{ field: "fallbackOutputText", text: fallbackOutputText },
252+ ...params.payloads.map((payload, index) => ({
253+field: `payloads[${index}].text`,
254+text: payload?.text,
255+})),
256+]);
257+const hasFatalErrorPayload = hasFatalStructuredErrorPayload || denialSignal !== undefined;
192258return {
193259 summary,
194260 outputText,
@@ -197,8 +263,10 @@ export function resolveCronPayloadOutcome(params: {
197263deliveryPayloads: resolvedDeliveryPayloads,
198264 deliveryPayloadHasStructuredContent,
199265 hasFatalErrorPayload,
200-embeddedRunError: hasFatalErrorPayload
266+embeddedRunError: hasFatalStructuredErrorPayload
201267 ? (lastErrorPayloadText ?? "cron isolated run returned an error payload")
202- : undefined,
268+ : denialSignal
269+ ? formatCronDenialSignal(denialSignal)
270+ : undefined,
203271};
204272}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。