You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Trigger multi-agent fan-out + per-agent delivery dedup — BaseTrigger gains optional agentIds?: string[] alongside the legacy agentId. A trigger now delivers the same interpolated message to the de-duplicated union of agentId + agentIds, with one trigger_events row written per delivery. The per-agent dedup map (${agentId}\0${sourceType}\0${sourceId}, 10-minute TTL) is reserved SYNCHRONOUSLY in fireTrigger before any await, so two concurrent events for the same physical Slack/email message can't both pass the check — covers the case where two integration instances (e.g. personal Slack + bot Slack) both see a shared-channel message, and overlapping triggers targeting one agent. Sources without a stable id (cron, manual fires) skip dedup. New TriggerFireOptions.dedupeSourceType / dedupeSourceId plumbed through evaluateEvent.
Google Calendar meetingUrl on events — CalendarEvent gains hangoutLink (Google's legacy field) and meetingUrl (best video join URL: hangoutLink, else a conferenceData.entryPointsvideo URI). Surfaces the join link for Google Meet and for Zoom / Teams / Webex events that publish a video entry point.
trigger-service unit tests — new trigger-service.test.ts covers fan-out resolution, per-agent dedup behavior, and TTL expiry.
Changed
Slack search-mode polling now newest-first with early-stop — pollViaSearch switches sort_dir from asc to desc and collects matches into a pending list dispatched chronologically at the end of the cycle. Each cycle remembers lastSearchMaxTs; subsequent cycles stop paging the moment a page's oldest match falls under lastSearchMaxTs − 5 min overlap. Steady-state cost drops to ~1 page per cycle instead of paging the full day window every tick, and busy accounts (where the newest messages used to sit on the LAST page and could be silently dropped past MAX_PAGES) no longer lose recent messages. The 5-minute overlap is well above Slack's ~10–30s search-index lag, and the per-channel watermark dedupes the overlap so nothing dispatches twice. New per-cycle seenKeys set defends against the same ${channel}:${ts} appearing on overlapping pages — replaces the previous max-based gate, which was incorrect under desc paging (the running max IS the first match returned).
Technical Details
deliveryDedupMap opportunistically prunes expired entries when size > 5000 to bound memory on high-volume sources.
releaseDelivery is exposed for callers that want to undo a reservation on dispatch failure (currently used by fireTrigger on sendCommand throw, so a transient failure doesn't permanently shadow the agent).
pollViaSearch floor floorBig = 0n on the first cycle disables early-stop so a cold start still fully backfills the day window before steady state kicks in.
lastSearchMaxTs is set across ALL returned matches, before any allowlist/watermark filtering — the boundary must reflect what the index returned, not what we chose to dispatch.