Problem / Motivation
Related to #274, specifically Reactive Polling — Phase 1.
Open Cowork's current scheduler is time-driven and unconditional:
timer fires → executeTask() → startSession(prompt, cwd)
Every scheduled trigger starts a new Agent session, even when the external state being monitored has not changed. This makes recurring monitoring unnecessarily expensive and prevents scheduled tasks from acting as lightweight watchers.
We need to separate condition checking from Agent execution:
timer fires → check condition → persist observed state
→ changed: start Agent session
→ unchanged: reschedule without starting a session
The implementation should extend the existing scheduler rather than introduce a parallel monitoring system.
Proposed Solution
Add an HTTP-only WatchTask capability on top of the existing scheduled-task infrastructure.
Scope
Reuse the existing:
ScheduledTaskManager timer engine
scheduled_tasks SQLite table
- Schedule IPC CRUD APIs
executeTask() → sessionManager.startSession() execution path
A scheduled task becomes a WatchTask when it has a valid watchConfig. Tasks without watchConfig must retain their existing behavior.
HTTP checker
Support the following comparison modes:
status: compare the HTTP response status
bodyHash: compare a hash of the HTTP response body
Suggested configuration shape:
type WatchConfig = {
checkType: 'http';
compareMode: 'status' | 'bodyHash';
checkConfig: {
url: string;
method?: 'GET' | 'HEAD';
timeoutMs?: number;
};
};
HEAD + bodyHash must be rejected because a HEAD response does not provide a meaningful response body.
Persisted state
Persist the following state for each WatchTask:
watchConfig
lastState
lastCheckedAt
consecutiveUnchanged
The SQLite migration must remain backward-compatible with existing databases and records.
Execution semantics
The scheduler pipeline becomes:
timer
→ checkCondition()
→ persist check state
→ changed?
├─ yes → executeTask() → startSession()
└─ no → skip Agent session
→ reschedule
Behavior requirements:
- The first successful check establishes the baseline and must not start an Agent session.
- An unchanged state increments
consecutiveUnchanged and skips Agent execution.
- A changed state starts the existing Agent execution path.
- A checker failure records
lastError, does not disable the task, and allows future checks to continue.
runNow() on a WatchTask runs the same check → persist → act pipeline.
- WatchTasks must use a repeating interval, daily, or weekly schedule.
- The minimum HTTP check interval is 60 seconds.
- Existing non-watch scheduled tasks must behave exactly as before.
HTTP safety boundaries
The checker must:
- Allow only
http: and https: URLs
- Enforce a request timeout
- Limit response body size
- Limit redirects and revalidate the protocol after each redirect
- Never persist the complete response body
- Handle malformed persisted
watchConfig without crashing the scheduler
Custom request headers are not required for this phase.
Out of Scope
This issue intentionally does not include:
- Command checker or host shell execution
- File checker
- Agent-based checker
- JSONPath or regex comparison
- Multiple-condition composition
- Notifications
- WatchTask UI forms
The original #274 Phase 1 proposal included a command checker. That capability should be handled separately as Phase 1.1 because periodic command execution introduces a new security-sensitive path that may bypass the existing Agent permission flow.
A future command checker should use SandboxAdapter.executeCommand() and define isolation, timeout, output-size, and user-confirmation requirements before implementation.
Acceptance Criteria
Alternatives Considered
Build a separate polling service
Rejected because the existing scheduler already provides timers, persistence, IPC CRUD, and Agent execution. A parallel system would duplicate infrastructure and complicate lifecycle management.
Include command checking in this phase
Deferred because directly executing commands from the scheduler could bypass SessionManager, Agent tool permissions, and user confirmation. This requires a separately reviewed sandbox and permission design.
Start an Agent session for every check
Rejected because the purpose of reactive polling is to keep condition checks lightweight and invoke an Agent only when external state changes.
Additional Context
Parent roadmap: #274
Platform: macOS and Windows.
Problem / Motivation
Related to #274, specifically Reactive Polling — Phase 1.
Open Cowork's current scheduler is time-driven and unconditional:
Every scheduled trigger starts a new Agent session, even when the external state being monitored has not changed. This makes recurring monitoring unnecessarily expensive and prevents scheduled tasks from acting as lightweight watchers.
We need to separate condition checking from Agent execution:
The implementation should extend the existing scheduler rather than introduce a parallel monitoring system.
Proposed Solution
Add an HTTP-only
WatchTaskcapability on top of the existing scheduled-task infrastructure.Scope
Reuse the existing:
ScheduledTaskManagertimer enginescheduled_tasksSQLite tableexecuteTask() → sessionManager.startSession()execution pathA scheduled task becomes a WatchTask when it has a valid
watchConfig. Tasks withoutwatchConfigmust retain their existing behavior.HTTP checker
Support the following comparison modes:
status: compare the HTTP response statusbodyHash: compare a hash of the HTTP response bodySuggested configuration shape:
HEAD + bodyHashmust be rejected because a HEAD response does not provide a meaningful response body.Persisted state
Persist the following state for each WatchTask:
watchConfiglastStatelastCheckedAtconsecutiveUnchangedThe SQLite migration must remain backward-compatible with existing databases and records.
Execution semantics
The scheduler pipeline becomes:
Behavior requirements:
consecutiveUnchangedand skips Agent execution.lastError, does not disable the task, and allows future checks to continue.runNow()on a WatchTask runs the same check → persist → act pipeline.HTTP safety boundaries
The checker must:
http:andhttps:URLswatchConfigwithout crashing the schedulerCustom request headers are not required for this phase.
Out of Scope
This issue intentionally does not include:
The original #274 Phase 1 proposal included a command checker. That capability should be handled separately as Phase 1.1 because periodic command execution introduces a new security-sensitive path that may bypass the existing Agent permission flow.
A future command checker should use
SandboxAdapter.executeCommand()and define isolation, timeout, output-size, and user-confirmation requirements before implementation.Acceptance Criteria
lastState,lastCheckedAt, andconsecutiveUnchangedare persisted correctlylastErrorwithout disabling future checksrunNow()follows the WatchTask check pipelinenpm run lintpassesnpx tsc --noEmitpassesnpx vitest runpassesnpm run buildpassesAlternatives Considered
Build a separate polling service
Rejected because the existing scheduler already provides timers, persistence, IPC CRUD, and Agent execution. A parallel system would duplicate infrastructure and complicate lifecycle management.
Include command checking in this phase
Deferred because directly executing commands from the scheduler could bypass
SessionManager, Agent tool permissions, and user confirmation. This requires a separately reviewed sandbox and permission design.Start an Agent session for every check
Rejected because the purpose of reactive polling is to keep condition checks lightweight and invoke an Agent only when external state changes.
Additional Context
Parent roadmap: #274
Platform: macOS and Windows.