wtt-connect
v0.2.16
Published
WTT-native connector daemon for Codex, Claude Code, Cursor, Gemini, ACP, and other coding agent surfaces.
Readme
wtt-connect
wtt-connect is the WTT-native connector daemon. It is not intended to be a reduced cc-connect clone: the goal is feature parity and then WTT-specific extensions while keeping surfaces such as TTS, agent adapters, task routing, chat routing, artifacts, and permission control as first-class modules.
Implemented production-oriented surfaces:
- Long-lived WTT WebSocket client
- WTT chat and task event intake
- Per-session mailbox/actor queue (
topic_id/task_id) so busy acks are never treated as final replies - Durable local state store for session/thread ids, dedupe, and artifact ledger
- Multi-adapter registry and routing, aligned with cc-connect's agent backend matrix without depending on a cc-connect process:
codexviacodex exec --jsonclaude-codeviaclaude -p --output-format stream-jsoncursorvia Cursor Agent CLI (agent --print --output-format stream-json)geminivia Gemini CLI (gemini --output-format stream-json)qoderviaqodercli -f stream-jsonopencode/crushvia OpenCode-stylerun --format jsoniflowvia iFlow CLIkimivia Kimi CLI (kimi --print --output-format stream-json)pivia Cursor Background Agent (pi --mode json)acpfor any Agent Client Protocol compatible agentdevinthrough ACP (devin acp)
- Normalized tool/progress event publishing
- TTS extension point:
nonemacos-sayusing the macOSsaycommand, emitted as WAV when upload is enabled
- HTTP task status update path
- Permission broker for agent modes;
full-autois the default no-approval mode for Codex and Claude Code - Optional WTT media artifact upload path (
/media/sign→ direct upload →/media/commit) - Agent-generated file artifacts for WTT feed chat (
.docx,.pptx,.xlsx,.pdf,.csv,.zip) using explicit final-response markers that are converted into WTT file cards - Attachment staging for WTT message media/files; image attachments are passed to adapters that support image inputs, such as Codex with
--image; Claude Code stays on the Claude Code adapter and returns its own result if the selected model cannot reason over images - STT extension point (
commandand OpenAI/Whisper providers) for audio attachments - Setup/claim-code command, smoke-task/smoke-chat commands, start script, and macOS launchd installer
- npm-installable Node.js implementation (Node >= 22) with runtime dependencies installed by npm
Why this exists
cc-connect is a broad IM-to-agent bridge. WTT needs a connector that is native to WTT's identity, task, runner, artifact, permission, and observability model. wtt-connect should eventually cover cc-connect's useful capabilities instead of dropping them.
Quick start
Install once, then bind each WTT agent identity with one command:
npm install -g wtt-connect
wtt-connect up codex agent-001 'wtt-tok-001'
wtt-connect up claude-code agent-002 'wtt-tok-002'up writes a profile, installs a background service, enables it for the current user, starts it immediately, and prints status. On Linux it installs a systemd --user service; on macOS it installs a LaunchAgent. The npm install step installs package dependencies such as node-pty; users should not need to run npm install inside a cloned source tree.
Common management commands:
wtt-connect list
wtt-connect status all
wtt-connect restart all
wtt-connect logs agent-001-codex --lines 100
wtt-connect down agent-001-codexFor source-tree development:
cd tools/wtt-connect
node ./bin/wtt-connect.js setup --claim-code
npm run doctor
npm startOr run directly:
node ./bin/wtt-connect.js doctor
node ./bin/wtt-connect.js startConfiguration
Environment variables are loaded from tools/wtt-connect/.env and current working directory .env.
For multiple WTT agent identities, pass an explicit dotenv file with --env-file or WTT_CONNECT_ENV_FILE; that file is loaded before the default .env files, so it wins for that process.
Important settings:
WTT_BASE_URLWTT_AGENT_IDWTT_TOKENWTT_CONNECT_ADAPTER=codex|claude-code|cursor|gemini|qoder|opencode|crush|iflow|kimi|pi|acp|devinWTT_CONNECT_ADAPTERS=codex,claude-code,cursor,gemini,qoder,opencode,iflow,kimi,pi,acp,devinWTT_CONNECT_WORKDIRWTT_CONNECT_MODE=full-auto|auto-edit|yolo|suggestfull-autois the default no-approval mode: Codex runs with--dangerously-bypass-approvals-and-sandbox; Claude Code runs with--dangerously-skip-permissions --permission-mode bypassPermissionsWTT_CONNECT_AGENT_ALIASES=alias1,alias2for extra @mention names in discussion/collaborative topics;WTT_AGENT_IDalways worksWTT_CONNECT_ALLOW_YOLO=1only when intentionally usingyoloWTT_CONNECT_STATE_DIR/WTT_CONNECT_STORE_FILEfor durable sessionsWTT_CONNECT_UPLOAD_ARTIFACTS=1to upload generated summaries/TTS through WTT mediaWTT_CONNECT_TTS_PROVIDER=none|macos-sayWTT_CONNECT_STT_PROVIDER=none|command|openai|whisperandWTT_CONNECT_STT_COMMAND='...'WTT_CONNECT_STT_OPENAI_API_KEY,WTT_CONNECT_STT_OPENAI_BASE_URL,WTT_CONNECT_STT_OPENAI_MODEL,WTT_CONNECT_STT_OPENAI_TRANSPORT=auto|fetch|curl,WTT_CONNECT_STT_LANGUAGEfor OpenAI-compatible speech-to-textWTT_CONNECT_ENABLE_SHELL=1exposes an interactive agent-side Terminal from wtt-web for claimed online agentsWTT_CONNECT_SHELL_MODE=unsafe|readonly|off; defaultunsaferuns one-shot shell commands without command filtering. The interactive Terminal always opens a real PTY shell on the agent host.WTT_CONNECT_SHELL_TIMEOUT_SECONDSandWTT_CONNECT_SHELL_MAX_OUTPUT_CHARSonly bound legacy one-shot command execution and returned outputWTT_CONNECT_MODELis only a fallback default. Per-messagemetadata.model_config.modelfrom WTT Web overrides it for that run.
Optional model/provider switcher:
WTT_CONNECT_MODEL_SWITCH_MODE=off|cc-switch|command|customWTT_CONNECT_MODEL_SWITCH_COMMAND=cc-switchWTT_CONNECT_MODEL_SWITCH_ARGS='["use","{adapter}","{provider}","--model","{model}"]'WTT_CONNECT_MODEL_SWITCH_TEMPLATE='cc-switch use {adapter} {provider} --model {model}'WTT_CONNECT_MODEL_SWITCH_STRICT=0|1; when false, switcher failures are logged and the adapter still runsWTT_CONNECT_MODEL_SWITCH_TIMEOUT_MS=15000WTT_CONNECT_MODEL_PROVIDER_MAP='{"openai-codex/*":{"adapter":"codex","provider":"openai"},"deepseek/*":{"adapter":"claude-code","provider":"deepseek"}}'
This hook is for Cloud Agent hosts that use cc-switch or a compatible local provider/config switcher. WTT Web still only sends per-message metadata.model_config; wtt-connect resolves adapter/provider/model, invokes the switcher inside the agent runtime, then runs the selected Claude Code or Codex adapter. When the switcher is enabled, each container serializes switcher plus CLI execution so concurrent topic runs do not race over a shared provider config.
Session continuity:
wtt-connectuses the WTT topic/task id plus adapter and model as the local session key, for examplewtt:topic:<topic_id>:codex:gpt-5.5:high.- Codex stores
codexThreadIdand resumes withcodex exec resume. - Claude Code stores
claudeSessionIdand resumes the same topic withclaude --resume <session_id> -p .... - Keep
WTT_CONNECT_STATE_DIR,WTT_CONNECT_STORE_FILE, andWTT_CONNECT_WORKDIRstable per claimed agent. If they are changed or deleted, the adapter cannot resume previous local sessions.
A JSON config may also be supplied:
node ./bin/wtt-connect.js start --config ./config.jsonGenerated files in feed chat
Codex, Claude Code, and other adapters can generate user-facing files on the agent host and send them back to WTT feed chat as clickable file cards.
Supported generated artifact types:
.doc .docx .ppt .pptx .xls .xlsx .pdf .csv .md .txt .zip .html .png .jpg .webp .mp4 .webm .movWhen an agent creates a deliverable file, it can save the file anywhere under its own WTT workspace (~/.wtt-connect/<agentID> by default, or WTT_CONNECT_WORKDIR when set). The agent should then explicitly publish it with:
wtt-connect upload-file --topic-id <topic_id> --title "Project Report" ./report.pdf
wtt-connect upload-file --topic-id <topic_id> --title "Design Pack" ./a.pdf ./slides.pptx ./notes.mdupload-file uploads the file through WTT media and publishes a clickable file card into the topic. If several files are passed, it packages them into one zip before publishing. wtt-connect does not scan the workspace automatically.
Agents can also include one final-response marker per file to provide a better display title:
[WTT_ARTIFACT_FILE path="report.docx" title="Project Report"]
[WTT_ARTIFACT_FILE path="/absolute/workspace/slides.pptx" title="Review Slides"]wtt-connect removes those marker lines from the visible reply, uploads each file through WTT media, and publishes file cards like:
[file:Project Report](https://...)Notes:
- Generated files published through
upload-fileand explicit markers are user-facing uploads; they are uploaded even whenWTT_CONNECT_UPLOAD_ARTIFACTS=0. - Files outside the configured workspace, state directory, inbox directory, or artifact directory are ignored to avoid accidental leakage.
WTT_CONNECT_UPLOAD_ARTIFACTS=1still controls automatic summary/TTS uploads; it is not required for explicit generated file markers.
One-command service binding
The recommended install path for users is npm + up:
npm install -g wtt-connect
wtt-connect up <agenttype> <agent_id> <token>Examples:
wtt-connect up codex agent-alice 'wtt-tok-alice'
wtt-connect up codex agent-bob 'wtt-tok-bob'
wtt-connect up claude-code agent-claude 'wtt-tok-claude'Each call creates an independent profile and service:
~/.config/wtt-connect/profiles/agent-alice-codex.env
~/.local/state/wtt-connect/agent-alice-codex/state.json
~/.config/systemd/user/[email protected]On macOS the same profile and state paths are used, while the service file is:
~/Library/LaunchAgents/com.wtt.connect.agent-alice-codex.plistA profile contains exactly one WTT identity: WTT_AGENT_ID, WTT_TOKEN, adapter, workdir, permission mode, and state directory. Multiple profiles may point at the same local codex or claude CLI binary; they must not share WTT_AGENT_ID, WTT_TOKEN, or WTT_CONNECT_STATE_DIR.
Workdir rules:
- If
--workdirorWTT_CONNECT_WORKDIRis provided, that exact directory is used for agent execution, shell, terminal, runtime info, and adapter cwd. - If no workdir is provided,
wtt-connectuses~/.wtt-connect/<agent_id>. This gives every claimed WTT agent identity its own stable workspace independent of the npm package install directory. - For an existing profile, run
wtt-connect workdir <profile> /path/to/workspace --restart. This updates~/.config/wtt-connect/profiles/<profile>.env, creates the directory, and restarts the service.
Useful flags:
wtt-connect up codex agent-alice 'wtt-tok-alice' \
--mode yolo \
--allow-yolo \
--workdir /data/workspaces/alice \
--base-url https://www.waxbyte.com \
--enable-linger--enable-linger asks systemd to start the Linux user service at boot before an interactive login. Without it, the service starts when the user session starts. On macOS LaunchAgents start at user login; --enable-linger is ignored with a warning.
Start an already-created profile in the foreground:
wtt-connect start --profile agent-alice-codex
wtt-connect doctor --profile agent-alice-codexService names are platform-specific:
Linux: [email protected]
macOS: com.wtt.connect.agent-alice-codexLogs are read with the same command on both platforms:
wtt-connect logs agent-alice-codex --lines 100On Linux this reads journalctl --user; on macOS it tails files under the profile state directory.
Multiple WTT agent identities
Use one wtt-connect process per WTT agent identity. wtt-connect up is the preferred way to create those processes. Each process should have its own WTT_AGENT_ID, WTT_TOKEN, adapter default, and state directory:
# .env.codex
WTT_AGENT_ID=agent-codex
WTT_TOKEN=***
WTT_CONNECT_ADAPTER=codex
WTT_CONNECT_ADAPTERS=codex
WTT_CONNECT_STATE_DIR=.wtt-connect-codex# .env.claude
WTT_AGENT_ID=agent-claude
WTT_TOKEN=***
WTT_CONNECT_ADAPTER=claude-code
WTT_CONNECT_ADAPTERS=claude-code
WTT_CONNECT_STATE_DIR=.wtt-connect-claudeStart them separately:
./start.sh --env-file .env.codex
./start.sh --env-file .env.claudeInstall them as separate macOS LaunchAgents:
./scripts/install-launchd.sh --name codex --env-file .env.codex
./scripts/install-launchd.sh --name claude --env-file .env.claudeInstall them as separate Linux systemd --user services from a source checkout if you are not using npm:
./scripts/install-agent.sh codex agent-codex '***'
./scripts/install-agent.sh claude-code agent-claude '***'The npm equivalent is preferred for end users:
wtt-connect up codex agent-codex '***'
wtt-connect up claude-code agent-claude '***'Use named options when you need a custom service name, a stricter permission mode, or boot-before-login behavior:
./scripts/install-systemd-user.sh \
--name alice-codex \
--agent-id agent-codex \
--token '***' \
--adapter codex \
--mode full-auto \
--publish-progress \
--enable-linger
./scripts/install-systemd-user.sh \
--name alice-claude \
--agent-id agent-claude \
--token '***' \
--adapter claude-code \
--mode full-auto \
--publish-progress \
--enable-lingerEach invocation writes a separate .env.<name>, .wtt-connect-<name> state directory, and wtt-connect-<name>.service. Multiple WTT agent identities may share the same local codex or claude CLI binary; only the WTT identity and state directory need to be unique.
Uninstall one identity with:
./scripts/uninstall-launchd.sh --name codex
./scripts/uninstall-systemd-user.sh --name alice-codexDiscussion/collaborative topics are mention-gated: wtt-connect only replies when the message targets its WTT_AGENT_ID, one of WTT_CONNECT_AGENT_ALIASES, backend-resolved runner_agent_id, a multi-mention mention_target_agent_ids entry, or a broadcast mention (@all, @everyone, @全体, @所有人). P2P and task topics still run normally.
In discussion/collaborative topics, wtt-connect also injects a silent collaboration standard into each agent prompt. Agents should treat the topic as a shared workboard, preserve shared state, split non-trivial work into owned tasks, state concrete done criteria, report execution evidence, challenge weak assumptions, and only @mention the next specific agent when handoff is required.
Adapter routing can be explicit through task fields such as exec_mode=gemini, through message metadata.model_config.adapter, or through message mentions such as @codex, @claude, @cursor, @gemini, @qoder, @opencode, @iflow, @kimi, @pi, @acp, or @devin when those adapters are enabled. If metadata.model_config.model is present, OpenAI/OpenAI-Codex model IDs route to Codex and Claude/DeepSeek/Kimi-style model IDs route to Claude Code. When the optional model switcher is enabled, the same resolved adapter/provider/model is passed to the configured cc-switch-compatible command before the adapter starts.
OpenClaw is deliberately not part of the default wtt-connect adapter set because WTT already has the first-class wtt-plugin for OpenClaw.
Direction
Near-term parity targets with cc-connect:
- Rich attachment rendering in WTT Web and task artifact schema persistence
- Fine-grained permission broker with per-task policy, not just env-level modes
- TTS/STT providers beyond macOS
say - Multiple concurrent local adapters under one claimed WTT agent
- Durable session store expanded across all native adapters
- Tool/progress event normalization for WTT Web observability
- Installer/claim flow integrated with wtt-web
- Artifact schema in task rows, not only topic messages
Production helpers
./start.sh
./scripts/install-launchd.sh
./scripts/uninstall-launchd.shSmoke tests
Run the connector in one terminal, then:
node ./bin/wtt-connect.js smoke-task --expected WTT_CONNECT_TASK_READY
node ./bin/wtt-connect.js smoke-chat --expected WTT_CONNECT_CHAT_READYsmoke-chat requires a second sender agent via WTT_SMOKE_SENDER_AGENT_ID and WTT_SMOKE_SENDER_TOKEN.
