Fixes #756 (#763)
Some checks failed
ci/woodpecker/push/ci-image Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/publish Pipeline was canceled

This commit was merged in pull request #763.
This commit is contained in:
2026-07-14 20:26:15 +00:00
parent c32d85a337
commit ba13c08890
30 changed files with 2914 additions and 365 deletions

View File

@@ -221,6 +221,69 @@ Delivery uses five gated milestones: runtime contracts/security; Pi service/stat
---
## Official Channel Plugin Workstream (#756)
### Problem and Objective
The Discord plugin currently couples Discord event handling, gateway bridging, and reply routing in one implementation and activates only on mentions. Mosaic needs an official channel adapter that behaves the same no matter whether the bound logical agent currently runs through Claude, Codex, Pi, OpenCode, or a future harness. The Discord connection and conversation address must remain stable while the gateway changes the runtime provider behind that logical session.
The objective is to make Discord the first implementation of a transport-neutral official channel contract, with explicit authorization and deterministic channel/thread routing that future Matrix, Slack, and other adapters can share.
### Scope
#### In Scope
1. `CHN-001`: Transport-neutral channel adapter, route, message, attachment, authorization-principal, response-target, and health contracts in `@mosaicstack/types`, including trusted per-binding logical-agent configuration selection.
2. `CHN-002`: Stable channel conversation addresses based on logical agent plus channel/thread identity; harness, model, and runtime-provider IDs are forbidden from channel session keys.
3. `DSC-001`: An authorized untagged message in a configured agent-bound channel routes to the agent and receives its response in that channel.
4. `DSC-002`: A bot mention in a configured parent channel creates a Discord thread, or reuses the thread already attached to that same native message; the mentioned turn and subsequent thread turns route and respond in that thread.
5. `DSC-003`: A message already inside an authorized thread inherits authorization from its configured parent and never attempts a nested thread.
6. `DSC-004`: Guild, parent channel, user, pairing, and role authorization remains default-deny before thread creation or gateway dispatch.
7. `DSC-005`: Discord service authentication, HMAC envelope integrity, replay protection, attachments, approvals, response chunking, and correlation behavior remain intact.
8. `DSC-006`: The Discord adapter exposes lifecycle and health behavior through the shared channel contract without importing a harness SDK.
#### Out of Scope
1. The logical-agent lease, fencing epoch, execution grant, checkpoint, or cross-harness takeover implementation tracked by #754/#755.
2. Dynamic Discord authorization administration in the web UI.
3. Multi-guild tenant isolation, DMs, slash commands, voice, reactions, or production bot deployment.
4. Implementing Matrix or Slack adapters in this slice.
### Non-Functional Requirements
1. **Security:** no thread or dispatch side effect occurs until guild, parent channel, user, pairing, role, and bounded per-user/channel rate checks pass; attachment metadata is shape- and size-bounded; credentials never enter source, messages, session keys, or logs.
2. **Portability:** channel contracts and stable conversation IDs contain no Claude, Codex, Pi, OpenCode, model, process, or provider-specific field; each configuration-owned binding selects its trusted logical agent without changing the channel identity.
3. **Reliability:** repeated messages for one channel/thread resolve the same conversation handle; reconnecting the adapter does not require a harness-specific rebinding.
4. **Maintainability:** Discord-specific API translation stays in the Discord package; gateway and future adapters depend on transport-neutral contracts.
5. **Observability:** thread creation or routing failure is reported without message content or credential material.
### Acceptance Criteria
1. `AC-CHN-01`: Contract and behavior tests prove the plugin route contains only logical agent plus channel/thread identity and produces the same stable conversation handle regardless of underlying harness selection.
2. `AC-CHN-02`: A mentioned authorized parent-channel message creates a thread (or reuses its already-attached thread), dispatches to the thread conversation, and targets the response to that thread.
3. `AC-CHN-03`: An untagged authorized parent-channel message dispatches to the parent conversation and targets the response to the parent channel.
4. `AC-CHN-04`: Untagged follow-ups inside an authorized thread dispatch and respond in that same thread without creating a nested thread.
5. `AC-CHN-05`: Unauthorized guilds, channels, users, unpaired users, insufficient roles, and rate-limited senders produce no thread and no gateway dispatch.
6. `AC-CHN-06`: Shared channel contracts are exported from `@mosaicstack/types`, Discord implements the lifecycle/health seam, and no harness SDK is imported by the plugin.
7. `AC-CHN-07`: Focused routing/auth tests, package tests, typecheck, lint, formatting, coverage, independent code/security review, and terminal-green CI pass.
### Constraints, Risks, and Assumptions
- Dependency: Mosaic gateway remains the policy, durable-session, audit, and runtime-provider boundary.
- Constraint: This work must not modify orchestrator-to-Pi migration or #754/#755 lease/fencing files.
- Risk: accepting untagged messages could create noisy or unintended agent input. Mitigation: only explicitly configured channels and paired, role-authorized users are accepted, with bounded per-user/channel message and thread rates.
- Risk: Discord thread creation can fail because of channel permissions, archived state, or API rate limits. Mitigation: fail without dispatching a turn whose response destination cannot be honored, and emit sanitized diagnostics.
- `ASSUMPTION:` Configured channels are dedicated agent interaction surfaces, so authorized untagged human messages are intentional agent input.
- `ASSUMPTION:` Mention in a parent channel selects a public thread; messages already in a thread remain there because Discord has no nested threads.
- `ASSUMPTION:` One Discord bot may serve multiple configuration-owned logical-agent bindings.
- `ASSUMPTION:` Static allowlists and paired-user roles are the authorization administration surface for this slice.
### Testing and Delivery Intent
Use TDD for remote-ingress routing and permission boundaries. Required evidence includes parent-channel mention, untagged parent message, existing-thread follow-up, existing-thread mention, thread reuse, unauthorized side-effect denial, stable harness-neutral conversation identity, adapter health, and regression coverage for signed envelopes and approvals. Deliver through issue #756, a reviewed squash PR to `main`, terminal-green CI, and issue closure.
---
## Architecture
### High-Level System Diagram
@@ -575,7 +638,8 @@ Discord remote control channel. Architecture inspired by OpenClaw (https://githu
- Single-guild binding only (v0.1.0) — prevents data leaks between servers
- Receives Discord messages, dispatches through gateway routing
- Streams agent responses back to Discord (chunked for 2000-char limit)
- Supports mention-based activation, thread management for multi-turn
- Routes authorized untagged messages in-channel; mentions create threads (or reuse the same message's attached thread) for multi-turn topics
- Uses stable logical-agent/channel conversation addresses independent of the active harness/provider
- Bot pairing and permission management (Discord user → Mosaic user mapping)
- DM support for private conversations
@@ -748,10 +812,12 @@ Telegram remote control channel.
### FR-9: Remote Control — Discord
- Discord bot that connects to the gateway
- Mention-based activation in channels
- Discord bot that connects to the gateway through a transport-neutral channel adapter contract
- Authorized messages in configured agent-bound channels work without a mention and respond in-channel
- Mentions in parent channels create threads, or reuse a thread already attached to that same native message, for multi-turn conversations
- Messages already in a thread remain there without requiring repeated mentions
- Stable logical-agent/channel conversation identity survives underlying harness/provider changes
- DM support for private conversations
- Thread creation for multi-turn conversations
- Chunked message delivery (Discord 2000-char limit)
- Bot configuration via web dashboard
- Permission management (which Discord users/roles can interact)
@@ -935,10 +1001,13 @@ Telegram remote control channel.
### AC-3: Discord Remote Control
- [ ] Discord bot connects and responds to mentions
- [ ] Messages route through gateway to agent pool
- [ ] Discord bot connects through the harness-neutral channel contract
- [ ] Authorized untagged channel messages route through the gateway and respond in-channel
- [ ] Mentioned parent-channel messages create a thread (or reuse their already-attached thread) and respond there
- [ ] Existing-thread follow-ups stay in the thread without repeated mentions
- [ ] Channel/session identity remains stable while the underlying harness/provider changes
- [ ] Responses stream back to Discord (chunked)
- [ ] Thread creation for multi-turn conversations
- [ ] Unauthorized guilds, channels, users, pairings, and roles create no thread and dispatch no message
### AC-4: Gateway Orchestration
@@ -1137,7 +1206,7 @@ All work is **alpha** (< 0.1.0) until Jason approves 0.1.0 beta release.
6. ASSUMPTION: **Log summarization uses Haiku-tier LLM by default, configurable.** Haiku is well-suited for summarization (compression, not generation — source material is in context). Guardrails: structured output via Zod schema (force extraction of decisions/tools/outcomes/errors as discrete fields), chunked per-session processing (no bulk conflation), extraction-focused prompts. Raw logs stay in hot tier (7 days) as safety net. Users can override the summarization model via routing engine config if they want higher fidelity. Rationale: Haiku is 10-20x cheaper than Sonnet; log summarization runs on schedule against large volumes where cost matters.
7. ASSUMPTION: **Discord plugin starts minimal and single-guild only**DM support, mention-based channel activation, thread management, chunked responses. Single guild binding to prevent data leaks between servers. Advanced features (voice, components, slash commands, multi-guild) are post-beta. Rationale: Proven pattern from OpenClaw; ship core interaction first; data isolation is non-negotiable.
7. ASSUMPTION: **Discord plugin starts minimal and single-guild only**explicitly configured agent-bound channels accept authorized untagged messages in-channel, while mentions create threads or reuse a thread already attached to that same native message; responses are chunked. Single guild binding prevents data leaks between servers. DM support, voice, components, slash commands, and multi-guild operation are post-beta. Rationale: Ship the requested core interaction model while preserving default-deny data isolation.
8. ASSUMPTION: **Telegram plugin is lower priority than Discord** and may ship as v0.0.7 or later if Discord takes longer than expected. Rationale: Jason indicated Discord as the high-priority remote channel.