Skip to main content

Distress

@magistr/distressv2026.08.09.1· 10d agoMODELSSKILLS
01README

A distress-call beacon for AI agents.

Any agent — including a background agent with no user watching — can page a human at any time and for any reason: a user problem, a backend failure, a task it cannot finish, a request for clarification, or its own distress. One command raises the call:

swamp model method run distress call \
  --input summary='stuck retrying the same tool call' \
  --input severity=urgent --input category=stuck

Model:

  • @magistr/distress — one instance per operator to be paged.

    • call — raise a distress call: summary, severity (info / concern / urgent / critical), category (welfare / stuck / blocked / user-problem / backend-error / clarification / other), plus who is calling, what they were doing, and what they suggest.
    • ack — acknowledge with a note. The note is the human's answer, and the agent can read it back — that is what turns a one-way page into a conversation.
    • resolve — close the call out.
    • list — what is open, and what the human said about it.
    • test — prove every configured channel can actually page you.
    • record — write back a delivery made by a workflow step, for a transport runModel cannot reach in-process.

Delivery is pluggable by configuration, not by code

A channel names an existing swamp model INSTANCE and one of its methods, so @magistr/telegram/send, @magistr/herdr, @keeb/discord/webhook — or anything else that can send a message — becomes a channel with no change to this extension:

channels:
  - name: herdr           # a toast while you are at the keyboard
    definition: herd
    method: notify
    preset: title-body
    minSeverity: info
  - name: telegram        # a push to your phone when you are not
    definition: tg-bot
    method: sendMessage
    preset: text
    minSeverity: concern
    proofSpec: sentMessage

Channels are tried in configured order and filtered by minSeverity; at or above broadcastFrom (default urgent) every eligible channel is used rather than just the first, so an emergency never rides on one transport being healthy.

This model holds no credentials of its own — each transport keeps its own secret, and the beacon never becomes a place credentials accumulate.

Delivery is proven, never assumed

Under swamp serve, context.runModel has been observed to resolve WITHOUT executing the callee — green runs that delivered nothing. For a beacon that is the worst possible failure, so a send counts only on evidence: the callee must return a resource handle, and with proofSpec set, a genuinely NEW record of that spec must appear. An unproven send is a failed channel and fails over to the next one; a call that reached nobody raises an error rather than reporting success. A callee's error text is redacted and bounded before it is written to a durable record.

The beacon cannot become the loop

An agent stuck in a tool-call loop will call for help in a loop too. Every call carries a dedupe key — supplied, or derived from category, summary and agent with digit runs collapsed, so "retry 12 / 13 / 14 failed" is ONE incident. Repeats inside dedupeWindowSeconds fold into the open call instead of paging; after escalateAfterRepeats folded repeats the call pages once more ("still stuck, N repeats") and the counter resets. A rolling maxCallsPerHour cap bounds total pages, with one throttle notice per hour so muted is never mistaken for calm — and suppressed calls are still recorded in full. critical may page through the cap; nothing evades dedupe.

Every call is durable, queryable swamp data: severity, category, agent identity, every delivery attempt with its proof or its error, and the acknowledgement and resolution.

02Release Notes

[2026.08.09.1] — 2026-08-09

Initial release.

Added

  • @magistr/distress model — a distress-call beacon any AI agent can use to page a human, at any time, for any reason.

  • Six methods. call (raise), ack (answer, readable by the agent), resolve (close), list (what is open and what was said), test (prove every channel can page you), record (write back a delivery made by a workflow step).

  • Pluggable channels. A channel names an existing swamp model instance and one of its methods, so @magistr/telegram/send, @magistr/herdr, @keeb/discord/webhook and anything else that sends messages work with no code change. Four argument presets (text, title-body, message, content) plus {{placeholder}} templating cover the rest. The model holds no credentials of its own.

  • Severity routing. Per-channel minSeverity, priority-ordered failover, and broadcastFrom (default urgent) above which every eligible channel is used rather than just the first.

  • Proven delivery. A send counts only when the callee returns a resource handle and — with proofSpec set — writes a genuinely new record of that spec. Unproven sends fail over to the next channel; a call that reached nobody raises an error after being persisted. This exists because context.runModel under swamp serve has been observed to resolve without executing the callee, turning three days of green runs into zero deliveries.

  • Loop protection. Dedupe keys derived with digit runs collapsed (so "retry 12/13/14 failed" is one incident), folding of repeats into the open call, escalation after N repeats that only resets its counter on a proven send, and a rolling hourly delivery cap with a once-per-hour throttle notice. Suppressed calls are always still recorded.

  • Durable records. Per-call call resources plus a beacon index with the rolling rate window and lifetime counters; listing and channelTest results.

  • Redaction. A callee's error text is scrubbed of bearer tokens, /bot<token>/ URL segments, key=/token= parameters and URL userinfo, then length-bounded, before it reaches a durable record.

  • Bundled Claude skill (.claude/skills/distress/) teaching an agent when raising a call is the right move — and when it is not.

  • 131 tests across the five suites required by STANDARD.md, with the property suite pinned by a callee-contract fixture captured from swamp model type describe.

  • record method and the stable-named outbound resource, for transports context.runModel cannot reach in-process. Verified live: a channel pointing at a vault-backed @magistr/telegram/send instance stalls exactly 30s and returns no resources without entering the callee — the same for getMe (no arguments, no network) as for sendMessage, while a direct method run answers in 151ms. Those transports are driven from a workflow step that reads outbound and reports back through record.

Fixed during development

Four defects caught before release — two by the test suites, two by running it for real — all recorded because each is a failure mode this model exists to prevent:

  • Call ids could collide. The id was derived from (instant, dedupe key) only, so two distinct calls raised in the same millisecond with the same key produced the same id and the second silently overwrote the first's record — a distress call disappearing. The id now carries the beacon's monotonic raised-count, which never repeats on an instance.
  • The index could drop the row it had just written. With the history limit reached and every retained row live, upsertEntry could prune the entry it was being asked to insert, leaving a call that ack and resolve could not find. The upserted row now always survives; live calls outrank resolved ones for the remaining slots.
  • A callee's recorded no-op counted as a delivery. Proof was "a new record of proofSpec appeared", and @magistr/herdr's notify writes its action record even when toasts are disabled in herdr's config — changed: false, status: "suppressed", detail: "notification not shown (disabled)". A live channel test came back green while every channel was dark. Channels now carry proofRequire, a set of field values the proof record must actually assert, and delivery is disbelieved without them.
  • Reserved data name. list and test wrote their results as latest, which swamp reserves; both blew up on first real invocation. They now write recent and channels, with a regression test over every method's written resource names.
03Models1
@magistr/distressv2026.08.09.1extensions/models/distress.ts
fn call(task?: string, agentName?: string, agentModel?: string, agentSession?: string, host?: string)
Raise a distress call. Routes by severity to the configured
ArgumentTypeDescription
task?stringWhat the agent was doing
agentName?stringWho is calling
agentModel?stringModel behind the agent
agentSession?stringSession/run id
host?stringWhere the agent runs
fn record(ok: boolean, error?: string)
Record a delivery made OUTSIDE this model — by a workflow step
ArgumentTypeDescription
okbooleanDid it actually deliver?
error?stringWhy it failed, when it did
fn ack(by?: string)
Acknowledge a call — the human's answer back to the agent. The
ArgumentTypeDescription
by?stringWho acknowledged
fn resolve(note?: string, by?: string)
Close a call out. A resolved call stops deduping, so the same
ArgumentTypeDescription
note?stringWhat the resolution was
by?stringWho resolved it
fn list(limit: number)
List calls, newest last. An agent calls this after paging to see
ArgumentTypeDescription
limitnumber
fn test()
Prove every configured channel can actually page you. Sends a test

Resources

call(infinite)— One distress call — severity, category, who raised it, every
beacon(infinite)— Operational state: the call index, the rolling delivery window
outbound(infinite)— The most recent call's rendered page, under a STABLE name so a
listing(infinite)— Result of the most recent `list` call
channelTest(infinite)— Per-channel result of the most recent `test` call
04Skills1
distress1 file
05Stats
A
100 / 100
Downloads
0
Archive size
49.4 KB
  • Has README or module doc2/2earned
  • README has a code example1/1earned
  • README is substantive1/1earned
  • Most symbols documented1/1earned
  • No slow types (deprecated)1/1earned
  • Dependencies pass trust audit2/2earned
  • Has description1/1earned
  • Platform support declared (or universal)2/2earned
  • License declared1/1earned
  • Verified public repository2/2earned
06Platforms
07Labels