惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Jina AI
Jina AI
博客园 - 司徒正美
大猫的无限游戏
大猫的无限游戏
博客园 - 三生石上(FineUI控件)
J
Java Code Geeks
博客园 - 聂微东
酷 壳 – CoolShell
酷 壳 – CoolShell
爱范儿
爱范儿
美团技术团队
腾讯CDC
博客园 - Franky
MyScale Blog
MyScale Blog
人人都是产品经理
人人都是产品经理
罗磊的独立博客
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
aimingoo的专栏
aimingoo的专栏
博客园_首页
V
V2EX
Martin Fowler
Martin Fowler
T
The Blog of Author Tim Ferriss

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
fix(cron): classify denied isolated runs · openclaw/openc...
steipete · 2026-04-27 · via Recent Commits to openclaw:main

@@ -21,6 +21,60 @@ export type CronPayloadOutcome = {

2121

embeddedRunError?: 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+2478

export function pickSummaryFromOutput(text: string | undefined) {

2579

const clean = (text ?? "").trim();

2680

if (!clean) {

@@ -157,7 +211,7 @@ export function resolveCronPayloadOutcome(params: {

157211

params.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;

161215

const normalizedFinalAssistantVisibleText = normalizeOptionalString(

162216

params.finalAssistantVisibleText,

163217

);

@@ -169,7 +223,7 @@ export function resolveCronPayloadOutcome(params: {

169223

const shouldUseFinalAssistantVisibleText =

170224

params.preferFinalAssistantVisibleText === true &&

171225

normalizedFinalAssistantVisibleText !== undefined &&

172-

!hasFatalErrorPayload &&

226+

!hasFatalStructuredErrorPayload &&

173227

!hasStructuredDeliveryPayloads;

174228

const 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;

192258

return {

193259

summary,

194260

outputText,

@@ -197,8 +263,10 @@ export function resolveCronPayloadOutcome(params: {

197263

deliveryPayloads: 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

}