Newer
Older
navi-1 / docs / synapse.md

Synapse integration

navi speaks to the GNEXUS Synapse notification hub in both directions:

  • Inbound — Synapse delivers platform events to navi's webhook; each event can trigger an agent reaction (a background agent run).
  • Outbound — navi emits low-level lifecycle events (reaction finished / failed, notify-tool pushes) into Synapse's ingest, where they are simply logged.

The gnexus-synapse package (vendored dependency) owns the wire format: verify_webhook / ack / make_signature on the receiving side, AsyncSynapseClient on the emitting side.

Configuration

Env var Purpose
SYNAPSE_SOURCE_URL Synapse base URL for the outgoing source API
SYNAPSE_SOURCE_API_KEY syn_* key registered in the Synapse admin panel
SYNAPSE_SOURCE_NAME source name shown in Synapse (default navi)

Without a source key nothing outgoing is sent, and Synapse-linked UI options are disabled (source_ready: false in settings responses).

Inbound deliveries need a per-user target: a token_ref + shared secret created in the Synapse admin panel and registered by the user in navi (POST /synapse-targets).

Inbound: delivery gateway

POST /webhooks/synapse (navi/api/routes/synapse.py) — verifies the signature against every active target secret (a mismatch never tells which target failed → 401), dedupes by event_id (Synapse retries until 2xx; replays are re-acked but never acted on twice), logs synapse.delivery_accepted, and hands the envelope to schedule_reaction() fire-and-forget, so the ack never waits for agent work.

Inbound: reaction runner

navi/synapse/reactions.py — the point of the feature. Pipeline:

  1. Gate — the matched target's user must have reactions_enabled: true; otherwise the event is accepted and discarded (synapse.reaction_disabled).
  2. Dispatcher meta-pass — a single LLM call to the hidden dispatcher profile (navi/profiles/dispatcher/, temperature 0.2, one shot, no tools). Input: the user's reaction instructions + the event envelope. Output: strict JSON {profile_id, task, understood} or {skip: true}. Unparseable output or a backend failure = skip (logged, never a crash).
  3. Profile resolution — only visible profiles may host a reaction; a hidden or unknown id falls back to secretary.
  4. Special session — a fresh session with special=True, name Synapse: {event_type} and session_metadata["synapse"] (event id/type/ dispatcher's understanding). Session + opening message are persisted before the run, so a crash mid-run still leaves a visible trail.
  5. Headless run — orchestrator create_run + run_agent with the target user's tool context. No live subscriber, but the run registry means the user watching the session in the UI sees the stream live.
  6. Finalise — app push per completion_notify (always / important = only when the run had errors / never) and a low-level reaction.finished / reaction.failed event to Synapse (best effort).

Outbound: source side

navi/synapse/outbound.py — emit_low_level(subject, action, payload, priority, dedup_key): silently no-ops when the source is not configured, otherwise emits through AsyncSynapseClient and closes it per call.

Users of it: the reaction runner (reaction, finished/failed) and the notify tool (navi-notification, action = push level).

notify tool

navi/tools/notify.py — the agent's signal channel. Parameters: message (required) + level (info | warning | intervention). Reads the user's push_target and delivers through both legs, then answers honestly: Delivered: app. Skipped: synapse (source key not configured) — the agent can see what actually happened and never retries into a wall. Priorities map info→normal, warning→high, intervention→critical.

Per-user settings

navi/synapse/settings_store.py (postgres synapse_settings, synapse_instruction_versions):

  • reactions_enabled (default false),
  • push_target: app | app_synapse | synapse (default app),
  • completion_notify: always | important | never (default important),
  • instructions: reaction instructions, editable by both the user and navi (synthesize_instructions tool edits with edited_by="navi"), every change recorded in the version history (last 100 kept, with author + reason).

REST: GET/PUT /synapse-settings (PUT records a version when instructions change), GET /synapse-settings/versions?limit=. GET/PUT replies include the non-persisted source_ready flag.

Service sessions in the UI

special=True sessions are excluded from the regular session list at the REST layer (GET /sessions?special=false|true; omitting the param shows everything — admin/API callers see all). The webclient sidebar has a service-sessions toggle that fetches special=true; special items render muted with a robot icon.