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

@@ -24,6 +24,7 @@ import (
plugin "git.hangman-lab.top/hzhang/Plexum-sdk-go/plugin"
"git.hangman-lab.top/hzhang/Plexum-fabric-channel-plugin/internal/attachments"
"git.hangman-lab.top/hzhang/Plexum-fabric-channel-plugin/internal/config"
"git.hangman-lab.top/hzhang/Plexum-fabric-channel-plugin/internal/fabric"
"git.hangman-lab.top/hzhang/Plexum-fabric-channel-plugin/internal/identity"
@@ -212,6 +213,10 @@ func (p *fabricPlugin) Init(ctx context.Context, host plugin.HostAPI) error {
// the host log.
logger := slog.New(&hostLogHandler{host: host, level: slog.LevelInfo})
sup := inbound.New(p.client, p.tokens, p.bindings, notifier, logger)
// F-6: enable attachment downloads. Files land under
// $TMPDIR/plexum-fabric/<msg-id>/<filename>; agents access via
// the exec tool (cat / file).
sup.Attachments = attachments.New("")
go func() {
defer close(p.inboundDone)
if err := sup.Run(ctxBg); err != nil {