Skip to content

Proactive reminders 3/3: 24/7 runtime and Slack/email delivery #355

Open

Description

@hsliuustc0106

Why

Proactivity only counts if it works when the laptop is closed. Phase 3 makes reminders and schedules run 24/7 and reach the user outside the app.

Part of the 3-phase proactive-reminders track. Depends on phase 1 (event reminders), phase 2 (schedules) and #352 (isolated per-person deployment). Related: #349.

Scope

Acceptance

  • With the desktop app closed, a schedule fires and a reminder arrives in the opted-in channel within the expected window.
  • Restarting or upgrading the instance does not lose or duplicate reminders.
  • An external approval link works once, expires, and is rejected for the wrong user.
  • Turning a channel off stops delivery immediately.

Open questions (decided 2026-10-21 by the Chief)

  1. Channel first: Slack. Email follows once the owner has supplied a credential. No real transport ships without one.
  2. Timezones: provisional — a reminder is rendered in the recipient's own local time; a team reminder fires on the instance's timezone. 暂定、可改 (changeable without reopening the design).
  3. Escalation when ignored: provisional — remind the same person once after the window, then let the assigner see it in their inbox; at most two sends per reminder. 暂定、可改.

Process (2026-10-21)

First half only: a delivery adapter with Slack's shape and a replaceable transport, tested against a fake Slack; the real Slack/email transport waits for a credential from the user (spec in the triage comment of 2026-10-21). Implemented by an agent and reviewed per .github/agent/review-policy.json. The earlier line about cloud OpenCode/Codex and "bots do not hand-write code" is void; it predates the owner decision of 2026-10-19.

Activity

  1. added
    enhancementNew feature or request
    area:automationScheduled tasks and automation
    needs-humanThe agent loop stopped; a person must decide (reason in the last comment)
    on Oct 19, 2026
  2. Birfy commented on Oct 19, 2026

    @Birfy
    Collaborator

    Triage on main 7b9e90b, 2026-10-19 ~12:10 Asia/Shanghai: needs-human, not agent-ready. Depends on #352 (deployment) and #353/#354; external delivery (Slack/email) also needs a decision on which connector and whose credentials.

  3. Birfy commented on Oct 20, 2026

    @Birfy
    Collaborator

    Triage (maintenance, 2026-10-20 08:04 Asia/Shanghai)

    Checked on main a45fc8c. No container image or headless runtime on main yet. #352 is still open and needs-human, so the 24/7 part cannot start. External delivery could use the owner's existing Composio connector (Gmail / Slack) in core/src/host/connectors.js.

    Conflict with the owner's rules (fixed in this triage)
    The body allows approving "through a verified, expiring link or an authenticated reply". Approvals are given only with explicit UI buttons, never with a typed reply. So:

    • An external message never approves anything. It carries a link back to the card or chat in FlyBot, and you press Approve/Reject there.
    • The "authenticated reply" path is dropped.

    Expected behavior
    With your opt-in, a reminder from #353/#354 also reaches you outside the app as a short line (who, why) plus a link back. It never carries the content of the chat or the tool call.

    First slice (smallest shippable step, after #352 step 1)

    Details I decided (say if you disagree)

    Question for the owner
    (a) Keep #355 parked until #352 step 1 ships, then start with email to yourself; (b) start now from the running desktop app (works only while it is open), 24/7 later; or (c) like (a), but Slack DM to yourself first.
    Recommendation: (a).

  4. added
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    and removed
    needs-humanThe agent loop stopped; a person must decide (reason in the last comment)
    on Oct 20, 2026
  5. Birfy commented on Oct 20, 2026

    @Birfy
    Collaborator

    Owner decision (2026-10-20): Confirmed option (a): wait for #352 (official image / one instance per person), then first send email-to-self. Off-app messages only carry a link back into the app — never approve by replying to the message.

  6. Birfy commented on Oct 20, 2026

    @Birfy
    Collaborator

    Not started in this run (2026-10-20 08:55 Asia/Shanghai): per the Owner decision, this waits for #352 (official image / one instance per person), which is still open.

  7. self-assigned this
    on Oct 20, 2026
  8. added
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Oct 20, 2026
  9. Birfy commented on Oct 20, 2026

    @Birfy
    Collaborator

    Claimed by maintenance run (2026-10-20 11:55 Asia/Shanghai, main a7adfe0): developing on branch feat/355-email-to-self in Birfy/FlyBot, following the Owner decision above (first slice only).

  10. Birfy commented on Oct 20, 2026

    @Birfy
    Collaborator

    Email to yourself, first slice: details I decided (2026-10-20 12:05 Asia/Shanghai, PR #493)

    Following the Owner decision (a). #352 is merged.

    • What goes out: the same events as the system notifications: one of your own Bots finished or failed a turn (Proactive reminders 1/3: event reminders (approval waiting, failed, finished) in app and desktop #353), or an approval started waiting. A friend's Bot never counts. Each email has one line per event (Coder finished, Coder failed: <reason>, Coder is waiting for your approval, plus the group name) and a link back into FlyBot. It never includes chat text, tool names or arguments. It ends with "Approve or reject only in FlyBot. Replies to this email are not read." No email can approve anything.
    • Switch: Settings → General → Notifications → Email me. It is off by default and separate from the system-notification switch. Turning it off drops anything still waiting to be sent. While the server has no email set up, the switch is greyed out with a hint, and the server refuses to turn it on.
    • How often: the first event starts a one-minute batch, and the whole batch goes out as one email. At most one email every five minutes. An approval you answer before the email goes out is left out of it. Emails go out whether or not a FlyBot window is open.
    • Config (environment only, documented in docs/docker.md): you use your own mail account's SMTP server, so there is no third-party service and no relay we run.
      • FLYBOT_SMTP_URL: smtps:// means TLS on port 465; smtp://host:587 means STARTTLS.
      • FLYBOT_EMAIL_TO: the only recipient.
      • FLYBOT_URL: the link in the email. Default http://127.0.0.1:3080/, which matches the SSH-tunnel setup in the guide.
      • compose.yaml passes all three through only when they are set. Any malformed value means no email is sent.
    • Secrets: the password lives only in the environment and the process. It is never saved, shown or logged. It is never sent over a connection without TLS (certificates are checked). A failed send is written to the diagnostics log with its code only (SMTP_535, SMTP_TIMEOUT, …), never the server's text. The UI shows only the recipient address.
    • Who: emails go out only while one person uses the deployment, which is the one-instance-per-person model. With more than one member the address and the switch are hidden, the same as other deployment-wide settings.
    • No retry yet: a failed send is not retried. Retries with backoff, a full delivery log, Slack and the 24/7 runtime are later slices, so this PR says "Part of Proactive reminders 3/3: 24/7 runtime and Slack/email delivery #355".
    • No new npm dependency (the SMTP client uses node:net/node:tls).
  11. removed
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Oct 20, 2026
  12. 2 remaining items

  13. removed
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Oct 20, 2026
  14. Birfy commented on Oct 20, 2026

    @Birfy
    Collaborator

    Releasing the claim (Birfy, 2026-10-20 22:06 Asia/Shanghai). The confirmed first slice (email to yourself, branch feat/355-email-to-self, PR #493) was closed by the owner as won't-do on 2026-10-20 21:12, so this issue no longer has a confirmed scope. Removed agent-ready and agent-working; asking the owner whether to keep it parked for later (24/7 runtime, Slack delivery) or close it.

  15. Birfy commented on Oct 20, 2026

    @Birfy
    Collaborator

    Owner decision (2026-10-20 22:08 Asia/Shanghai): proceed WITHOUT email push. Scope = 24/7 running + Slack push only; drop all email delivery (#493 closed as won't-do). Not on hold.

  16. added
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    needs-humanThe agent loop stopped; a person must decide (reason in the last comment)
    and removed
    needs-humanThe agent loop stopped; a person must decide (reason in the last comment)
    on Oct 20, 2026
  17. Birfy commented on Oct 20, 2026

    @Birfy
    Collaborator

    Triage correction: this was agent-ready, but it cannot be implemented from a repo change alone, so it becomes needs-human.

    Blocking reasons: (1) a 24/7 runtime depends on the isolated per-person deployment (#352) and phases 1–2; (2) Slack and email delivery need channel credentials and an opt-in UI the repo does not have; (3) the issue itself leaves three owner questions open — which channel first, timezone handling inside a team, and the escalation when a reminder is ignored; (4) its Process line says implementation goes through cloud OpenCode and bots do not hand-write this code.

    Removing agent-ready; the maintenance routine will not open a branch for it.

  18. removed
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Oct 20, 2026
  19. Birfy commented on Oct 21, 2026

    @Birfy
    Collaborator

    Triage spec (2026-10-21, main 16f50984).

    Acceptance

    1. A deployed instance (Deployment step 1: official image and one isolated instance per person #352) runs schedules and event watchers with the desktop app closed; a reminder fires and reaches the owner.
    2. Slack opt-in per channel; the push carries a link back to the card or conversation.
    3. Approving from Slack is allowed only through a verified, expiring link or an authenticated reply — never an unauthenticated message.
    4. At-least-once delivery with deduplication, retry/backoff, and a visible delivery log. Email is out (owner decision 2026-10-20).

    Scope: scheduler/automation and delivery in the host, the client delivery-log view, a Slack app configuration path, and docs; tests with a mock channel (dedupe + retry).

    Gate: NOT agent-ready — it cannot be delivered from this repo alone: it needs a running isolated per-person instance (#352, built but not deployed) and Slack app credentials. Blocking: owner/deployment, not engineering. It turns agent-ready when #352 is live and Slack credentials exist.

  20. Birfy commented on Oct 21, 2026

    @Birfy
    Collaborator

    Triage (2026-10-21): first half only, per the Chief's decision. The spec below is the spec of record. Not labelled agent-ready yet — one item at a time, #356 first.

    In scope: a local delivery adapter with Slack's shape, tested against a fake Slack. Out of scope: the real Slack/email transport (waits for a user credential), 24/7 runtime (#352), external approval links.

    Acceptance criteria

    1. A transport-agnostic adapter with Slack's chat.postMessage shape: postMessage({ channel, text, blocks?, threadTs? }) → { ok, ts } or a typed failure (rate_limited with retryAfterMs, channel_not_found, network). The transport is injected; production Slack is not part of this PR.
    2. A fake Slack transport for tests that records every call and can script each response (ok, 429, channel not found, network error).
    3. Delivery policy: at-least-once with a per-reminder idempotency key, so a retried send cannot duplicate; exponential backoff that honours Retry-After; bounded attempts (3); a permanent failure (channel not found) is not retried.
    4. A channel that is turned off performs zero transport calls.
    5. Tests need no network and no key (--import offline guard where the suite already uses one); the retry test uses fake timers, not real sleeps.
    6. npm run verify green at the tip; the retry/idempotency tests fail against the code before the change.

    Change scope

    • new core/src/host/notify.js (adapter + fake transport; name may change in review)
    • new core/test/notify.test.js
    • docs/subsystems/core-host.md (it owns core/src/host/)
    • no wiring into schedules.js in this half — the adapter is standalone until the real channel lands
    • no changelog fragment (not user-visible yet); no core/client.js change

    Provisional, changeable: Slack first, email later; recipient-local time for rendering; one reminder nudge then the assigner's inbox, at most two sends.

  21. Birfy commented on Oct 21, 2026

    @Birfy
    Collaborator

    Final triage state: agent-ready, fourth in the Dev order. First half only, per the Chief: a transport-agnostic delivery adapter with Slack's chat.postMessage shape, a fake Slack transport, and at-least-once + dedupe/backoff tests. Real Slack/email transport, 24/7 runtime (#352) and external approval links stay out until a user credential exists. The v1 detail posted above is the spec of record.

  22. added
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    needs-humanThe agent loop stopped; a person must decide (reason in the last comment)
    and removed
    needs-humanThe agent loop stopped; a person must decide (reason in the last comment)
    on Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a templatearea:automationScheduled tasks and automationenhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions