feat: Phase F-6 — attachments + final docs (F-8 = no-op)

F-6 attachments:
- internal/attachments/ (~145 LOC + 8 tests): per-message Downloader
  that fetches Fabric attachment URLs into $TMPDIR/plexum-fabric/<msg-id>/
  with the agent's guild token, sanitizing filenames + message ids
  against path-traversal
- AppendFooter renders downloaded paths as a markdown footer
  ("Attachments:\n  - /tmp/... (mime, N bytes)\n")
- Agents access via the exec MCP tool (cat / file / etc.)

internal/inbound.Supervisor:
- new Attachments *attachments.Downloader field (nil → skip with warn)
- inbound.dispatch: when message has attachments, blocking-download +
  AppendFooter before emitting notification (so agent's first turn
  sees the paths)

cmd/plexum-fabric-channel-plugin:
- sup.Attachments = attachments.New("") wired at init

F-8 coalesce: no-op. openclaw plugin's coalesce buffered the
text→thinking→tool→text segments openclaw emits across multiple
deliver() calls per turn. Plexum's loop.Run returns ONE final assistant
text per turn (via extractFinalText), so coalescing isn't a concern.
The channel outbound posts a single message naturally.

F-5b presence-sync: deferred. The openclaw plugin pushes HarborForge
on-call status to Fabric's per-recipient presence so the backend can
busy-discard 'announce' deliveries. Plexum's state machine has
different semantics (idle/working/busy/offline) and is per-Plexum-agent
not per-user; mapping requires more design.

README updated with the phase status table + 16-tool list.

Tests: 8 new in internal/attachments (35 total in this repo).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
h z
2026-05-31 15:42:23 +01:00
parent ed3676ebc8
commit 6def33161b
5 changed files with 384 additions and 20 deletions

View File

@@ -1,21 +1,33 @@
# Plexum-fabric-channel-plugin
Native Plexum channel plugin connecting Plexum agents to **Fabric** guilds as
real channel members. Inspired by `Fabric.OpenclawPlugin`Plexum port,
delivered in phases.
real channel members. Port of `Fabric.OpenclawPlugin`delivered in phases.
## Status
**Phase F-1 (foundation) — current**: identity registry, Center auth, REST
client, plugin scaffold, channel-binding discovery. The `send` outbound tool
posts plain text to a Fabric channel as the bound agent.
| Phase | Scope | Status |
|-------|-------|--------|
| **F-1** | identity + REST + plugin scaffold + `send` outbound | ✅ |
| **F-2** | socket.io inbound + wakeup gate + token refresh + per-channel serial | ✅ |
| **F-3** | exponential reconnect backoff | ✅ |
| **F-4** | tool surface batch 1 (8 tools) | ✅ |
| **F-5** | command-sync (slash autocomplete) | ✅ |
| **F-6** | attachments → temp-dir + footer | ✅ |
| **F-7** | sub-discussion KV + create-*-channel + discussion-complete | ✅ |
| **F-8** | coalesce parity | ⏭ no-op — Plexum doesn't segment deliver |
| **F-5b** | presence-sync | ⏭ deferred — Plexum state machine ≠ HF semantics |
**Phase F-2 (deferred)**: socket.io inbound, wakeup-gated dispatch, token
refresh, per-channel serial queue.
### Agent-facing tools (16)
**Phase F-3+ (deferred)**: tools.ts port (~15 MCP tools — channel/canvas/
sub-discussion/etc.), presence sync, command sync, attachments, coalesce
parity.
- `send` — host-driven channel outbound (channel manager → plugin)
- `fabric-send-message`, `fabric-send-sys-msg` — post normal / system msgs
- `fabric-channel-list`, `fabric-guild-list` — discovery
- `fabric-message-history` — paginate by seq
- `fabric-channel`, `fabric-channel-set-purpose` — channel metadata
- `fabric-canvas` — get/share/update/remove canvas
- `create-{chat,work,report,discussion}-channel` — create new channels
- `discussion-complete` — summary + close
- `create-sub-discussion`, `close-sub-discussion` — sub-discussion lifecycle
## Install