Repository navigation
Add persistent Parallel watches - #39
shriyatheunicorn merged 6 commits into
Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
| if (job.kind === "sync") { | ||
| if (!watch.remote_id) throw new Error("Missing remote monitor"); | ||
| const group = typeof job.payload.group === "string" ? job.payload.group : undefined; | ||
| let cursor: string | undefined; |
There was a problem hiding this comment.
can we save where the recovery check got to and pick up from there on the next run?
right now, if fetching missed updates takes longer than the worker's 20-second limit, the next attempt starts over and fetches the same events again. when i reproduced this locally, i noticed after eight attempts, the job is marked failed and future hourly checks don't restart it, so automatic recovery stops for that watch until someone manually intervenes. my hunch is that saving progress and continuing on the next run should let us keep the time limit w/o getting stuck
| async list(owner: Owner) { | ||
| return (await this.pool.query<Watch & { updates: unknown[]; problems: unknown[] }>(`SELECT w.*, COALESCE((SELECT jsonb_agg(e.payload) FROM (SELECT payload FROM bell_monitor_events WHERE watch_id=w.id ORDER BY received_at DESC LIMIT 5) e),'[]') AS updates, | ||
| COALESCE((SELECT jsonb_agg(jsonb_build_object('kind',kind,'error',last_error,'failed',failed)) FROM bell_monitor_jobs WHERE watch_id=w.id AND last_error IS NOT NULL),'[]') AS problems | ||
| FROM bell_monitors w WHERE issuer=$1 AND owner_id=$2 ORDER BY created_at DESC LIMIT 50`, [owner.issuer, owner.id])).rows; |
There was a problem hiding this comment.
are we able to give watch_list a way to fetch more than the latest 50 watches?
cancelled watches count toward this limit too. i tested one active watch followed by 50 newer cancelled watches, and the active watch disappeared from the results even though it was still running.
ie in a fresh conversation, BELL then has no way to find that watch's ID to stop it or change its frequency. returning 50 at a time is fine, but we may want a way to request the next batch so older watches stay accessible
A recovery scan that ran past the worker's work budget threw away its place, so the next attempt refetched the same pages from the newest one. A watch with more missed history than one budget could never finish, and after eight such attempts the job was marked failed. Scheduled checks skip failed jobs, so automatic recovery stopped for that watch until an operator requeued it by hand. Check the budget between pages and save the scan's place in the job payload: the next page cursor, the start time, the newest identity seen, whether prior coverage was found, and the distinct runs counted so far. Each claim fetches at least one whole page, so a saved cursor never splits a page and the next run picks up where this one stopped. Stored event IDs keep deduplicating, so resuming cannot resend an alert. Saving progress is no longer treated as a failure. It releases the attempt it consumed and clears the job's last error, so a long recovery does not walk into the eight-attempt limit. Resuming stays bounded: the scan only continues while its cursor moves forward, and for at most twenty resumes, after which it reports an exceeded budget as before. A scan that did reach the failed state is restarted, keeping its saved progress, by the next scheduled check for that watch. Co-authored-by: George Pickett <georgeatparallel@users.noreply.github.com>
watch_list returned the 50 newest watches and nothing else, with
cancelled ones counted against that limit. One active watch followed by
50 newer cancelled ones therefore dropped off the result entirely, and a
fresh conversation had no way to recover its ID to stop it or change its
frequency.
Order watches with the open ones first - anything not cancelled or
failed - and newest first within each group, so a running watch stays on
the first page however many watches were cancelled after it. Keep 50 per
page and return a next_cursor when older watches remain. The cursor is a
keyset marker over that same order rather than an offset, so pages do
not shift when a watch changes status between calls, and an unusable one
is rejected instead of silently reading from the start.
watch_list now takes an optional cursor and an optional status of all,
open, or closed, and returns { watches, next_cursor } in place of a bare
array. Document both in the tool description, the agent instructions,
and docs/monitors.md.
Co-authored-by: George Pickett <georgeatparallel@users.noreply.github.com>
368b05b to
c13e889
Compare
8e70524
into
browserbase:shriyatheunicorn/parallel-research
Problem
Bell should keep watching a public topic after the conversation ends, at the frequency the user asks for.
Solution
Adds persistent watches through the Parallel Monitor API. From a private Linq chat, you can create a watch, list recent updates, change its frequency, or stop it. Checks default to daily and support whole hours, days, or weeks from 1 hour to 30 days.
Parallel runs the research. Bell saves state in Postgres and sends cited updates through Linq without keeping an agent or browser session open. Frequency changes reuse the existing monitor and stay visibly pending until confirmed. Retries preserve delivery identity, and recovery accepts timezone offsets without dropping valid events beside a malformed record.
Apply migrations 004 and 005, configure credentials, and start the worker scheduler to enable watches. Stop old workers before deploying this version. Setup and recovery instructions cover the remaining deployment steps.
Testing
Monitoring