Skip to content

feat(agent): add durable event subscriptions#1405

Open
devRickyyy wants to merge 1 commit into
mainfrom
agent/event-subscriptions
Open

feat(agent): add durable event subscriptions#1405
devRickyyy wants to merge 1 commit into
mainfrom
agent/event-subscriptions

Conversation

@devRickyyy

@devRickyyy devRickyyy commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add a canonical, enumerable catalog for versioned Agent terminal-turn events
  • persist one-shot subscriptions and delivery outbox rows atomically with terminal turn settlement
  • resume subscriber sessions through normal Agent turns with deterministic submit IDs and structured event metadata
  • recover expired leases and retry accepted-but-unacknowledged deliveries without executing a turn twice
  • expose Agent-context CLI commands to enumerate, create, list, and cancel subscriptions

Architecture

V1 supports agent.turn.completed, agent.turn.failed, agent.turn.canceled, and agent.turn.interrupted. The storage schema is source-generic (source_kind, source_id, source_subject_id) so future app sources can reuse the delivery machinery after defining canonical facts and catalog entries.

Terminal settlement and delivery preparation share a SQLite transaction participant. Runtime delivery is at-least-once; event-delivery:<delivery_id> is the stable client submit ID that makes retry after a crash idempotent. Subscriptions are one-shot, self-subscription is rejected, and soft-deleted source/subscriber sessions cannot poison future dispatch.

CLI

tutti agent event-types --json
tutti agent subscriptions create --workspace-id <workspace> --event-type agent.turn.completed --source-session-id <source> --subscription-id <id> --json
tutti agent subscriptions list --workspace-id <workspace> --json
tutti agent subscriptions cancel --workspace-id <workspace> --subscription-id <id> --json

When invoked inside an Agent session, subscriptions create defaults the subscriber to the invoking session. Outside that context, pass --session-id <subscriber>.

Verification

  • GitHub CI: full Go Tests, Go Lint, Tooling Consistency, PR Change Classification, and Fix Scope Soft Gate all pass
  • golangci-lint on changed daemon packages: pass (0 issues)
  • go test ./... in packages/agent/host: pass
  • go test ./... in packages/agent/store-sqlite: pass
  • changed daemon package tests (data/workspace, service/agent, Agent-context CLI): pass
  • go build ./... in services/tuttid: pass
  • Agent host boundary and git diff --check: pass
  • changed Markdown Prettier check: pass
  • crash-window conformance: accepted submit + failed delivery acknowledgement + recovery produces one runtime execution and a completed delivery

After rebasing onto the latest main, go test ./... for the whole daemon was
also attempted. Feature-adjacent packages passed; unrelated builtin-app and
black-box cases could not start because the worktree does not contain
generated/tutti-onboarding/tutti-onboarding-0.1.0.zip. The repository's
generate:builtin-apps command was attempted but did not return in this
environment. go build ./... and every package changed by this PR pass.

pnpm check:changed was also attempted. Its generated Go lint lanes currently pass . together with explicit packages and fail with no go files to analyze; its builtin-app precheck also did not return. Equivalent package-scoped checks above were run directly.

Documentation

  • add docs/architecture/agent-event-subscriptions.md
  • clarify that the business WebSocket stream is an observer surface, not a durable orchestration bus
  • document host recovery order, ownership, delivery semantics, and extension path

Scope / follow-ups

  • V1 exposes Agent-turn sources through the Agent-context CLI; HTTP/UI and app-source registration are follow-ups.
  • Event continuation is typed in Host submission metadata. A distinct persisted canonical turn origin can be added in a future turn-table migration without changing delivery semantics.

Research references


Note

Medium Risk
Touches core Host recovery ordering, turn submission semantics, and transactional turn settlement; mistakes could duplicate continuations or drop durable orchestration after crashes, though idempotent submit IDs and conformance tests mitigate that.

Overview
Introduces durable Agent event subscriptions so one session can resume when another session’s turn reaches a terminal outcome (completed, failed, canceled, interrupted). Subscriptions are one-shot, replay-safe via caller-owned IDs, and distinct from the business WebSocket stream.

Storage and matching: New SQLite tables and migration, a closed v1 event catalog in store-sqlite/canonical, and EventSubscriptionParticipant that atomically marks subscriptions matched and enqueues prepared deliveries in the same transaction as terminal turn settlement. Session delete cancels active subscriptions or fails in-flight deliveries to avoid poison retries.

Host orchestration: EventSubscriptionStore port, create/list/cancel APIs, a leased delivery worker (backoff, startup requeue) wired into Host.Run and Recover (after goal inbox, before stale-turn settlement). Deliveries call SendInput with SubmissionKindEventContinuation, structured event JSON, and deterministic event-delivery:<id> client submit IDs so at-least-once delivery yields one logical continuation turn.

Integration: tuttid wires the participant on the writer store, delegates subscription CRUD through the agent service, and exposes Agent-context CLI (event-types, subscriptions create/list/cancel). Conformance adds an additive EventSubscriptionScenarios driver (crash-window idempotency). Architecture docs describe the contract and clarify separation from the business event stream.

Reviewed by Cursor Bugbot for commit 42a780c. Bugbot is set up for automated code reviews on this repo. Configure here.

Signed-off-by: Test <test@example.com>
@jomeswang
jomeswang force-pushed the agent/event-subscriptions branch from 05ff7b6 to 42a780c Compare July 19, 2026 02:28
@devRickyyy
devRickyyy marked this pull request as ready for review July 19, 2026 02:35
@cursor

cursor Bot commented Jul 19, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_bf64f56f-f69d-48fe-90aa-fc96b816d8cf)

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 2 additional findings.

Open in Devin Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant