Repository navigation
Add discord-send.py for direct Discord messaging - #36
Closed
liususan091219 wants to merge 1 commit into
Closed
liususan091219 wants to merge 1 commit into
liususan091219 wants to merge 1 commit into
Conversation
Simple script to send messages to channels or DMs via gateway. No task bridge needed — just call directly: python3 src/discord-send.py <channel_id> "message" python3 src/discord-send.py <channel_id> "msg" --reply-to <msg_id> python3 src/discord-send.py --dm <user_id> "message" Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
liususan091219
force-pushed
the
feature/discord-proactive-send
branch
from
March 29, 2026 16:15
51d478c to
9cf31bc
Compare
This was referenced Apr 20, 2026
yixuan-ag2
added a commit
to yixuan-ag2/sutando
that referenced
this pull request
Aug 18, 2026
- capability gate requires version 1 + every normative capability
- parked human retry: NO premature accepted ack — the ack waits for the
authorization that mints the real epoch, and the deferred dial keeps
its human-retry provenance; duplicates of the parked request stay
silent until that ack exists
- accepted acks are never evicted from the dedup cache (duplicates never
redial twice has no expiry); the cap bounds only non-accepted entries
- stop() is a terminal latch: late activated rejections, lifecycle
events, timers and commands are inert after shutdown
- model progress (turn.start) now feeds the reducer's silence anchor,
generation-fenced by the event's own transport generation; pending
tools are keyed by {generation,id} so a stale-generation completion
can neither settle a reused current id nor advance the anchor
- the delivered-audio baseline moved into the coordinator: a fresh
epoch's reset-from-zero counters that are already positive count as
delivery (a response landing before its epoch's first tick was lost)
- the reducer clock domain is monotonic (performance.now) in the live
wiring — wall-clock jumps can no longer strand waiting-retry or
bypass cooldowns; snapshot speech timestamps translate by age; wire
frames and ledger rows keep wall time
- the legacy CLOSED guard stands down whenever the coordinator owns the
episode (any non-idle phase or a recovered origin), and its cast-call
fake attach no longer mints a coordinator client epoch
- terminal reattach: bodhi's suppressClientAutoActions gates greeting /
context replay / the CLOSED auto-reconnect (bodhi sonichi#36 amendment);
pin -> yixuan-ag2#bce828f
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
yixuan-ag2
added a commit
to yixuan-ag2/sutando
that referenced
this pull request
Aug 18, 2026
…hi#36 landed) The interim fork pin is gone. The merged main additionally hardened the surface beyond the reviewed branch heads: STT capture-generation fencing (a pre-recovery external transcript can no longer cross the boundary as fresh evidence) and turn.start now carries attemptEpoch alongside transportGeneration. Both additive; coordinator suite and full suite green against the merged build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
sonichi
pushed a commit
that referenced
this pull request
Aug 21, 2026
… + arming) (#3135) * feat(voice): ACTIVE-silence watchdog — trigger facts + recovery reducer + Phase 0a shadow mode First implementation slice of the approved ACTIVE-silence recovery design (desktop docs/design-voice-active-silence-recovery.md; the 2026-08-17 incident: 3+ minutes of user speech into a dead ACTIVE session, recovery only by abandonment). This PR detects and records; it never touches the live session. - voice-health-matrix: MatrixResult.facts — typed, verdict-independent, fail-closed raw facts (formulas fixed: ingressAdvanced = delta(deliveredFrames) > 0; modelSilentFor15s null-safe), attached to every result. Truth-table tests incl. first-tick/epoch-boundary/ reconnect-window fail-closed rows. - voice-active-silence-watchdog: pure reduceRecovery event reducer implementing the design's normative transition table — latched speech evidence, continuous-silence anchor, epoch-fenced admission, episode attempt budget, waiting-retry with schedule-retry effects (settles design residue 5), fatal-backoff clear/park semantics (residue 6), validated VOICE_ACTIVE_SILENCE_MODE / _TICKS parsing. The 2026-08-17 incident is a named test (fires on the 3rd qualifying tick). - voice-watchdog-shadow: Phase 0a diagnostic observer — derives events from the health tick chronologically (epoch-change attach detection, no stale-speech relatch), epoch-keyed tool veto (background work excluded; first-edge adoption), would-restart / would-terminal / post-fire-progress evidence rows; armed degrades to shadow (no bodhi capability descriptor exists yet). - voice-watchdog-ledger: bounded, counted-drop, awaitable JSONL appender; flushed on shutdown. - voice-audio-health: SpeechEvidence.lastOnsetAt — the utterance onset retained past the hangover, so 30s ticks see the FIRST sample of a completed utterance (real-ledger integration test). - voice-agent: additive wiring only (tool hooks, tick-end observe, shutdown flush). The CONNECTING watchdog and CLOSED recovery guard are untouched; the coordinator refactor and armed mode land in PR 2 with the bodhi recovery surface, per the design's phasing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(voice): shadow round-2 review — boundary-first replay, fenced tool settles, honest post-fire milestones Second Codex verification round, all findings: - client-epoch boundaries now apply BEFORE snapshot-carried events and rebase the speech watermarks, so fresh new-epoch speech latches instead of being cleared by its own reload (named test); - tool settles are fenced by originating epoch (a stale generation's settle emits no outcome and cannot advance the anchor; named test); - post-fire classification anchors to the episode's FIRST fire only, splits upstream progress from functional response (post-fire-progress vs post-fire-functional rows), and counts toolOutcome as progress; - meeting-mode mutations reach the shadow from every mutation site via noteMeetingMode (1s request path + summon/dismiss auto-switch), replayed chronologically — meeting-window speech between ticks never latches (named test); - ledger rows gain capabilitySetId + per-row monotonic offset; drop-counter changes are surfaced in the log; prefer-const lint fixed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(voice): shadow observer cannot take the agent down Every public entry on VoiceWatchdogShadow sits on a live path, and voice-agent installs a crash-only uncaughtException handler (writeCrashRecordAndExit). observeTick is the last statement in the bare 30s setInterval callback with nothing wrapping it, and the module had zero containment across its 340 lines — so a null deref or an unexpected snapshot shape in the shadow reducer terminated the voice agent on a timer, taking the working paths down with it. Guard lives inside the module rather than at the call sites: there are seven of them (noteMeetingMode x4, noteToolCall, noteToolSettled, observeTick) and a call site added later would not inherit a wrapper. flush() is guarded against a SYNC throw too, since the shutdown path awaits it. Control, same tests, guard bypassed vs present: guard bypassed FAIL a throwing reducer never escapes the tick FAIL a throwing flush never escapes shutdown guard present both ok, 23/23 checks pass Raised by @sonichi. * test(voice): the containment ledger was constructed with the wrong signature WatchdogLedger takes one options object ({path, meta, onError?}); my containment block passed two positional args, so o.path and o.meta were both undefined, mkdirSync(dirname(undefined)) threw into the bare catch, and every append failed into an explicitly-noop onError. The object constructed fine and wrote nowhere. Eleven sibling sites in the same file use the object form; this was the lone outlier. It does not weaken what the block asserts today — guard() only calls this.log(), never the ledger, and the assertion is escaped === null. The reason to fix it is that it is a loaded trap: the moment someone extends this block to assert a degraded observation was RECORDED, that assertion passes against a ledger that wrote nothing, with the noop onError eating the evidence. Not caught by tsc because tsconfig is include:["src"], so no test file is in typecheck scope at all — a wrong-signature call in tests/ is structurally undetectable here. That is a repo-level gap, not this PR's. Found by @liususan091219. * feat(voice): ACTIVE-silence recovery coordinator — armed-mode driver The impure driver around PR 1's pure reducer, per the design doc's Phase 1: executes restart effects as injection-free held recoverUpstream calls with reducer<->dial attempt-epoch correlation via the lifecycle stream; owns the cooldown/backoff retry timer (re-validated at delivery); latches terminal into the durable voice-stalled push with attach-time resend on a freshly minted clientEpoch; serves the human retry wire (schema-faithful parse, {voiceSessionId,requestId} dedup replaying the original ack, accepted/stale/not-terminal dispositions, fatal-backoff parking); clears terminal only on client-confirmed delivery or an accepted retry. Structural session surface — compiles against any bodhi pin; recoverySurfaceSupported() is the runtime gate. 17 tests drive it end-to-end against a scripted fake session with a virtual clock and manual timers (the design's integration fake). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(voice): arm the coordinator in voice-agent behind VOICE_ACTIVE_SILENCE_MODE Armed only on explicit VOICE_ACTIVE_SILENCE_MODE=armed AND the full bodhi recovery surface (falls back to shadow with a loud line); VOICE_ACTIVE_SILENCE_TICKS=0 still disables. The Phase 0a shadow keeps running unchanged in every mode. Wiring: client command/attach/detach hooks and the connection-lifecycle stream (bodhi #34/#36), tool and meeting-mode taps beside the shadow's, fatal-backoff set/clear, a per-epoch delivered-audio baseline (client-confirmed playback first, server egress fallback) feeding functional-recovery clears, and the ordinary CLOSED guard gated while terminal — the design's no-uncounted- bypass rule. bodhi pin -> yixuan-ag2#b323779 (PR #36 head; interim — repin to the sonichi merge SHA when #35/#36 land). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(review): coordinator hardening — nine findings from the Codex round - capability gate requires version 1 + every normative capability - parked human retry: NO premature accepted ack — the ack waits for the authorization that mints the real epoch, and the deferred dial keeps its human-retry provenance; duplicates of the parked request stay silent until that ack exists - accepted acks are never evicted from the dedup cache (duplicates never redial twice has no expiry); the cap bounds only non-accepted entries - stop() is a terminal latch: late activated rejections, lifecycle events, timers and commands are inert after shutdown - model progress (turn.start) now feeds the reducer's silence anchor, generation-fenced by the event's own transport generation; pending tools are keyed by {generation,id} so a stale-generation completion can neither settle a reused current id nor advance the anchor - the delivered-audio baseline moved into the coordinator: a fresh epoch's reset-from-zero counters that are already positive count as delivery (a response landing before its epoch's first tick was lost) - the reducer clock domain is monotonic (performance.now) in the live wiring — wall-clock jumps can no longer strand waiting-retry or bypass cooldowns; snapshot speech timestamps translate by age; wire frames and ledger rows keep wall time - the legacy CLOSED guard stands down whenever the coordinator owns the episode (any non-idle phase or a recovered origin), and its cast-call fake attach no longer mints a coordinator client epoch - terminal reattach: bodhi's suppressClientAutoActions gates greeting / context replay / the CLOSED auto-reconnect (bodhi #36 amendment); pin -> yixuan-ag2#bce828f Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(review): second independent round — four blockers + canary telemetry 1. bodhi's attach auto-actions now gate on ownsRecovery, not just terminal: a reattach during waiting-retry started an uncounted CLOSED auto-redial racing the coordinator's own timer (the synthetic hold already gates injections; this gates the dial) 2. fatal backoff crossed clock domains — a wall-time deadline entered the monotonic reducer and never expired (with 1 fixed, the session would stay dead forever); the reducer now receives its own domain, the wall deadline stays for agent-state 3. server egress without heartbeat confirmation can no longer clear terminal: sent is not delivered — egress advancement now counts only as model activity (anchor), and only client-confirmed playback (chunksEnded) dispatches userVisibleResponse; the test that pinned the old fallback as intended now pins the opposite 4. tool settle resolves by STORED epoch (oldest first) instead of prefer-current, so a late old-generation completion can neither settle a reused current id nor advance the current anchor; every admitted successor generation prunes stranded entries, so they cannot veto recovery forever (the old test's observable was vacuous — toolOutcome produces no ledger row; anchors are asserted now) Canary telemetry majors: incumbent-closed outcome rows (closed vs forced), first-upstream-progress per restart, first-user-visible- response on the clearing delivery — the Phase 1 gates are computable from the recovery ledger now. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: repin bodhi to the sonichi merge SHA c726e1d (#35+#36 landed) The interim fork pin is gone. The merged main additionally hardened the surface beyond the reviewed branch heads: STT capture-generation fencing (a pre-recovery external transcript can no longer cross the boundary as fresh evidence) and turn.start now carries attemptEpoch alongside transportGeneration. Both additive; coordinator suite and full suite green against the merged build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(voice): state the consumers-own-structural-gating contract on MatrixFacts Review ask from @qingyun-wu on #3130: the safety of the narrowed `factsAvailable` rests on a contract that until now lived implicitly in the one existing consumer — voice-active-silence-watchdog's tick branch requires `ev.state === 'ACTIVE' && s.clientAttached` before the facts can matter. `factsAvailable` answers "did the data exist this tick?", not "is the session fit to act?". A second consumer reading it as safe-to-act would fire without the structural gate. Writing the contract on the family doc is the cheapest place to stop that, since it is where the field's meaning is defined. Comment-only: no behavior change, no type change. * fix(voice): a stopped recovery coordinator owns nothing ownsRecovery was `phase !== 'idle' || origin === 'active-silence'` and never consulted the stop() latch, so stopping mid-episode left it true. voice-agent stands the legacy F5 redial down on exactly that value, so a coordinator that can no longer dial would also be suppressing the path that could. Latent today: stop() has one caller, shutdown(), which ends in process.exit(0), so nothing outlives it. The safety therefore rests on "stop() implies the process is exiting" -- true, but stated nowhere and not enforced. A future caller disarming mid-session or restarting would get no dial from either side. Reported by sonichi on 0eea97b. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013BSd8tgpzEMbH2nURksZwG --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
src/discord-send.py— simple script to send messages to Discord channels or DMsdiscord-bridge.pyUsage
Test plan
🤖 Generated with Claude Code