v4.0.0 - Beacon #33
nunocoracao
announced in
Announcements
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
[4.0.0] Beacon
Beacon is the consolidated dashboard / notifications / insights / integrations release — glanceable dashboard, proactive OS notifications, retrospective insights, outbound + inbound integrations.
Added
N working · N needs attention · N idle · N done todaybetween the dashboard header and the project grid; counts derived from existing zustand stores so it updates live with no new gRPC.All,Working,Needs attention,Idle,Has ready tasks) with live counts; selection persists inlocalStorage[wf-dashboard-filter]. Predicates shared viagui/src/renderer/src/lib/dashboard-filters.ts.Ns / Nm / Nh Mmnext to the agent badge, sourced from a newAgentStatus.started_atproto field stamped inRunningAgent.StartedAt. Flips tovar(--wf-warning)past 30 minutes.Active now / 5m ago / 4h ago / 2mo agosegment derived from the most recent taskupdated_at. Hand-rolled relative-time formatter ingui/src/renderer/src/lib/relative-time.ts.gui/src/renderer/src/stores/agent-preview-store.tsref-counts the underlyingAgentService.SubscribeScreenstream.AlertTrianglechip +N failedsegment in the counts row + red progress segment when any task hasstatus === 'done' && success === false.Next:line withWorking: <current task title>(withFlameicon) when the agent is actively running. No proto change — uses the existingAgentStatus.task_title.useTerminalStorefiltered by alive sessions for the project; pulses when any session emitted output in the last 2s. Click expands the bottom panel.LayoutGrid/Rows3toggle in the header; list mode renders one ~46px row per project. Selection persists inlocalStorage[wf-dashboard-layout]. Per-project rendering ingui/src/renderer/src/views/Dashboard/ProjectRow.tsx.internal/daemon/notifypackage with a typedBus, channel fan-out (slow-consumer drop), stableMakeID(sha256(kind|project_id|task_number|emitted_at_unix)[:8]), and JSONL append to~/.watchfire/logs/<project_id>/notifications.logfor headless fallback.internal/daemon/server/task_failed.go::emitTaskFailedondone && !success. Title<project> — task #NNNN failed, body is the task title + optional failure reason.RunningAgent.RunStartedAt. BodyN tasks done · M failedover the run window.assets/sounds/task-{done,failed}.wav(mono 22050 Hz, ~25 KB each). PureshouldPlaySound(kind, prefs)decision ingui/src/renderer/src/stores/notifications-sound.ts. OS toast goes silent precisely when the renderer plays its own audio.internal/daemon/tray/tray.gorebuilds on every project / task / agent / settings change; sections forNeeds attention/Working/IdleplusNotifications (N today) ▸submenu reading the JSONL fallback. Click-through routes via the newDaemonService.SubscribeFocusEventsstream.internal/tui/globalsettings.go) and GUI (gui/src/renderer/src/views/Settings/NotificationsSection.tsx) expose master / per-event / sounds / volume / quiet-hours / per-project mute. Schema underdefaults.notificationsin~/.watchfire/settings.yaml. Gating helpermodels.ShouldNotify.internal/daemon/diffpackage resolves diffs pre-merge (<merge-base>...HEADonwatchfire/<n>) and post-merge (locates the merge commit viagit log --grep). StructuredFileDiffSet; cap at 10000 lines; cache at~/.watchfire/diff-cache/<project_id>/<task_number>.json. GUI Inspect tab + TUI overlay (bound tod).<n>.metrics.yamlsiblings carrying duration, exit reason, agent, tokens, cost. Newinternal/daemon/metricspackage with parsers for Claude Code, Codex, opencode, Gemini, Copilot (stub). Capture from a non-blocking goroutine onhandleTaskChanged. Newwatchfire metrics backfillCLI.internal/daemon/insights/project.goaggregates one project's tasks per window. New GUI Insights tab + TUI overlay (bound toi) with KPI strip, stacked-bar tasks-per-day, agent donut, duration histogram.localStorage[wf-insights-window]persists the 7d / 30d / 90d / All selector.internal/daemon/insights/global.goaggregates the whole fleet per window; cached at~/.watchfire/insights-cache/_global.json. Dashboard rollup card under the Beacon status bar; TUI fleet overlay bound toCtrl+f.InsightsService.ExportReportRPC withoneofscope (project_id/global/single_task). Markdown templates ininternal/daemon/insights/templates/; CSV uses# section: <name>headers. Single<ExportPill>component on the dashboard + ProjectView headers; TUI bindsCtrl+e.digestRunnerschedules with a re-armabletime.Timerfrommodels.DigestSchedule.NextFire(DST-stable, with 24-hour catch-up on daemon start). Markdown rendered to~/.watchfire/digests/<YYYY-MM-DD>.mdregardless of toast suppression. NewWEEKLY_DIGESTnotification kind +FOCUS_TARGET_DIGEST.internal/daemon/relaypackage with anAdapterinterface and aDispatchersubscribing tonotify.Bus. Per-adapter retry ([500ms, 2s, 8s]) + circuit breaker (3 failures / 5-minute window). GenericWebhookAdapterPOSTs the canonical payload withX-Watchfire-Signature: sha256=<hex>HMAC. Secrets via OS keyring (internal/config/keyring.go) with file-store fallback.internal/daemon/relay/slack.gorenders threetext/templateBlock Kit envelopes (TASK_FAILED / RUN_COMPLETE / WEEKLY_DIGEST) with header / section / context / actions blocks. Project-color →:large_<color>_square:shortcode map inslack_color.go.internal/daemon/relay/discord.gorenders three embed envelopes with project-color tinting. SharedhexToInt/rfc3339template helpers. Defensive 4000-rune description trim with single-WARN log on overflow. Newwatchfire integrationsCLI parent withlistandtestsubcommands.github.auto_pr.enabled: true. End-of-task lifecycle ininternal/daemon/git/pr.go::OpenPR:gh auth status→ parse<owner>/<repo>→git push --force-with-lease→ render PR body viapr_body.md.tmpl→gh api -X POST /repos/:owner/:repo/pulls. Sentinel errors distinguish silent fallback (one WARN per project lifetime) from per-attempt failures.IntegrationsServicegRPC service withList/Save/Delete/TestRPCs;Savecarries aoneofpayload, secrets are write-only on the wire. GUIIntegrationsSection.tsxwith per-type detail panels; TUI overlay reachable viaCtrl+I.internal/daemon/echo/server.gobindsListenAddr(default127.0.0.1:8765), 5 s graceful shutdown drain, 1 MiB body cap + panic recovery middleware, unauthenticated/echo/health.RegisterProvider(method, path, handler)for plug-in handlers. Bind failure logs ERROR but doesn't crash the daemon.internal/daemon/echo/verify.goshipsVerifyGitHub(HMAC-SHA256 againstsha256=<hex>),VerifySlack(HMAC-SHA256 overv0:<timestamp>:<body>with 5-minute drift),VerifyDiscord(Ed25519 overtimestamp || body, same drift) — all constant-time.internal/daemon/echo/idempotency.gois an LRU+TTL cache (1000 entries / 24h,container/list-backed,sync.Mutex-protected);Seen(key)refreshes TTL on hit.internal/daemon/echo/commands.go::Route(ctx, cmd, subcmd, rest, CommandContext) CommandResponsepowers slash-command transports. Three commands (status/retry <task>/cancel <task>);CommandResponse{text, blocks, ephemeral, in_channel}is transport-agnostic.internal/daemon/echo/handler_discord.goexposesPOST /echo/discord/interactionswith end-to-end Ed25519 verification + replay window + idempotency. PING → PONG; APPLICATION_COMMAND → dispatch tocommands.Route, render viadiscord_render.go::RenderInteraction. Slash-command registration viawatchfire integrations register-discord <guild_id>(idempotent).gui/src/renderer/src/views/Settings/InboundSection.tsxshows a Listening pill polled at 5 s, editableListenAddr+PublicURLwith restart button, Copy-as-<provider>-URL buttons, four write-only secret inputs, per-provider last-delivery timestamps. TUI mirrors via a new "Inbound" tab inside the Integrations overlay.Changed
positionorder with bucketing into needs-attention → working → has-ready-tasks → idle (input-array index as final tiebreaker for stability). Predicate helpers ingui/src/renderer/src/lib/dashboard-filters.ts. A mutedSorted by activitylabel appears when the activity order differs from the underlying position order.Fixed
projectIdand survive navigation; Cmd+` toggles a non-destructivepanelCollapsedflag.destroyProjectSessions(projectId)is called only fromremoveProject.BottomPanel.tsxalways-mounts everyTerminalTabwith avisibleflag so xterm.js scrollback survives React reconciliation.gui/src/main/login-shell.tsruns$SHELL -l -c env, parses PATH + dev-tool env vars, with a fallback PATH merge against the standard user-install locations. Caches per Electron process. Newdefaults.terminal_shellglobal setting picks the shell binary (X_OK validated). Fixes [Bug] #32Migration
weekly_digestdefaults off, quiet hours default off.ghon PATH andgh auth statusreturning 0; missing prerequisites fall back to silent merge with one WARN per project lifetime.InboundConfig= no listener. Concrete handlers return 503 until the per-provider secret is configured.Known issues
handler_github.goforpull_request.closedevents did not ship with Beacon — auto-PR loop closes manually for now (filed as v5.0 follow-up).commands.Routedid not ship with Beacon —/watchfire status / retry / cancelworks in Discord but not in Slack (filed as v5.0 follow-up).This discussion was created from the release v4.0.0 - Beacon.
Beta Was this translation helpful? Give feedback.
All reactions