fix(infra): extend exec completion detection to cover local background exec formats [AI-assisted] (#64376)

* fix: address issue

* fix: address PR review feedback

* fix: address PR review feedback

* fix: address PR review feedback

* chore(changelog): add exec completion owner-downgrade entry

---------

Co-authored-by: Devin Robison <drobison@nvidia.com>
This commit is contained in:
Michael Appel
2026-04-10 13:07:14 -04:00
committed by GitHub
parent e1a2a26ec9
commit 19a2e9ddb5
3 changed files with 20 additions and 2 deletions

View File

@@ -124,6 +124,7 @@ Docs: https://docs.openclaw.ai
- Browser/sandbox: gate `/sandbox/novnc` behind bridge auth and stop surfacing sandbox observer URLs in model-visible prompt context. (#63882) Thanks @eleqtrizit.
- Discord/sandbox: include `image` in sandbox media param normalization so Discord event cover images cannot bypass sandbox path rewriting. (#64377) Thanks @mmaps.
- Agents/exec: extend exec completion detection to cover local background exec formats so the owner-downgrade fires correctly for all exec paths. (#64376) Thanks @mmaps.
## 2026.4.9
### Changes

View File

@@ -71,7 +71,13 @@ describe("heartbeat event prompts", () => {
describe("heartbeat event classification", () => {
it.each([
{ value: "exec finished: ok", expected: true },
{ value: "Exec Finished: failed", expected: true },
{ value: "Exec finished (node=abc, code 0)", expected: true },
{ value: "Exec Finished (node=abc, code 1)", expected: true },
{ value: "Exec completed (abc12345, code 0) :: some output", expected: true },
{ value: "Exec failed (abc12345, signal SIGTERM) :: error output", expected: true },
{ value: "Exec completed (rotate api keys)", expected: false },
{ value: "Exec failed: notify me if this happens", expected: false },
{ value: "Reminder: if exec failed, notify me", expected: false },
{ value: "cron finished", expected: false },
])("classifies exec completion events for %j", ({ value, expected }) => {
expect(isExecCompletionEvent(value)).toBe(expected);
@@ -87,6 +93,11 @@ describe("heartbeat event classification", () => {
{ value: "heartbeat poll: noop", expected: false },
{ value: "heartbeat wake: noop", expected: false },
{ value: "exec finished: ok", expected: false },
{ value: "Exec finished (node=abc, code 0)", expected: false },
{ value: "Exec completed (abc12345, code 0) :: some output", expected: false },
{ value: "Exec failed (abc12345, signal SIGTERM) :: error output", expected: false },
{ value: "Exec completed (rotate api keys)", expected: true },
{ value: "Reminder: if exec failed, notify me", expected: true },
])("classifies cron system events for %j", ({ value, expected }) => {
expect(isCronSystemEvent(value)).toBe(expected);
});

View File

@@ -85,7 +85,13 @@ function isHeartbeatNoiseEvent(evt: string): boolean {
}
export function isExecCompletionEvent(evt: string): boolean {
return normalizeLowercaseStringOrEmpty(evt).includes("exec finished");
const normalized = normalizeLowercaseStringOrEmpty(evt).trimStart();
return (
/^exec finished(?::|\s*\()/.test(normalized) ||
/^exec (completed|failed) \([a-z0-9_-]{1,64}, (code -?\d+|signal [^)]+)\)( :: .*)?$/.test(
normalized,
)
);
}
// Returns true when a system event should be treated as real cron reminder content.