Promote v1.4.0 to production - #2352
Conversation
Co-authored-by: Matt Rubens <2600+mrubens@users.noreply.github.com>
Co-authored-by: @mrubens <2600+mrubens@users.noreply.github.com>
Co-authored-by: @daniel-lxs <57051444+daniel-lxs@users.noreply.github.com>
Co-authored-by: @daniel-lxs <57051444+daniel-lxs@users.noreply.github.com>
Co-authored-by: @mrubens <2600+mrubens@users.noreply.github.com>
Co-authored-by: @mrubens <2600+mrubens@users.noreply.github.com>
Co-authored-by: @mrubens <2600+mrubens@users.noreply.github.com>
Co-authored-by: @daniel-lxs <57051444+daniel-lxs@users.noreply.github.com>
…2260) Co-authored-by: @daniel-lxs <57051444+daniel-lxs@users.noreply.github.com>
* feat: session wakeups let a Fast Session schedule a message to itself
Add the manage_wakeups Fast native tool (create, list, get, cancel) backed by
a session_wakeups table. A wakeup is a durable row plus one delayed BullMQ job;
when it fires, a scheduled_wakeup platform event is admitted into the
conversation's existing parent-event inbox and runs as a normal turn with the
full history in context. Occurrences are claimed with a compare-and-set on
next_run_at, so duplicate jobs cannot double-fire, and a 60s recovery sweep
re-adds hints for due rows. One-shot wakeups always reply; recurring ones
stay quiet unless notable and retire after five consecutive failed turns.
Archiving a Session cancels its wakeups.
* fix: make session wakeup creation resilient to model placeholders and BullMQ job ids
- BullMQ rejects custom job ids containing ':'; use '-' between the wakeup
id and occurrence time so delayed fire jobs actually enqueue.
- Strip empty strings, null, 'none'-style placeholders, and non-positive
caps from manage_wakeups arguments before validation. Models fill every
optional field, and each strict rejection cost a retry.
- A once schedule ignores stray maxRuns/until and prefers inMinutes when a
computed 'at' is sent alongside it, instead of failing.
* refactor: take the wakeup schedule as one string instead of a structured union
Models fill every optional structured field with placeholders, and each
rejected placeholder cost a retry. The tool now takes a single required
schedule string ("in 20m", "at <iso>", "every 10m x3", "every 10m until
<iso>", "cron 0 9 * * 1-5 America/New_York") parsed server-side into the
same stored schedule. This removes the discriminated union and the
maxRuns/until fields from the tool surface; the create action is now name,
prompt, schedule, and an optional reportPolicy.
* fix: honour cancellation for already-admitted wakeups and cap high-frequency cron
- Delivery of a scheduled_wakeup event now re-reads the row and skips when
the wakeup was cancelled or failed after its occurrence was admitted, so
cancel and archive keep their guarantee even against an in-flight event.
A row that completed at claim time (one-shot or final run) still runs.
- Cron schedules are held to the same tight-interval cap as intervals by
sampling the gap between upcoming occurrences; "cron * * * * *" now needs
"x<count>" or "until <iso>", which the cron grammar accepts alongside an
optional timezone.
* fix: keep the wakeup schedule parser linear and the cron gap helper private
The parser already collapses whitespace and the contract caps the string
length, so the patterns use literal single spaces instead of \s+ runs that
CodeQL flagged as polynomial. estimateCronMinGapMinutes is only used inside
the schedule module.
* fix: never deliver a wakeup into an archived Session
Archiving cancels a Session's wakeups, but that cancellation is best-effort
after the archive itself. Delivery now also checks the Session and skips a
scheduled_wakeup whose Session is archived, so a failed cancellation cannot
make an archived Session speak.
* fix: revalidate a wakeup right before it replies
A cancel or archive that lands while the wake turn is generating must still
win. The wakeup turn's postReply is now guarded: it re-checks the wakeup row
and the Session immediately before posting, drops the reply if either was
superseded, and aborts the turn's signal so no further tool calls run. The
next drain of the event settles it as skipped.
* fix: serialize session wakeup creation and enforce the active cap
* fix: deduplicate wakeup schedules independent of JSONB key order
* fix: preserve relative reminder identity across create retries
* fix: serialize session archival with Fast reply delivery
Co-authored-by: Roomote <roomote@roomote.dev>
…urns (#2183) * [Fix] Reactions and platform events no longer supersede parked Fast turns Every inline admission discarded the conversation's older pending inline rows on the assumption that a newer human message stands in for the earlier request. #2156 routed emoji reactions and web platform events through the same path, so a reaction while a turn was parked for a retry, or waiting to resume after an interruption, silently dropped that turn and turned its retry notice into an interruption message. Only typed human messages supersede now. The next-turn and settle reconciles also leave a retry notice alone while another durable row for the conversation is still pending, so the resumed run edits it into the answer instead of posting beside a false interruption. * Keep the expired-lease sweep as the backstop for stalled hand-offs The pending-row guard now counts only a live claim or a scheduled retry on the expired-lease path, so a released or expired row whose queue wakeup never runs cannot leave a stale retry notice active forever. * Re-run review --------- Co-authored-by: Matt Rubens <2600+mrubens@users.noreply.github.com>
…2274) * fix: durably accept Session messages while working * fix: deliver Slack follow-ups to bound Sessions while working Use the canonical Fast Session binding for inbound delivery before applying thread-history heuristics. Preserve linked-user authorization and existing queue behavior. Remove the earlier web and independent SDK changes from this PR's net scope. * fix: queue Fast follow-ups across response closeout Stop exposing detached promptAsync steering without an owned completion contract. Preserve follow-ups for the existing durable whole-turn queue and leave child-task steering unchanged. Replace the earlier unrelated Slack routing scope with provider and closeout regression coverage. --------- Co-authored-by: @daniel-lxs <57051444+daniel-lxs@users.noreply.github.com>
Co-authored-by: @mrubens <2600+mrubens@users.noreply.github.com>
Co-authored-by: @mrubens <2600+mrubens@users.noreply.github.com>
Co-authored-by: @mrubens <2600+mrubens@users.noreply.github.com>
Co-authored-by: @mrubens <2600+mrubens@users.noreply.github.com>
Co-authored-by: @daniel-lxs <57051444+daniel-lxs@users.noreply.github.com>
* fix: restore signed PR images in merge announcements * fix: bound merge image parsing and escape HTML fixtures --------- Co-authored-by: @mrubens <2600+mrubens@users.noreply.github.com>
Co-authored-by: @mrubens <2600+mrubens@users.noreply.github.com>
Co-authored-by: @mrubens <2600+mrubens@users.noreply.github.com>
* improve: let agents choose useful visual proof * fix: keep visual proof coverage tied to the stated claim --------- Co-authored-by: @mrubens <2600+mrubens@users.noreply.github.com>
…ies (#2339) * feat: allow authorized task writes across active repositories * fix: limit environment repository change to GitHub write scope --------- Co-authored-by: @daniel-lxs <57051444+daniel-lxs@users.noreply.github.com>
) * feat: show Discord and Telegram typing during parent turns * fix: reassert Discord typing after parent replies --------- Co-authored-by: @daniel-lxs <57051444+daniel-lxs@users.noreply.github.com>
* feat: deliver native Slack videos from Fast replies * fix: gate Slack video uploads on accepted replies * fix: use link-only fallback for Slack video uploads * test: isolate SDK suites that mutate global Slack state --------- Co-authored-by: @mrubens <2600+mrubens@users.noreply.github.com>
Co-authored-by: Roomote <roomote@roomote.dev>
Co-authored-by: @mrubens <2600+mrubens@users.noreply.github.com>
) Co-authored-by: @mrubens <2600+mrubens@users.noreply.github.com>
Co-authored-by: @mrubens <2600+mrubens@users.noreply.github.com>
Co-authored-by: Roomote <roomote@roomote.dev>
Co-authored-by: @mrubens <2600+mrubens@users.noreply.github.com>
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
| await enqueueFastAgentParentEvent({ |
There was a problem hiding this comment.
A cancellation can win between this durable event admission and the compare-and-set claim below. In that interleaving, cancelSessionWakeup marks the row cancelled, claimSessionWakeupFire returns null, but the already-persisted parent event is still drained because the parent-event queue does not re-check the wakeup state. The cancelled reminder therefore still runs once, contradicting the cancellation contract. Make admission and cancellation mutually observable (or discard scheduled-wakeup events whose row is no longer active) before delivering the turn.
There was a problem hiding this comment.
Re-evaluated this specific interleaving on repaired head 76073d83938a3727b1324b547a79bcf3839454a8 (its file tree is identical to the original candidate). Admission still precedes the compare-and-set claim, but delivery does re-read authoritative wakeup state: isScheduledWakeupDeliverable rejects cancelled/failed rows, and deliverFastAgentParentEventWithLock returns skipped before creating a turn. The reply guard also rechecks before posting (lines 2321–2338).
Existing post-admission and mid-turn cancellation tests passed during resolution preparation (3 focused tests passed; the identical tree was verified after CI reconciliation). This contradicts the finding's premise that the queued event is delivered without rechecking cancellation. A stale durable event may still be drained, but that is not the claimed extra reminder execution when cancellation has already won.
No runtime change is proposed for this stated interleaving. Leaving the thread unresolved for review confirmation rather than labeling it fixed or silently dismissing it. If a distinct race survives the delivery check, the next step is a targeted reproduction of that precise ordering, not broadening this ancestry-only release repair.
There was a problem hiding this comment.
Confirmed. The prior finding was incorrect: scheduled wakeups are revalidated before the turn starts and immediately before a reply posts, so cancellation after event admission does not produce the claimed reminder execution. Retracting it.
CI reconciliation applied and verifiedReconcile Release Candidate run 34248498484 successfully pushed the approved reconciliation, then failed its post-push PR metadata check with
The new head has exactly the original candidate and pinned main as parents. GitHub reports it conflict-free ( GitHub still reports |
Promote v1.4.0
Frozen at
16cc5395fe80bfa450b02fab27f44f5cfe79c09d— the commit where1.4.0was versioned. Commits merged todevelopafterward require an explicit candidate refresh or ship in the next release.v1.4.0and triggers the existing GHCRv*image publish (latestchannel).release/v1.4.0branch can be deleted after this PR merges.Changelog
1.4.0 (2026-09-08)
Roomote 1.4 brings reminders, clearer shared Sessions, and richer video evidence together with easier automation setup and more reliable everyday work.
Highlights
Minor changes
Patch changes