feat(schedule): add HTTP reactive polling for scheduled tasks - #307
feat(schedule): add HTTP reactive polling for scheduled tasks#307max-cyq wants to merge 1 commit into
Conversation
Add an HTTP-only WatchTask capability on top of ScheduledTaskManager that checks external state before starting an Agent session. Core changes: - Shared HTTP watch config contract with status/bodyHash modes, canonical URL normalization, timeout validation (1-30s), and HEAD+bodyHash rejection - Backward-compatible SQLite migration via ensureColumn for watch_config, last_state, last_checked_at, consecutive_unchanged columns - Store mapping with malformed persisted config detection (watchConfigError) - Bounded Node 22 HTTP checker: 10s default timeout, 1MiB body limit, max 3 manual redirects with protocol revalidation per hop, streaming SHA-256 hash - Manager create/update validation: requires repeating schedule, minimum 60s interval, equivalent config preserves state, semantic change resets baseline - Scheduler check→persist→act pipeline: null baseline first success no Agent, unchanged skip, changed persist-before-Agent, checker/Store failures no Agent, Agent failure no same-change retry, malformed config never executes - WatchTask busy-trigger recovery: future repeat slot persisted when timer fires during in-flight execution - Production wiring in both headless and GUI ScheduledTaskManager constructors - IPC type-only extension: runtime state output-only, compile-time boundary proof, no UI/component/i18n changes - 15 new/updated test files, 1166 total tests passing Closes OpenCoworkAI#304 Refs OpenCoworkAI#274
There was a problem hiding this comment.
Review mode: initial
Findings
- [Minor] Generic error message in
assertNevercould leak internal state details in pathological scenarios.
Insrc/main/schedule/scheduled-task-store.ts:131, theassertNeverfunction converts the unexpected value viaString(value). Ifvalueis an object (which should never happen given the discriminated union), this produces[object Object], which is unhelpful and could theoretically expose internal structure if the object is logged elsewhere. While this path is unreachable by design, consider usingJSON.stringify(value)or a static message for safety.
Suggested fix:function assertNever(value: never): never { throw new Error(`Unexpected persisted watch configuration result.`); }
Summary
Review mode: initial
Review policy: advisory — the check reflects automation health/completion only; it does not approve the PR or resolve findings.
The PR introduces a well-structured HTTP reactive polling capability for scheduled tasks (WatchTask) with thorough validation, error handling, and state management. The implementation is clean, follows existing patterns, and includes comprehensive test coverage (1166 tests passing). No blocking issues found. The only minor suggestion is to harden the assertNever error message. The code respects security boundaries (HTTP-only, size limits, timeouts, no body persistence) and does not introduce regressions.
Testing
Not run (automation). All submitted tests pass as per PR metadata.
Open Cowork Bot
Summary
Add an HTTP-only
WatchTaskcapability on top of the existingScheduledTaskManager. A scheduled task becomes a WatchTask when it has a validwatchConfig. The scheduler pipeline changes from unconditional execution to:Scope
statusandbodyHashcomparison modesrunNow()immediateSafety boundaries
fetch,node:crypto,node:httpOut of scope
Command checker, file checker, Agent checker, UI forms, notifications, custom headers, retry/backoff. These are deferred to separate work.
Test coverage
npm test)npm run lint: 0 errorsnpm run typecheck: passesnpm run build: passesCloses #304
Refs #274