Skip to content

notifications: per-mission terminal-event notifications#113

Merged
mlund01 merged 13 commits into
mainfrom
feature/notifications
Jun 15, 2026
Merged

notifications: per-mission terminal-event notifications#113
mlund01 merged 13 commits into
mainfrom
feature/notifications

Conversation

@mlund01

@mlund01 mlund01 commented Jun 13, 2026

Copy link
Copy Markdown
Owner

What

Opt-in mission notification system: delivers mission_completed / mission_failed / mission_stopped to a gateway and/or the command center. HITL is untouched.

Per-mission HCL:

mission "critical" {
  notification {
    gateway        { events = ["mission_failed"]  channel = "#ops-alerts" }
    command_center { enabled = true }
  }
  ...
}
  • config — per-mission notification { gateway {} command_center {} }; each channel takes enabled + optional events filter; gateway-only channel override. No block ⇒ no notifications. Cross-block validation rejects a gateway channel with no configured gateway. (command_center has no validation — it no-ops when no CC is connected.)
  • notificationDispatcher fans a Record to enabled, event-matching sinks.
  • gatewayManager.Notify + NotifySink forward to the gateway subprocess (with the per-mission channel override).
  • wsbridge — command-center sink (TypeNotification, no-op when no CC connected); dispatch wired into the mission terminal path. mission_completed carries aggregated task outputs via Runner.CollectOutputs().

Tests: config parsing/validation, dispatcher fan-out/filtering, Manager.Notify.

⚠️ Build note (review-only for now)

Depends on new versions of:

go.mod still pins the old versions, so this won't build until those are tagged and the require lines are bumped. Intentional — opened for code review. No replace directives (per repo policy).

mlund01 added 5 commits June 13, 2026 09:39
Add an opt-in mission notification system that delivers mission_completed /
mission_failed / mission_stopped to a gateway and/or the command center.

- config: per-mission `notification { gateway {} command_center {} }` block;
  each channel takes `enabled` + optional `events` filter; gateway-only
  `channel` override. No block => no notifications. Cross-block validation
  rejects a gateway channel with no configured gateway.
- notification: Dispatcher fans a Record out to enabled, event-matching sinks.
- gateway: Manager.Notify + NotifySink forward to the gateway subprocess.
- wsbridge: command-center sink (TypeNotification envelope, no-op when no CC
  connected); dispatch wired into the mission terminal path. mission_completed
  carries aggregated task outputs (Runner.CollectOutputs).

HITL is untouched. Depends on new squadron-gateway-sdk (OnNotification) and
squadron-wire (TypeNotification) versions — bump before merge.
readPump/writePump read the shared c.ws field on every op, so on reconnect
connectToURL swapped in a new socket and started a second pair of pumps
without stopping the old ones — the stale writePump then wrote to the new
socket concurrently with the new writePump, panicking with "concurrent write
to websocket connection".

Each connection now owns its handles: pumps capture their conn (and a
per-connection quit/done) as locals, connectToURL signals the previous
connection's pumps to stop and closes the old socket under a new connMu
before swapping in the new one, and Close tears down under the same lock.

Adds a reconnect regression test and makes the wsbridge test mock thread-safe
so the suite is race-clean.
Per-channel config is now: `enabled` (bool, defaults true — set false to keep
a channel configured but turn delivery off) plus a required, explicit `events`
list. Valid event values are mission_completed, mission_failed, mission_stopped,
or "all" (expands to every terminal event). Both gateway and command_center use
the same shape; channel is the only gateway-only extra.

Omitting `events` is now a validation error rather than silently defaulting to
all — delivery is always explicit.
Add missions/notifications.mdx covering the per-mission `notification` block —
gateway + command_center channels, the enabled toggle (default true), the
required explicit `events` list with the "all" shorthand, the gateway-only
channel override, what each terminal event carries, and validation rules.
Register it in the missions nav and cross-link from the gateways and
command_center config pages.
mission_stopped is no longer a notification event: it's removed from the valid
events set (so "all" expands to mission_completed + mission_failed), and a
user-initiated stop no longer dispatches a notification. mission_stopped
remains a normal mission-lifecycle event on the command-center event stream —
this only affects the notification feature. Docs + tests updated.
Comment thread docs/content/missions/notifications.mdx Outdated
Comment thread docs/content/missions/notifications.mdx Outdated
Comment thread docs/content/missions/notifications.mdx Outdated
Comment thread notification/dispatcher.go Outdated
mlund01 added 5 commits June 14, 2026 10:45
Per PR feedback, remove the per-task output aggregation from notifications: a
mission_completed notification is now just a completion notice (no Outputs).
Drops Runner.CollectOutputs and the Outputs plumbing on the dispatch record
and both sinks.

Docs: remove the unnecessary HITL aside and the in-memory note, and document
that a gateway channel override accepts a channel name (not just an id).
When a gateway is configured, agents get a builtins.gateway.post tool that
posts a message (with an optional channel name/id override) to the gateway's
external system via the new SDK PostMessage RPC. The gateway Manager satisfies
aitools.GatewayBridge directly; the bridge threads through the runner and agent
options alongside the human-input bridge.

The tool is only a valid reference when a gateway block is present (rejected by
squadron verify otherwise) and is hidden from the command-center tool list when
no gateway is configured. Tests + gateways doc added.
The gateway Manager fetches the gateway's MessageToolSpec on startup; the
GatewayBridge exposes its description + JSON Schema, and builtins.gateway.post
surfaces them to the LLM (raw-schema passthrough) and forwards the agent's raw
payload to the gateway. Lets each gateway define its own rich-message shape
(text, embeds/blocks, attachments). Tests + gateways doc updated.
Comment thread docs/content/config/gateways.mdx Outdated
mlund01 added 3 commits June 14, 2026 22:04
The post tool now owns an `attachments` field (injected into the gateway
schema) taking {slot, path} references into the mission's memory/scratchpad.
Squadron resolves each, reads the bytes (25 MiB/file cap), strips them from
the gateway payload, and ships them to the gateway to upload. Eliminates the
SSRF surface of fetching arbitrary model-chosen URLs. Bump SDK to v0.0.3.
@mlund01 mlund01 merged commit 75f21b2 into main Jun 15, 2026
3 checks passed
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