Distress
Security Notice
This extension includes AI agent skills that can modify AI assistant behavior. Review the skill files before installing.
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=stuckModel:
@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 transportrunModelcannot 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: sentMessageChannels 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.
[2026.08.09.1] — 2026-08-09
Initial release.
Added
@magistr/distressmodel — 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/webhookand 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, andbroadcastFrom(defaulturgent) 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
proofSpecset — 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 becausecontext.runModelunderswamp servehas 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
callresources plus abeaconindex with the rolling rate window and lifetime counters;listingandchannelTestresults.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 fromswamp model type describe.recordmethod and the stable-namedoutboundresource, for transportscontext.runModelcannot reach in-process. Verified live: a channel pointing at a vault-backed@magistr/telegram/sendinstance stalls exactly 30s and returns no resources without entering the callee — the same forgetMe(no arguments, no network) as forsendMessage, while a direct method run answers in 151ms. Those transports are driven from a workflow step that readsoutboundand reports back throughrecord.
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,
upsertEntrycould prune the entry it was being asked to insert, leaving a call thatackandresolvecould 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
proofSpecappeared", and@magistr/herdr'snotifywrites itsactionrecord 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 carryproofRequire, a set of field values the proof record must actually assert, and delivery is disbelieved without them. - Reserved data name.
listandtestwrote their results aslatest, which swamp reserves; both blew up on first real invocation. They now writerecentandchannels, with a regression test over every method's written resource names.
| Argument | Type | Description |
|---|---|---|
| task? | string | What the agent was doing |
| agentName? | string | Who is calling |
| agentModel? | string | Model behind the agent |
| agentSession? | string | Session/run id |
| host? | string | Where the agent runs |
| Argument | Type | Description |
|---|---|---|
| ok | boolean | Did it actually deliver? |
| error? | string | Why it failed, when it did |
| Argument | Type | Description |
|---|---|---|
| by? | string | Who acknowledged |
| Argument | Type | Description |
|---|---|---|
| note? | string | What the resolution was |
| by? | string | Who resolved it |
| Argument | Type | Description |
|---|---|---|
| limit | number |
Resources
- 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