ScuttleBot

scuttlebot / skills / openai-relay / hooks / README.md
Source Blame History 320 lines
50baf1a… lmata 1 # Codex Hook Primer
50baf1a… lmata 2
50baf1a… lmata 3 These hooks are the pre-tool fallback path for a live Codex tool loop.
50baf1a… lmata 4 Continuous IRC-to-terminal input plus outbound message and tool mirroring are
24a217e… lmata 5 handled by the compiled `cmd/codex-relay` broker, which now sits on the shared
24a217e… lmata 6 `pkg/sessionrelay` connector package.
50baf1a… lmata 7
50baf1a… lmata 8 If you need to add another runtime later, use
50baf1a… lmata 9 [`../../scuttlebot-relay/ADDING_AGENTS.md`](../../scuttlebot-relay/ADDING_AGENTS.md)
50baf1a… lmata 10 as the shared authoring contract.
50baf1a… lmata 11
50baf1a… lmata 12 Files in this directory:
50baf1a… lmata 13 - `scuttlebot-post.sh`
50baf1a… lmata 14 - `scuttlebot-check.sh`
50baf1a… lmata 15
50baf1a… lmata 16 Related launcher:
50baf1a… lmata 17 - `../../../cmd/codex-relay/main.go`
50baf1a… lmata 18 - `../scripts/codex-relay.sh`
50baf1a… lmata 19 - `../scripts/install-codex-relay.sh`
50baf1a… lmata 20
50baf1a… lmata 21 Source of truth:
50baf1a… lmata 22 - the repo copies in this directory and `../scripts/`
50baf1a… lmata 23 - not the installed copies under `~/.codex/` or `~/.local/bin/`
50baf1a… lmata 24
50baf1a… lmata 25 ## What they do
50baf1a… lmata 26
50baf1a… lmata 27 `scuttlebot-post.sh`
50baf1a… lmata 28 - runs after each tool call
50baf1a… lmata 29 - posts a one-line activity summary into a scuttlebot channel when Codex is not launched through `codex-relay`
50baf1a… lmata 30 - uses the session nick as the IRC/web bridge sender nick
50baf1a… lmata 31
50baf1a… lmata 32 `scuttlebot-check.sh`
50baf1a… lmata 33 - runs before the next action
50baf1a… lmata 34 - fetches recent channel messages from scuttlebot
50baf1a… lmata 35 - ignores bots and agent status nicks
50baf1a… lmata 36 - blocks only when a human explicitly mentions this session nick
50baf1a… lmata 37 - prints a JSON decision block that Codex can surface into the live tool loop
50baf1a… lmata 38
50baf1a… lmata 39 With the broker plus hooks together, you get the full control loop:
50baf1a… lmata 40 1. `cmd/codex-relay` posts `online`.
50baf1a… lmata 41 2. `cmd/codex-relay` mirrors assistant output and tool activity from the active session log.
50baf1a… lmata 42 3. The operator mentions the Codex session nick.
50baf1a… lmata 43 4. `cmd/codex-relay` injects that IRC message into the live terminal session immediately.
50baf1a… lmata 44 5. `scuttlebot-check.sh` still blocks before the next tool action if needed.
50baf1a… lmata 45
50baf1a… lmata 46 For immediate startup visibility and continuous IRC input injection, launch Codex
50baf1a… lmata 47 through the compiled broker installed as `~/.local/bin/codex-relay`. The repo
50baf1a… lmata 48 wrapper `../scripts/codex-relay.sh` is only a development convenience.
50baf1a… lmata 49
50baf1a… lmata 50 ## Default nick format
50baf1a… lmata 51
50baf1a… lmata 52 If `SCUTTLEBOT_NICK` is unset, the hooks derive a stable session nick:
50baf1a… lmata 53
50baf1a… lmata 54 ```text
50baf1a… lmata 55 codex-{basename of cwd}-{session id}
50baf1a… lmata 56 ```
50baf1a… lmata 57
50baf1a… lmata 58 Session id resolution order:
50baf1a… lmata 59 1. `SCUTTLEBOT_SESSION_ID`
50baf1a… lmata 60 2. `CODEX_SESSION_ID`
50baf1a… lmata 61 3. parent process id (`PPID`)
50baf1a… lmata 62
50baf1a… lmata 63 Examples:
50baf1a… lmata 64 - `codex-scuttlebot-8421`
50baf1a… lmata 65 - `codex-calliope-qa`
50baf1a… lmata 66
50baf1a… lmata 67 This is deliberate. Multiple Codex sessions in the same repo must not collide.
50baf1a… lmata 68
50baf1a… lmata 69 ## Required environment
50baf1a… lmata 70
50baf1a… lmata 71 Required:
50baf1a… lmata 72 - `SCUTTLEBOT_URL`
50baf1a… lmata 73 - `SCUTTLEBOT_TOKEN`
50baf1a… lmata 74 - `SCUTTLEBOT_CHANNEL`
50baf1a… lmata 75 - `curl` and `jq` available on `PATH`
50baf1a… lmata 76
50baf1a… lmata 77 Optional:
50baf1a… lmata 78 - `SCUTTLEBOT_NICK`
50baf1a… lmata 79 - `SCUTTLEBOT_SESSION_ID`
1d3caa2… lmata 80 - `SCUTTLEBOT_CHANNELS`
1d3caa2… lmata 81 - `SCUTTLEBOT_CHANNEL_STATE_FILE`
24a217e… lmata 82 - `SCUTTLEBOT_TRANSPORT`
24a217e… lmata 83 - `SCUTTLEBOT_IRC_ADDR`
24a217e… lmata 84 - `SCUTTLEBOT_IRC_PASS`
24a217e… lmata 85 - `SCUTTLEBOT_IRC_DELETE_ON_CLOSE`
50baf1a… lmata 86 - `SCUTTLEBOT_HOOKS_ENABLED`
50baf1a… lmata 87 - `SCUTTLEBOT_INTERRUPT_ON_MESSAGE`
50baf1a… lmata 88 - `SCUTTLEBOT_POLL_INTERVAL`
24a217e… lmata 89 - `SCUTTLEBOT_PRESENCE_HEARTBEAT`
50baf1a… lmata 90 - `SCUTTLEBOT_CONFIG_FILE`
50baf1a… lmata 91 - `SCUTTLEBOT_ACTIVITY_VIA_BROKER`
50baf1a… lmata 92
50baf1a… lmata 93 Example:
50baf1a… lmata 94
50baf1a… lmata 95 ```bash
50baf1a… lmata 96 export SCUTTLEBOT_URL=http://localhost:8080
50baf1a… lmata 97 export SCUTTLEBOT_TOKEN=$(./run.sh token)
50baf1a… lmata 98 export SCUTTLEBOT_CHANNEL=general
1d3caa2… lmata 99 export SCUTTLEBOT_CHANNELS=general,task-42
50baf1a… lmata 100 ```
50baf1a… lmata 101
50baf1a… lmata 102 The hooks also auto-load a shared relay env file if it exists:
50baf1a… lmata 103
50baf1a… lmata 104 ```bash
50baf1a… lmata 105 cat > ~/.config/scuttlebot-relay.env <<'EOF'
50baf1a… lmata 106 SCUTTLEBOT_URL=http://localhost:8080
50baf1a… lmata 107 SCUTTLEBOT_TOKEN=...
50baf1a… lmata 108 SCUTTLEBOT_CHANNEL=general
1d3caa2… lmata 109 SCUTTLEBOT_CHANNELS=general
24a217e… lmata 110 SCUTTLEBOT_TRANSPORT=http
24a217e… lmata 111 SCUTTLEBOT_IRC_ADDR=127.0.0.1:6667
50baf1a… lmata 112 SCUTTLEBOT_HOOKS_ENABLED=1
50baf1a… lmata 113 SCUTTLEBOT_INTERRUPT_ON_MESSAGE=1
50baf1a… lmata 114 SCUTTLEBOT_POLL_INTERVAL=2s
24a217e… lmata 115 SCUTTLEBOT_PRESENCE_HEARTBEAT=60s
50baf1a… lmata 116 EOF
50baf1a… lmata 117 ```
b8ce843… lmata 118
b8ce843… lmata 119 Leave `SCUTTLEBOT_IRC_PASS` unset for the default broker convention so IRC mode
b8ce843… lmata 120 auto-registers ephemeral session nicks. Use `--irc-pass <passphrase>` only when
b8ce843… lmata 121 you intentionally want a fixed identity.
50baf1a… lmata 122
50baf1a… lmata 123 Disable the hooks entirely:
50baf1a… lmata 124
50baf1a… lmata 125 ```bash
50baf1a… lmata 126 export SCUTTLEBOT_HOOKS_ENABLED=0
50baf1a… lmata 127 ```
50baf1a… lmata 128
50baf1a… lmata 129 ## Hook config
50baf1a… lmata 130
50baf1a… lmata 131 Preferred path: run the tracked installer and let it wire the files up for you.
50baf1a… lmata 132
50baf1a… lmata 133 ```bash
50baf1a… lmata 134 bash skills/openai-relay/scripts/install-codex-relay.sh \
50baf1a… lmata 135 --url http://localhost:8080 \
50baf1a… lmata 136 --token "$(./run.sh token)" \
1d3caa2… lmata 137 --channel general \
1d3caa2… lmata 138 --channels general,task-42
50baf1a… lmata 139 ```
50baf1a… lmata 140
50baf1a… lmata 141 Manual path:
50baf1a… lmata 142
50baf1a… lmata 143 Install the scripts:
50baf1a… lmata 144
50baf1a… lmata 145 ```bash
50baf1a… lmata 146 mkdir -p ~/.codex/hooks
50baf1a… lmata 147 cp skills/openai-relay/hooks/scuttlebot-post.sh ~/.codex/hooks/
50baf1a… lmata 148 cp skills/openai-relay/hooks/scuttlebot-check.sh ~/.codex/hooks/
50baf1a… lmata 149 chmod +x ~/.codex/hooks/scuttlebot-post.sh ~/.codex/hooks/scuttlebot-check.sh
50baf1a… lmata 150 ```
50baf1a… lmata 151
50baf1a… lmata 152 Configure native Codex hooks in `~/.codex/hooks.json`:
50baf1a… lmata 153
50baf1a… lmata 154 ```json
50baf1a… lmata 155 {
50baf1a… lmata 156 "hooks": {
50baf1a… lmata 157 "pre-tool-use": [
50baf1a… lmata 158 {
50baf1a… lmata 159 "matcher": "Bash|Edit|Write",
50baf1a… lmata 160 "hooks": [
50baf1a… lmata 161 { "type": "command", "command": "$HOME/.codex/hooks/scuttlebot-check.sh" }
50baf1a… lmata 162 ]
50baf1a… lmata 163 }
50baf1a… lmata 164 ],
50baf1a… lmata 165 "post-tool-use": [
50baf1a… lmata 166 {
50baf1a… lmata 167 "matcher": "Bash|Read|Edit|Write|Glob|Grep|Agent",
50baf1a… lmata 168 "hooks": [
50baf1a… lmata 169 { "type": "command", "command": "$HOME/.codex/hooks/scuttlebot-post.sh" }
50baf1a… lmata 170 ]
50baf1a… lmata 171 }
50baf1a… lmata 172 ]
50baf1a… lmata 173 }
50baf1a… lmata 174 }
50baf1a… lmata 175 ```
50baf1a… lmata 176
50baf1a… lmata 177 Enable the feature in `~/.codex/config.toml`:
50baf1a… lmata 178
50baf1a… lmata 179 ```toml
50baf1a… lmata 180 [features]
50baf1a… lmata 181 codex_hooks = true
50baf1a… lmata 182 ```
50baf1a… lmata 183
50baf1a… lmata 184 Install the compiled broker if you want startup/offline presence plus continuous
50baf1a… lmata 185 IRC input injection:
50baf1a… lmata 186
50baf1a… lmata 187 ```bash
50baf1a… lmata 188 mkdir -p ~/.local/bin
50baf1a… lmata 189 go build -o ~/.local/bin/codex-relay ./cmd/codex-relay
50baf1a… lmata 190 chmod +x ~/.local/bin/codex-relay
50baf1a… lmata 191 ```
50baf1a… lmata 192
50baf1a… lmata 193 Launch with:
50baf1a… lmata 194
50baf1a… lmata 195 ```bash
50baf1a… lmata 196 ~/.local/bin/codex-relay
50baf1a… lmata 197 ```
50baf1a… lmata 198
50baf1a… lmata 199 Optional shell alias:
50baf1a… lmata 200
50baf1a… lmata 201 ```bash
50baf1a… lmata 202 alias codex="$HOME/.local/bin/codex-relay"
50baf1a… lmata 203 ```
50baf1a… lmata 204
50baf1a… lmata 205 Do not replace the real `codex` binary in `PATH` with a shell wrapper.
50baf1a… lmata 206
50baf1a… lmata 207 ## Message filtering semantics
50baf1a… lmata 208
50baf1a… lmata 209 The check hook only surfaces messages that satisfy all of the following:
50baf1a… lmata 210 - newer than the last check for this session
50baf1a… lmata 211 - not posted by this session nick
50baf1a… lmata 212 - not posted by known service bots
50baf1a… lmata 213 - not posted by `claude-*`, `codex-*`, or `gemini-*` status nicks
50baf1a… lmata 214 - explicitly mention this session nick
50baf1a… lmata 215
50baf1a… lmata 216 This is the critical fallback behavior. Ambient channel chat must not halt a live tool loop.
50baf1a… lmata 217
50baf1a… lmata 218 Examples that block:
50baf1a… lmata 219
50baf1a… lmata 220 ```text
8800fb6… lmata 221 operator: codex-scuttlebot-8421 stop and re-read the schema
8800fb6… lmata 222 operator: codex-scuttlebot-8421 wrong file, look at internal/api first
50baf1a… lmata 223 ```
50baf1a… lmata 224
50baf1a… lmata 225 Examples that do not block:
50baf1a… lmata 226
50baf1a… lmata 227 ```text
8800fb6… lmata 228 operator: can someone check the schema
50baf1a… lmata 229 codex-otherrepo-7712: read internal/config/config.go
8800fb6… lmata 230 bridge: [operator] hello
50baf1a… lmata 231 ```
50baf1a… lmata 232
50baf1a… lmata 233 ## Per-session state
50baf1a… lmata 234
50baf1a… lmata 235 The check hook stores its last-seen timestamp in:
50baf1a… lmata 236
50baf1a… lmata 237 ```text
50baf1a… lmata 238 /tmp/.scuttlebot-last-check-{checksum}
50baf1a… lmata 239 ```
50baf1a… lmata 240
50baf1a… lmata 241 The checksum is derived from:
50baf1a… lmata 242 - session nick
50baf1a… lmata 243 - current working directory
1d3caa2… lmata 244
1d3caa2… lmata 245 Live channel changes come from `SCUTTLEBOT_CHANNEL_STATE_FILE`, which the broker
1d3caa2… lmata 246 rewrites as `/join` and `/part` commands change the current session channel set.
50baf1a… lmata 247
50baf1a… lmata 248 That avoids one session consuming another session's instructions.
50baf1a… lmata 249
50baf1a… lmata 250 ## Smoke test
50baf1a… lmata 251
50baf1a… lmata 252 Launcher smoke test:
50baf1a… lmata 253
50baf1a… lmata 254 ```bash
50baf1a… lmata 255 ~/.local/bin/codex-relay --version
50baf1a… lmata 256 ```
50baf1a… lmata 257
50baf1a… lmata 258 Expected IRC behavior:
50baf1a… lmata 259 - no relay announcements, because metadata-only invocations skip them
50baf1a… lmata 260
50baf1a… lmata 261 Hook smoke test:
50baf1a… lmata 262
50baf1a… lmata 263 Post a synthetic activity event:
50baf1a… lmata 264
50baf1a… lmata 265 ```bash
50baf1a… lmata 266 printf '{"tool_name":"Read","cwd":"%s","tool_input":{"file_path":"%s/README.md"}}\n' "$PWD" "$PWD" \
50baf1a… lmata 267 | SCUTTLEBOT_URL=http://localhost:8080 \
50baf1a… lmata 268 SCUTTLEBOT_TOKEN="$(./run.sh token)" \
50baf1a… lmata 269 SCUTTLEBOT_CHANNEL=general \
50baf1a… lmata 270 SCUTTLEBOT_SESSION_ID=smoke \
50baf1a… lmata 271 bash skills/openai-relay/hooks/scuttlebot-post.sh
50baf1a… lmata 272 ```
50baf1a… lmata 273
50baf1a… lmata 274 Then mention the expected nick from the operator side:
50baf1a… lmata 275
50baf1a… lmata 276 ```bash
50baf1a… lmata 277 curl -sf -X POST http://localhost:8080/v1/channels/general/messages \
50baf1a… lmata 278 -H "Authorization: Bearer $(./run.sh token)" \
50baf1a… lmata 279 -H "Content-Type: application/json" \
50baf1a… lmata 280 -d '{"nick":"<your-operator-nick>","text":"codex-scuttlebot-smoke stop and check the bridge TTL"}'
50baf1a… lmata 281 ```
50baf1a… lmata 282
50baf1a… lmata 283 Run the check hook:
50baf1a… lmata 284
50baf1a… lmata 285 ```bash
50baf1a… lmata 286 SCUTTLEBOT_URL=http://localhost:8080 \
50baf1a… lmata 287 SCUTTLEBOT_TOKEN="$(./run.sh token)" \
50baf1a… lmata 288 SCUTTLEBOT_CHANNEL=general \
50baf1a… lmata 289 SCUTTLEBOT_SESSION_ID=smoke \
50baf1a… lmata 290 bash skills/openai-relay/hooks/scuttlebot-check.sh
50baf1a… lmata 291 ```
50baf1a… lmata 292
50baf1a… lmata 293 Expected output:
50baf1a… lmata 294
50baf1a… lmata 295 ```json
50baf1a… lmata 296 {"decision":"block","reason":"[IRC instruction from operator] <your-operator-nick>: codex-scuttlebot-smoke stop and check the bridge TTL"}
50baf1a… lmata 297 ```
50baf1a… lmata 298
50baf1a… lmata 299 ## Operational notes
50baf1a… lmata 300
50baf1a… lmata 301 - `cmd/codex-relay` continuously polls for addressed IRC messages and injects them into the live Codex PTY.
24a217e… lmata 302 - `cmd/codex-relay` can do that over either the HTTP bridge API or a real IRC socket.
50baf1a… lmata 303 - `cmd/codex-relay` also tails the active session JSONL and mirrors assistant output plus tool activity into IRC.
50baf1a… lmata 304 - `SCUTTLEBOT_INTERRUPT_ON_MESSAGE=0` disables the automatic busy-session interrupt before injected IRC instructions.
50baf1a… lmata 305 - With the default `SCUTTLEBOT_INTERRUPT_ON_MESSAGE=1`, the broker only sends Ctrl-C when Codex appears busy. Idle sessions are injected directly and auto-submitted so the broker does not accidentally quit Codex at the prompt.
50baf1a… lmata 306 - `SCUTTLEBOT_POLL_INTERVAL=1s` changes the broker poll interval.
24a217e… lmata 307 - `SCUTTLEBOT_TRANSPORT=irc` gives the live session a true IRC presence; `SCUTTLEBOT_IRC_PASS` skips auto-registration if you already manage the NickServ account yourself.
24a217e… lmata 308 - `SCUTTLEBOT_PRESENCE_HEARTBEAT=60s` keeps quiet HTTP-mode sessions in the active user list without visible chatter.
24a217e… lmata 309 - The hooks themselves still use the scuttlebot HTTP API, not direct IRC.
50baf1a… lmata 310 - If scuttlebot is down or unreachable, the hooks soft-fail and return quickly.
50baf1a… lmata 311 - `SCUTTLEBOT_HOOKS_ENABLED=0` disables both hooks explicitly.
50baf1a… lmata 312 - `SCUTTLEBOT_ACTIVITY_VIA_BROKER=1` suppresses `scuttlebot-post.sh` so broker-launched sessions do not duplicate activity posts.
50baf1a… lmata 313 - `../scripts/install-codex-relay.sh --disabled` writes that disabled state into the shared env file.
50baf1a… lmata 314 - For fleet launch instructions, see [`../FLEET.md`](../FLEET.md).
50baf1a… lmata 315 - They are safe to keep in the repo and copy into home hook directories.
50baf1a… lmata 316 - Do not hardcode bearer tokens into the scripts.
50baf1a… lmata 317 - Restart Codex after enabling `codex_hooks` or changing `~/.codex/hooks.json`.
50baf1a… lmata 318 - If you need a fixed nick for a long-lived session, set `SCUTTLEBOT_NICK`.
50baf1a… lmata 319 - The broker is the right place for session-start/session-stop presence because
50baf1a… lmata 320 Codex hooks only fire around tool events.

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button