Commit Graph

12 Commits

Author SHA1 Message Date
fac6debfa5 feat(plugin): fabric-channel tool (members / join / leave)
One tool, three actions backed by FabricClient channelMembers (GET
/channels/:id/members -> [{userId,bypass}]), joinChannel, and new
leaveChannel (POST /channels/:id/leave).

Verified: client-level smoke against the running guild — members
initial=[tester], after join echo2 present, after leave echo2 gone.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 15:33:47 +01:00
aaabb0ddb0 feat(plugin): fabric-canvas tool; fabric-register env=AGENT_ID only
- bin/fabric-register.mjs: only AGENT_ID is read from the environment;
  --api-key is flag-only (no FABRIC_API_KEY); dropped FABRIC_CENTER_API_BASE
  / FABRIC_IDENTITY_FILE / OPENCLAW_PATH env fallbacks (flags + sensible
  defaults; --center still falls back to openclaw.json).
- New fabric-canvas tool (one tool, four actions): read / share / update /
  close the channel's single pinned canvas. Backed by FabricClient
  get/share/update/removeCanvas (GET/PUT/PATCH/DELETE; empty 2xx body ->
  null). update/close are sharer-only server-side.
- README updated.

Verified: client-level smoke against the running guild —
read(empty→null) → share(v1) → read → update(v2) → close(→null) all pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 15:28:13 +01:00
26c12533fb refactor(plugin): fabric-register is a script, not a tool
Binding an agent's Fabric API key was an OpenClaw tool; make it a
self-contained Node script installed to ~/.openclaw/bin/fabric-register
instead.

- bin/fabric-register.mjs: no plugin deps; AGENT_ID env wins, else
  --agent-id required; --api-key validated via POST /auth/agent/login;
  on success upserts ~/.openclaw/fabric-identity.json (format matches
  IdentityRegistry). Flags/env for center, identity-file, openclaw-path.
- install.mjs: copy the script to ~/.openclaw/bin (chmod 0755) on
  install, remove on uninstall; Next-steps updated.
- tools.ts: drop the fabric-register tool; ctxGuild error now points to
  the script / static accounts config.
- README updated.

Verified: missing-id -> exit 2; --agent-id and AGENT_ID both bind and
write a valid identity file; bad key -> 401, no write.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 13:12:48 +01:00
892db9f9be feat(plugin): record non-wakeup messages as session history (no model)
Previously a non-wakeup message returned immediately and was fully
discarded — the agent kept zero record of it, so when later woken in a
discuss/work channel it replied without the conversation context.

Now non-wakeup messages are ingested into the agent's OpenClaw session
via recordInboundSession (createIfMissing) WITHOUT dispatch: the real
model is not invoked and nothing is sent back to Fabric. This is
correct for the turn engine — only the woken speaker emits a normal
message or /no-reply; non-woken agents stay silent — while still
giving the agent full channel context whenever it IS woken.

Verified live: report-channel (all recipients wakeup=false) message
logs 'recorded (no wakeup, history only)' with 0 dispatch/deliver/
posted; wakeup path unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 10:42:36 +01:00
fc7efd0227 fix(plugin): force automatic source-reply delivery (fixes no-reply)
OpenClaw defaults group-chat replies to sourceReplyDeliveryMode
'message_tool_only', which suppresses auto-delivery of the agent's
text reply (it expects the agent to call a message tool). With
ChatType 'group', the Fabric plugin's deliver callback was therefore
NEVER invoked — the agent ran but no reply ever returned to Fabric.

Fabric already gates *when* an agent speaks via the per-recipient
wakeup flag, so once a turn is dispatched the reply must always flow
back. Pass replyOptions.sourceReplyDeliveryMode='automatic' so
OpenClaw delivers the agent's reply through  regardless of
the group default (source-reply-delivery-mode honors a truthy
requested mode).

Verified live end-to-end: human posts -> wakeup -> agent runs ->
'fabric: deliver' + 'fabric: posted reply' -> agent message appears
in the Fabric channel.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 10:02:47 +01:00
d79a04b8a3 fix(plugin): refresh guild token per dispatch (fixes attachment 401)
Guild access tokens are short-lived (~15 min); the inbound socket
survives via socket.io reconnect but the token captured at connect
time goes stale, so attachment downloads (and reply posts) start
401ing on long-lived agents. Re-login with the agent's Fabric API key
on a short TTL and use the fresh token for fetch + post.

Verified live: 'fabric: fetched 1 attachment(s)' now succeeds where it
previously logged 'attachment fetch 401'.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 00:03:31 +01:00
cc655ffcc3 fix(plugin): pass only local MediaPaths (drop SSRF-blocked MediaUrls)
Live round-trip test showed openclaw's SSRF guard blocking the
localhost guild file URL passed via MediaUrls. We already download the
bytes with the agent's guild token, so MediaUrls is redundant and
noisy — provide only local MediaPaths/MediaTypes. Verified: plugin
logs 'fetched N attachment(s)' and the SSRF WARN is gone.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 21:50:34 +01:00
42228e0a23 chore(plugin): rebuild dist (file delivery)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 20:17:28 +01:00
f59c693186 fix: never split replies into multiple messages (Fabric has no length limit)
Unlike Discord, Fabric has no message-length cap. Single-chunk chunker
(text -> [text]), textChunkLimit=MAX_SAFE_INTEGER, capabilities
blockStreaming=false, replyOptions.disableBlockStreaming=true -> every
agent reply delivered as exactly one Fabric message.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 18:30:25 +01:00
9cb262367e feat: working v1 — full Fabric<->openclaw round-trip verified
Real channel-turn dispatch (resolveAgentRoute + finalizeInboundContext +
dispatchInboundReplyWithBase), wakeup->drop/dispatch, messaging target
grammar (fabric:<id>) + outbound.sendText, tools use execute/parameters.
Verified live: human msg in Fabric -> wakeup -> openclaw agent runs ->
reply posted back into the Fabric channel as the agent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 18:24:35 +01:00
ece77ff2c7 feat: loadable openclaw channel plugin v1 (agent=account)
Rewritten against the real openclaw v2026.5.7 plugin SDK (generic
third-party channel path): createChannelPluginBase + createChatChannelPlugin
with required capabilities, minimal ChannelSetupAdapter, agent=account
config resolution, attached outbound -> Fabric POST, inbound socket per
account -> runtime.channel.turn (wakeup->admission). Compat notes mark
SDK-coupled seams for future openclaw upgrades. Verified: builds clean,
installs, 'openclaw channels list' -> Fabric installed/configured/enabled.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 17:54:32 +01:00
7fed6d07f6 build: PaddedCell-style install.mjs + SDK-aligned packaging
install.mjs (--install/--build-only/--uninstall/--openclaw-profile-path),
tsconfig outDir dist/fabric, package.json openclaw file dep + main.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 17:30:37 +01:00