Raven ScottandCursor cf23b25c39 feat: agentctl guild search and thread mesh helpers
Add searchResultsMin wait matcher, connectGuildMeshThread, and document
new headless scenarios.

Co-authored-by: Cursor <[email protected]>
2026-05-23 07:03:51 -04:00

pearcord-agentctl

Agent control plane for Pearcord — send the same IPC messages the UI sends, read view snapshots, and run scripted journeys against a live worker or an in-process headless session.

Mission

Enable autonomous agents (and developers) to drive Pearcord like a real user without clicking the Electron UI: register, create guilds, select channels, send messages, and assert on platform state.

Modes

Mode When How
Attached Desktop pear run with PEARCORD_AGENTCTL=1 TCP JSON-lines on 127.0.0.1:39482 (configurable)
Headless CI, smokes, agent scripts HeadlessSession + pearcord-platform + pearcord-ui-flow in-process

Both modes use pearcord-ui-flow dispatchUiMessage — the same handler as apps/pearcord/index.js.

AgentClient resolves RPC replies before treating { event: "state" | "sidecar" } push lines as events (fixes wait-event responses that include an event field).

Quick start

1. Start Pearcord with the control server

cd apps/pearcord
PEARCORD_AGENTCTL=1 pear run

Worker logs: agentctl listening {"host":"127.0.0.1","port":39482}.

2. Ping from another terminal

cd apps/pearcord
npm run agentctl:ping
# or
bare ../../modules/pearcord-agentctl/bin/agentctl.cjs ping

3. Drive IPC

npm run agentctl:ipc -- '{"type":"register","username":"agent1","displayName":"Agent One"}'
npm run agentctl:wait -- '{"onboarded":true}' --timeout 30000
npm run agentctl:ipc -- '{"type":"create-guild","name":"Agent Guild"}'
npm run agentctl:wait -- '{"mode":"guild"}'
npm run agentctl:view

4. Headless scenario (no GUI)

npm run agentctl:headless -- scenarios/agentctl-onboard.json

Protocol (TCP)

One JSON object per line (newline-delimited).

Requests

op Fields Description
ping Health check
ipc payload UI IPC object (type, …)
view light? Current platform view
wait match, timeoutMs?, light? Block until view matches partial spec
wait-event match, timeoutMs?, since? Block until sidecar error / log event
run scenario Run scenario JSON on attached worker
log-tail lines? Last N lines of pearcord.log
shutdown Close agentctl server

Responses

{ "id": 1, "ok": true, "view": { ... } }
{ "id": 2, "ok": false, "error": "wait timeout", "view": { ... } }

Events (server → client, no id)

{ "event": "state", "view": { ... } }
{ "event": "sidecar", "payload": { "type": "error", "message": "..." }, "at": 1734567890123 }

Set PEARCORD_AGENTCTL_TOKEN to require the same secret on every request when the server is enabled.

Environment

Variable Default Role
PEARCORD_AGENTCTL off 1 enables TCP server in worker
PEARCORD_AGENTCTL_HOST 127.0.0.1 Bind/connect host
PEARCORD_AGENTCTL_PORT 39482 TCP port
PEARCORD_STORAGE ~/.config/pearcord Log tail path
PEARCORD_LOG_LEVEL info Headless session logging

API

const { AgentServer, AgentClient, HeadlessSession, matchView } = require('pearcord-agentctl')

See docs/AGENTCTL.md for full reference, security notes, and scenario format.

Security

  • Binds localhost only — not exposed to the LAN.
  • Optional PEARCORD_AGENTCTL_TOKEN shared secret; still localhost-only.
  • Can execute any UI IPC type (moderation, guild admin, etc.) — same power as the logged-in user session.

Smoke tests

cd apps/pearcord
npm run test:agentctl-headless
npm run test:agentctl-slash
npm run test:agentctl-dm-voice
npm run test:agentctl-wait-event
npm run test:agentctl-server-wire
npm run test:agentctl-leave-voice-noop
npm run test:agentctl-settings-deep-link
npm run test:agentctl-settings-navigate
npm run test:agentctl-settings-guild-deep-link
npm run test:agentctl-mesh-channel-mention
npm run test:agentctl-composer-channel-mention

Bundled scenarios under apps/pearcord/scenarios/:

File Purpose
agentctl-onboard.json Register + onboard
agentctl-slash.json Slash command roundtrip
agentctl-dm.json DM open
agentctl-voice.json Join first voice channel
agentctl-deep-link.json pearcord://explore navigate
agentctl-composer-mention.json @everyone message
agentctl-leave-voice-noop.json leave-voice when disconnected (v0.8.257)
agentctl-settings-deep-link.json format-settings-deep-link IPC (v0.8.257)
agentctl-settings-navigate.json navigate-deep-linkpearcord://settings (v0.8.258)
agentctl-settings-guild-deep-link.json Register + guild + pearcord://settings?scope=guild&guildId=… (v0.8.259)
agentctl-composer-channel-mention.json Send @channel message (v0.8.258)
agentctl-guild-search.json Guild-wide update-search hit (v0.8.260)
agentctl-thread-mesh.json Host thread + reply; guest mesh sync (v0.8.260)

mesh.connectGuildMeshThread(scenarioPath) — host steps + invite + create-thread + reply; guest must receive threadReply token.

Scenario step extras (headless + attached run): joinFirstVoice, openDmPeer, messagesMin, messageContentIncludes, searchResultsMin on wait.

Dependencies

  • pearcord-platform, pearcord-ui-flow, pearcord-log
  • bare-tcp for attached mode
S
Description
Pearcord module: agent TCP/headless control plane for live app + CI scenarios
Readme
26 MiB
Languages
JavaScript 100%