Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,9 @@ $agmsg # Codex, Gemini CLI, Antigravity

`spawn <type> <name>` は事前に `<name>` を参加させ、actasのスラッシュコマンド(`/<your-command> actas <name>`、インストールしたコマンド名に合わせる)を初期プロンプトとして対象のCLIを起動する。現在のセッションが**tmux**内であれば新しいペイン(`--window` で新しいウィンドウ、`--split h|v` で分割方向)を開き、そうでなければ新しい**OSターミナル**ウィンドウを開く。

`--boot-prompt <text>` を渡すと、新しいエージェントに初期タスクを渡せる — ブートプロンプトはactasのスラッシュコマンドに続けて(改行区切りで)指定したテキストになるので、エージェントは同じ最初のターンでアイデンティティを主張し**かつ**タスクに着手する。Monitorを持たず、アイドルになった後の `send` メッセージに気づくことが決してない**codex**ピアに対してワンショットのゴールを渡す唯一の方法がこれだ。
`--boot-prompt <text>` を渡すと、新しいエージェントに初期タスクを渡せる — ブートプロンプトはactasのスラッシュコマンドに続けて(改行区切りで)指定したテキストになるので、エージェントは同じ最初のターンでアイデンティティを主張し**かつ**タスクに着手する。
これは**codex**ピアへワンショットのゴールを確実に渡す方法である。
アイドル中に後続メッセージを受信するには、`mode monitor` への明示的なオプトインが必要になる。

デフォルトでは `spawn` は**新しいエージェントが実際にリッスンし始めるまでブロックする** — ウォッチャーがアタッチし、レディネスの目印に触れる — その後 `status=ready` を表示するので、`spawn` が返ってきた瞬間にエージェントの起動直後の空白時間を気にせず作業を送れる。フックアンドフォーゲットなら `--no-wait`、待機時間の上限を決めたいなら `--ready-timeout <secs>`(デフォルト90、タイムアウト時は `status=timeout` を表示して終了コード3、呼び出し側は再spawnできる)を使う。Codexはこの待機をスキップする(Monitorがないため)。

Expand Down Expand Up @@ -286,9 +288,16 @@ despawnは指定されたメンバーにのみ作用する — `despawn` を実
$agmsg — または /skills → agmsg
```

Codexは `mode monitor` を**ベータ**のapp-serverブリッジとしてサポートし、加えて `mode turn` と `mode off` にも対応している。
Codexは `mode monitor` を**ベータ**のイベント駆動受信としてサポートし、加えて `mode turn` と `mode off` にも対応している。

> ⚠️ **monitorベータはCodexの起動方法を変える — 理解した上でのみオプトインすること。** CodexにはMonitorツールがないため、`mode monitor` はインタラクティブシェル内で `codex` をagmsgのmonitorシム経由にルーティングするシェル関数を表示する。monitorモードのプロジェクトでは、このシムがインタラクティブな起動を、受信したagmsgメッセージを現在のCodexスレッドのターンに変換するブリッジ経由にルーティングする。`codex exec` とmonitor対象外のプロジェクトは実物のCodexにそのまま通る。これは実験的なCodex app-serverの挙動に依存しており、既知の粗さがある(TUIを閉じるとオーファンが残る — #149、プロジェクトごとに1アイデンティティのみ — #150)。
> ⚠️ **monitorは、1つのCodexタスクを無人で継続させる明示的なオプトインである。**
> 受信箱が空の間はシェルだけで待機し、モデルのターンを作らない。
> 未読が届いた時だけ同じタスクを再開し、そのタスクが最初のツール呼び出しで公式の `inbox.sh` を実行する。
> 本文の取得と既読化、実作業、検証、返信は、再開されたタスクだけが担う。
> ACKだけのメッセージには返信せず、無限往復を防ぐ。
> heartbeat、cron、定期ポーリングによる自動実行は作らない。
> 既存の安全境界と承認境界は維持される。
> 利用可能な場合は、可視のapp-serverブリッジを優先する。

グローバルなPATHシムを好むなら、`~/.agents/skills/<cmd>/scripts/drivers/types/codex/codex-shim-install.sh install` を実行し、`~/.agents/bin` を実物のCodexバイナリより前にPATHに置く。`~/.agents/skills/<cmd>/scripts/drivers/types/codex/codex-monitor.sh` で直接起動することもできる。Codexのサンドボックスはスキルの `db/`、`teams/`、`run/` ディレクトリへの書き込みを許可する必要がある — `~/.codex/config.toml` が存在する場合、`install.sh` がその `writable_roots` を設定する。セットアップの詳細と内部動作: [docs/codex-monitor-beta.md](docs/codex-monitor-beta.md)。

Expand Down
21 changes: 14 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ Where `actas` switches *this* session to a different role, `spawn` brings up a *

`spawn <type> <name>` pre-joins `<name>`, then launches the target CLI with the actas slash command (`/<your-command> actas <name>`, matching your install command name) as its initial prompt. If the current session is inside **tmux**, it opens in a new pane (or `--window` for a new window, `--split h|v` for the direction); otherwise it opens a new **OS terminal** window.

Pass `--boot-prompt <text>` to hand the new agent an initial task: the boot prompt becomes the actas slash command followed (newline-separated) by your text, so the agent claims its identity **and** acts on the task in the same first turn. This is the only way to give a one-shot goal to a **codex** peer, which has no Monitor and so never notices a message you `send` after it goes idle.
Pass `--boot-prompt <text>` to hand the new agent an initial task: the boot prompt becomes the actas slash command followed (newline-separated) by your text, so the agent claims its identity **and** acts on the task in the same first turn. This is the reliable way to give a one-shot goal to a **codex** peer; receiving later messages while idle requires explicit `mode monitor` opt-in.

By default `spawn` **blocks until the new agent is actually listening** — its watcher attaches and touches a readiness sentinel — then prints `status=ready`, so you can send work the moment `spawn` returns without losing it to the agent's cold start. Use `--no-wait` for fire-and-forget, or `--ready-timeout <secs>` to bound the wait (default 90; on timeout it prints `status=timeout` and exits 3 so a caller can re-spawn). Codex skips the wait (it has no Monitor).

Expand Down Expand Up @@ -248,6 +248,7 @@ How incoming messages reach your agent. Pick one at first join via the prompt, o
| **`monitor`** (default on Claude Code) | SessionStart hook → Monitor tool → blocking SQLite stream | ~5s | Claude Code users wanting real-time push |
| **`turn`** (default on Codex / Copilot CLI / OpenCode) | Stop hook fires `check-inbox.sh` between assistant turns | until your next interaction | Codex / Copilot CLI / OpenCode (no Monitor tool); Claude Code users on a quieter loop |
| **`both`** | monitor primary, turn as per-session safety net | ~5s; falls back to turn-end on watcher failure | belt-and-suspenders |
| **`scheduled`** (Codex only) | native ChatGPT Scheduled task returning to the same task | 2 min, then 15 min, then 1 hour | unattended Codex follow-up without a relay |
| **`off`** | no automatic delivery | manual `/agmsg` only | minimalists |

### Picking a mode
Expand All @@ -256,6 +257,7 @@ How incoming messages reach your agent. Pick one at first join via the prompt, o
/agmsg mode monitor — switch this project to real-time push (Claude Code)
/agmsg mode turn — switch to between-turns checking
/agmsg mode both — monitor with turn as a safety net
/agmsg mode scheduled — native ChatGPT Scheduled monitoring in this task
/agmsg mode off — manual /agmsg only
/agmsg mode — show current mode
```
Expand Down Expand Up @@ -300,11 +302,15 @@ The command updates `db/config.yaml`, rewrites the project's hook entries, and p
$agmsg — or /skills → agmsg
```

Codex supports `mode monitor` as a **beta** app-server bridge, plus `mode turn` and `mode off`.
Codex supports `mode scheduled` through a native ChatGPT Scheduled task, plus
`mode monitor` as a **beta** visible app-server receiver, `mode turn`, and
`mode off`.

> ⚠️ **The monitor beta changes how Codex starts — opt in only if you understand it.** Codex has no Monitor tool, so `mode monitor` prints a shell function that makes `codex` route through agmsg's monitor shim in your interactive shell. In monitor-mode projects the shim routes interactive launches through a bridge that turns incoming agmsg messages into turns on the current Codex thread; `codex exec` and non-monitor projects pass straight through to the real Codex. It depends on experimental Codex app-server behavior and has known rough edges (orphans on TUI close — #149; one identity per project — #150).
> ⚠️ **Monitor is active only when a visible app-server bridge attaches to the persisted Codex task.** Background `codex exec resume` delivery is prohibited because a successful CLI turn can consume and answer mail without displaying the handling in Codex Desktop. When no visible bridge is available, agmsg keeps mail unread and downgrades the effective mode to `turn`. The separate `scheduled` path uses only ChatGPT's native Scheduled-task feature; neither path creates a heartbeat, cron job, launchd job, Desktop relay, or app restart.

If you prefer a global PATH shim, run `~/.agents/skills/<cmd>/scripts/drivers/types/codex/codex-shim-install.sh install` and put `~/.agents/bin` before the real Codex binary on PATH. You can also launch with `~/.agents/skills/<cmd>/scripts/drivers/types/codex/codex-monitor.sh`. Codex sandboxing must allow writes to the skill's `db/`, `teams/`, and `run/` dirs — `install.sh` configures those `writable_roots` when `~/.codex/config.toml` exists. Setup notes and internals: [docs/codex-monitor-beta.md](docs/codex-monitor-beta.md).
Run `$agmsg scheduled start <role>` to create the native adaptive schedule in
the current task. See [docs/codex-scheduled-monitor.md](docs/codex-scheduled-monitor.md).
If you prefer the beta bridge and a global PATH shim, run `~/.agents/skills/<cmd>/scripts/drivers/types/codex/codex-shim-install.sh install` and put `~/.agents/bin` before the real Codex binary on PATH. You can also launch with `~/.agents/skills/<cmd>/scripts/drivers/types/codex/codex-monitor.sh`. Codex sandboxing must allow writes to the skill's `db/`, `teams/`, and `run/` dirs — `install.sh` configures those `writable_roots` when `~/.codex/config.toml` exists. Bridge setup notes and internals: [docs/codex-monitor-beta.md](docs/codex-monitor-beta.md).

### GitHub Copilot CLI

Expand Down Expand Up @@ -495,9 +501,10 @@ writable_roots = [
]
```

Codex only supports `mode turn` and `mode off`; it does not have Claude Code's
Monitor tool. The sandbox allowlist is still required for writes performed by
manual `$agmsg` commands and turn-end inbox checks.
Codex supports manual, `turn`, native `scheduled`, and beta visible-bridge
workflows. It does not have Claude Code's Monitor tool. The sandbox allowlist is
required for writes performed by manual commands, turn-end inbox checks, and
the native Scheduled state machine.

Some Codex runtimes or automations may inject a managed permission profile for a
single run. In that case, the run-specific writable roots must also include the
Expand Down
94 changes: 47 additions & 47 deletions docs/codex-monitor-beta.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
# Codex Monitor Beta

Codex does not expose Claude Code's Monitor tool. agmsg's Codex monitor beta
approximates the same experience by launching Codex through an app-server bridge.
Codex does not expose Claude Code's Monitor tool. agmsg's Codex monitor beta can
deliver mail only through an app-server bridge that renders the handling in the
same visible Codex thread. The last explicit `actas` role is rebound from
SessionStart after restart or compaction.

> ⚠️ **Experimental beta — read before enabling.** This changes how Codex starts.
> Enabling monitor mode prints a shell function that makes `codex` route through
> agmsg's monitor shim in your interactive shell. In monitor-mode projects the
> shim re-routes interactive launches through an app-server bridge; everywhere
> else it passes straight through. **Only enable this if you are comfortable with
> the `codex` command being intercepted in that shell.** It also depends on Codex
> app-server behavior and may break as Codex changes. Known rough edges:
> enabling monitor takes effect only after you **restart Codex and send your
> first message** — the SessionStart hook fires on the first turn, not the
> moment Codex opens, so the bridge is absent until you interact once; an
> already-running session stays unmonitored until you restart it (#151); the
> bridge is not torn down when you close the TUI (orphans linger until reboot
> or `mode off`/manual kill, see #149); and only one Codex identity per project
> is supported (#150).
> Monitor mode is active only after a visible app-server bridge attaches to the
> selected thread. If no bridge is available, agmsg keeps mail unread and changes
> the effective mode to `turn`. Background `codex exec resume`, cron, heartbeat,
> and ad hoc scheduled polling are prohibited because they can process mail
> without showing the work to the human operator. The separate native ChatGPT
> Scheduled path returns to the same task and is documented in
> [codex-scheduled-monitor.md](codex-scheduled-monitor.md).

## Quick Start

Expand All @@ -28,10 +24,18 @@ Enable monitor mode in a project:

The command:

1. Enables agmsg's Codex SessionStart/SessionEnd hooks for the project.
2. Prints a shell function that routes interactive `codex` launches through the
1. Enables Codex SessionStart/SessionEnd hooks plus the visible Stop-hook
fallback for the project.
2. Persists the last explicit `actas` role so SessionStart can rebind it.
3. After `actas` binds a concrete thread, attaches the visible app-server bridge
for that exact team/role/thread tuple or downgrades to `turn`.
4. Prints a shell function that routes interactive `codex` launches through the
monitor shim.

The bridge may observe unread count and high-water id, but it does not read
message bodies or mark messages read. The visible persisted thread owns the
official inbox read, substantive work, progress reporting, and any reply.

The Codex sandbox must allow writes to the installed skill's runtime state:

```text
Expand Down Expand Up @@ -60,6 +64,15 @@ codex
In monitor-mode projects, the function routes interactive Codex launches through
the bridge. Outside monitor-mode projects, it passes through to the real Codex.

When Codex.app is opened normally, SessionStart restores the last `actas` role.
If no visible app-server is available, the effective delivery mode becomes
`turn`; the Stop hook checks the inbox on a later visible assistant turn. agmsg
does not claim that autonomous monitoring is active in this state.

`mode off`, `mode turn`, `drop`/`reset`, and SessionEnd stop the matching
receiver and remove its LaunchAgent/runtime files. No cron, heartbeat, or
scheduled polling automation is created for this path.

## Optional PATH Shim

If you prefer the previous global PATH shim setup, install it explicitly:
Expand Down Expand Up @@ -138,9 +151,11 @@ connect to the unix socket (EPERM). Instead:
sandbox, reads the request file and starts `codex-bridge.js`.
3. The bridge connects to the same app-server over **WebSocket-over-UDS**,
resumes the thread, and arms `watch-once.sh` via the app-server `process/spawn`
API (which polls the agmsg DB for unread rows, `read_at IS NULL`).
4. On an unread message it inlines the text into a `turn/start` on that thread —
surfacing it in the live Codex TUI — then re-arms after the turn ends.
API (which checks the agmsg DB for unread rows, `read_at IS NULL`).
4. On unread state it starts a turn on that thread with an instruction to run
the official `inbox.sh`. The bridge does not read the message body or mark it
read on the normal path. The visible Codex turn owns reading, substantive
work, verification, and any reply, then the bridge re-arms after the turn.

Turns are serialized (one per thread): a message that arrives while a turn is
running stays unread and is delivered after the turn completes. The turn ends
Expand Down Expand Up @@ -172,10 +187,11 @@ flowchart TD
watch --> db[("agmsg SQLite DB (read_at IS NULL)")]
db --> unread{"Unread message?"}
unread -- "no (timeout)" --> watch
unread -- "yes" --> inbox["inline unread inbox text"]
inbox --> turn["turn/start on the thread"]
unread -- "yes" --> turn["turn/start with official inbox instruction"]
turn --> tui["Current Codex TUI thread"]
tui --> ended["turn ends: completed / idle / watchdog"]
tui --> inbox["official inbox.sh reads and marks messages"]
inbox --> work["substantive work, verification, and reply"]
work --> ended["turn ends: completed / idle / watchdog"]
ended --> watch
```

Expand Down Expand Up @@ -204,40 +220,24 @@ shape: a short-interval scheduler that runs a heavyweight agent as the poller,
with no cheap no-op path, so empty inboxes still pay the full cost — and Codex
Desktop's per-session UI/log accumulation amplifies it.

### Gate with `watch-once.sh`, launch the agent only on a hit
### `watch-once.sh` is not a Codex Desktop delivery fallback

agmsg already ships the cheap gate this needs. `watch-once.sh` is a shell-only,
one-shot inbox oracle — no agent, no Codex turn. It is the same primitive the
Codex monitor bridge uses (see [Bridge Mechanics](#bridge-mechanics)) to avoid
starting a turn on an empty inbox.
`watch-once.sh` is a shell-only, one-shot inbox oracle. The visible app-server
bridge uses it to avoid starting a turn on an empty inbox.

```text
exit 0 unread inbound exists (prints: status=pending count=<n> max_id=<id>)
exit 2 nothing pending (prints: status=timeout)
exit 1 configuration / runtime error
```

Two-stage worker — the shell gate decides whether the expensive agent runs:

```bash
#!/usr/bin/env bash
set -euo pipefail
SKILL=~/.agents/skills/agmsg/scripts
PROJECT="/path/to/project"

# 1. Cheap shell-only check. --timeout 0 makes it a single poll, then exit.
# --team/--name scope the gate to one identity (matches the single-flight
# key below, and disambiguates when the same agent name exists in two teams).
if "$SKILL/watch-once.sh" "$PROJECT" codex --team myteam --name myagent --timeout 0; then
# 2. Unread exists — only now pay for a full Codex/Claude session.
codex exec "Handle the new agmsg messages for this project."
fi
# exit 2 (nothing pending) falls through and the worker ends cheaply.
```
Do not combine it with a scheduler and `codex exec` as a substitute for Codex
Desktop delivery. That path cannot guarantee that the received message,
progress, reply, and result appear in the user's visible thread.

### Defense in depth

For an unattended worker, layer these on top of the gate:
For a separately authorized non-Desktop worker, layer these on top of the gate:

- **Single-flight lock per `(team, agent)`** so overlapping ticks don't stack
concurrent agents:
Expand All @@ -263,7 +263,7 @@ For an unattended worker, layer these on top of the gate:

1. Make the worker inactive / unschedule the `cron` job so it stops spawning.
2. Back off delivery: `delivery.sh set turn codex "$PROJECT"` (or `off`) to stop
monitor-driven launches.
monitor delivery.
3. Kill stale monitors / spawned sessions and any orphaned bridge
(`mode off` tears the bridge down; see #149).
4. Inspect Codex Desktop log-DB bloat: `~/.codex/logs_*.sqlite` and its WAL.
Expand Down
Loading