feat: Phase F-3 + F-4 — exp backoff + agent tool surface (batch 1)
F-3 refinements:
- internal/inbound: replace fixed 3s reconnect wait with exponential
backoff (1s → 60s, ×2, reset when prior session lasted >30s); proxy
for "healthy" vs "flapping" and avoids hot reconnect loops when the
server is sick
F-4 agent tool surface (port of openclaw plugin's tools.ts):
- internal/tools/tools.go (~370 LOC): Registry binds Deps {Client,
Tokens, Identities} and exposes 8 agent-facing tools:
fabric-send-message post a normal message to any channel
fabric-send-sys-msg post a kind=sys message (bypasses turn engine)
fabric-channel-list list channels visible in a guild
fabric-guild-list list guilds the agent is in
fabric-message-history paginate channel messages by seq
fabric-channel-set-purpose PATCH the channel's purpose
fabric-channel fetch metadata + members for one channel
fabric-canvas get/share/update/remove channel canvas
- internal/tools/contracts.go: static ToolContract list — kept in sync
with install.sh's manifest emitter
- Every agent-scoped tool requires agent_id in input args (Plexum SDK
doesn't propagate calling agent id through CallTool today)
- guild_node_id defaults to agent's first guild for fabric-send-message
internal/fabric/client.go: new REST methods needed by tools —
PostSystemMessage, CreateChannel, CloseChannel, JoinChannel,
LeaveChannel, SetChannelPurpose, GetCanvas, ShareCanvas, UpdateCanvas,
RemoveCanvas, SyncCommands.
cmd/plexum-fabric-channel-plugin/main.go:
- Manifest declares the tool surface via tools.New(...).Contracts()
- CallTool dispatches "send" to handleSend (outbound for channel
manager), everything else to tools.Registry.Handler(name)
scripts/install.sh:
- Manifest tools[] now lists all 9 tools with schemas — matches what
internal/tools/contracts.go advertises
Live verified against running Fabric stack:
$ plexum plugin-call fabric-guild-list '{"agent_id":"fabrictester"}'
→ "guilds for agent fabrictester (1): test-guild2 @ http://localhost:7003"
$ plexum plugin-call fabric-channel-list '{...,"guild_node_id":"test-guild2"}'
→ 2 channels listed
$ plexum plugin-call fabric-message-history '{...,"limit":5}'
→ 5 messages with timestamps + authors
F-5+ deferred:
- create-{chat,work,report,discussion}-channel (batch 2)
- sub-discussion family (state store + 3 tools)
- presence-sync + command-sync
- attachments
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -69,6 +69,29 @@ for f in sorted(os.listdir('${CHANNELS_DIR}')):
|
||||
print(json.dumps(out))
|
||||
")
|
||||
fi
|
||||
# Tools list: kept in sync with internal/tools/contracts.go. Each
|
||||
# entry mirrors a ToolContract in allContracts.
|
||||
TOOLS_JSON='[
|
||||
{"name":"send","description":"Outbound channel reply — host calls this after an inbound dispatch.",
|
||||
"inputSchema":{"type":"object","properties":{"channel_name":{"type":"string"},"session_id":{"type":"string"},"message":{"type":"string"}},"required":["channel_name","message"]}},
|
||||
{"name":"fabric-send-message","description":"Post a normal message to a Fabric channel as the bound agent.",
|
||||
"inputSchema":{"type":"object","properties":{"agent_id":{"type":"string"},"guild_node_id":{"type":"string"},"channel_id":{"type":"string"},"content":{"type":"string"}},"required":["agent_id","channel_id","content"]}},
|
||||
{"name":"fabric-send-sys-msg","description":"Post a system-kind message (not subject to turn engine / wakeup).",
|
||||
"inputSchema":{"type":"object","properties":{"agent_id":{"type":"string"},"guild_node_id":{"type":"string"},"channel_id":{"type":"string"},"content":{"type":"string"}},"required":["agent_id","guild_node_id","channel_id","content"]}},
|
||||
{"name":"fabric-channel-list","description":"List channels visible to the agent in a guild.",
|
||||
"inputSchema":{"type":"object","properties":{"agent_id":{"type":"string"},"guild_node_id":{"type":"string"}},"required":["agent_id","guild_node_id"]}},
|
||||
{"name":"fabric-guild-list","description":"List the guilds the agent belongs to.",
|
||||
"inputSchema":{"type":"object","properties":{"agent_id":{"type":"string"}},"required":["agent_id"]}},
|
||||
{"name":"fabric-message-history","description":"Paginate channel messages by seq.",
|
||||
"inputSchema":{"type":"object","properties":{"agent_id":{"type":"string"},"guild_node_id":{"type":"string"},"channel_id":{"type":"string"},"seq_from":{"type":"integer"},"seq_to":{"type":"integer"},"limit":{"type":"integer"}},"required":["agent_id","guild_node_id","channel_id"]}},
|
||||
{"name":"fabric-channel-set-purpose","description":"Update the channel'"'"'s free-form purpose text.",
|
||||
"inputSchema":{"type":"object","properties":{"agent_id":{"type":"string"},"guild_node_id":{"type":"string"},"channel_id":{"type":"string"},"purpose":{"type":"string"}},"required":["agent_id","guild_node_id","channel_id","purpose"]}},
|
||||
{"name":"fabric-channel","description":"Fetch metadata + member list for a specific channel.",
|
||||
"inputSchema":{"type":"object","properties":{"agent_id":{"type":"string"},"guild_node_id":{"type":"string"},"channel_id":{"type":"string"}},"required":["agent_id","guild_node_id","channel_id"]}},
|
||||
{"name":"fabric-canvas","description":"Read or write the channel canvas (op: get|share|update|remove).",
|
||||
"inputSchema":{"type":"object","properties":{"agent_id":{"type":"string"},"guild_node_id":{"type":"string"},"channel_id":{"type":"string"},"op":{"type":"string","enum":["get","share","update","remove"]},"title":{"type":"string"},"format":{"type":"string","enum":["md","html","text"]},"source":{"type":"string"}},"required":["agent_id","guild_node_id","channel_id","op"]}}
|
||||
]'
|
||||
|
||||
cat > "${MANIFEST_PATH}" <<EOF
|
||||
{
|
||||
"name": "plexum-fabric-channel",
|
||||
@@ -77,21 +100,7 @@ print(json.dumps(out))
|
||||
"executable": "plexum-fabric-channel-plugin",
|
||||
"contracts": {
|
||||
"channels": ${CHANNELS_JSON},
|
||||
"tools": [
|
||||
{
|
||||
"name": "send",
|
||||
"description": "Post a plain-text message to the bound Fabric channel as the agent user.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"channel_name": {"type": "string"},
|
||||
"session_id": {"type": "string"},
|
||||
"message": {"type": "string"}
|
||||
},
|
||||
"required": ["channel_name", "message"]
|
||||
}
|
||||
}
|
||||
]
|
||||
"tools": ${TOOLS_JSON}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
Reference in New Issue
Block a user