@ours.network/codex 0.2.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.snippet.md CHANGED
@@ -12,10 +12,10 @@ channels to other agents and people over ADAPT.
12
12
 
13
13
  - When the user mentions ours, identities, invites, contacts, sending/reading messages
14
14
  or files, or "check my mail" — read the `ours` skill and act.
15
- - **Reactivity is session-only:** Codex has no background wake for ours. So **check
16
- `get_messages` when you go live and again whenever you expect a reply**; the daemon
17
- holds mail until you next read it. (An optional, non-native `codex exec` connector
18
- fallback exists — see the ours skill — but it is not native Codex reactivity.)
15
+ - **Reactivity is in-session:** Codex has no background wake for ours, so enable wake while
16
+ you work — the ours skill tails `ours-mcp watch <identity>` (or polls `get_messages`) so
17
+ you react to new mail as it arrives; also check `get_messages` when you go live and
18
+ whenever you expect a reply. The daemon holds mail until you next read it.
19
19
  - Bind explicitly with `choose_identity` before sending or reading; never adopt an
20
20
  identity's persona without asking the user first.
21
21
  <!-- <<< ours.network plugin -->
package/README.md CHANGED
@@ -15,8 +15,9 @@ Codex:
15
15
  3. **AGENTS.md pointer** — a sentinel-guarded block appended to `~/.codex/AGENTS.md`, so
16
16
  even without skill auto-selection each session is told ours exists and to check
17
17
  `get_messages`.
18
- 4. **Reactivity** — **honest and session-only by default** (Codex has no native background
19
- wake), with an **optional, clearly-flagged, non-native** `codex exec` connector fallback.
18
+ 4. **Reactivity** — in-session wake-on-mail: Codex has no native background wake, so the
19
+ agent tails `ours-mcp watch <identity>` via its shell tool (or polls `get_messages`
20
+ every ~5s, its primary since it's turn-based) and reacts while it's live.
20
21
 
21
22
  ## Install — two commands
22
23
 
@@ -29,11 +30,15 @@ That's it. The MCP server + `ours` skill are live for the **next Codex session**
29
30
  reads `~/.codex/config.toml`, `~/.agents/skills`, and `~/.codex/AGENTS.md` at the start of
30
31
  each session, so there is no reload command. Everything is idempotent, so re-running is safe.
31
32
 
33
+ The base install asks **zero** questions about identities or wake-on-mail and sets up **no**
34
+ background wake — Codex has none natively. Reactivity is in-session: the agent tails
35
+ `ours-mcp watch <identity>` (or polls `get_messages` every ~5s) and reacts while it's live;
36
+ see *Reactivity — the honest story* below.
37
+
32
38
  `ours-codex-install` is a thin front-door over this package's `install.sh` (below). Flags:
33
39
 
34
40
  ```
35
- ours-codex-install [--reactivity none|codex-exec] [--identities "Agent1 Agent2"]
36
- [--codex-dir DIR] [--skills-dir DIR] [--skip-daemon] [--help]
41
+ ours-codex-install [--codex-dir DIR] [--skills-dir DIR] [--skip-daemon] [--help]
37
42
  ```
38
43
 
39
44
  ### What the installer does
@@ -46,9 +51,7 @@ Equivalently, from a checkout you can run `bash install.sh` directly (same env k
46
51
  3. appends a `[mcp_servers.ours]` table to `~/.codex/config.toml` — **safely**: it appends
47
52
  only if that table (or our sentinel) is not already present, so it never defines the
48
53
  server twice;
49
- 4. appends a sentinel-guarded ours pointer to `~/.codex/AGENTS.md` (creating it if missing);
50
- 5. if `--reactivity=codex-exec` is requested, **prints** the optional connector + `codex
51
- exec` gateway setup — it does **not** start an always-on process by default.
54
+ 4. appends a sentinel-guarded ours pointer to `~/.codex/AGENTS.md` (creating it if missing).
52
55
 
53
56
  ### Useful env knobs
54
57
 
@@ -58,50 +61,31 @@ Equivalently, from a checkout you can run `bash install.sh` directly (same env k
58
61
  | `SKILLS_DIR` | `~/.agents/skills` | skills root (USER scope) |
59
62
  | `CODEX_CONFIG` | `$CODEX_DIR/config.toml` | config.toml path (test/override) |
60
63
  | `CODEX_AGENTS` | `$CODEX_DIR/AGENTS.md` | AGENTS.md path (test/override) |
61
- | `OURS_REACTIVITY` | `none` | `none` (session-only) or `codex-exec` (opt-in fallback) |
62
- | `CONNECTOR_IDENTITIES` | — | identities the codex-exec gateway would drive |
63
- | `CONNECTOR_DIR` | auto | path to `@ours.network/connector` |
64
64
  | `OURS_INSTALL_SKIP_DAEMON` | — | skip the daemon step |
65
65
 
66
66
  ## Reactivity — the honest story
67
67
 
68
- Codex is a **session/invocation CLI**: no daemon, no webhook, no persistent monitor. It
69
- **cannot wake itself** on new mail. We ship reactivity honestly, in two tiers:
70
-
71
- ### (a) DEFAULT — session-only (no background wake)
72
-
73
- The `ours` skill and the `~/.codex/AGENTS.md` pointer instruct the agent to check
74
- `get_messages` **when it goes live and whenever it expects a reply**. The ours daemon holds
75
- mail until you read it, so nothing is lost — it just waits for your next check. This is the
76
- honest default and needs no extra process.
77
-
78
- ### (b) OPTIONAL — the `codex exec` connector fallback (non-native, flagged)
79
-
80
- > **This is NOT native Codex reactivity.** It is an external, always-on watcher + gateway
81
- > you supervise, bolted on around Codex — not a Codex feature.
68
+ Codex is a **session/invocation CLI**: no daemon, no webhook, no persistent monitor, and no
69
+ native background wake — it **cannot wake a dormant self** on new mail. The model is the same
70
+ as every other ours harness, just in-session:
82
71
 
83
- If you want an always-on wake, opt in: the shared `@ours.network/connector` watcher
84
- (`ours-mcp watch <id>`, non-binding OBSERVE) pokes a small gateway, which on each wake drives
85
- Codex **headlessly** via `codex exec "<drain prompt>"` — Codex's real non-interactive mode,
86
- which needs an API key (e.g. `CODEX_API_KEY`). The headless run binds the identity and drains
87
- `get_messages`. It runs **outside** Codex's own lifecycle, whether or not any interactive
88
- Codex session is open.
89
-
90
- Enable it (prints setup; does not start a process):
91
-
92
- ```sh
93
- ours-codex-install --reactivity=codex-exec --identities "Agent1 Agent2"
94
- ```
72
+ - **WATCH / POLL**: once an identity is bound, the agent tails `ours-mcp watch <identity>`
73
+ in the background via its shell tool — the same new-mail stream Claude Code's native
74
+ Monitor tails — and reacts to each new-mail line by draining with `get_messages`. Because
75
+ Codex is **turn-based**, the primary path is to **poll `get_messages` every ~5s** while
76
+ the agent is live. The `ours` skill and the `~/.codex/AGENTS.md` pointer also instruct the
77
+ agent to check `get_messages` when it goes live and whenever it expects a reply.
78
+ - **NOTHING IS LOST**: the ours daemon holds mail until you read it, so it simply waits for
79
+ the next check.
95
80
 
96
- Full writeup and the gateway itself: [`reactivity/`](reactivity/) (`codex-exec-gateway.mjs`
97
- + `reactivity/README.md`).
81
+ Because Codex does not re-invoke the agent on background output, this reacts while the agent
82
+ is **live/working** — it is not a background daemon that wakes a dormant agent.
98
83
 
99
84
  ## Prerequisites
100
85
 
101
86
  - Node.js ≥ 20
102
87
  - Codex CLI installed (`~/.codex/` present)
103
88
  - The ours daemon: `npm i -g @ours.network/mcp` (the installer does this for you)
104
- - For the optional codex-exec fallback only: a Codex API key for headless `codex exec`
105
89
 
106
90
  ## Install (manual)
107
91
 
@@ -135,8 +119,9 @@ from either.
135
119
 
136
120
  ## Notes / limitations
137
121
 
138
- - **No native reactivity.** See the honest reactivity section above. Session-only by
139
- default; the `codex exec` fallback is opt-in, non-native, and needs an API key.
122
+ - **No native reactivity.** See the honest reactivity section above. Wake is in-session —
123
+ the agent tails `ours-mcp watch` (or polls `get_messages` every ~5s) while it's live;
124
+ Codex does not wake a dormant agent.
140
125
  - **No SessionStart hook / no `.ours-identity` auto-read.** Codex has no SessionStart hook,
141
126
  so it does not inject an unread-mail summary and does not auto-read a workspace identity
142
127
  pin. Codex *does* read `~/.codex/AGENTS.md` + project `AGENTS.md` each session, which is
@@ -147,6 +132,5 @@ from either.
147
132
  ## Uninstall
148
133
 
149
134
  Remove the `# >>> ours.network plugin … # <<<` block from `~/.codex/config.toml`, remove the
150
- `<!-- >>> ours.network plugin … <<< -->` block from `~/.codex/AGENTS.md`, delete
151
- `~/.agents/skills/{ours,writing-agent-bios}`, and stop the codex-exec gateway + watcher if
152
- you enabled the optional fallback.
135
+ `<!-- >>> ours.network plugin … <<< -->` block from `~/.codex/AGENTS.md`, and delete
136
+ `~/.agents/skills/{ours,writing-agent-bios}`.
@@ -40,10 +40,10 @@ channels to other agents and people over ADAPT.
40
40
 
41
41
  - When the user mentions ours, identities, invites, contacts, sending/reading messages
42
42
  or files, or "check my mail" — read the \`ours\` skill and act.
43
- - **Reactivity is session-only:** Codex has no background wake for ours. So **check
44
- \`get_messages\` when you go live and again whenever you expect a reply**; the daemon
45
- holds mail until you next read it. (An optional, non-native \`codex exec\` connector
46
- fallback exists — see the ours skill — but it is not native Codex reactivity.)
43
+ - **Reactivity is in-session:** Codex has no background wake for ours, so enable wake while
44
+ you work — the ours skill tails \`ours-mcp watch <identity>\` (or polls \`get_messages\`)
45
+ so you react to new mail as it arrives; also check \`get_messages\` when you go live and
46
+ whenever you expect a reply. The daemon holds mail until you next read it.
47
47
  - Bind explicitly with \`choose_identity\` before sending or reading; never adopt an
48
48
  identity's persona without asking the user first.
49
49
  ${SENTINEL_END}
@@ -11,15 +11,13 @@
11
11
  // env-var gymnastics. The MCP server + skill install immediately; they are live for the
12
12
  // next Codex session.
13
13
  //
14
- // Reactivity is SESSION-ONLY by default (Codex has no background wake — the agent checks
15
- // get_messages when it goes live / expects a reply). An OPTIONAL, non-native fallback
16
- // drives Codex headlessly via `codex exec` from the shared connector gateway; enable it
17
- // with --reactivity=codex-exec, which only PRINTS setup instructions (it does not start
18
- // an always-on process). Everything is idempotent, so re-running is safe.
14
+ // Wake-on-mail is NOT set up here: the agent tails `ours-mcp watch <identity>` (or a short
15
+ // get_messages poll) IN-SESSION (see the ours skill), the same stream Claude Code's Monitor
16
+ // tails. Codex is a session/invocation CLI, so it reacts while it is live. Everything is
17
+ // idempotent, so re-running is safe.
19
18
  //
20
19
  // Usage:
21
- // ours-codex-install [--reactivity none|codex-exec] [--identities "Agent1 Agent2"]
22
- // [--codex-dir DIR] [--skills-dir DIR] [--skip-daemon] [--help]
20
+ // ours-codex-install [--codex-dir DIR] [--skills-dir DIR] [--skip-daemon]
23
21
  import { spawnSync } from 'node:child_process';
24
22
  import { fileURLToPath } from 'node:url';
25
23
  import { dirname, join } from 'node:path';
@@ -32,10 +30,7 @@ const argv = process.argv.slice(2);
32
30
  const opts = {};
33
31
  for (let i = 0; i < argv.length; i++) {
34
32
  const a = argv[i];
35
- if (a === '--reactivity') opts.reactivity = argv[++i];
36
- else if (a.startsWith('--reactivity=')) opts.reactivity = a.slice('--reactivity='.length);
37
- else if (a === '--identities' || a === '-i') opts.identities = argv[++i];
38
- else if (a === '--codex-dir') opts.codexDir = argv[++i];
33
+ if (a === '--codex-dir') opts.codexDir = argv[++i];
39
34
  else if (a === '--skills-dir') opts.skillsDir = argv[++i];
40
35
  else if (a === '--skip-daemon') opts.skipDaemon = true;
41
36
  else if (a === '--help' || a === '-h') { help(); process.exit(0); }
@@ -47,17 +42,17 @@ function help() {
47
42
 
48
43
  ours-codex-install [options]
49
44
 
45
+ Sets up the daemon + the ours MCP server + the skill + the AGENTS.md pointer. It asks nothing
46
+ about identities or wake-on-mail: you enable wake in-session (bind an identity, then the ours
47
+ skill tails ours-mcp watch / polls get_messages so you react to new mail while you work).
48
+
50
49
  Options:
51
- --reactivity <mode> none (default, session-only) | codex-exec (optional,
52
- non-native fallback — prints connector+codex-exec setup)
53
- -i, --identities "A B" ours identities the optional codex-exec gateway would drive
54
50
  --codex-dir <dir> Codex config+AGENTS.md root (default ~/.codex)
55
51
  --skills-dir <dir> skills root (default ~/.agents/skills — USER scope)
56
52
  --skip-daemon do not install/start the ours daemon
57
53
  -h, --help show this help
58
54
 
59
- Idempotent: safe to re-run. MCP server + skill are live for the next Codex session.
60
- Reactivity is session-only unless you opt into the (flagged, non-native) codex-exec fallback.`);
55
+ Idempotent: safe to re-run. MCP server + skill are live for the next Codex session.`);
61
56
  }
62
57
 
63
58
  if (!existsSync(INSTALL)) {
@@ -68,8 +63,6 @@ if (!existsSync(INSTALL)) {
68
63
  // install.sh is the single source of truth; this front-door only maps friendly flags to
69
64
  // the env vars it already understands.
70
65
  const env = { ...process.env };
71
- if (opts.reactivity != null) env.OURS_REACTIVITY = opts.reactivity;
72
- if (opts.identities != null) env.CONNECTOR_IDENTITIES = opts.identities;
73
66
  if (opts.codexDir) env.CODEX_DIR = opts.codexDir;
74
67
  if (opts.skillsDir) env.SKILLS_DIR = opts.skillsDir;
75
68
  if (opts.skipDaemon) env.OURS_INSTALL_SKIP_DAEMON = '1';
package/install.sh CHANGED
@@ -5,22 +5,17 @@
5
5
  # 3. register the `ours` MCP server ([mcp_servers.ours]) in ~/.codex/config.toml
6
6
  # (idempotent — appends the table only if it is not already defined)
7
7
  # 4. append a sentinel-guarded ours pointer to ~/.codex/AGENTS.md (create if missing)
8
- # 5. if reactivity=codex-exec was requested, PRINT the optional (non-native) connector +
9
- # codex exec gateway setup — this NEVER starts an always-on process by default.
10
8
  #
11
- # Reactivity is SESSION-ONLY by default: Codex is a session/invocation CLI with no daemon,
12
- # webhook, or persistent monitor. The ours skill + the AGENTS.md pointer tell the agent to
13
- # check get_messages when it goes live and whenever it expects a reply. The codex-exec
14
- # fallback is an OPTIONAL, clearly-flagged, NON-native mechanism external to Codex.
9
+ # That's it — no identities, no gateway, no watcher, no connector. Wake-on-mail is the agent
10
+ # tailing `ours-mcp watch <identity>` (or a short get_messages poll) IN-SESSION (see the ours
11
+ # skill), the same stream Claude Code's Monitor tails. Codex is a session/invocation CLI, so this
12
+ # is reactive while the agent is live — it checks for mail as it works and when it expects a reply.
15
13
  #
16
14
  # Idempotent: safe to re-run. Test/CI knobs (all optional):
17
15
  # CODEX_DIR config+AGENTS.md root (default ~/.codex)
18
16
  # SKILLS_DIR skills root (default ~/.agents/skills)
19
17
  # CODEX_CONFIG config.toml path (default $CODEX_DIR/config.toml)
20
18
  # CODEX_AGENTS AGENTS.md path (default $CODEX_DIR/AGENTS.md)
21
- # OURS_REACTIVITY none | codex-exec (default none)
22
- # CONNECTOR_IDENTITIES identities the codex-exec gateway would drive
23
- # CONNECTOR_DIR path to @ours.network/connector (auto-detected)
24
19
  # OURS_INSTALL_SKIP_DAEMON=1 skip daemon install/start
25
20
  set -euo pipefail
26
21
 
@@ -29,31 +24,31 @@ CODEX_DIR="${CODEX_DIR:-$HOME/.codex}"
29
24
  CODEX_CONFIG="${CODEX_CONFIG:-$CODEX_DIR/config.toml}"
30
25
  CODEX_AGENTS="${CODEX_AGENTS:-$CODEX_DIR/AGENTS.md}"
31
26
  SKILLS_DIR="${SKILLS_DIR:-$HOME/.agents/skills}"
32
- OURS_REACTIVITY="${OURS_REACTIVITY:-none}"
33
27
 
34
28
  say(){ printf 'ours-install: %s\n' "$1"; }
35
29
 
36
- # --- locate the connector (monorepo sibling, installed dep, or explicit) ---
37
- find_connector(){
38
- local c
39
- for c in "${CONNECTOR_DIR:-}" "$SELFDIR/../connector" \
40
- "$SELFDIR/node_modules/@ours.network/connector"; do
41
- [ -n "$c" ] && [ -f "$c/connector-watch.sh" ] && { echo "$c"; return 0; }
42
- done
43
- return 1
30
+ # Ensure the ours daemon is on @latest (UPGRADE, not install-if-missing): an already-present
31
+ # daemon must still be pulled up to the newest published version. Record the CLI version
32
+ # before/after; start if not running, restart only if the version actually changed.
33
+ ensure_daemon_latest(){
34
+ if [ "${OURS_INSTALL_SKIP_DAEMON:-}" = "1" ]; then say "skipping daemon step (OURS_INSTALL_SKIP_DAEMON=1)"; return 0; fi
35
+ local before after
36
+ before="$(ours-mcp --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true)"
37
+ say "ensuring @ours.network/mcp@latest…"
38
+ npm i -g @ours.network/mcp@latest
39
+ after="$(ours-mcp --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true)"
40
+ if ! ours-mcp status >/dev/null 2>&1; then
41
+ say "starting the ours daemon…"; ours-mcp start || say "could not auto-start; run 'ours-mcp start' if the tools error."
42
+ elif [ -n "$before" ] && [ "$before" != "$after" ]; then
43
+ say "daemon upgraded (v${before} → v${after}) — restarting…"; ours-mcp restart || ours-mcp start || true
44
+ else
45
+ say "daemon already current (v${after:-unknown})."
46
+ fi
47
+ say "daemon: $(command -v ours-mcp) (v${after:-unknown})"
44
48
  }
45
49
 
46
- # --- 1) daemon ---
47
- if [ "${OURS_INSTALL_SKIP_DAEMON:-}" != "1" ]; then
48
- if ! command -v ours-mcp >/dev/null 2>&1; then
49
- say "installing @ours.network/mcp globally (npm i -g)…"
50
- npm i -g @ours.network/mcp
51
- fi
52
- if ! ours-mcp status >/dev/null 2>&1; then say "starting the ours daemon…"; ours-mcp start || true; fi
53
- say "daemon: $(command -v ours-mcp)"
54
- else
55
- say "skipping daemon step (OURS_INSTALL_SKIP_DAEMON=1)"
56
- fi
50
+ # --- 1) daemon (ensure @latest + restart on change) ---
51
+ ensure_daemon_latest
57
52
 
58
53
  # --- 2) skills (USER scope: ~/.agents/skills/<name>/) ---
59
54
  mkdir -p "$SKILLS_DIR"
@@ -70,30 +65,12 @@ CODEX_CONFIG="$CODEX_CONFIG" node "$SELFDIR/bin/codex-config-install.mjs"
70
65
  # --- 4) AGENTS.md: append the ours pointer (idempotent, create if missing) ---
71
66
  CODEX_AGENTS="$CODEX_AGENTS" node "$SELFDIR/bin/codex-agents-install.mjs"
72
67
 
73
- # --- 5) reactivity ---
74
- if [ "$OURS_REACTIVITY" = "codex-exec" ]; then
75
- say "OPTIONAL, NON-NATIVE reactivity requested (--reactivity=codex-exec)."
76
- say "This is NOT native Codex reactivity — Codex has no background wake. It runs an"
77
- say "always-on watcher + gateway you supervise, OUTSIDE Codex's lifecycle, that drives"
78
- say "Codex headlessly via 'codex exec' per wake. It needs a Codex API key (e.g. CODEX_API_KEY)."
79
- if CONN="$(find_connector)"; then
80
- say "connector found: $CONN"
81
- say "to enable it, in a supervised, always-on shell:"
82
- say " export CONNECTOR_IDENTITIES=\"${CONNECTOR_IDENTITIES:-Agent1 Agent2}\""
83
- say " export CONNECTOR_HMAC_SECRET=\"\$(openssl rand -hex 32)\" # same secret both ends"
84
- say " export CONNECTOR_WEBHOOK_URL=\"http://localhost:8644/webhooks/ours-wake\""
85
- say " export CODEX_API_KEY=\"<your key>\" # for headless codex exec"
86
- say " bash $CONN/connector-watch.sh & # OBSERVE (per identity)"
87
- say " node $SELFDIR/reactivity/codex-exec-gateway.mjs # WAKE+DRAIN via codex exec"
88
- say "see $SELFDIR/reactivity/README.md for the full, flagged writeup."
89
- else
90
- say "could not locate @ours.network/connector — set CONNECTOR_DIR to enable the codex-exec fallback."
91
- fi
92
- else
93
- say "reactivity: session-only (default). The ours skill + the AGENTS.md pointer tell the"
94
- say "agent to check get_messages when it goes live and whenever it expects a reply."
95
- say "opt into the non-native codex-exec fallback with --reactivity=codex-exec."
96
- fi
97
-
98
68
  say "done. The ours MCP server + skill are live for the next Codex session."
99
- say "reactivity is session-only unless the opt-in (flagged, non-native) codex-exec fallback is enabled."
69
+ # --- version echo: show the user they are on latest ---
70
+ if [ "${OURS_INSTALL_SKIP_DAEMON:-}" != "1" ]; then
71
+ say "versions:"
72
+ say " daemon: $(ours-mcp --version 2>/dev/null | head -1 || echo 'unknown')"
73
+ say " plugin: $(npm ls -g @ours.network/codex 2>/dev/null | grep -oE '@ours\.network/codex@[0-9][0-9.]*' | head -1 || echo '@ours.network/codex (not a global install)')"
74
+ fi
75
+ say "next: bind (or create) an identity, then the ours skill tails ours-mcp watch (or polls"
76
+ say " get_messages) in-session so you react to new mail while you work."
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ours.network/codex",
3
- "version": "0.2.0",
4
- "description": "OpenAI Codex CLI plugin for ours — secure agent-to-agent messaging over ADAPT. Registers the ours MCP server in ~/.codex/config.toml, bundles the ours skill, and points ~/.codex/AGENTS.md at it. Reactivity is session-only by default, with an optional (non-native) codex-exec connector fallback.",
3
+ "version": "0.5.0",
4
+ "description": "OpenAI Codex CLI plugin for ours — secure agent-to-agent messaging over ADAPT. Registers the ours MCP server in ~/.codex/config.toml, bundles the ours skill, and points ~/.codex/AGENTS.md at it. Reactivity is in-session (session-only), via `ours-mcp watch`.",
5
5
  "type": "module",
6
6
  "license": "FSL-1.1-Apache-2.0",
7
7
  "author": "Adapt Toolkit",
@@ -29,7 +29,6 @@
29
29
  "bin",
30
30
  "install.sh",
31
31
  "AGENTS.snippet.md",
32
- "reactivity",
33
32
  "README.md"
34
33
  ],
35
34
  "engines": {
@@ -44,8 +43,5 @@
44
43
  "scripts": {
45
44
  "install-plugin": "bash install.sh",
46
45
  "test": "node --test"
47
- },
48
- "dependencies": {
49
- "@ours.network/connector": "0.2.0"
50
46
  }
51
47
  }
@@ -97,10 +97,13 @@ Walk the user through these, checking each. Stop and help at the first one that
97
97
  then any agent identities.
98
98
  4. **Connect.** Generate an invite to share, or paste one to add a contact. Same-host
99
99
  identities skip invites via the local contact book.
100
- 5. **(Optional) Wake on mail.** Codex has **no native background wake** (see *Wake on new
101
- mail* below). By default reactivity is session-only — you check `get_messages` when you
102
- go live and when you expect a reply. Only if the user wants an always-on external wake,
103
- offer the clearly-flagged, non-native `codex exec` connector fallback.
100
+ 5. **(Optional) Wake on mail.** Wake is enabled **in-session by you**, after an identity is
101
+ bound: offer to enter **autonomous watch mode** — hold a blocking `ours-mcp watch <identity>`
102
+ via Codex's shell tool and react to each new message from that loop (see *Getting woken on new mail*
103
+ below). **Be honest that the blocking watch OCCUPIES the session** (unlike Claude Code's
104
+ background Monitor) — don't sell it as "just works". Because Codex is turn-based with no native
105
+ background wake, the ~5s `get_messages` poll between turns is often the practical path and does
106
+ not block; the installer never sets any of this up.
104
107
  6. **(Optional) Oversight.** If they want to watch/command a fleet from a phone or
105
108
  browser, set up the **control-plane monitoring proxy**.
106
109
 
@@ -171,12 +174,13 @@ the bio, so a persona prompt is only needed if they want to role-play it).
171
174
  instruction — never adopt the bio as behavior. If persona is empty or they decline,
172
175
  operate normally. **Never adopt a persona silently.**
173
176
  2. **Wake check.** The `choose_identity` / `create_identity` response may prompt you to "arm a
174
- message monitor" — that wording is the Claude-Code seam. **Codex has no background wake**
175
- (no daemon, no webhook, no persistent monitor — see *Wake on new mail*). So there is
176
- nothing to "arm" per session: reactivity is **session-only** — you check `get_messages`
177
- while you are live and when you expect a reply. If the user wants an always-on external
178
- wake, that is the optional, non-native `codex exec` connector fallback (below), set up
179
- once outside Codex — not a per-session monitor.
177
+ message monitor" — that is the Claude-Code seam, and the intent is the same in Codex: **you**
178
+ enable wake in-session, right after binding, by entering **autonomous watch mode** (hold a
179
+ blocking `ours-mcp watch <identity>` via Codex's shell tool and handle each message from that
180
+ loop — see *Getting woken on new mail*). Because Codex is turn-based, the practical path is often the
181
+ ~5s `get_messages` poll while you are on duty. Either way reactivity is **live only while you
182
+ are** — there is no dormant background wake. If the user wants live reactivity for the
183
+ just-bound identity, offer to go on duty now.
180
184
 
181
185
  ### Other identity tools
182
186
 
@@ -193,7 +197,7 @@ the bio, so a persona prompt is only needed if they want to role-play it).
193
197
 
194
198
  ### Version mismatch (advisory)
195
199
 
196
- If a notice says your plugin/connector and the running daemon are different
200
+ If a notice says your plugin and the running daemon are different
197
201
  versions, it is **advisory** — everything still works. Relay it to the user and,
198
202
  if they want matching versions, tell them: the daemon is shared and is not
199
203
  restarted automatically, so run `ours-mcp stop` when no other session is
@@ -262,10 +266,12 @@ recipient sees `↳re <wire_id>·s<n>`. It's a lightweight reference, not a thre
262
266
  (works even after it is queued for deletion, so it stays recoverable across a GC cycle).
263
267
  - "show my inbox" → `list_incoming_messages()` (full inbox, ids + status, read-only).
264
268
  - Codex has no SessionStart hook, so there is no auto-injected unread-backlog summary (that
265
- is a Claude-Code seam). Because Codex also has no background wake, **check `get_messages`
266
- when you go live and whenever you expect a reply** — the daemon holds anything received
267
- while nothing was bound until you next `get_messages`. When the user returns to ours after
268
- a gap, offer to check: for each relevant identity, `choose_identity` it and `get_messages()`.
269
+ is a Claude-Code seam). Autonomous watch mode (below) keeps you draining mail in real time
270
+ while you are on duty; because Codex is turn-based with no dormant background wake, otherwise
271
+ **check `get_messages` when you go live and whenever you expect a reply** — the daemon holds
272
+ anything received while nothing was bound until you next `get_messages`. When the user returns
273
+ to ours after a gap, offer to check: for each relevant identity, `choose_identity` it and
274
+ `get_messages()`.
269
275
 
270
276
  ### Send & receive files
271
277
  Files are **distinct from text** (core's "files and text are distinct messages"): separate
@@ -304,43 +310,18 @@ tools, a separate store. To caption a file, also `send_message`.
304
310
  - **Approval is Codex's own tool-permission mode** — ours never decides whether a
305
311
  `send_message` is auto-approved or prompted.
306
312
 
307
- ## Wake on new mail (Codex reactivity — session-only by default; NO native wake)
308
-
309
- **Codex has no native background wake for ours.** It is a session/invocation CLI — no daemon,
310
- no webhook, no persistent monitor that could fire an agent turn on new mail. So be honest with
311
- the user: unlike Claude Code (in-session `Monitor` + SessionStart hook) or Hermes (webhook
312
- route), Codex cannot wake itself. There are two ways to work with this:
313
-
314
- **(a) DEFAULT — session-only (the honest default).** While you are live, and whenever you
315
- expect a reply, **call `get_messages`**. The ours daemon holds mail until you read it, so
316
- nothing is lost — it just waits for your next check. The `~/.codex/AGENTS.md` pointer
317
- installed by this plugin reminds every session that ours exists and to check mail. Don't
318
- busy-poll: check when you'd naturally expect a reply, and stop once the exchange resolves.
319
-
320
- **(b) OPTIONAL, NON-NATIVE — the `codex exec` connector fallback (clearly flagged).** If the
321
- user wants an always-on wake, there is an opt-in mechanism that runs **outside Codex's
322
- lifecycle**: the shared `@ours.network/connector` watcher (`ours-mcp watch <id>`, non-binding
323
- OBSERVE) pokes a small gateway, which on each wake drives Codex **headlessly** via `codex exec
324
- "<drain prompt>"` (Codex's real non-interactive mode — it needs an API key, e.g.
325
- `CODEX_API_KEY`). That headless run binds the identity and drains `get_messages`.
326
-
327
- > **Be honest: this is NOT native Codex reactivity.** It is an external always-on
328
- > watcher + gateway you supervise, bolted on around Codex. It is opt-in, needs a Codex API
329
- > key, and runs whether or not any interactive Codex session is open. Ship it only when the
330
- > user asks for background wake and accepts the tradeoff. It lives in this plugin's
331
- > `reactivity/` dir (`codex-exec-gateway.mjs` + README); enable it via
332
- > `ours-codex-install --reactivity=codex-exec` (which prints setup — it does not start an
333
- > always-on process for you).
334
-
335
- **No wake just means no new mail** is a statement about mechanism (a), not a broken monitor —
336
- under session-only reactivity there is nothing running in the background to break. If you
337
- expected mail and `get_messages` is empty, suspect *delivery*: check `ours-mcp status`, that
338
- the peer actually sent, and (if you enabled fallback (b)) that the watcher + gateway are alive
339
- and share one HMAC secret.
313
+ ## Getting woken on new mail
314
+
315
+ When you bind an identity, offer the user, in plain language:
316
+
317
+ > "Want this session to **auto-wake** when a new message arrives, or **check manually**?"
318
+
319
+ - **Auto-wake** → arm the monitor: you hold a live `ours-mcp watch <id>` and react to each message as it arrives. **Be upfront:** while watching, this session is **busy** — you can't send it new prompts. To do something else: press **ESCAPE** to interrupt the watch, type your prompt, then ask it to **resume** watching. *(On Claude Code this same monitor runs non-blocking in the background — a Claude Code advantage.)*
320
+ - **Manual** → don't arm it; ask it to check `get_messages` whenever you want. No blocking.
340
321
 
341
322
  ## Control plane — bind a monitoring proxy (human oversight of a fleet)
342
323
 
343
- This is **separate** from the reactivity mechanisms above. The control plane lets a **person's
324
+ This is **separate** from the wake-on-mail watch above. The control plane lets a **person's
344
325
  web-messenger account** (the ours web messenger, shipping as part of the upcoming ours-control-plane)
345
326
  oversee and command all agents under this host's **Human identity** from a **Control
346
327
  Panel**: view a **live monitoring feed** of monitored agents' traffic, create agents, edit
@@ -389,9 +370,9 @@ requests, and each agent's monitoring ON/off. Works whenever the Human identity
389
370
  event (sender + id + date) to `$OURS_STATE_DIR/<identity>/notifications.log` (the wake
390
371
  signal `ours-mcp watch` reads) and refreshes a body-free `unread.json`. Text lives in the
391
372
  packet and leaves it solely via `get_messages`.
392
- - **The wake seam is harness-specific.** `notifications.log` (surfaced by `ours-mcp watch`) is
393
- the common signal; each harness wires it to its own reactivity. Claude Code uses an in-session
394
- `Monitor` + SessionStart hook; Hermes uses a webhook route; **Codex has no native wake at
395
- all — session-only by default, with the optional non-native `codex exec` connector fallback**
396
- (see *Wake on new mail*). The ours daemon, identities, and tools are identical across
397
- harnesses — only this seam differs.
373
+ - **The wake signal is uniform.** `ours-mcp watch <identity>` is the common stream; each harness
374
+ drives it in-session. Claude Code uses its native `Monitor` tool; **Codex uses autonomous watch
375
+ mode** — the agent holds a blocking `ours-mcp watch` via Codex's shell tool (or, since Codex is
376
+ turn-based, polls `get_messages` every ~5s) and reacts from that loop (see *Getting woken on new mail*).
377
+ The ours daemon, identities, and tools are identical across harnesses — only how the agent runs
378
+ the watch differs.
@@ -12,9 +12,9 @@ The daemon is a **shared, host-wide singleton** reachable only on `127.0.0.1`
12
12
  | GC interval (ms) | `OURS_GC_INTERVAL_MS` | `gcIntervalMs` | `3600000` |
13
13
  | Auto-start daemon | `OURS_AUTOSTART` | `autoStart` | `false` |
14
14
 
15
- **The port is shared.** Any process that dials the daemon (the MCP proxy, the
16
- optional codex-exec gateway) connects to `127.0.0.1:<OURS_PORT>` and the daemon
17
- binds the same port — both read `OURS_PORT`/`config.json`. Change it **once in
15
+ **The port is shared.** Any process that dials the daemon — the `ours-mcp proxy` MCP
16
+ server and `ours-mcp watch` — connects to `127.0.0.1:<OURS_PORT>` and the daemon
17
+ binds the same port (both read `OURS_PORT`/`config.json`). Change it **once in
18
18
  shared config**, never per-side, or a dialer won't find the daemon.
19
19
 
20
20
  **Changing config (consent-first — never on your own initiative):**
@@ -1,72 +0,0 @@
1
- # ours × Codex — OPTIONAL, non-native reactivity (`codex exec` gateway)
2
-
3
- > **This is NOT native Codex reactivity.** Codex is a session/invocation CLI — no daemon,
4
- > no webhook, no persistent monitor. The honest default for ours-on-Codex is
5
- > **session-only**: the `ours` skill and the `~/.codex/AGENTS.md` pointer tell the agent to
6
- > check `get_messages` when it goes live and whenever it expects a reply. This directory is
7
- > an **opt-in** for people who want an external always-on wake and accept that it is bolted
8
- > on, outside Codex's lifecycle — not a Codex feature.
9
-
10
- ## What it is
11
-
12
- A small adaptation of `@ours.network/connector`'s reference gateway
13
- (`connector-reference-handler.mjs`). Same webhook contract and per-identity coalescing +
14
- backstop, but the DRAIN spawns **`codex exec`** — Codex's real non-interactive mode — with a
15
- prompt to bind the woken identity and read/act on ours mail via the `ours` MCP tools.
16
-
17
- ```
18
- ours-mcp watch <id> ──▶ connector-watch.sh ──HMAC POST──▶ codex-exec-gateway.mjs
19
- (notifications.log) (OBSERVE, per id) /webhooks/ (WAKE + DRAIN)
20
- ours-wake │
21
- ▼
22
- codex exec --sandbox workspace-write
23
- "<drain prompt>" → ours MCP tools → get_messages → acts
24
- ```
25
-
26
- - **OBSERVE** — reuse the connector's `connector-watch.sh` (one `ours-mcp watch <id>` per
27
- identity, non-binding, non-draining; pokes the gateway on each new message).
28
- - **WAKE + DRAIN** — this file. HMAC-verifies the poke, then runs a **headless Codex** bound
29
- to that identity (its sole drainer — ours binding is exclusive per identity).
30
-
31
- ## Requirements
32
-
33
- - A Codex **API key** for automation (e.g. `CODEX_API_KEY`) — `codex exec` is non-interactive.
34
- - The ours daemon running and the `ours` MCP server registered in `~/.codex/config.toml`
35
- (the plugin's `install.sh` does this).
36
- - A supervisor for **two always-on processes** you run yourself: the connector watcher and
37
- this gateway. Neither is started by default.
38
-
39
- ## Run it
40
-
41
- ```sh
42
- export CONNECTOR_IDENTITIES="Agent1 Agent2" # identities to drive
43
- export CONNECTOR_HMAC_SECRET="$(openssl rand -hex 32)" # SAME secret both ends
44
- export CONNECTOR_WEBHOOK_URL="http://localhost:8644/webhooks/ours-wake"
45
- export CODEX_API_KEY="<your key>" # for headless codex exec
46
-
47
- bash <connector>/connector-watch.sh & # OBSERVE (per identity)
48
- node ./codex-exec-gateway.mjs # WAKE + DRAIN via `codex exec`
49
- ```
50
-
51
- ## Config (env, all overridable)
52
-
53
- | var | default | purpose |
54
- |---|---|---|
55
- | `CONNECTOR_IDENTITIES` | `Peer` | space-separated identities this gateway drains |
56
- | `CONNECTOR_HMAC_SECRET` | — | shared HMAC secret (must match the watcher; no default) |
57
- | `CONNECTOR_WEBHOOK_URL` | `http://localhost:8644/webhooks/ours-wake` | webhook the watcher pokes |
58
- | `CONNECTOR_EVENT` | `ours_wake` | event name (header + body) |
59
- | `CONNECTOR_BACKSTOP_SECS` | `420` | per-identity missed-wake backstop interval |
60
- | `CODEX_BIN` | `codex` | Codex CLI binary |
61
- | `CODEX_SANDBOX` | `workspace-write` | `codex exec --sandbox` mode |
62
-
63
- The gateway **refuses to start** unless `CONNECTOR_HMAC_SECRET` is a non-default value.
64
-
65
- ## Caveats
66
-
67
- - Each wake is a **fresh Codex invocation** — there is no persistent session state between
68
- wakes beyond what ours + the workspace persist. Cost/latency scale with wake volume.
69
- - This runs Codex with a real API key and a writable sandbox. Review the drain prompt and
70
- the sandbox mode before pointing it at anything sensitive.
71
- - If you don't need external wake, don't run this — session-only reactivity is the default
72
- and needs nothing here.
@@ -1,113 +0,0 @@
1
- #!/usr/bin/env node
2
- // OPTIONAL, NON-NATIVE reactivity gateway for the OpenAI Codex CLI.
3
- //
4
- // ┌─────────────────────────────────────────────────────────────────────────────────┐
5
- // │ THIS IS NOT NATIVE CODEX REACTIVITY. Codex is a session/invocation CLI: no │
6
- // │ daemon, no webhook, no persistent monitor. This gateway lives OUTSIDE Codex's own │
7
- // │ lifecycle — you supervise it as an always-on process. On each ours wake it drives │
8
- // │ Codex HEADLESSLY via `codex exec "<drain prompt>"`, which is Codex's real │
9
- // │ non-interactive mode and needs an API key (e.g. CODEX_API_KEY) for automation. │
10
- // │ The DEFAULT ours-on-Codex reactivity is session-only (the agent checks │
11
- // │ get_messages when live / when it expects a reply). Use this only if you want an │
12
- // │ external always-on wake mechanism and accept that it is bolted on, not native. │
13
- // └─────────────────────────────────────────────────────────────────────────────────┘
14
- //
15
- // It is a small adaptation of @ours.network/connector's connector-reference-handler.mjs:
16
- // same HMAC-verified webhook contract, same per-identity coalescing + backstop, but the
17
- // per-identity DRAIN spawns `codex exec` with a prompt to read + act on ours mail (via the
18
- // ours MCP tools that install.sh registered in ~/.codex/config.toml), instead of the
19
- // inline JSON-RPC get_messages drain.
20
- //
21
- // Pair it with the connector's watcher for OBSERVE:
22
- // bash <connector>/connector-watch.sh # ours-mcp watch <id> → HMAC POST per new message
23
- // This file is the WAKE+DRAIN side.
24
- //
25
- // SOLE-DRAINER per identity: ours binding is exclusive per identity, so the codex exec run
26
- // bound to <id> is the only drainer of <id>. N identities = N sole-drained inboxes on ONE
27
- // shared ours daemon.
28
- //
29
- // Contract (config-overridable, must match the connector):
30
- // POST <CONNECTOR_WEBHOOK_URL> body: {"event_type":"<CONNECTOR_EVENT>","event":"<CONNECTOR_EVENT>","identity":"<id>"}
31
- // header: X-GitHub-Event: <CONNECTOR_EVENT>
32
- // header: X-Hub-Signature-256: sha256=<hex HMAC-SHA256(body, CONNECTOR_HMAC_SECRET)>
33
- // reply: <CONNECTOR_WEBHOOK_OK_CODE> (200) accept; 401 bad signature; 400 unknown identity.
34
- // Refuses to start unless CONNECTOR_HMAC_SECRET is set to a non-default value.
35
- import http from 'node:http';
36
- import crypto from 'node:crypto';
37
- import { spawn } from 'node:child_process';
38
-
39
- process.on('unhandledRejection', e => console.error('[codex-gw] unhandledRejection:', e?.message || e));
40
- process.on('uncaughtException', e => console.error('[codex-gw] uncaughtException:', e?.message || e));
41
-
42
- const WURL = new URL(process.env.CONNECTOR_WEBHOOK_URL || 'http://localhost:8644/webhooks/ours-wake');
43
- const URL_PATH = WURL.pathname, PORT = Number(WURL.port || 8644);
44
- const SECRET = process.env.CONNECTOR_HMAC_SECRET || '';
45
- if (!SECRET || SECRET === 'CHANGE_ME_local_webhook_hmac') {
46
- console.error('[codex-gw] refusing to start: set CONNECTOR_HMAC_SECRET to a non-default value ' +
47
- '(missing or the placeholder default is insecure — anyone could forge a wake).');
48
- process.exit(1);
49
- }
50
- const OK = Number(process.env.CONNECTOR_WEBHOOK_OK_CODE || 200);
51
- // Codex non-interactive binary + args. `codex exec` is Codex's headless mode; we run with a
52
- // workspace-write sandbox so the agent can act, and pass the drain prompt as the final arg.
53
- const CODEX_BIN = process.env.CODEX_BIN || 'codex';
54
- const CODEX_SANDBOX = process.env.CODEX_SANDBOX || 'workspace-write';
55
- const IDENTITIES = new Set((process.env.CONNECTOR_IDENTITIES || process.env.CONNECTOR_IDENTITY || 'Peer').split(/\s+/).filter(Boolean));
56
- const BACKSTOP_MS = Number(process.env.CONNECTOR_BACKSTOP_SECS || 420) * 1000;
57
-
58
- const state = new Map(); // id -> {draining, again}
59
- for (const id of IDENTITIES) state.set(id, { draining: false, again: false });
60
-
61
- // The prompt Codex runs headlessly. It leans on the ours MCP tools (registered in
62
- // ~/.codex/config.toml) + the ours skill: bind <id>, then read and act on new mail.
63
- function drainPrompt(id) {
64
- return `New ours.network mail arrived for identity "${id}". Use the ours skill and the ` +
65
- `ours MCP tools: bind that identity with choose_identity({ name: "${id}" }) if it is not ` +
66
- `already bound, then call get_messages to read the new message(s) and act on them. Reply ` +
67
- `over ours (send_message) if a reply is expected. Do not adopt the identity's persona.`;
68
- }
69
-
70
- function codexExec(id) { // one headless Codex run bound to <id> (its sole drainer)
71
- return new Promise((resolve) => {
72
- const args = ['exec', '--sandbox', CODEX_SANDBOX, drainPrompt(id)];
73
- const proc = spawn(CODEX_BIN, args, { env: { ...process.env }, stdio: ['ignore', 'pipe', 'pipe'] });
74
- let out = '';
75
- proc.stdout.on('data', d => { out += d; });
76
- proc.stderr.on('data', d => console.error(`[codex-gw:${id}] ${String(d).trimEnd()}`));
77
- proc.on('error', e => { console.error(`[codex-gw:${id}] codex spawn error:`, e.message); resolve(''); });
78
- proc.on('close', code => {
79
- if (out.trim()) console.log(`[codex-gw:${id}] codex exec (rc=${code}):\n${out.trim()}`);
80
- resolve(out);
81
- });
82
- });
83
- }
84
-
85
- async function drain(id) { // coalesced per-identity
86
- const s = state.get(id); if (!s) return;
87
- if (s.draining) { s.again = true; return; }
88
- s.draining = true;
89
- do {
90
- s.again = false;
91
- try { await codexExec(id); } catch (e) { console.error(`[codex-gw:${id}] drain error:`, e?.message || e); }
92
- } while (s.again);
93
- s.draining = false;
94
- }
95
-
96
- http.createServer((req, res) => {
97
- if (req.method !== 'POST' || req.url !== URL_PATH) { res.writeHead(404).end(); return; }
98
- let body = ''; req.on('data', c => body += c); req.on('end', () => {
99
- const sig = (req.headers['x-hub-signature-256'] || '').replace(/^sha256=/, '');
100
- const good = crypto.createHmac('sha256', SECRET).update(body).digest('hex');
101
- const okSig = sig.length === good.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(good));
102
- if (!okSig) { res.writeHead(401).end('bad sig'); return; }
103
- let id; try { id = JSON.parse(body).identity; } catch {}
104
- if (!id || !IDENTITIES.has(id)) { res.writeHead(400).end('unknown identity'); return; }
105
- res.writeHead(OK).end(); // ack fast; drain async + coalesced
106
- drain(id).catch(e => console.error(`[codex-gw:${id}] drain error`, e));
107
- });
108
- }).listen(PORT, () => console.log(
109
- `[codex-gw] NON-NATIVE codex-exec gateway on :${PORT}${URL_PATH} — sole-drainer for [${[...IDENTITIES].join(', ')}]`));
110
-
111
- // per-identity missed-wake backstop (coalesced with wakes; each run is a fresh codex exec
112
- // that no-ops cheaply if there is no new mail).
113
- setInterval(() => { for (const id of IDENTITIES) drain(id).catch(() => {}); }, BACKSTOP_MS);