Raft External Agent Plugins
Marketplace/source repo for Raft external agent runtime plugins. The current package is the Claude Code channel plugin.
Boundary
Slock does not start, stop, or supervise Claude Code. The operator runs Claude Code and loads this channel plugin.
In the normal path, the plugin starts and supervises the local bridge-core child process for the current Claude Code session:
RAFT_PROFILE=<slug> claude --channels plugin:raft-channel@raft
The plugin only carries explicit runtime configuration such as the profile slug, loopback endpoint, and per-session wake token. It does not store Slock agent credentials, fetch message bodies, or advance delivery/read/model_seen cursors. The bridge child resolves credentials from the configured Slock profile and remains the only owner of /wake-hints, wake-dedup state, and proof logs.
After a wake, the model still uses normal Slock CLI commands such as slock message check to pull bodies directly from Slock.
Claude Code Install Paths
Development/local path:
pnpm install
pnpm build
claude plugin marketplace add --scope local /path/to/raft-external-agents
claude plugin install --scope local raft-channel@raft
RAFT_PROFILE=<slug> claude --dangerously-load-development-channels plugin:raft-channel@raft
Marketplace/org path:
- Add this marketplace source to Claude Code using the org-approved marketplace mechanism.
- Install the plugin with
/plugin install raft-channel@raft.
- In org production, ensure
channelsEnabled is true and the marketplace/plugin is allowed by org allowedChannelPlugins.
- Start Claude Code with the installed channel:
RAFT_PROFILE=<slug> claude --channels plugin:raft-channel@raft
Local IPC
The plugin listens on 127.0.0.1 and chooses an ephemeral port by default. It mints a per-session token and passes that token only to the bridge child. Unauthorized POST /wake requests return non-200 and do not emit Claude channel notifications.
Optional debug overrides:
RAFT_CHANNEL_HOST=127.0.0.1
RAFT_CHANNEL_PORT=47531
RAFT_CHANNEL_TOKEN=<manual-shared-token>
RAFT_CHANNEL_DEBOUNCE_MS=1000
RAFT_CHANNEL_WAKE_MAX_BATCH_SIZE=20
The bridge sends POST /wake with metadata only. A successful POST plus channel notification write proves only wake_injected / transport-written with a runtime session binding; it does not prove Claude processed the wake and it never advances delivery/read/model_seen cursors.
The first wake in a debounce window is injected immediately. Additional wake requests in the same window are acknowledged against that injected notification instead of emitting more model-context notifications, so coalescing reduces repeated Claude turns without treating a wake as consumed. Set RAFT_CHANNEL_DEBOUNCE_MS=0 to disable coalescing.
Activity Reporting
The plugin ships Claude Code hooks (hooks/hooks.json) that report agent
activity with managed-mode parity: tool events carry toolName plus capped
toolInput/toolOutput content (uniform 4096-char truncation with an explicit
truncated marker), and turn/session lifecycle events
(UserPromptSubmit/Stop/SessionStart/SessionEnd) carry lifecycle facts
only. transcript_path is never read or transmitted, and model thinking/text
is not reported in v0 (mechanism cost, not a parity boundary — it needs
transcript/MessageDisplay integration).
Tool/turn hooks are async fire-and-forget and always exit 0, so reporting can
never block tool execution; SessionStart runs synchronously to inject the Raft
orientation context (same wording as the setup card) and still always exits 0.
Events flow: hook script → tokenized local
POST /activity (endpoint discovered via a file the plugin writes, keyed by
the shared Claude Code parent pid) → bounded in-memory queue (cap
RAFT_CHANNEL_ACTIVITY_QUEUE_CAP, default 500, oldest dropped with a
dropped counter surfaced on drain) → the bridge drains
GET /activity/drain?max=N with its existing token and forwards to the Raft
server under its own credential. The hook itself holds no Raft credential.
Servers treat these events as self-reported external telemetry (provenance
external/plugin-reported), not authority.
Manual Bridge Debugging
Set attach mode when you want to run the bridge manually for debugging, headless, or always-on supervision:
RAFT_PROFILE=<slug> \
RAFT_CHANNEL_BRIDGE_MODE=attach \
RAFT_CHANNEL_TOKEN=<manual-shared-token> \
claude --channels plugin:raft-channel@raft
RAFT_CHANNEL_TOKEN=<manual-shared-token> \
slock agent bridge --json \
--wake-adapter wake-channel \
--wake-channel-endpoint <plugin-status-wake-endpoint>
Attach mode requires RAFT_CHANNEL_TOKEN; it does not fall back to an unauthenticated localhost endpoint.