Push notifications + one-tap unblock (ntfy/Telegram)#621
Closed
bborn wants to merge 2 commits into
Closed
Conversation
Adds a first-class push notifier that fires on task lifecycle events
(blocked/needs-input, auth-required, completed, failed) and lets you act
from your phone with one tap.
- internal/notify: provider-agnostic Notifier with ntfy (JSON publish,
structured action buttons) and Telegram (deep-link buttons) providers.
OFF by default; settings read live from the DB.
- Wires into the existing events.Emitter via a Notifier interface — every
mutation path (executor, MCP, CLI, TUI) already routes through it, so no
parallel event path. Fires on the emitter's wait group so short-lived
CLI/MCP commands flush pushes before exit.
- needs-input / auth-required pushes carry a one-tap ntfy "http" action that
POSTs to the existing POST /api/tasks/{id}/input endpoint to resume the
agent, plus an "Open task" deep link. Reason falls back to the latest
taskyou_needs_input question for a meaningful body.
- Config via `ty settings` (notify_enabled, notify_ntfy_*, notify_telegram_*,
notify_base_url, notify_unblock_reply); token keys are secret-hidden.
- Tests: provider unit tests, event-mapping/filtering, reason enrichment, and
end-to-end DB → emitter → notifier → HTTP through the real wiring.
- Docs: docs/notifications.md + README feature bullet.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…deliver
Live end-to-end testing surfaced a race: the notifier read its settings
from the DB inside the async delivery goroutine, but short-lived CLI
commands (e.g. `ty status`) `defer database.Close()` the moment Run
returns — before PersistentPostRun flushes the emitter wait group. The
goroutine then read a closed DB, saw notifications as disabled, and
silently dropped the push. (Daemon/MCP were unaffected: the DB stays open.)
- events.Notifier.Notify now returns a delivery closure. The emitter calls
Notify synchronously (DB guaranteed open) and runs the returned closure on
its wait group; the closure performs only network I/O, touching no DB.
- notify.Notifier.Notify reads settings/logs and builds the message up front,
returns the sender closure (nil = nothing to send). Adds Deliver() for
synchronous callers/tests.
- Surface delivery failures: daemon executor wires logf to its logger;
openTaskDB attaches a stderr logf gated by TY_NOTIFY_DEBUG. Best-effort
pushes were otherwise completely silent on failure.
- Update events/notify tests for the new signature.
Verified live: blocking a task delivered an ntfy push (title + project +
the actual needs-input question), and tapping the http action POSTed to
/api/tasks/{id}/input and landed the reply in the agent's tmux pane.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
bborn
added a commit
that referenced
this pull request
Jun 26, 2026
…very race Ports the two wins from PR #621 onto the mobile-console notifier: 1. One-tap unblock. ntfy pushes for actionable events (blocked/auth_required) now carry an Actions header: an http button that POSTs the canned reply to the existing POST /api/tasks/{id}/input (resuming the agent without opening anything), plus a view button deep-linking into the mobile console (/m) for a custom reply — console and one-tap, together. Reply text is configurable via notify_reply (default "continue"); validated live against ntfy.sh. 2. CLI-exit delivery race fix. The notifier read its settings inside the async delivery goroutine, but short-lived CLI/MCP commands defer db.Close() the instant Run returns — before PersistentPostRun flushes the emitter wait group — so the goroutine hit a closed DB, saw notifications as disabled, and silently dropped the push (daemon/web runs were unaffected; their DB stays open). Notifier.Prepare now reads settings + builds the request synchronously and returns a network-only delivery closure; events.Emit calls it before spawning the goroutine. Existing header/webhook behavior and tests are unchanged; adds tests for the action button (default + custom reply, and no action when no base URL is set).
Owner
Author
|
Closing in favor of #614, which is the broader feature (phone-first mobile console + push notifications). The two unique wins from this PR — the one-tap ntfy action button (POST to /api/tasks/{id}/input) and the CLI-exit delivery race fix — have been ported onto #614 in commit e0a1894. Consolidating onto one notifier avoids two parallel implementations. See #614 (comment) |
bborn
added a commit
that referenced
this pull request
Jun 29, 2026
Rework after factoring in PR #621 (internal/notify). Email becomes a provider in that framework rather than a standalone sidecar: outbound fires on the same events as ntfy/Telegram; inbound IMAP poller runs as a daemon goroutine; thread -map lives in the main ty DB; replies resume via the existing POST /api/tasks/{id}/input handler (unified with push/web/TUI). Adds taskyou_notify MCP tool for agent-initiated FYI. The extensions/ty-email sidecar is retired and its IMAP/SMTP/allowlist/loop-protection logic ported in. Depends on #621. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A first-class push notifier for TaskYou's weakest spot: getting notified — and able to act — when a task needs you, away from the machine. Borrows herdr's mobile story (ntfy push + one-tap action).
Pushes fire on task lifecycle events and let you resume a blocked agent with one tap from your phone.
How it works
internal/notify— a provider-agnosticNotifierwith two providers:events.Emitterthrough a smallNotifierinterface. Every mutation path already routes through that emitter (executor, MCPtaskyou_needs_input/taskyou_complete, CLI, TUI), so all of them produce pushes. Notifications run on the emitter's wait group, so short-lived CLI/MCP commands flush them before exit.notify_enabled=trueand a provider is configured. Settings are read live from the DB (no restart).Events → pushes
task.blocked(incl.taskyou_needs_input)task.auth_requiredtask.completedtask.failedBody carries task title + project + a short reason (the reason falls back to the latest
taskyou_needs_inputquestion for a meaningful message).One-tap unblock
needs-input / auth-required pushes carry an ntfy
httpaction that POSTs to the existingPOST /api/tasks/{id}/inputendpoint with a canned reply (default"continue"), whichtmux send-keysinto the executor pane and resumes the agent. Plus an "Open task" deep link to type a custom reply. Action links usenotify_base_url(falls back tohttp://localhost:<http_api_port>).Config (
ty settings)notify_enabled,notify_base_url,notify_unblock_reply,notify_ntfy_server,notify_ntfy_topic,notify_ntfy_token(secret),notify_telegram_token(secret),notify_telegram_chat_id. Token keys are hidden fromty settingsand the settings API. Documented in docs/notifications.md + README.Tests
db.UpdateTaskStatus(blocked)→events.Emitter→notify→ HTTP publish, asserting the action targetsPOST /api/tasks/{id}/input.go build ./...,go test, andgolangci-lint run(v2.8.0) pass on all touched packages.Acceptance check
taskyou_needs_inputdelivers a push within seconds (verified end-to-end in tests through the real emitter path).POST /api/tasks/{id}/input→tmux send-keys, the same endpoint ty-web already uses).Follow-ups / notes
notify_base_urlpoints at that reachable origin.🤖 Generated with Claude Code